diff options
32 files changed, 242 insertions, 215 deletions
@@ -32,20 +32,25 @@ load "#{MRUBY_ROOT}/tasks/benchmark.rake" load "#{MRUBY_ROOT}/tasks/gitlab.rake" +def install_D(src, dst) + opts = { :verbose => $verbose } + FileUtils.rm_f dst, opts + FileUtils.mkdir_p File.dirname(dst), opts + FileUtils.cp src, dst, opts +end + ############################## # generic build targets, rules task :default => :all bin_path = ENV['INSTALL_DIR'] || "#{MRUBY_ROOT}/bin" -FileUtils.mkdir_p bin_path, { :verbose => $verbose } depfiles = MRuby.targets['host'].bins.map do |bin| install_path = MRuby.targets['host'].exefile("#{bin_path}/#{bin}") source_path = MRuby.targets['host'].exefile("#{MRuby.targets['host'].build_dir}/bin/#{bin}") file install_path => source_path do |t| - FileUtils.rm_f t.name, { :verbose => $verbose } - FileUtils.cp t.prerequisites.first, t.name, { :verbose => $verbose } + install_D t.prerequisites.first, t.name end install_path @@ -78,8 +83,7 @@ MRuby.each_target do |target| install_path = MRuby.targets['host'].exefile("#{bin_path}/#{bin}") file install_path => exec do |t| - FileUtils.rm_f t.name, { :verbose => $verbose } - FileUtils.cp t.prerequisites.first, t.name, { :verbose => $verbose } + install_D t.prerequisites.first, t.name end depfiles += [ install_path ] elsif target == MRuby.targets['host-debug'] @@ -87,8 +91,7 @@ MRuby.each_target do |target| install_path = MRuby.targets['host-debug'].exefile("#{bin_path}/#{bin}") file install_path => exec do |t| - FileUtils.rm_f t.name, { :verbose => $verbose } - FileUtils.cp t.prerequisites.first, t.name, { :verbose => $verbose } + install_D t.prerequisites.first, t.name end depfiles += [ install_path ] end diff --git a/benchmark/bm_app_lc_fizzbuzz.rb b/benchmark/bm_app_lc_fizzbuzz.rb index 26283cc3f..de8268577 100644 --- a/benchmark/bm_app_lc_fizzbuzz.rb +++ b/benchmark/bm_app_lc_fizzbuzz.rb @@ -48,5 +48,4 @@ answer = to_array(solution).map do |p| to_string(p) end -answer_str = answer.to_a -# puts answer_str +# puts answer diff --git a/include/mruby.h b/include/mruby.h index 8c8360784..2f2d98677 100644 --- a/include/mruby.h +++ b/include/mruby.h @@ -239,8 +239,8 @@ typedef struct mrb_state { #endif mrb_sym symidx; - struct kh_n2s *name2sym; /* symbol hash */ struct symbol_name *symtbl; /* symbol table */ + mrb_sym symhash[256]; size_t symcapa; #ifdef MRB_ENABLE_DEBUG_HOOK diff --git a/include/mruby/data.h b/include/mruby/data.h index 31d6bd8fb..415684342 100644 --- a/include/mruby/data.h +++ b/include/mruby/data.h @@ -39,10 +39,11 @@ MRB_API struct RData *mrb_data_object_alloc(mrb_state *mrb, struct RClass* klass #define Data_Wrap_Struct(mrb,klass,type,ptr)\ mrb_data_object_alloc(mrb,klass,ptr,type) -#define Data_Make_Struct(mrb,klass,strct,type,sval,data) do { \ +#define Data_Make_Struct(mrb,klass,strct,type,sval,data_obj) do { \ + (data_obj) = Data_Wrap_Struct(mrb,klass,type,NULL);\ (sval) = mrb_malloc(mrb, sizeof(strct)); \ { static const strct zero = { 0 }; *(sval) = zero; };\ - (data) = Data_Wrap_Struct(mrb,klass,type,sval);\ + (data_obj)->data = (sval);\ } while (0) #define RDATA(obj) ((struct RData *)(mrb_ptr(obj))) diff --git a/include/mruby/string.h b/include/mruby/string.h index 3fe8295ff..6fe0556b0 100644 --- a/include/mruby/string.h +++ b/include/mruby/string.h @@ -439,6 +439,10 @@ void mrb_regexp_check(mrb_state *mrb, mrb_value obj); #define mrb_str_buf_cat(mrb, str, ptr, len) mrb_str_cat(mrb, str, ptr, len) #define mrb_str_buf_append(mrb, str, str2) mrb_str_cat_str(mrb, str, str2) +#ifdef MRB_UTF8_STRING +mrb_int mrb_utf8_len(const char *str, mrb_int byte_len); +#endif + MRB_END_DECL #endif /* MRUBY_STRING_H */ diff --git a/lib/mruby-core-ext.rb b/lib/mruby-core-ext.rb index 4c6d3ca76..08e6f6148 100644 --- a/lib/mruby-core-ext.rb +++ b/lib/mruby-core-ext.rb @@ -18,18 +18,20 @@ class String end # Compatible with 1.9 on 1.8 - def %(params) - if params.is_a?(Hash) - str = self.clone - params.each do |k, v| - str.gsub!("%{#{k}}") { v } - end - str - else - if params.is_a?(Array) - sprintf(self, *params) + unless (sprintf("%{a}", :a => 1) rescue false) + def %(params) + if params.is_a?(Hash) + str = self.clone + params.each do |k, v| + str.gsub!("%{#{k}}") { v } + end + str else - sprintf(self, params) + if params.is_a?(Array) + sprintf(self, *params) + else + sprintf(self, params) + end end end end @@ -37,17 +39,21 @@ end class Symbol # Compatible with 1.9 on 1.8 - def to_proc - proc { |obj, *args| obj.send(self, *args) } + unless method_defined?(:to_proc) + def to_proc + proc { |obj, *args| obj.send(self, *args) } + end end end module Enumerable # Compatible with 1.9 on 1.8 - def each_with_object(memo) - return to_enum :each_with_object, memo unless block_given? - each { |obj| yield obj, memo } - memo + unless method_defined?(:each_with_object) + def each_with_object(memo) + return to_enum :each_with_object, memo unless block_given? + each { |obj| yield obj, memo } + memo + end end end diff --git a/lib/mruby/build.rb b/lib/mruby/build.rb index 63125d4fa..4f81a970b 100644 --- a/lib/mruby/build.rb +++ b/lib/mruby/build.rb @@ -270,8 +270,11 @@ EOS def exefile(name) if name.is_a?(Array) name.flatten.map { |n| exefile(n) } - else + elsif File.extname(name).empty? "#{name}#{exts.executable}" + else + # `name` sometimes have (non-standard) extension (e.g. `.bat`). + name end end diff --git a/mrbgems/mruby-array-ext/mrblib/array.rb b/mrbgems/mruby-array-ext/mrblib/array.rb index 6096696cb..387bd6c90 100644 --- a/mrbgems/mruby-array-ext/mrblib/array.rb +++ b/mrbgems/mruby-array-ext/mrblib/array.rb @@ -266,7 +266,6 @@ class Array self end - NONE=Object.new ## # call-seq: # ary.fetch(index) -> obj diff --git a/mrbgems/mruby-bin-config/mrbgem.rake b/mrbgems/mruby-bin-config/mrbgem.rake index cca7423ac..b9ba0e42c 100644 --- a/mrbgems/mruby-bin-config/mrbgem.rake +++ b/mrbgems/mruby-bin-config/mrbgem.rake @@ -1,17 +1,3 @@ -module MRuby - class Build - def exefile(name) - if name.is_a?(Array) - name.flatten.map { |n| exefile(n) } - elsif name !~ /\./ - "#{name}#{exts.executable}" - else - name - end - end - end -end - MRuby.each_target do next if kind_of? MRuby::CrossBuild diff --git a/mrbgems/mruby-bin-debugger/tools/mrdb/cmdmisc.c b/mrbgems/mruby-bin-debugger/tools/mrdb/cmdmisc.c index 0a864567d..2fc661cdf 100644 --- a/mrbgems/mruby-bin-debugger/tools/mrdb/cmdmisc.c +++ b/mrbgems/mruby-bin-debugger/tools/mrdb/cmdmisc.c @@ -495,7 +495,7 @@ dbgcmd_quit(mrb_state *mrb, mrdb_state *mrdb) if (mrdb->dbg->xm == DBG_QUIT) { struct RClass *exc; - exc = mrb_define_class(mrb, "DebuggerExit", mrb_class_get(mrb, "Exception")); + exc = mrb_define_class(mrb, "DebuggerExit", mrb->eException_class); mrb_raise(mrb, exc, "Exit mrdb."); } return DBGST_PROMPT; diff --git a/mrbgems/mruby-bin-debugger/tools/mrdb/cmdrun.c b/mrbgems/mruby-bin-debugger/tools/mrdb/cmdrun.c index cb4c738fc..233c86cef 100644 --- a/mrbgems/mruby-bin-debugger/tools/mrdb/cmdrun.c +++ b/mrbgems/mruby-bin-debugger/tools/mrdb/cmdrun.c @@ -19,7 +19,7 @@ dbgcmd_run(mrb_state *mrb, mrdb_state *mrdb) if (dbg->xphase == DBG_PHASE_RUNNING){ struct RClass *exc; puts("Start it from the beginning."); - exc = mrb_define_class(mrb, "DebuggerRestart", mrb_class_get(mrb, "Exception")); + exc = mrb_define_class(mrb, "DebuggerRestart", mrb->eException_class); mrb_raise(mrb, exc, "Restart mrdb."); } } diff --git a/mrbgems/mruby-compiler/core/codegen.c b/mrbgems/mruby-compiler/core/codegen.c index a17272ba7..cc46f835f 100644 --- a/mrbgems/mruby-compiler/core/codegen.c +++ b/mrbgems/mruby-compiler/core/codegen.c @@ -2806,7 +2806,10 @@ codegen(codegen_scope *s, node *tree, int val) idx = new_sym(s, nsym(tree->car->cdr)); genop_2(s, OP_CLASS, cursp(), idx); body = tree->cdr->cdr->car; - if (!(nint(body->cdr->car) == NODE_BEGIN && body->cdr->cdr == NULL)) { + if (nint(body->cdr->car) == NODE_BEGIN && body->cdr->cdr == NULL) { + genop_1(s, OP_LOADNIL, cursp()); + } + else { idx = scope_body(s, body, val); genop_2(s, OP_EXEC, cursp(), idx); } @@ -2834,8 +2837,11 @@ codegen(codegen_scope *s, node *tree, int val) pop(); idx = new_sym(s, nsym(tree->car->cdr)); genop_2(s, OP_MODULE, cursp(), idx); - if (!(nint(tree->cdr->car->cdr->car) == NODE_BEGIN && - tree->cdr->car->cdr->cdr == NULL)) { + if (nint(tree->cdr->car->cdr->car) == NODE_BEGIN && + tree->cdr->car->cdr->cdr == NULL) { + genop_1(s, OP_LOADNIL, cursp()); + } + else { idx = scope_body(s, tree->cdr->car, val); genop_2(s, OP_EXEC, cursp(), idx); } @@ -2852,8 +2858,11 @@ codegen(codegen_scope *s, node *tree, int val) codegen(s, tree->car, VAL); pop(); genop_1(s, OP_SCLASS, cursp()); - if (!(nint(tree->cdr->car->cdr->car) == NODE_BEGIN && - tree->cdr->car->cdr->cdr == NULL)) { + if (nint(tree->cdr->car->cdr->car) == NODE_BEGIN && + tree->cdr->car->cdr->cdr == NULL) { + genop_1(s, OP_LOADNIL, cursp()); + } + else { idx = scope_body(s, tree->cdr->car, val); genop_2(s, OP_EXEC, cursp(), idx); } diff --git a/mrbgems/mruby-enum-ext/mrblib/enum.rb b/mrbgems/mruby-enum-ext/mrblib/enum.rb index fedf8b1ae..99b9cddba 100644 --- a/mrbgems/mruby-enum-ext/mrblib/enum.rb +++ b/mrbgems/mruby-enum-ext/mrblib/enum.rb @@ -205,7 +205,6 @@ module Enumerable ary.collect{|e,i| orig[i]} end - NONE = Object.new ## # call-seq: # enum.first -> obj or nil diff --git a/mrbgems/mruby-enumerator/mrblib/enumerator.rb b/mrbgems/mruby-enumerator/mrblib/enumerator.rb index 5697c6609..cbf53974a 100644 --- a/mrbgems/mruby-enumerator/mrblib/enumerator.rb +++ b/mrbgems/mruby-enumerator/mrblib/enumerator.rb @@ -121,7 +121,7 @@ class Enumerator @obj = obj @meth = meth - @args = args.dup + @args = args @fib = nil @dst = nil @lookahead = nil diff --git a/mrbgems/mruby-io/test/file.rb b/mrbgems/mruby-io/test/file.rb index 8d2be04c8..7b67ded26 100644 --- a/mrbgems/mruby-io/test/file.rb +++ b/mrbgems/mruby-io/test/file.rb @@ -1,16 +1,16 @@ ## -# IO Test +# File Test -assert('File', '15.2.21') do - File.class == Class +assert('File TEST SETUP') do + MRubyIOTestUtil.io_test_setup end -assert('File', '15.2.21.2') do - File.superclass == IO +assert('File', '15.2.21') do + assert_equal Class, File.class end -assert('File TEST SETUP') do - MRubyIOTestUtil.io_test_setup +assert('File', '15.2.21.2') do + assert_equal IO, File.superclass end assert('File#initialize', '15.2.21.4.1') do @@ -27,7 +27,7 @@ assert('File#path', '15.2.21.4.2') do assert_equal $mrbtest_io_rfname, io.path io.close assert_equal $mrbtest_io_rfname, io.path - io.closed? + assert_true io.closed? end assert('File.basename') do diff --git a/mrbgems/mruby-io/test/gc_filedes.sh b/mrbgems/mruby-io/test/gc_filedes.sh deleted file mode 100644 index 6e5d1bbf1..000000000 --- a/mrbgems/mruby-io/test/gc_filedes.sh +++ /dev/null @@ -1,4 +0,0 @@ -#!/bin/sh - -ulimit -n 20 -mruby -e '100.times { File.open "'$0'" }' diff --git a/mrbgems/mruby-io/test/io.rb b/mrbgems/mruby-io/test/io.rb index e4a449696..881e94185 100644 --- a/mrbgems/mruby-io/test/io.rb +++ b/mrbgems/mruby-io/test/io.rb @@ -1,26 +1,6 @@ ## # IO Test -unless Object.respond_to? :assert_nothing_raised - def assert_nothing_raised(*exp) - ret = true - if $mrbtest_assert - $mrbtest_assert_idx += 1 - msg = exp.last.class == String ? exp.pop : "" - begin - yield - rescue Exception => e - msg = "#{msg} exception raised." - diff = " Class: <#{e.class}>\n" + - " Message: #{e.message}" - $mrbtest_assert.push([$mrbtest_assert_idx, msg, diff]) - ret = false - end - end - ret - end -end - assert('IO TEST SETUP') do MRubyIOTestUtil.io_test_setup $cr = MRubyIOTestUtil.win? ? 1 : 0 # "\n" include CR or not diff --git a/mrbgems/mruby-print/mrblib/print.rb b/mrbgems/mruby-print/mrblib/print.rb index fa83c47de..27567d858 100644 --- a/mrbgems/mruby-print/mrblib/print.rb +++ b/mrbgems/mruby-print/mrblib/print.rb @@ -52,9 +52,6 @@ module Kernel def printf(*args) raise NotImplementedError.new('printf not available') end - def sprintf(*args) - raise NotImplementedError.new('sprintf not available') - end else def printf(*args) __printstr__(sprintf(*args)) diff --git a/mrbgems/mruby-print/src/print.c b/mrbgems/mruby-print/src/print.c index e181b06e0..f7f99fc77 100644 --- a/mrbgems/mruby-print/src/print.c +++ b/mrbgems/mruby-print/src/print.c @@ -23,7 +23,6 @@ printstr(mrb_state *mrb, mrb_value obj) char* utf8 = RSTRING_PTR(obj); int wlen = MultiByteToWideChar(CP_UTF8, 0, utf8, mlen, NULL, 0); wchar_t* utf16 = (wchar_t*)mrb_malloc(mrb, (wlen+1) * sizeof(wchar_t)); - if (utf16 == NULL) return; if (MultiByteToWideChar(CP_UTF8, 0, utf8, mlen, utf16, wlen) > 0) { utf16[wlen] = 0; WriteConsoleW(GetStdHandle(STD_OUTPUT_HANDLE), diff --git a/mrbgems/mruby-sleep/src/mrb_sleep.c b/mrbgems/mruby-sleep/src/mrb_sleep.c index 0428f29eb..3f8ef90cf 100644 --- a/mrbgems/mruby-sleep/src/mrb_sleep.c +++ b/mrbgems/mruby-sleep/src/mrb_sleep.c @@ -51,7 +51,7 @@ mrb_f_sleep(mrb_state *mrb, mrb_value self) usleep(sec * 1000000); } else { - mrb_raise(mrb, E_ARGUMENT_ERROR, "time interval must be positive integer"); + mrb_raise(mrb, E_ARGUMENT_ERROR, "time interval must not be negative"); } #else mrb_int sec; @@ -60,7 +60,7 @@ mrb_f_sleep(mrb_state *mrb, mrb_value self) if (sec >= 0) { sleep(sec); } else { - mrb_raise(mrb, E_ARGUMENT_ERROR, "time interval must be positive integer"); + mrb_raise(mrb, E_ARGUMENT_ERROR, "time interval must not be negative"); } #endif end = time(0) - beg; @@ -94,7 +94,7 @@ mrb_f_usleep(mrb_state *mrb, mrb_value self) if (usec >= 0) { usleep(usec); } else { - mrb_raise(mrb, E_ARGUMENT_ERROR, "time interval must be positive integer"); + mrb_raise(mrb, E_ARGUMENT_ERROR, "time interval must not be negative integer"); } #ifdef _WIN32 diff --git a/mrbgems/mruby-sleep/test/sleep_test.rb b/mrbgems/mruby-sleep/test/sleep_test.rb index 349f169b3..f05b7a30b 100644 --- a/mrbgems/mruby-sleep/test/sleep_test.rb +++ b/mrbgems/mruby-sleep/test/sleep_test.rb @@ -1,36 +1,29 @@ -def run_with_catching_error &b - e = nil - begin - b.call - rescue => _e - e = _e - end - - return e -end - assert("sleep works") do - e = run_with_catching_error { sleep 1 } + assert_nothing_raised { sleep(1) } + assert_nothing_raised { sleep(0) } +end - assert_nil e +assert("sleep would accept non-negative float value") do + skip unless Object.const_defined?(:Float) + assert_nothing_raised { sleep(0.01) } + assert_nothing_raised { sleep(0.0) } + assert_nothing_raised { sleep(-0.0) } end -assert("sleep would not accept negative value") do - e = run_with_catching_error{ sleep(-1) } +assert("sleep would not accept negative integer value") do + assert_raise(ArgumentError) { sleep(-1) } +end - assert_not_equal e, nil - assert_equal e.class, ArgumentError +assert("sleep would not accept negative float value") do + skip unless Object.const_defined?(:Float) + assert_raise(ArgumentError) { sleep(-0.1) } end assert("usleep works") do - e = run_with_catching_error { usleep 100 } - - assert_nil e + assert_nothing_raised { usleep(100) } + assert_nothing_raised { usleep(0) } end assert("usleep would not accept negative value") do - e = run_with_catching_error{ usleep(-100) } - - assert_not_equal e, nil - assert_equal e.class, ArgumentError + assert_raise(ArgumentError) { usleep(-100) } end diff --git a/mrbgems/mruby-struct/src/struct.c b/mrbgems/mruby-struct/src/struct.c index 832583f35..c0ce71219 100644 --- a/mrbgems/mruby-struct/src/struct.c +++ b/mrbgems/mruby-struct/src/struct.c @@ -399,7 +399,7 @@ struct_aref_sym(mrb_state *mrb, mrb_value obj, mrb_sym id) return ptr[i]; } } - mrb_raisef(mrb, E_INDEX_ERROR, "'%S' is not a struct member", mrb_sym2str(mrb, id)); + mrb_name_error(mrb, id, "no member '%S' in struct", mrb_sym2str(mrb, id)); return mrb_nil_value(); /* not reached */ } diff --git a/mrbgems/mruby-symbol-ext/src/symbol.c b/mrbgems/mruby-symbol-ext/src/symbol.c index a992dbfce..215226502 100644 --- a/mrbgems/mruby-symbol-ext/src/symbol.c +++ b/mrbgems/mruby-symbol-ext/src/symbol.c @@ -1,6 +1,7 @@ #include <mruby.h> #include <mruby/khash.h> #include <mruby/array.h> +#include <mruby/string.h> typedef struct symbol_name { size_t len; @@ -45,7 +46,13 @@ static mrb_value mrb_sym_length(mrb_state *mrb, mrb_value self) { mrb_int len; +#ifdef MRB_UTF8_STRING + mrb_int byte_len; + const char *name = mrb_sym2name_len(mrb, mrb_symbol(self), &byte_len); + len = mrb_utf8_len(name, byte_len); +#else mrb_sym2name_len(mrb, mrb_symbol(self), &len); +#endif return mrb_fixnum_value(len); } diff --git a/mrbgems/mruby-symbol-ext/test/symbol.rb b/mrbgems/mruby-symbol-ext/test/symbol.rb index 63c1bd826..2c7a62b0c 100644 --- a/mrbgems/mruby-symbol-ext/test/symbol.rb +++ b/mrbgems/mruby-symbol-ext/test/symbol.rb @@ -7,9 +7,18 @@ assert('Symbol.all_symbols') do assert_equal foo, symbols end -assert("Symbol#length") do - assert_equal 5, :hello.size - assert_equal 5, :mruby.length +%w[size length].each do |n| + assert("Symbol##{n}") do + assert_equal 5, :hello.__send__(n) + assert_equal 4, :"aA\0b".__send__(n) + if "あ".size == 1 # enable MRB_UTF8_STRING? + assert_equal 8, :"こんにちは世界!".__send__(n) + assert_equal 4, :"aあ\0b".__send__(n) + else + assert_equal 22, :"こんにちは世界!".__send__(n) + assert_equal 6, :"aあ\0b".__send__(n) + end + end end assert("Symbol#capitalize") do diff --git a/mrbgems/mruby-time/src/time.c b/mrbgems/mruby-time/src/time.c index c3a0ac435..d70fb442c 100644 --- a/mrbgems/mruby-time/src/time.c +++ b/mrbgems/mruby-time/src/time.c @@ -263,12 +263,12 @@ time_alloc(mrb_state *mrb, double sec, double usec, enum mrb_timezone timezone) tm->sec = tsec; tm->usec = (time_t)llround((sec - tm->sec) * 1.0e6 + usec); if (tm->usec < 0) { - long sec2 = (long)NDIV(usec,1000000); /* negative div */ + long sec2 = (long)NDIV(tm->usec,1000000); /* negative div */ tm->usec -= sec2 * 1000000; tm->sec += sec2; } else if (tm->usec >= 1000000) { - long sec2 = (long)(usec / 1000000); + long sec2 = (long)(tm->usec / 1000000); tm->usec -= sec2 * 1000000; tm->sec += sec2; } diff --git a/mrblib/enum.rb b/mrblib/enum.rb index a97a26f97..9bd74e1c4 100644 --- a/mrblib/enum.rb +++ b/mrblib/enum.rb @@ -13,6 +13,8 @@ # @ISO 15.3.2 module Enumerable + NONE = Object.new + ## # Call the given block for each element # which is yield by +each+. Return false diff --git a/src/class.c b/src/class.c index dd5b65cc3..359bb4fea 100644 --- a/src/class.c +++ b/src/class.c @@ -1815,7 +1815,7 @@ mrb_define_alias(mrb_state *mrb, struct RClass *klass, const char *name1, const * show information on the thing we're attached to as well. */ -static mrb_value +mrb_value mrb_mod_to_s(mrb_state *mrb, mrb_value klass) { mrb_value str; diff --git a/src/string.c b/src/string.c index 52941473c..19962fb30 100644 --- a/src/string.c +++ b/src/string.c @@ -20,6 +20,7 @@ #include <mruby/class.h> #include <mruby/range.h> #include <mruby/string.h> +#include <mruby/numeric.h> #include <mruby/re.h> typedef struct mrb_shared_string { @@ -156,13 +157,6 @@ mrb_str_new(mrb_state *mrb, const char *p, size_t len) return mrb_obj_value(str_new(mrb, p, len)); } -/* - * call-seq: (Caution! NULL string) - * String.new(str="") => new_str - * - * Returns a new string object containing a copy of <i>str</i>. - */ - MRB_API mrb_value mrb_str_new_cstr(mrb_state *mrb, const char *p) { @@ -238,27 +232,36 @@ utf8len(const char* p, const char* e) return len; } -static mrb_int -utf8_strlen(mrb_value str, mrb_int len) +mrb_int +mrb_utf8_len(const char *str, mrb_int byte_len) { mrb_int total = 0; - char* p = RSTRING_PTR(str); - char* e = p; - if (RSTRING(str)->flags & MRB_STR_NO_UTF) { - return RSTRING_LEN(str); - } - e += len < 0 ? RSTRING_LEN(str) : len; - while (p<e) { + const char *p = str; + const char *e = p + byte_len; + + while (p < e) { p += utf8len(p, e); total++; } - if (RSTRING_LEN(str) == total) { - RSTRING(str)->flags |= MRB_STR_NO_UTF; - } return total; } -#define RSTRING_CHAR_LEN(s) utf8_strlen(s, -1) +static mrb_int +utf8_strlen(mrb_value str) +{ + mrb_int byte_len = RSTRING_LEN(str); + + if (RSTRING(str)->flags & MRB_STR_NO_UTF) { + return byte_len; + } + else { + mrb_int utf8_len = mrb_utf8_len(RSTRING_PTR(str), byte_len); + if (byte_len == utf8_len) RSTRING(str)->flags |= MRB_STR_NO_UTF; + return utf8_len; + } +} + +#define RSTRING_CHAR_LEN(s) utf8_strlen(s) /* map character index to byte offset index */ static mrb_int @@ -739,12 +742,6 @@ mrb_str_to_cstr(mrb_state *mrb, mrb_value str0) return RSTR_PTR(s); } -/* - * call-seq: (Caution! String("abcd") change) - * String("abcdefg") = String("abcd") + String("efg") - * - * Returns a new string object containing a copy of <i>str</i>. - */ MRB_API void mrb_str_concat(mrb_state *mrb, mrb_value self, mrb_value other) { @@ -752,12 +749,6 @@ mrb_str_concat(mrb_state *mrb, mrb_value self, mrb_value other) mrb_str_cat_str(mrb, self, other); } -/* - * call-seq: (Caution! String("abcd") remain) - * String("abcdefg") = String("abcd") + String("efg") - * - * Returns a new string object containing a copy of <i>str</i>. - */ MRB_API mrb_value mrb_str_plus(mrb_state *mrb, mrb_value a, mrb_value b) { @@ -775,10 +766,13 @@ mrb_str_plus(mrb_state *mrb, mrb_value a, mrb_value b) /* 15.2.10.5.2 */ /* - * call-seq: (Caution! String("abcd") remain) for stack_argument - * String("abcdefg") = String("abcd") + String("efg") + * call-seq: + * str + other_str -> new_str * - * Returns a new string object containing a copy of <i>str</i>. + * Concatenation---Returns a new <code>String</code> containing + * <i>other_str</i> concatenated to <i>str</i>. + * + * "Hello from " + self.to_s #=> "Hello from main" */ static mrb_value mrb_str_plus_m(mrb_state *mrb, mrb_value self) @@ -979,13 +973,22 @@ mrb_str_equal_m(mrb_state *mrb, mrb_value str1) return mrb_bool_value(mrb_str_equal(mrb, str1, str2)); } /* ---------------------------------- */ +mrb_value mrb_mod_to_s(mrb_state *mrb, mrb_value klass); + MRB_API mrb_value mrb_str_to_str(mrb_state *mrb, mrb_value str) { - if (!mrb_string_p(str)) { + switch (mrb_type(str)) { + case MRB_TT_STRING: + return str; + case MRB_TT_FIXNUM: + return mrb_fixnum_to_str(mrb, str, 10); + case MRB_TT_CLASS: + case MRB_TT_MODULE: + return mrb_mod_to_s(mrb, str); + default: return mrb_convert_type(mrb, str, MRB_TT_STRING, "String", "to_s"); } - return str; } MRB_API const char* @@ -1587,8 +1590,6 @@ mrb_str_index_m(mrb_state *mrb, mrb_value str) return mrb_fixnum_value(pos); } -#define STR_REPLACE_SHARED_MIN 10 - /* 15.2.10.5.24 */ /* 15.2.10.5.28 */ /* diff --git a/src/symbol.c b/src/symbol.c index 6b4c7200c..4242f3d8e 100644 --- a/src/symbol.c +++ b/src/symbol.c @@ -15,28 +15,11 @@ /* ------------------------------------------------------ */ typedef struct symbol_name { mrb_bool lit : 1; + uint8_t prev; uint16_t len; const char *name; } symbol_name; -static inline khint_t -sym_hash_func(mrb_state *mrb, mrb_sym s) -{ - khint_t h = 0; - size_t i, len = mrb->symtbl[s].len; - const char *p = mrb->symtbl[s].name; - - for (i=0; i<len; i++) { - h = (h << 5) - h + *p++; - } - return h; -} -#define sym_hash_equal(mrb,a, b) (mrb->symtbl[a].len == mrb->symtbl[b].len && memcmp(mrb->symtbl[a].name, mrb->symtbl[b].name, mrb->symtbl[a].len) == 0) - -KHASH_DECLARE(n2s, mrb_sym, mrb_sym, FALSE) -KHASH_DEFINE (n2s, mrb_sym, mrb_sym, FALSE, sym_hash_func, sym_hash_equal) -/* ------------------------------------------------------ */ - static void sym_validate_len(mrb_state *mrb, size_t len) { @@ -45,24 +28,62 @@ sym_validate_len(mrb_state *mrb, size_t len) } } +uint8_t +symhash(const char *key, size_t len) +{ + uint32_t hash, i; + + for(hash = i = 0; i < len; ++i) { + hash += key[i]; + hash += (hash << 10); + hash ^= (hash >> 6); + } + hash += (hash << 3); + hash ^= (hash >> 11); + hash += (hash << 15); + return hash & 0xff; +} + +static mrb_sym +find_symbol(mrb_state *mrb, const char *name, uint16_t len, uint8_t hash) +{ + mrb_sym i; + symbol_name *sname; + + i = mrb->symhash[hash]; + if (i == 0) return 0; + do { + sname = &mrb->symtbl[i]; + if (sname->len == len && memcmp(sname->name, name, len) == 0) { + return i; + } + if (sname->prev == 0xff) { + i -= 0xff; + sname = &mrb->symtbl[i]; + while (mrb->symtbl < sname) { + if (sname->len == len && memcmp(sname->name, name, len) == 0) { + return (mrb_sym)(sname - mrb->symtbl); + } + sname--; + } + return 0; + } + i -= sname->prev; + } while (sname->prev > 0); + return 0; +} + static mrb_sym sym_intern(mrb_state *mrb, const char *name, size_t len, mrb_bool lit) { - khash_t(n2s) *h = mrb->name2sym; - symbol_name *sname = mrb->symtbl; /* symtbl[0] for working memory */ - khiter_t k; mrb_sym sym; - char *p; + symbol_name *sname; + uint8_t hash; sym_validate_len(mrb, len); - if (sname) { - sname->lit = lit; - sname->len = (uint16_t)len; - sname->name = name; - k = kh_get(n2s, mrb, h, 0); - if (k != kh_end(h)) - return kh_key(h, k); - } + hash = symhash(name, len); + sym = find_symbol(mrb, name, len, hash); + if (sym > 0) return sym; /* registering a new symbol */ sym = ++mrb->symidx; @@ -78,13 +99,23 @@ sym_intern(mrb_state *mrb, const char *name, size_t len, mrb_bool lit) sname->lit = TRUE; } else { - p = (char *)mrb_malloc(mrb, len+1); + char *p = (char *)mrb_malloc(mrb, len+1); memcpy(p, name, len); p[len] = 0; sname->name = (const char*)p; sname->lit = FALSE; } - kh_put(n2s, mrb, h, sym); + if (mrb->symhash[hash]) { + mrb_sym i = sym - mrb->symhash[hash]; + if (i > 0xff) + sname->prev = 0xff; + else + sname->prev = i; + } + else { + sname->prev = 0; + } + mrb->symhash[hash] = sym; return sym; } @@ -116,25 +147,18 @@ mrb_intern_str(mrb_state *mrb, mrb_value str) MRB_API mrb_value mrb_check_intern(mrb_state *mrb, const char *name, size_t len) { - khash_t(n2s) *h = mrb->name2sym; - symbol_name *sname = mrb->symtbl; - khiter_t k; + mrb_sym sym; sym_validate_len(mrb, len); - sname->len = (uint16_t)len; - sname->name = name; - - k = kh_get(n2s, mrb, h, 0); - if (k != kh_end(h)) { - return mrb_symbol_value(kh_key(h, k)); - } + sym = find_symbol(mrb, name, len, symhash(name, len)); + if (sym > 0) return mrb_symbol_value(sym); return mrb_nil_value(); } MRB_API mrb_value mrb_check_intern_cstr(mrb_state *mrb, const char *name) { - return mrb_check_intern(mrb, name, (mrb_int)strlen(name)); + return mrb_check_intern(mrb, name, strlen(name)); } MRB_API mrb_value @@ -143,7 +167,6 @@ mrb_check_intern_str(mrb_state *mrb, mrb_value str) return mrb_check_intern(mrb, RSTRING_PTR(str), RSTRING_LEN(str)); } -/* lenp must be a pointer to a size_t variable */ MRB_API const char* mrb_sym2name_len(mrb_state *mrb, mrb_sym sym, mrb_int *lenp) { @@ -167,13 +190,11 @@ mrb_free_symtbl(mrb_state *mrb) } } mrb_free(mrb, mrb->symtbl); - kh_destroy(n2s, mrb, mrb->name2sym); } void mrb_init_symtbl(mrb_state *mrb) { - mrb->name2sym = kh_init(n2s, mrb); } /********************************************************************** diff --git a/test/t/class.rb b/test/t/class.rb index f37a891a4..6a0a3225c 100644 --- a/test/t/class.rb +++ b/test/t/class.rb @@ -236,6 +236,11 @@ assert('class to return the last value') do assert_equal(m, :m) end +assert('class to return nil if body is empty') do + assert_nil(class C end) + assert_nil(class << self; end) +end + assert('raise when superclass is not a class') do module FirstModule; end assert_raise(TypeError, 'should raise TypeError') do diff --git a/test/t/module.rb b/test/t/module.rb index 78cb5d07f..f01245e88 100644 --- a/test/t/module.rb +++ b/test/t/module.rb @@ -708,6 +708,15 @@ assert('module with non-class/module outer raises TypeError') do assert_raise(TypeError) { module []::M2 end } end +assert('module to return the last value') do + m = module M; :m end + assert_equal(m, :m) +end + +assert('module to return nil if body is empty') do + assert_nil(module M end) +end + assert('get constant of parent module in singleton class; issue #3568') do actual = module GetConstantInSingletonTest EXPECTED = "value" diff --git a/test/t/string.rb b/test/t/string.rb index e0f0eb99c..7f81c9335 100644 --- a/test/t/string.rb +++ b/test/t/string.rb @@ -464,12 +464,11 @@ assert('String#reverse', '15.2.10.5.29') do end assert('String#reverse(UTF-8)', '15.2.10.5.29') do - assert_equal "ち", "こんにちは世界"[3] - assert_equal nil, "こんにちは世界"[20] - assert_equal "世", "こんにちは世界"[-2] - assert_equal "世界", "こんにちは世界"[-2..-1] - assert_equal "んに", "こんにちは世界"[1,2] - assert_equal "世", "こんにちは世界"["世"] + a = 'こんにちは世界!' + a.reverse + + assert_equal 'こんにちは世界!', a + assert_equal '!界世はちにんこ', 'こんにちは世界!'.reverse end if UTF8STRING assert('String#reverse!', '15.2.10.5.30') do |
