diff options
57 files changed, 950 insertions, 464 deletions
@@ -123,3 +123,8 @@ task :deep_clean => ["clean"] do end puts "Cleaned up mrbgems build folder" end + +desc 'generate document' +task :doc do + load "#{MRUBY_ROOT}/doc/language/generator.rb" +end diff --git a/bintest/mrbc.rb b/bintest/mrbc.rb new file mode 100644 index 000000000..b016378a1 --- /dev/null +++ b/bintest/mrbc.rb @@ -0,0 +1,12 @@ +require 'tempfile' + +assert('Compiling multiple files without new line in last line. #2361') do + a, b, out = Tempfile.new('a.rb'), Tempfile.new('b.rb'), Tempfile.new('out.mrb') + a.write('module A; end') + a.flush + b.write('module B; end') + b.flush + result = `bin/mrbc -c -o #{out.path} #{a.path} #{b.path} 2>&1` + assert_equal "bin/mrbc:#{a.path}:Syntax OK", result.chomp + assert_equal 0, $?.exitstatus +end diff --git a/doc/language/Core.md b/doc/language/Core.md index 033939865..d7331e7f3 100644 --- a/doc/language/Core.md +++ b/doc/language/Core.md @@ -1328,6 +1328,12 @@ ISO Code | Source File | C Function --- | --- | --- 15.3.1.2.5 | src/kernel.c | mrb_f_block_given_p_m +#### local_variables + +ISO Code | Source File | C Function +--- | --- | --- +15.3.1.2.7 | src/kernel.c | mrb_local_variables + #### raise ISO Code | Source File | C Function @@ -1498,6 +1504,12 @@ ISO Code | Source File | C Function --- | --- | --- 15.3.1.3.26 | src/kernel.c | mrb_obj_is_kind_of_m +#### local_variables + +ISO Code | Source File | C Function +--- | --- | --- +15.3.1.3.28 | src/kernel.c | mrb_local_variables + #### methods ISO Code | Source File | C Function diff --git a/doc/mrbgems/README.md b/doc/mrbgems/README.md index 7ac225730..231914905 100644 --- a/doc/mrbgems/README.md +++ b/doc/mrbgems/README.md @@ -76,6 +76,8 @@ The maximal GEM structure looks like this: +- GEM_NAME <- Name of GEM | + +- include/ <- Header for Ruby extension (will exported) + | +- mrblib/ <- Source for Ruby extension | +- src/ <- Source for C extension @@ -87,10 +89,10 @@ The maximal GEM structure looks like this: +- README.md <- Readme for GEM The folder *mrblib* contains pure Ruby files to extend mruby. The folder *src* -contains C files to extend mruby. The folder *test* contains C and pure Ruby files -for testing purposes which will be used by `mrbtest`. *mrbgem.rake* contains -the specification to compile C and Ruby files. *README.md* is a short description -of your GEM. +contains C/C++ files to extend mruby. The folder *include* contains C/C++ header +files. The folder *test* contains C/C++ and pure Ruby files for testing purposes +which will be used by `mrbtest`. *mrbgem.rake* contains the specification +to compile C and Ruby files. *README.md* is a short description of your GEM. ## Build process @@ -159,6 +161,22 @@ Its format is same as argument of method `MRuby::Build#gem`, expect that it can' When a special version of depedency is required, use `MRuby::Build#gem` in *build_config.rb* to override default gem. +If you have conflicting GEMs use the following method: +* `spec.add_conflict(gem, *requirements)` + * The `requirements` argument is same as in `add_dependency` method. + +like following code: + + MRuby::Gem::Specification.new 'some-regexp-binding' do |spec| + spec.license = 'BSD' + spec.author = 'John Doe' + + spec.add_conflict 'mruby-onig-regexp', '> 0.0.0' + spec.add_conflict 'mruby-hs-regexp' + spec.add_conflict 'mruby-pcre-regexp' + spec.add_conflict 'mruby-regexp-pcre' + end + In case your GEM has more complex build requirements you can use the following options additionally inside of your GEM specification: @@ -173,6 +191,19 @@ the following options additionally inside of your GEM specification: * `spec.test_objs` (Object test files for integration into mrbtest) * `spec.test_preload` (Initialization files for mrbtest) +### include_paths and depencency + +Your GEM can export include paths to another GEMs that depends on your GEM. +By default, `/...absolute path.../{GEM_NAME}/include` will be exported. +So it is recommended not to put GEM's local header files on include/. + +These exports are retroactive. +For example: when B depends to C and A depends to B, A will get include paths exported by C. + +Exported include_paths are automatically appended to GEM local include_paths by Minirake. +You can use `spec.export_include_paths` accessor if you want more complex build. + + ## C Extension mruby can be extended with C. This is possible by using the C API to diff --git a/include/mruby.h b/include/mruby.h index c1f45bf0b..dcc01b2dd 100644 --- a/include/mruby.h +++ b/include/mruby.h @@ -102,6 +102,8 @@ enum gc_state { struct mrb_jmpbuf; +typedef void (*mrb_atexit_func)(struct mrb_state*); + typedef struct mrb_state { struct mrb_jmpbuf *jmp; @@ -167,8 +169,12 @@ typedef struct mrb_state { struct RClass *eException_class; struct RClass *eStandardError_class; + struct RObject *nomem_err; /* pre-allocated NoMemoryError */ void *ud; /* auxiliary data */ + + mrb_atexit_func *atexit_stack; + mrb_int atexit_stack_len; } mrb_state; #if __STDC_VERSION__ >= 201112L @@ -328,8 +334,7 @@ mrb_value mrb_obj_clone(mrb_state *mrb, mrb_value self); /* need to include <ctype.h> to use these macros */ #ifndef ISPRINT -/* #define ISASCII(c) isascii((int)(unsigned char)(c)) */ -#define ISASCII(c) 1 +#define ISASCII(c) (!(((int)(unsigned char)(c)) & ~0x7f)) #define ISPRINT(c) (ISASCII(c) && isprint((int)(unsigned char)(c))) #define ISSPACE(c) (ISASCII(c) && isspace((int)(unsigned char)(c))) #define ISUPPER(c) (ISASCII(c) && isupper((int)(unsigned char)(c))) @@ -413,6 +418,8 @@ void* mrb_pool_realloc(struct mrb_pool*, void*, size_t oldlen, size_t newlen); mrb_bool mrb_pool_can_realloc(struct mrb_pool*, void*, size_t); void* mrb_alloca(mrb_state *mrb, size_t); +void mrb_state_atexit(mrb_state *mrb, mrb_atexit_func func); + #ifdef MRB_DEBUG #include <assert.h> #define mrb_assert(p) assert(p) @@ -422,6 +429,12 @@ void* mrb_alloca(mrb_state *mrb, size_t); #define mrb_assert_int_fit(t1,n,t2,max) ((void)0) #endif +#if __STDC_VERSION__ >= 201112L +#define mrb_static_assert(exp, str) _Static_assert(exp, str) +#else +#define mrb_static_assert(exp, str) mrb_assert(exp) +#endif + mrb_value mrb_format(mrb_state *mrb, const char *format, ...); #if defined(__cplusplus) diff --git a/include/mruby/error.h b/include/mruby/error.h index 7ae2d4348..4d37f1701 100644 --- a/include/mruby/error.h +++ b/include/mruby/error.h @@ -19,6 +19,7 @@ void mrb_exc_print(mrb_state *mrb, struct RObject *exc); void mrb_print_backtrace(mrb_state *mrb); mrb_value mrb_exc_backtrace(mrb_state *mrb, mrb_value exc); mrb_value mrb_get_backtrace(mrb_state *mrb); +mrb_noreturn void mrb_no_method_error(mrb_state *mrb, mrb_sym id, mrb_int argc, const mrb_value *argv, const char *fmt, ...); /* declaration for fail method */ mrb_value mrb_f_raise(mrb_state*, mrb_value); diff --git a/include/mruby/string.h b/include/mruby/string.h index dd73f5f1e..f8a1fa7bd 100644 --- a/include/mruby/string.h +++ b/include/mruby/string.h @@ -11,8 +11,6 @@ extern "C" { #endif -#define IS_EVSTR(p,e) ((p) < (e) && (*(p) == '$' || *(p) == '@' || *(p) == '{')) - extern const char mrb_digitmap[]; #define RSTRING_EMBED_LEN_MAX ((mrb_int)(sizeof(void*) * 3 - 1)) @@ -32,21 +30,42 @@ struct RString { } as; }; -#define mrb_str_ptr(s) ((struct RString*)(mrb_ptr(s))) -#define RSTRING(s) ((struct RString*)(mrb_ptr(s))) -#define RSTRING_PTR(s)\ - ((RSTRING(s)->flags & MRB_STR_EMBED) ?\ - RSTRING(s)->as.ary :\ - RSTRING(s)->as.heap.ptr) -#define RSTRING_LEN(s)\ - ((RSTRING(s)->flags & MRB_STR_EMBED) ?\ - (mrb_int)((RSTRING(s)->flags & MRB_STR_EMBED_LEN_MASK) >> MRB_STR_EMBED_LEN_SHIFT) :\ - RSTRING(s)->as.heap.len) -#define RSTRING_CAPA(s)\ - ((RSTRING(s)->flags & MRB_STR_EMBED) ?\ - RSTRING_EMBED_LEN_MAX :\ - RSTRING(s)->as.heap.aux.capa) -#define RSTRING_END(s) (RSTRING_PTR(s) + RSTRING_LEN(s)) +#define RSTR_EMBED_P(s) ((s)->flags & MRB_STR_EMBED) +#define RSTR_SET_EMBED_FLAG(s) ((s)->flags |= MRB_STR_EMBED) +#define RSTR_UNSET_EMBED_FLAG(s) ((s)->flags &= ~(MRB_STR_EMBED|MRB_STR_EMBED_LEN_MASK)) +#define RSTR_SET_EMBED_LEN(s, n) do {\ + size_t tmp_n = (n);\ + s->flags &= ~MRB_STR_EMBED_LEN_MASK;\ + s->flags |= (tmp_n) << MRB_STR_EMBED_LEN_SHIFT;\ +} while (0) +#define RSTR_SET_LEN(s, n) do {\ + if (RSTR_EMBED_P(s)) {\ + RSTR_SET_EMBED_LEN((s),(n));\ + } else {\ + s->as.heap.len = (mrb_int)(n);\ + }\ +} while (0) +#define RSTR_EMBED_LEN(s)\ + (mrb_int)(((s)->flags & MRB_STR_EMBED_LEN_MASK) >> MRB_STR_EMBED_LEN_SHIFT) +#define RSTR_PTR(s) ((RSTR_EMBED_P(s)) ? (s)->as.ary : (s)->as.heap.ptr) +#define RSTR_LEN(s) ((RSTR_EMBED_P(s)) ? RSTR_EMBED_LEN(s) : (s)->as.heap.len) +#define RSTR_CAPA(s) (RSTR_EMBED_P(s) ? RSTRING_EMBED_LEN_MAX : (s)->as.heap.aux.capa) + +#define RSTR_SHARED_P(s) ((s)->flags & MRB_STR_SHARED) +#define RSTR_SET_SHARED_FLAG(s) ((s)->flags |= MRB_STR_SHARED) +#define RSTR_UNSET_SHARED_FLAG(s) ((s)->flags &= ~MRB_STR_SHARED) + +#define RSTR_NOFREE_P(s) ((s)->flags & MRB_STR_NOFREE) +#define RSTR_SET_NOFREE_FLAG(s) ((s)->flags |= MRB_STR_NOFREE) +#define RSTR_UNSET_NOFREE_FLAG(s) ((s)->flags &= ~MRB_STR_NOFREE) + +#define mrb_str_ptr(s) ((struct RString*)(mrb_ptr(s))) +#define RSTRING(s) mrb_str_ptr(s) +#define RSTRING_PTR(s) RSTR_PTR(RSTRING(s)) +#define RSTRING_EMBED_LEN(s) RSTR_ENBED_LEN(RSTRING(s)) +#define RSTRING_LEN(s) RSTR_LEN(RSTRING(s)) +#define RSTRING_CAPA(s) RSTR_CAPA(RSTRING(s)) +#define RSTRING_END(s) (RSTRING_PTR(s) + RSTRING_LEN(s)) mrb_int mrb_str_strlen(mrb_state*, struct RString*); #define MRB_STR_SHARED 1 diff --git a/include/mruby/value.h b/include/mruby/value.h index 83696715d..98af9626d 100644 --- a/include/mruby/value.h +++ b/include/mruby/value.h @@ -70,7 +70,9 @@ typedef short mrb_sym; # ifndef __cplusplus # define inline __inline # endif -# define snprintf _snprintf +# if _MSC_VER < 1900 +# define snprintf _snprintf +# endif # if _MSC_VER < 1800 # include <float.h> # define isfinite(n) _finite(n) diff --git a/mrbgems/mruby-array-ext/src/array.c b/mrbgems/mruby-array-ext/src/array.c index 014137e99..d69f0ac44 100644 --- a/mrbgems/mruby-array-ext/src/array.c +++ b/mrbgems/mruby-array-ext/src/array.c @@ -2,6 +2,7 @@ #include "mruby/value.h" #include "mruby/array.h" #include "mruby/range.h" +#include "mruby/hash.h" /* * call-seq: @@ -105,6 +106,48 @@ mrb_ary_values_at(mrb_state *mrb, mrb_value self) return mrb_get_values_at(mrb, self, RARRAY_LEN(self), argc, argv, mrb_ary_ref); } +/* + * call-seq: + * ary.to_h -> Hash + * + * Returns the result of interpreting <i>aray</i> as an array of + * <tt>[key, value]</tt> paris. + * + * [[:foo, :bar], [1, 2]].to_h + * # => {:foo => :bar, 1 => 2} + */ + +static mrb_value +mrb_ary_to_h(mrb_state *mrb, mrb_value ary) +{ + mrb_int i; + mrb_value v, hash; + + hash = mrb_hash_new_capa(mrb, 0); + + for (i = 0; i < RARRAY_LEN(ary); ++i) { + v = mrb_check_array_type(mrb, RARRAY_PTR(ary)[i]); + + if (mrb_nil_p(v)) { + mrb_raisef(mrb, E_TYPE_ERROR, "wrong element type %S at %S (expected array)", + mrb_str_new_cstr(mrb, mrb_obj_classname(mrb, RARRAY_PTR(ary)[i])), + mrb_fixnum_value(i) + ); + } + + if (RARRAY_LEN(v) != 2) { + mrb_raisef(mrb, E_ARGUMENT_ERROR, "wrong array length at %S (expected 2, was %S)", + mrb_fixnum_value(i), + mrb_fixnum_value(RARRAY_LEN(v)) + ); + } + + mrb_hash_set(mrb, hash, RARRAY_PTR(v)[0], RARRAY_PTR(v)[1]); + } + + return hash; +} + void mrb_mruby_array_ext_gem_init(mrb_state* mrb) { @@ -114,6 +157,7 @@ mrb_mruby_array_ext_gem_init(mrb_state* mrb) mrb_define_method(mrb, a, "at", mrb_ary_at, MRB_ARGS_REQ(1)); mrb_define_method(mrb, a, "rassoc", mrb_ary_rassoc, MRB_ARGS_REQ(1)); mrb_define_method(mrb, a, "values_at", mrb_ary_values_at, MRB_ARGS_ANY()); + mrb_define_method(mrb, a, "to_h", mrb_ary_to_h, MRB_ARGS_REQ(0)); } void diff --git a/mrbgems/mruby-array-ext/test/array.rb b/mrbgems/mruby-array-ext/test/array.rb index cb5652dde..8c919f7e0 100644 --- a/mrbgems/mruby-array-ext/test/array.rb +++ b/mrbgems/mruby-array-ext/test/array.rb @@ -285,3 +285,11 @@ assert('Array#values_at') do assert_equal ['none', nil, nil, 'red', 'green', 'purple'], a.values_at(4..6, 0...3) assert_raise(TypeError) { a.values_at 'tt' } end + +assert('Array#to_h') do + assert_equal({}, [].to_h) + assert_equal({a: 1, b:2}, [[:a, 1], [:b, 2]].to_h) + + assert_raise(TypeError) { [1].to_h } + assert_raise(ArgumentError) { [[1]].to_h } +end diff --git a/mrbgems/mruby-bin-mirb/tools/mirb/mirb.c b/mrbgems/mruby-bin-mirb/tools/mirb/mirb.c index 5c9524161..dece361a5 100644 --- a/mrbgems/mruby-bin-mirb/tools/mirb/mirb.c +++ b/mrbgems/mruby-bin-mirb/tools/mirb/mirb.c @@ -258,7 +258,7 @@ check_keyword(const char *buf, const char *word) size_t len = strlen(word); /* skip preceding spaces */ - while (*p && isspace(*p)) { + while (*p && isspace((unsigned char)*p)) { p++; } /* check keyword */ @@ -268,7 +268,7 @@ check_keyword(const char *buf, const char *word) p += len; /* skip trailing spaces */ while (*p) { - if (!isspace(*p)) return 0; + if (!isspace((unsigned char)*p)) return 0; p++; } return 1; @@ -424,7 +424,7 @@ main(int argc, char **argv) else { /* no */ if (!mrb_respond_to(mrb, result, mrb_intern_lit(mrb, "inspect"))){ - result = mrb_any_to_s(mrb,result); + result = mrb_any_to_s(mrb, result); } p(mrb, result, 1); } diff --git a/mrbgems/mruby-bin-mruby/bintest/mruby.rb b/mrbgems/mruby-bin-mruby/bintest/mruby.rb index a453400fc..de211c1ba 100644 --- a/mrbgems/mruby-bin-mruby/bintest/mruby.rb +++ b/mrbgems/mruby-bin-mruby/bintest/mruby.rb @@ -30,3 +30,17 @@ assert '$0 value' do # one liner assert_equal '"-e"', `./bin/mruby -e 'p $0'`.chomp end + +assert '__END__', '8.6' do + script = Tempfile.new('test.rb') + + script.write <<EOS +p 'test' + __END__ = 'fin' +p __END__ +__END__ +p 'legend' +EOS + script.flush + assert_equal "\"test\"\n\"fin\"\n", `./bin/mruby #{script.path}` +end diff --git a/mrbgems/mruby-enum-ext/mrblib/enum.rb b/mrbgems/mruby-enum-ext/mrblib/enum.rb index 5abb75d54..b13d00a84 100644 --- a/mrbgems/mruby-enum-ext/mrblib/enum.rb +++ b/mrbgems/mruby-enum-ext/mrblib/enum.rb @@ -669,4 +669,30 @@ module Enumerable end ary end + + ## + # call-seq: + # enum.to_h -> 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} + # + + def to_h + h = {} + self.each do |*v| + v = v.__svalue + raise TypeError, "wrong element type #{v.class} (expected Array)" unless v.is_a? Array + raise ArgumentError, "element has wrong array length (expected 2, was #{v.size})" if v.size != 2 + h[v[0]] = v[1] + end + h + end + + def nil.to_h + {} + end end diff --git a/mrbgems/mruby-enum-ext/test/enum.rb b/mrbgems/mruby-enum-ext/test/enum.rb index bce9cb15d..08b553fe5 100644 --- a/mrbgems/mruby-enum-ext/test/enum.rb +++ b/mrbgems/mruby-enum-ext/test/enum.rb @@ -144,3 +144,19 @@ assert("Enumerable#zip") do assert_equal [[1, 4, 7], [2, 5, 8]], [1, 2].zip(a, b) assert_equal [[4, 1, 8], [5, 2, nil], [6, nil, nil]], a.zip([1, 2], [8]) end + +assert("Enumerable#to_h") do + c = Class.new { + include Enumerable + def each + yield [1,2] + yield [3,4] + end + } + h0 = {1=>2, 3=>4} + h = c.new.to_h + assert_equal Hash, h.class + assert_equal h0, h + # mruby-enum-ext also provides nil.to_h + assert_equal Hash.new, nil.to_h +end diff --git a/mrbgems/mruby-hash-ext/mrblib/hash.rb b/mrbgems/mruby-hash-ext/mrblib/hash.rb index 504848a74..9da08dc3a 100644 --- a/mrbgems/mruby-hash-ext/mrblib/hash.rb +++ b/mrbgems/mruby-hash-ext/mrblib/hash.rb @@ -182,4 +182,15 @@ class Hash end nil end + + ## + # call-seq: + # hsh.to_h -> hsh or new_hash + # + # Returns +self+. If called on a subclass of Hash, converts + # the receiver to a Hash object. + # + def to_h + self + end end diff --git a/mrbgems/mruby-hash-ext/test/hash.rb b/mrbgems/mruby-hash-ext/test/hash.rb index 62cfc8856..2bc5b911a 100644 --- a/mrbgems/mruby-hash-ext/test/hash.rb +++ b/mrbgems/mruby-hash-ext/test/hash.rb @@ -115,3 +115,9 @@ assert("Hash#key") do assert_nil h.key('nil') assert_equal 'nil', h.key(nil) end + +assert("Hash#to_h") do + h = { "a" => 100, "b" => 200 } + assert_equal Hash, h.to_h.class + assert_equal h, h.to_h +end diff --git a/mrbgems/mruby-kernel-ext/src/kernel.c b/mrbgems/mruby-kernel-ext/src/kernel.c index 1ce63ac94..a6ecd72bd 100644 --- a/mrbgems/mruby-kernel-ext/src/kernel.c +++ b/mrbgems/mruby-kernel-ext/src/kernel.c @@ -26,7 +26,7 @@ mrb_mruby_kernel_ext_gem_init(mrb_state *mrb) { struct RClass *krn = mrb->kernel_module; - mrb_define_module_function(mrb, krn, "fail", mrb_f_raise, MRB_ARGS_NONE()); + mrb_define_module_function(mrb, krn, "fail", mrb_f_raise, MRB_ARGS_OPT(2)); mrb_define_method(mrb, krn, "__method__", mrb_f_method, MRB_ARGS_NONE()); } diff --git a/mrbgems/mruby-sprintf/mrblib/string.rb b/mrbgems/mruby-sprintf/mrblib/string.rb new file mode 100644 index 000000000..d7e55536a --- /dev/null +++ b/mrbgems/mruby-sprintf/mrblib/string.rb @@ -0,0 +1,9 @@ +class String + def %(args) + if args.is_a? Array + sprintf(self, *args) + else + sprintf(self, args) + end + end +end diff --git a/mrbgems/mruby-sprintf/test/sprintf.rb b/mrbgems/mruby-sprintf/test/sprintf.rb index 52e94fb83..7007df1fa 100644 --- a/mrbgems/mruby-sprintf/test/sprintf.rb +++ b/mrbgems/mruby-sprintf/test/sprintf.rb @@ -1,3 +1,8 @@ -## -# Kernel#sprintf Kernel#format Test +#assert('Kernel.sprintf') do +#end +assert('String#%') do + assert_equal "one=1", "one=%d" % 1 + assert_equal "1 one 1.0", "%d %s %3.1f" % [ 1, "one", 1.01 ] + assert_equal "123 < 456", "%{num} < %<str>s" % { num: 123, str: "456" } +end diff --git a/mrbgems/mruby-string-ext/mrblib/string.rb b/mrbgems/mruby-string-ext/mrblib/string.rb index 45c631b94..1cfb7e2f5 100644 --- a/mrbgems/mruby-string-ext/mrblib/string.rb +++ b/mrbgems/mruby-string-ext/mrblib/string.rb @@ -1,4 +1,28 @@ class String + + ## + # call-seq: + # string.clear -> string + # + # Makes string empty. + # + # a = "abcde" + # a.clear #=> "" + # + def clear + self.replace("") + end + + ## + # 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>. + # + # " hello ".lstrip #=> "hello " + # "hello".lstrip #=> "hello" + # def lstrip a = 0 z = self.size - 1 @@ -6,6 +30,16 @@ class String (z >= 0) ? self[a..z] : "" end + ## + # 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>. + # + # " hello ".rstrip #=> " hello" + # "hello".rstrip #=> "hello" + # def rstrip a = 0 z = self.size - 1 @@ -13,6 +47,15 @@ class String (z >= 0) ? self[a..z] : "" end + ## + # call-seq: + # str.strip -> new_str + # + # Returns a copy of <i>str</i> with leading and trailing whitespace removed. + # + # " hello ".strip #=> "hello" + # "\tgoodbye\r\n".strip #=> "goodbye" + # def strip a = 0 z = self.size - 1 @@ -21,31 +64,61 @@ class String (z >= 0) ? self[a..z] : "" end + ## + # 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>. + # + # " hello ".lstrip #=> "hello " + # "hello".lstrip! #=> nil + # def lstrip! s = self.lstrip (s == self) ? nil : self.replace(s) end + ## + # 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>. + # + # " hello ".rstrip #=> " hello" + # "hello".rstrip! #=> nil + # def rstrip! s = self.rstrip (s == self) ? nil : self.replace(s) end + ## + # call-seq: + # str.strip! -> str or nil + # + # Removes leading and trailing whitespace from <i>str</i>. Returns + # <code>nil</code> if <i>str</i> was not altered. + # def strip! s = self.strip (s == self) ? nil : self.replace(s) end -# call-seq: -# str.casecmp(other_str) -> -1, 0, +1 or nil -# -# Case-insensitive version of <code>String#<=></code>. -# -# "abcdef".casecmp("abcde") #=> 1 -# "aBcDeF".casecmp("abcdef") #=> 0 -# "abcdef".casecmp("abcdefg") #=> -1 -# "abcdef".casecmp("ABCDEF") #=> 0 -# + ## + # call-seq: + # str.casecmp(other_str) -> -1, 0, +1 or nil + # + # Case-insensitive version of <code>String#<=></code>. + # + # "abcdef".casecmp("abcde") #=> 1 + # "aBcDeF".casecmp("abcdef") #=> 0 + # "abcdef".casecmp("abcdefg") #=> -1 + # "abcdef".casecmp("ABCDEF") #=> 0 + # def casecmp(str) self.downcase <=> str.to_str.downcase rescue NoMethodError diff --git a/mrbgems/mruby-string-ext/src/string.c b/mrbgems/mruby-string-ext/src/string.c index 2c0a406ad..f04f12c4b 100644 --- a/mrbgems/mruby-string-ext/src/string.c +++ b/mrbgems/mruby-string-ext/src/string.c @@ -1,6 +1,8 @@ #include <ctype.h> #include <string.h> #include "mruby.h" +#include "mruby/array.h" +#include "mruby/class.h" #include "mruby/string.h" static mrb_value @@ -175,6 +177,68 @@ mrb_str_oct(mrb_state *mrb, mrb_value self) return mrb_str_to_inum(mrb, self, 8, FALSE); } +/* + * call-seq: + * string.chr -> string + * + * Returns a one-character string at the beginning of the string. + * + * a = "abcde" + * a.chr #=> "a" + */ +static mrb_value +mrb_str_chr(mrb_state *mrb, mrb_value self) +{ + return mrb_str_substr(mrb, self, 0, 1); +} + +/* + * call-seq: + * string.lines -> array of string + * + * Returns strings per line; + * + * a = "abc\ndef" + * a.lines #=> ["abc\n", "def"] + */ +static mrb_value +mrb_str_lines(mrb_state *mrb, mrb_value self) +{ + mrb_value result; + mrb_value blk; + int ai; + mrb_int len; + mrb_value arg; + char *p = RSTRING_PTR(self), *t; + char *e = p + RSTRING_LEN(self); + + mrb_get_args(mrb, "&", &blk); + + result = mrb_ary_new(mrb); + + if (!mrb_nil_p(blk)) { + while (p < e) { + t = p; + while (p < e && *p != '\n') p++; + if (*p == '\n') p++; + len = (mrb_int) (p - t); + arg = mrb_str_new(mrb, t, len); + mrb_yield_argv(mrb, blk, 1, &arg); + } + return self; + } + while (p < e) { + ai = mrb_gc_arena_save(mrb); + t = p; + while (p < e && *p != '\n') p++; + if (*p == '\n') p++; + len = (mrb_int) (p - t); + mrb_ary_push(mrb, result, mrb_str_new(mrb, t, len)); + mrb_gc_arena_restore(mrb, ai); + } + return result; +} + void mrb_mruby_string_ext_gem_init(mrb_state* mrb) { @@ -190,6 +254,8 @@ mrb_mruby_string_ext_gem_init(mrb_state* mrb) mrb_define_method(mrb, s, "end_with?", mrb_str_end_with, MRB_ARGS_REST()); mrb_define_method(mrb, s, "hex", mrb_str_hex, MRB_ARGS_NONE()); mrb_define_method(mrb, s, "oct", mrb_str_oct, MRB_ARGS_NONE()); + mrb_define_method(mrb, s, "chr", mrb_str_chr, MRB_ARGS_NONE()); + mrb_define_method(mrb, s, "lines", mrb_str_lines, MRB_ARGS_NONE()); } void diff --git a/mrbgems/mruby-string-ext/test/string.rb b/mrbgems/mruby-string-ext/test/string.rb index 01712a607..72c919b35 100644 --- a/mrbgems/mruby-string-ext/test/string.rb +++ b/mrbgems/mruby-string-ext/test/string.rb @@ -161,3 +161,35 @@ assert('String#oct') do assert_equal 8, "010".oct assert_equal (-8), "-10".oct end + +assert('String#chr') do + assert_equal "a", "abcde".chr +end + +assert('String#lines') do + assert_equal ["Hel\n", "lo\n", "World!"], "Hel\nlo\nWorld!".lines + assert_equal ["Hel\n", "lo\n", "World!\n"], "Hel\nlo\nWorld!\n".lines + assert_equal ["\n", "\n", "\n"], "\n\n\n".lines + assert_equal [], "".lines +end + +assert('String#clear') do + # embed string + s = "foo" + assert_equal("", s.clear) + assert_equal("", s) + + # not embed string and not shared string + s = "foo" * 100 + a = s + assert_equal("", s.clear) + assert_equal("", s) + assert_equal("", a) + + # shared string + s = "foo" * 100 + a = s[10, 90] # create shared string + assert_equal("", s.clear) # clear + assert_equal("", s) # s is cleared + assert_not_equal("", a) # a should not be affected +end diff --git a/mrbgems/mruby-string-utf8/mrbgem.rake b/mrbgems/mruby-string-utf8/mrbgem.rake index 86d0a6da3..7642d4e07 100644 --- a/mrbgems/mruby-string-utf8/mrbgem.rake +++ b/mrbgems/mruby-string-utf8/mrbgem.rake @@ -2,4 +2,5 @@ MRuby::Gem::Specification.new('mruby-string-utf8') do |spec| spec.license = 'MIT' spec.author = 'mruby developers' spec.summary = 'UTF-8 support in String class' + spec.add_dependency('mruby-string-ext', :core => 'mruby-string-ext') end diff --git a/mrbgems/mruby-string-utf8/src/string.c b/mrbgems/mruby-string-utf8/src/string.c index edda491fc..874fa8dbb 100644 --- a/mrbgems/mruby-string-utf8/src/string.c +++ b/mrbgems/mruby-string-utf8/src/string.c @@ -1,17 +1,12 @@ #include "mruby.h" #include "mruby/array.h" +#include "mruby/class.h" #include "mruby/string.h" #include "mruby/range.h" #include "mruby/re.h" #include <ctype.h> #include <string.h> -#define STR_EMBED_P(s) ((s)->flags & MRB_STR_EMBED) -#define STR_EMBED_LEN(s)\ - (mrb_int)(((s)->flags & MRB_STR_EMBED_LEN_MASK) >> MRB_STR_EMBED_LEN_SHIFT) -#define STR_PTR(s) ((STR_EMBED_P(s)) ? (s)->as.ary : (s)->as.heap.ptr) -#define STR_LEN(s) ((STR_EMBED_P(s)) ? STR_EMBED_LEN(s) : (mrb_int)(s)->as.heap.len) - static const char utf8len_codepage[256] = { 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, @@ -36,27 +31,6 @@ static char utf8len_codepage_zero[256] = 3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,4,4,4,4,4,0,0,0,0,0,0,0,0,0,0,0, }; -static const char isspacetable[256] = { - 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 -}; - -#define ascii_isspace(c) isspacetable[(unsigned char)(c)] - static mrb_int utf8code(unsigned char* p) { @@ -127,9 +101,7 @@ mrb_utf8_strlen(mrb_value str, mrb_int len) static mrb_value mrb_str_size(mrb_state *mrb, mrb_value str) { - mrb_int size = mrb_utf8_strlen(str, -1); - - return mrb_fixnum_value(size); + return mrb_fixnum_value(mrb_utf8_strlen(str, -1)); } #define RSTRING_LEN_UTF8(s) mrb_utf8_strlen(s, -1) @@ -161,10 +133,10 @@ mrb_memsearch_qs(const unsigned char *xs, mrb_int m, const unsigned char *ys, mr qstable[i] = m + 1; for (; x < xe; ++x) qstable[*x] = xe - x; - /* Searching */ + /* Searching */ for (; y + m <= ys + n; y += *(qstable + y[m])) { if (*xs == *y && memcmp(xs, y, m) == 0) - return y - ys; + return y - ys; } return -1; } @@ -272,17 +244,17 @@ str_rindex(mrb_state *mrb, mrb_value str, mrb_value sub, mrb_int pos) mrb_int len = RSTRING_LEN(sub); /* substring longer than string */ - if (STR_LEN(ps) < len) return -1; - if (STR_LEN(ps) - pos < len) { - pos = STR_LEN(ps) - len; + if (RSTR_LEN(ps) < len) return -1; + if (RSTR_LEN(ps) - pos < len) { + pos = RSTR_LEN(ps) - len; } - sbeg = STR_PTR(ps); - s = STR_PTR(ps) + pos; + sbeg = RSTR_PTR(ps); + s = RSTR_PTR(ps) + pos; t = RSTRING_PTR(sub); if (len) { while (sbeg <= s) { if (memcmp(s, t, len) == 0) { - return s - STR_PTR(ps); + return s - RSTR_PTR(ps); } s--; } @@ -572,7 +544,7 @@ mrb_str_split_m(mrb_state *mrb, mrb_value str) if (mrb_string_p(spat)) { split_type = string; if (RSTRING_LEN(spat) == 1 && RSTRING_PTR(spat)[0] == ' '){ - split_type = awk; + split_type = awk; } } else { @@ -594,7 +566,7 @@ mrb_str_split_m(mrb_state *mrb, mrb_value str) int ai = mrb_gc_arena_save(mrb); c = (unsigned char)*ptr++; if (skip) { - if (ascii_isspace(c)) { + if (ISSPACE(c)) { beg = ptr - bptr; } else { @@ -603,7 +575,7 @@ mrb_str_split_m(mrb_state *mrb, mrb_value str) if (lim_p && lim <= i) break; } } - else if (ascii_isspace(c)) { + else if (ISSPACE(c)) { mrb_ary_push(mrb, result, str_subseq(mrb, str, beg, end-beg)); mrb_gc_arena_restore(mrb, ai); skip = 1; @@ -667,6 +639,80 @@ mrb_str_split_m(mrb_state *mrb, mrb_value str) return result; } +static mrb_value +mrb_str_chr(mrb_state *mrb, mrb_value self) +{ + return str_substr(mrb, self, 0, 1); +} + +static mrb_value +mrb_str_chars(mrb_state *mrb, mrb_value self) +{ + mrb_value result; + mrb_value blk; + int ai; + mrb_int len; + mrb_value arg; + char *p = RSTRING_PTR(self); + char *e = p + RSTRING_LEN(self); + + mrb_get_args(mrb, "&", &blk); + + result = mrb_ary_new(mrb); + + if (!mrb_nil_p(blk)) { + while (p < e) { + len = utf8len((unsigned char*) p); + arg = mrb_str_new(mrb, p, len); + mrb_yield_argv(mrb, blk, 1, &arg); + p += len; + } + return self; + } + while (p < e) { + ai = mrb_gc_arena_save(mrb); + len = utf8len((unsigned char*) p); + mrb_ary_push(mrb, result, mrb_str_new(mrb, p, len)); + mrb_gc_arena_restore(mrb, ai); + p += len; + } + return result; +} + +static mrb_value +mrb_str_codepoints(mrb_state *mrb, mrb_value self) +{ + mrb_value result; + mrb_value blk; + int ai; + mrb_int len; + mrb_value arg; + char *p = RSTRING_PTR(self); + char *e = p + RSTRING_LEN(self); + + mrb_get_args(mrb, "&", &blk); + + result = mrb_ary_new(mrb); + + if (!mrb_nil_p(blk)) { + while (p < e) { + len = utf8len((unsigned char*) p); + arg = mrb_fixnum_value(utf8code((unsigned char*) p)); + mrb_yield_argv(mrb, blk, 1, &arg); + p += len; + } + return self; + } + while (p < e) { + ai = mrb_gc_arena_save(mrb); + len = utf8len((unsigned char*) p); + mrb_ary_push(mrb, result, mrb_fixnum_value(utf8code((unsigned char*) p))); + mrb_gc_arena_restore(mrb, ai); + p += len; + } + return result; +} + void mrb_mruby_string_utf8_gem_init(mrb_state* mrb) { @@ -682,6 +728,11 @@ mrb_mruby_string_utf8_gem_init(mrb_state* mrb) mrb_define_method(mrb, s, "reverse", mrb_str_reverse, MRB_ARGS_NONE()); mrb_define_method(mrb, s, "reverse!", mrb_str_reverse_bang, MRB_ARGS_NONE()); mrb_define_method(mrb, s, "rindex", mrb_str_rindex_m, MRB_ARGS_ANY()); + mrb_define_method(mrb, s, "chr", mrb_str_chr, MRB_ARGS_NONE()); + mrb_define_method(mrb, s, "chars", mrb_str_chars, MRB_ARGS_NONE()); + mrb_alias_method(mrb, s, mrb_intern_lit(mrb, "each_char"), mrb_intern_lit(mrb, "chars")); + mrb_define_method(mrb, s, "codepoints", mrb_str_codepoints, MRB_ARGS_NONE()); + mrb_alias_method(mrb, s, mrb_intern_lit(mrb, "each_codepoint"), mrb_intern_lit(mrb, "codepoints")); mrb_define_method(mrb, mrb->fixnum_class, "chr", mrb_fixnum_chr, MRB_ARGS_NONE()); } diff --git a/mrbgems/mruby-string-utf8/test/string.rb b/mrbgems/mruby-string-utf8/test/string.rb index 1bfa8512c..5b4180037 100644 --- a/mrbgems/mruby-string-utf8/test/string.rb +++ b/mrbgems/mruby-string-utf8/test/string.rb @@ -66,3 +66,44 @@ assert('String#rindex') do assert_equal 12, str.rindex('ち') assert_equal 3, str.rindex('ち', 10) end + +assert('String#chr(utf-8)') do + assert_equal "こ", "こんにちは世界!".chr +end + +assert('String#chars') do + expect = ['こ', 'ん', 'に', 'ち', 'は', '世', '界', '!'] + assert_equal expect, "こんにちは世界!".chars + s = "" + "こんにちは世界!".chars do |x| + s += x + end + assert_equal "こんにちは世界!", s +end + +assert('String#each_char') do + expect = ['こ', 'ん', 'に', 'ち', 'は', '世', '界', '!'] + s = "" + "こんにちは世界!".each_char do |x| + s += x + end + assert_equal "こんにちは世界!", s +end +assert('String#codepoints') do + expect = [12371, 12435, 12395, 12385, 12399, 19990, 30028, 33] + assert_equal expect, "こんにちは世界!".codepoints + cp = [] + "こんにちは世界!".codepoints do |x| + cp << x + end + assert_equal expect, cp +end + +assert('String#each_codepoint') do + expect = [12371, 12435, 12395, 12385, 12399, 19990, 30028, 33] + cp = [] + "こんにちは世界!".each_codepoint do |x| + cp << x + end + assert_equal expect, cp +end diff --git a/mrbgems/mruby-struct/src/struct.c b/mrbgems/mruby-struct/src/struct.c index 6894ffec9..930384806 100644 --- a/mrbgems/mruby-struct/src/struct.c +++ b/mrbgems/mruby-struct/src/struct.c @@ -4,8 +4,8 @@ ** See Copyright Notice in mruby.h */ +#include <ctype.h> #include <string.h> -#include <stdarg.h> #include "mruby.h" #include "mruby/array.h" #include "mruby/string.h" @@ -40,13 +40,7 @@ struct_ivar_get(mrb_state *mrb, mrb_value c, mrb_sym id) } } -mrb_value -mrb_struct_iv_get(mrb_state *mrb, mrb_value c, const char *name) -{ - return struct_ivar_get(mrb, c, mrb_intern_cstr(mrb, name)); -} - -mrb_value +static mrb_value mrb_struct_s_members(mrb_state *mrb, mrb_value klass) { mrb_value members = struct_ivar_get(mrb, klass, mrb_intern_lit(mrb, "__members__")); @@ -60,7 +54,7 @@ mrb_struct_s_members(mrb_state *mrb, mrb_value klass) return members; } -mrb_value +static mrb_value mrb_struct_members(mrb_state *mrb, mrb_value s) { mrb_value members = mrb_struct_s_members(mrb, mrb_obj_value(mrb_obj_class(mrb, s))); @@ -109,7 +103,7 @@ mrb_struct_members_m(mrb_state *mrb, mrb_value obj) return mrb_struct_s_members_m(mrb, mrb_obj_value(mrb_obj_class(mrb, obj))); } -mrb_value +static mrb_value mrb_struct_getmember(mrb_state *mrb, mrb_value obj, mrb_sym id) { mrb_value members, slot, *ptr, *ptr_members; @@ -149,7 +143,7 @@ static mrb_value mrb_struct_ref9(mrb_state* mrb, mrb_value obj) {return RSTRUCT_ #define numberof(array) (int)(sizeof(array) / sizeof((array)[0])) #define N_REF_FUNC numberof(ref_func) -static mrb_value (*const ref_func[])(mrb_state*, mrb_value) = { +static const mrb_func_t ref_func[] = { mrb_struct_ref0, mrb_struct_ref1, mrb_struct_ref2, @@ -162,7 +156,7 @@ static mrb_value (*const ref_func[])(mrb_state*, mrb_value) = { mrb_struct_ref9, }; -mrb_sym +static mrb_sym mrb_id_attrset(mrb_state *mrb, mrb_sym id) { const char *name; @@ -185,8 +179,7 @@ static mrb_value mrb_struct_set(mrb_state *mrb, mrb_value obj, mrb_value val) { const char *name; - size_t i, len; - mrb_int slen; + mrb_int i, len, slen; mrb_sym mid; mrb_value members, slot, *ptr, *ptr_members; @@ -217,19 +210,18 @@ mrb_struct_set_m(mrb_state *mrb, mrb_value obj) return mrb_struct_set(mrb, obj, val); } -#define is_notop_id(id) (id) /* ((id)>tLAST_TOKEN) */ -#define is_local_id(id) (is_notop_id(id)) /* &&((id)&ID_SCOPE_MASK)==ID_LOCAL) */ -int -mrb_is_local_id(mrb_sym id) +static mrb_bool +is_local_id(mrb_state *mrb, const char *name) { - return is_local_id(id); + if (!name) return FALSE; + return !ISUPPER(name[0]); } -#define is_const_id(id) (is_notop_id(id)) /* &&((id)&ID_SCOPE_MASK)==ID_CONST) */ -int -mrb_is_const_id(mrb_sym id) +static mrb_bool +is_const_id(mrb_state *mrb, const char *name) { - return is_const_id(id); + if (!name) return FALSE; + return ISUPPER(name[0]); } static mrb_value @@ -248,7 +240,7 @@ make_struct(mrb_state *mrb, mrb_value name, mrb_value members, struct RClass * k /* old style: should we warn? */ name = mrb_str_to_str(mrb, name); id = mrb_obj_to_sym(mrb, name); - if (!mrb_is_const_id(id)) { + if (!is_const_id(mrb, mrb_sym2name_len(mrb, id, NULL))) { mrb_name_error(mrb, id, "identifier %S needs to be constant", name); } if (mrb_const_defined_at(mrb, klass, id)) { @@ -270,7 +262,9 @@ make_struct(mrb_state *mrb, mrb_value name, mrb_value members, struct RClass * k ai = mrb_gc_arena_save(mrb); for (i=0; i< len; i++) { mrb_sym id = mrb_symbol(ptr_members[i]); - if (mrb_is_local_id(id) || mrb_is_const_id(id)) { + const char *name = mrb_sym2name_len(mrb, id, NULL); + + if (is_local_id(mrb, name) || is_const_id(mrb, name)) { if (i < N_REF_FUNC) { mrb_define_method_id(mrb, c, id, ref_func[i], MRB_ARGS_NONE()); } @@ -284,27 +278,6 @@ make_struct(mrb_state *mrb, mrb_value name, mrb_value members, struct RClass * k return nstr; } -mrb_value -mrb_struct_define(mrb_state *mrb, const char *name, ...) -{ - va_list ar; - mrb_value nm, ary; - char *mem; - - if (!name) nm = mrb_nil_value(); - else nm = mrb_str_new_cstr(mrb, name); - ary = mrb_ary_new(mrb); - - va_start(ar, name); - while ((mem = va_arg(ar, char*)) != 0) { - mrb_sym slot = mrb_intern_cstr(mrb, mem); - mrb_ary_push(mrb, ary, mrb_symbol_value(slot)); - } - va_end(ar); - - return make_struct(mrb, nm, ary, struct_class(mrb)); -} - /* 15.2.18.3.1 */ /* * call-seq: @@ -344,7 +317,7 @@ mrb_struct_s_def(mrb_state *mrb, mrb_value klass) { mrb_value name, rest; mrb_value *pargv; - int argcnt; + mrb_int argcnt; mrb_int i; mrb_value b, st; mrb_sym id; @@ -391,7 +364,7 @@ mrb_struct_s_def(mrb_state *mrb, mrb_value klass) return st; } -static int +static mrb_int num_members(mrb_state *mrb, struct RClass *klass) { mrb_value members; @@ -407,10 +380,10 @@ num_members(mrb_state *mrb, struct RClass *klass) /* */ static mrb_value -mrb_struct_initialize_withArg(mrb_state *mrb, int argc, mrb_value *argv, mrb_value self) +mrb_struct_initialize_withArg(mrb_state *mrb, mrb_int argc, mrb_value *argv, mrb_value self) { struct RClass *klass = mrb_obj_class(mrb, self); - int i, n; + mrb_int i, n; n = num_members(mrb, klass); if (n < argc) { @@ -436,14 +409,8 @@ mrb_struct_initialize_m(mrb_state *mrb, /*int argc, mrb_value *argv,*/ mrb_value return mrb_struct_initialize_withArg(mrb, argc, argv, self); } -mrb_value -mrb_struct_initialize(mrb_state *mrb, mrb_value self, mrb_value values) -{ - return mrb_struct_initialize_withArg(mrb, RARRAY_LEN(values), RARRAY_PTR(values), self); -} - static mrb_value -inspect_struct(mrb_state *mrb, mrb_value s, int recur) +inspect_struct(mrb_state *mrb, mrb_value s, mrb_bool recur) { const char *cn = mrb_class_name(mrb, mrb_obj_class(mrb, s)); mrb_value members, str = mrb_str_new_lit(mrb, "#<struct "); @@ -464,6 +431,8 @@ inspect_struct(mrb_state *mrb, mrb_value s, int recur) for (i=0; i<len; i++) { mrb_value slot; mrb_sym id; + const char *name; + mrb_int len; if (i > 0) { mrb_str_cat_lit(mrb, str, ", "); @@ -473,11 +442,8 @@ inspect_struct(mrb_state *mrb, mrb_value s, int recur) } slot = ptr_members[i]; id = mrb_symbol(slot); - if (mrb_is_local_id(id) || mrb_is_const_id(id)) { - const char *name; - mrb_int len; - - name = mrb_sym2name_len(mrb, id, &len); + name = mrb_sym2name_len(mrb, id, &len); + if (is_local_id(mrb, name) || is_const_id(mrb, name)) { mrb_str_append(mrb, str, mrb_str_new(mrb, name, len)); } else { @@ -501,12 +467,12 @@ inspect_struct(mrb_state *mrb, mrb_value s, int recur) static mrb_value mrb_struct_inspect(mrb_state *mrb, mrb_value s) { - return inspect_struct(mrb, s, 0); + return inspect_struct(mrb, s, FALSE); } /* 15.2.18.4.9 */ /* :nodoc: */ -mrb_value +static mrb_value mrb_struct_init_copy(mrb_state *mrb, mrb_value copy) { mrb_value s; @@ -584,7 +550,7 @@ struct_aref_int(mrb_state *mrb, mrb_value s, mrb_int i) * joe[:name] #=> "Joe Smith" * joe[0] #=> "Joe Smith" */ -mrb_value +static mrb_value mrb_struct_aref(mrb_state *mrb, mrb_value s) { mrb_value idx; @@ -651,7 +617,7 @@ mrb_struct_aset_sym(mrb_state *mrb, mrb_value s, mrb_sym id, mrb_value val) * joe.zip #=> "90210" */ -mrb_value +static mrb_value mrb_struct_aset(mrb_state *mrb, mrb_value s) { mrb_int i; diff --git a/mrbgems/mruby-time/src/time.c b/mrbgems/mruby-time/src/time.c index 3f8ffabef..410b36173 100644 --- a/mrbgems/mruby-time/src/time.c +++ b/mrbgems/mruby-time/src/time.c @@ -713,7 +713,7 @@ mrb_mruby_time_gem_init(mrb_state* mrb) tc = mrb_define_class(mrb, "Time", mrb->object_class); MRB_SET_INSTANCE_TT(tc, MRB_TT_DATA); mrb_include_module(mrb, tc, mrb_module_get(mrb, "Comparable")); - mrb_define_class_method(mrb, tc, "at", mrb_time_at, MRB_ARGS_ANY()); /* 15.2.19.6.1 */ + mrb_define_class_method(mrb, tc, "at", mrb_time_at, MRB_ARGS_REQ(1) | MRB_ARGS_OPT(1)); /* 15.2.19.6.1 */ mrb_define_class_method(mrb, tc, "gm", mrb_time_gm, MRB_ARGS_ARG(1,6)); /* 15.2.19.6.2 */ mrb_define_class_method(mrb, tc, "local", mrb_time_local, MRB_ARGS_ARG(1,6)); /* 15.2.19.6.3 */ mrb_define_class_method(mrb, tc, "mktime", mrb_time_local, MRB_ARGS_ARG(1,6));/* 15.2.19.6.4 */ diff --git a/mrblib/error.rb b/mrblib/error.rb index 6e8181e9d..a5b6b3223 100644 --- a/mrblib/error.rb +++ b/mrblib/error.rb @@ -48,6 +48,12 @@ end # ISO 15.2.32 class NoMethodError < NameError + attr_reader :args + + def initialize(message=nil, name=nil, args=nil) + @args = args + super message, name + end end # ISO 15.2.33 diff --git a/mrblib/kernel.rb b/mrblib/kernel.rb index 81d7acf5d..d0fe47300 100644 --- a/mrblib/kernel.rb +++ b/mrblib/kernel.rb @@ -4,39 +4,24 @@ # ISO 15.3.1 module Kernel - # 15.3.1.2.1 - def self.`(s) - raise NotImplementedError.new("` not implemented") - end - + # 15.3.1.2.1 Kernel.` + # provided by Kernel#` # 15.3.1.3.5 def `(s) - Kernel.`(s) + raise NotImplementedError.new("backquotes not implemented") end ## - # Calls the given block repetitively. - # - # ISO 15.3.1.2.8 - # provided by Kernel#loop - # def self.loop #(&block) - # while(true) - # yield - # end - # end + # 15.3.1.2.3 Kernel.eval + # 15.3.1.3.12 Kernel#eval + # NotImplemented by mruby core; use mruby-eval gem - # 15.3.1.2.3 - def self.eval(s) - raise NotImplementedError.new("eval not implemented") - end - - # 15.3.1.3.12 - def eval(s) - Kernel.eval(s) - end + ## + # ISO 15.3.1.2.8 Kernel.loop + # provided by Kernel#loop ## - # Alias for +Kernel.loop+. + # Calls the given block repetitively. # # ISO 15.3.1.3.29 def loop diff --git a/mrblib/mrblib.rake b/mrblib/mrblib.rake index 0c549f5b8..d156a2683 100644 --- a/mrblib/mrblib.rake +++ b/mrblib/mrblib.rake @@ -6,8 +6,8 @@ MRuby.each_target do self.libmruby << objfile("#{current_build_dir}/mrblib") file objfile("#{current_build_dir}/mrblib") => "#{current_build_dir}/mrblib.c" - file "#{current_build_dir}/mrblib.c" => [mrbcfile] + Dir.glob("#{current_dir}/*.rb").sort do |t| - mrbc_, *rbfiles = t.prerequisites + file "#{current_build_dir}/mrblib.c" => [mrbcfile, __FILE__] + Dir.glob("#{current_dir}/*.rb").sort do |t| + _, _, *rbfiles = t.prerequisites FileUtils.mkdir_p File.dirname(t.name) open(t.name, 'w') do |f| _pp "GEN", "*.rb", "#{t.name.relative_path}" diff --git a/mrblib/print.rb b/mrblib/print.rb deleted file mode 100644 index 1ae3ae84b..000000000 --- a/mrblib/print.rb +++ /dev/null @@ -1,18 +0,0 @@ -## -# Kernel -# -# ISO 15.3.1 -module Kernel - def print(*a) - raise NotImplementedError.new('print not available') - end - def puts(*a) - raise NotImplementedError.new('puts not available') - end - def p(*a) - raise NotImplementedError.new('p not available') - end - def printf(*args) - raise NotImplementedError.new('printf not available') - end -end diff --git a/src/backtrace.c b/src/backtrace.c index 7768a3206..1e1f9fa1a 100644 --- a/src/backtrace.c +++ b/src/backtrace.c @@ -71,7 +71,6 @@ output_backtrace(mrb_state *mrb, mrb_int ciidx, mrb_code *pc0, output_stream_fun for (i = ciidx; i >= 0; i--) { ci = &mrb->c->cibase[i]; filename = NULL; - lineno = -1; if (!ci->proc) continue; if (MRB_PROC_CFUNC_P(ci->proc)) { diff --git a/src/class.c b/src/class.c index db9a8ac26..ba078911b 100644 --- a/src/class.c +++ b/src/class.c @@ -1216,8 +1216,7 @@ mrb_bob_missing(mrb_state *mrb, mrb_value mod) repr = mrb_any_to_s(mrb, mod); } - mrb_raisef(mrb, E_NOMETHOD_ERROR, "undefined method '%S' for %S", - mrb_sym2str(mrb, name), repr); + mrb_no_method_error(mrb, name, alen, a, "undefined method '%S' for %S", mrb_sym2str(mrb, name), repr); /* not reached */ return mrb_nil_value(); } diff --git a/src/codegen.c b/src/codegen.c index cec0d226f..03c752826 100644 --- a/src/codegen.c +++ b/src/codegen.c @@ -475,6 +475,8 @@ new_msym(codegen_scope *s, mrb_sym sym) { size_t i, len; + mrb_assert(s->irep); + len = s->irep->slen; if (len > 256) len = 256; for (i=0; i<len; i++) { @@ -549,7 +551,8 @@ for_body(codegen_scope *s, node *tree) /* generate receiver */ codegen(s, tree->cdr->car, VAL); /* generate loop-block */ - s = scope_new(s->mrb, s, tree->car); + s = scope_new(s->mrb, s, NULL); + push(); /* push for a block parameter */ lp = loop_push(s, LOOP_FOR); lp->pc1 = new_label(s); @@ -1105,21 +1108,23 @@ readint_mrb_int(codegen_scope *s, const char *p, int base, mrb_bool neg, mrb_boo codegen_error(s, "malformed readint input"); } - if (neg) { - if ((MRB_INT_MIN + n)/base > result) { - *overflow = TRUE; - return 0; + if(base > 0) { + if (neg) { + if ((MRB_INT_MIN + n)/base > result) { + *overflow = TRUE; + return 0; + } + result *= base; + result -= n; } - result *= base; - result -= n; - } - else { - if ((MRB_INT_MAX - n)/base < result) { - *overflow = TRUE; - return 0; + else { + if ((MRB_INT_MAX - n)/base < result) { + *overflow = TRUE; + return 0; + } + result *= base; + result += n; } - result *= base; - result += n; } p++; } @@ -2651,7 +2656,7 @@ print_r(mrb_state *mrb, mrb_irep *irep, size_t n, int pre) if (n == 0) return 0; - for (i=0; i<irep->nlocals; i++) { + for (i=0; i+1<irep->nlocals; i++) { if (irep->lv[i].r == n) { mrb_sym sym = irep->lv[i].name; if (pre) printf(" "); @@ -2994,7 +2999,7 @@ codedump(mrb_state *mrb, mrb_irep *irep) { mrb_value v = irep->pool[GETARG_Bx(c)]; mrb_value s = mrb_str_dump(mrb, mrb_str_new(mrb, RSTRING_PTR(v), RSTRING_LEN(v))); - printf("OP_STRING\tR%d\tL(%d)\t; %s", GETARG_A(c), GETARG_B(c), RSTRING_PTR(s)); + printf("OP_STRING\tR%d\tL(%d)\t; %s", GETARG_A(c), GETARG_Bx(c), RSTRING_PTR(s)); } print_lv(mrb, irep, c, RA); break; diff --git a/src/dump.c b/src/dump.c index 1acb466a0..b820f1a68 100644 --- a/src/dump.c +++ b/src/dump.c @@ -343,7 +343,6 @@ write_section_irep(mrb_state *mrb, mrb_irep *irep, uint8_t *bin) if (result != MRB_DUMP_OK) { return result; } - cur += rsize; section_size += rsize; write_section_irep_header(mrb, section_size, bin); diff --git a/src/error.c b/src/error.c index 360df8f2e..5ca013527 100644 --- a/src/error.c +++ b/src/error.c @@ -227,7 +227,9 @@ mrb_noreturn void mrb_exc_raise(mrb_state *mrb, mrb_value exc) { mrb->exc = mrb_obj_ptr(exc); - exc_debug_info(mrb, mrb->exc); + if (!mrb->out_of_memory) { + exc_debug_info(mrb, mrb->exc); + } if (!mrb->jmp) { mrb_p(mrb, exc); abort(); @@ -442,12 +444,26 @@ mrb_sys_fail(mrb_state *mrb, const char *mesg) } } +mrb_noreturn void +mrb_no_method_error(mrb_state *mrb, mrb_sym id, mrb_int argc, const mrb_value *argv, char const* fmt, ...) +{ + mrb_value exc; + va_list ap; + + va_start(ap, fmt); + exc = mrb_funcall(mrb, mrb_obj_value(E_NOMETHOD_ERROR), "new", 3, + mrb_vformat(mrb, fmt, ap), mrb_symbol_value(id), + mrb_ary_new_from_values(mrb, argc, argv)); + va_end(ap); + mrb_exc_raise(mrb, exc); +} + void mrb_init_exception(mrb_state *mrb) { - struct RClass *exception, *script_error; + struct RClass *exception, *runtime_error, *script_error; - mrb->eException_class = exception = mrb_define_class(mrb, "Exception", mrb->object_class); /* 15.2.22 */ + mrb->eException_class = exception = mrb_define_class(mrb, "Exception", mrb->object_class); /* 15.2.22 */ mrb_define_class_method(mrb, exception, "exception", mrb_instance_new, MRB_ARGS_ANY()); mrb_define_method(mrb, exception, "exception", exc_exception, MRB_ARGS_ANY()); mrb_define_method(mrb, exception, "initialize", exc_initialize, MRB_ARGS_ANY()); @@ -457,8 +473,9 @@ mrb_init_exception(mrb_state *mrb) mrb_define_method(mrb, exception, "inspect", exc_inspect, MRB_ARGS_NONE()); mrb_define_method(mrb, exception, "backtrace", mrb_exc_backtrace, MRB_ARGS_NONE()); - mrb->eStandardError_class = mrb_define_class(mrb, "StandardError", mrb->eException_class); /* 15.2.23 */ - mrb_define_class(mrb, "RuntimeError", mrb->eStandardError_class); /* 15.2.28 */ - script_error = mrb_define_class(mrb, "ScriptError", mrb->eException_class); /* 15.2.37 */ - mrb_define_class(mrb, "SyntaxError", script_error); /* 15.2.38 */ + mrb->eStandardError_class = mrb_define_class(mrb, "StandardError", mrb->eException_class); /* 15.2.23 */ + runtime_error = mrb_define_class(mrb, "RuntimeError", mrb->eStandardError_class); /* 15.2.28 */ + mrb->nomem_err = mrb_obj_ptr(mrb_exc_new_str(mrb, runtime_error, mrb_str_new_lit(mrb, "Out of memory"))); + script_error = mrb_define_class(mrb, "ScriptError", mrb->eException_class); /* 15.2.37 */ + mrb_define_class(mrb, "SyntaxError", script_error); /* 15.2.38 */ } @@ -189,7 +189,7 @@ mrb_realloc(mrb_state *mrb, void *p, size_t len) } else { mrb->out_of_memory = TRUE; - mrb_raise(mrb, E_RUNTIME_ERROR, "Out of memory"); + mrb_exc_raise(mrb, mrb_obj_value(mrb->nomem_err)); } } else { @@ -705,6 +705,8 @@ root_scan_phase(mrb_state *mrb) mrb_gc_mark(mrb, (struct RBasic*)mrb->top_self); /* mark exception */ mrb_gc_mark(mrb, (struct RBasic*)mrb->exc); + /* mark pre-allocated exception */ + mrb_gc_mark(mrb, (struct RBasic*)mrb->nomem_err); mark_context(mrb, mrb->root_c); if (mrb->root_c->fib) { diff --git a/src/hash.c b/src/hash.c index 997610953..1d449db3f 100644 --- a/src/hash.c +++ b/src/hash.c @@ -216,11 +216,12 @@ mrb_hash_set(mrb_state *mrb, mrb_value hash, mrb_value key, mrb_value val) if (r != 0) { /* expand */ int ai = mrb_gc_arena_save(mrb); - kh_key(h, k) = KEY(key); + key = kh_key(h, k) = KEY(key); mrb_gc_arena_restore(mrb, ai); kh_value(h, k).n = kh_size(h)-1; } + mrb_field_write_barrier_value(mrb, (struct RBasic*)RHASH(hash), key); mrb_field_write_barrier_value(mrb, (struct RBasic*)RHASH(hash), val); return; } diff --git a/src/init.c b/src/init.c index c08c4b046..9489cea10 100644 --- a/src/init.c +++ b/src/init.c @@ -54,11 +54,3 @@ mrb_init_core(mrb_state *mrb) mrb_init_mrbgems(mrb); DONE; #endif } - -void -mrb_final_core(mrb_state *mrb) -{ -#ifndef DISABLE_GEMS - mrb_final_mrbgems(mrb); DONE; -#endif -} diff --git a/src/kernel.c b/src/kernel.c index f6f2872ea..0258e5c15 100644 --- a/src/kernel.c +++ b/src/kernel.c @@ -1118,7 +1118,7 @@ mrb_init_kernel(mrb_state *mrb) mrb_define_class_method(mrb, krn, "iterator?", mrb_f_block_given_p_m, MRB_ARGS_NONE()); /* 15.3.1.2.5 */ mrb_define_class_method(mrb, krn, "local_variables", mrb_local_variables, MRB_ARGS_NONE()); /* 15.3.1.2.7 */ ; /* 15.3.1.2.11 */ - mrb_define_class_method(mrb, krn, "raise", mrb_f_raise, MRB_ARGS_ANY()); /* 15.3.1.2.12 */ + mrb_define_class_method(mrb, krn, "raise", mrb_f_raise, MRB_ARGS_OPT(2)); /* 15.3.1.2.12 */ mrb_define_method(mrb, krn, "singleton_class", mrb_singleton_class, MRB_ARGS_NONE()); diff --git a/src/mruby_core.rake b/src/mruby_core.rake index 04be0736c..88fca83fc 100644 --- a/src/mruby_core.rake +++ b/src/mruby_core.rake @@ -13,7 +13,7 @@ MRuby.each_target do cxx_abi_dependency = %w(codegen error vm) cxx_abi_objs = cxx_abi_dependency.map { |v| src = "#{current_build_dir}/#{v}.cxx" - file src => "#{current_dir}/#{v}.c" do |t| + file src => ["#{current_dir}/#{v}.c", __FILE__] do |t| File.open(t.name, 'w') do |f| f.write <<EOS #define __STDC_CONSTANT_MACROS @@ -37,7 +37,7 @@ EOS } cxx_abi_objs << objfile("#{current_build_dir}/y.tab") - file "#{current_build_dir}/y.tab.cxx" => "#{current_build_dir}/y.tab.c" do |t| + file "#{current_build_dir}/y.tab.cxx" => ["#{current_build_dir}/y.tab.c", __FILE__] do |t| File.open(t.name, 'w') do |f| f.write <<EOS #define __STDC_CONSTANT_MACROS diff --git a/src/parse.y b/src/parse.y index ab5caa28e..2c7e788d9 100644 --- a/src/parse.y +++ b/src/parse.y @@ -42,11 +42,7 @@ static void yywarning(parser_state *p, const char *s); static void backref_error(parser_state *p, node *n); static void tokadd(parser_state *p, int32_t c); -#ifndef isascii -#define isascii(c) (((c) & ~0x7f) == 0) -#endif - -#define identchar(c) (isalnum(c) || (c) == '_' || !isascii(c)) +#define identchar(c) (ISALNUM(c) || (c) == '_' || !ISASCII(c)) typedef unsigned int stack_type; @@ -2879,15 +2875,15 @@ var_ref : variable | keyword_self { $$ = new_self(p); - } + } | keyword_true { $$ = new_true(p); - } + } | keyword_false { $$ = new_false(p); - } + } | keyword__FILE__ { if (!p->filename) { @@ -3381,7 +3377,9 @@ nextc(parser_state *p) c = (unsigned char)*p->s++; } } - p->column++; + if (c >= 0) { + p->column++; + } if (c == '\r') { c = nextc(p); if (c != '\n') { @@ -3396,16 +3394,17 @@ nextc(parser_state *p) if (!p->cxt) return -1; else { if (p->cxt->partial_hook(p) < 0) - return -1; - return -2; + return -1; /* end of program(s) */ + return -2; /* end of a file in the program files */ } } static void pushback(parser_state *p, int c) { - if (c < 0) return; - p->column--; + if (c >= 0) { + p->column--; + } p->pb = cons((node*)(intptr_t)c, p->pb); } @@ -3429,7 +3428,7 @@ peekc_n(parser_state *p, int n) do { c0 = nextc(p); - if (c0 < 0) return c0; + if (c0 == -1) return c0; /* do not skip partial EOF */ list = push(list, (node*)(intptr_t)c0); } while(n--); if (p->pb) { @@ -3785,7 +3784,7 @@ read_escape(parser_state *p) eof: case -1: - case -2: + case -2: /* end of a file */ yyerror(p, "Invalid escape character syntax"); return '\0'; @@ -3915,7 +3914,8 @@ parse_string(parser_state *p) return tHD_LITERAL_DELIM; } } - } while (ISSPACE(c = nextc(p))); + c = nextc(p); + } while (ISSPACE(c)); pushback(p, c); return tLITERAL_DELIM; } @@ -4100,7 +4100,7 @@ parser_yylex(parser_state *p) case '#': /* it's a comment */ skip(p, '\n'); /* fall through */ - case -2: /* end of partial script. */ + case -2: /* end of a file */ case '\n': maybe_heredoc: heredoc_treat_nextline(p); @@ -4135,7 +4135,7 @@ parser_yylex(parser_state *p) goto retry; } case -1: /* EOF */ - case -2: /* end of partial script */ + case -2: /* end of a file */ goto normal_newline; default: pushback(p, c); @@ -4209,14 +4209,14 @@ parser_yylex(parser_state *p) static const char end[] = "\n=end"; if (peeks(p, begin)) { c = peekc_n(p, sizeof(begin)-1); - if (c < 0 || isspace(c)) { + if (c < 0 || ISSPACE(c)) { do { if (!skips(p, end)) { yyerror(p, "embedded document meets end of file"); return 0; } c = nextc(p); - } while (!(c < 0 || isspace(c))); + } while (!(c < 0 || ISSPACE(c))); if (c != '\n') skip(p, '\n'); p->lineno++; p->column = 0; @@ -4247,7 +4247,6 @@ parser_yylex(parser_state *p) return '='; case '<': - last_state = p->lstate; c = nextc(p); if (c == '<' && p->lstate != EXPR_DOT && @@ -4341,7 +4340,7 @@ parser_yylex(parser_state *p) yyerror(p, "incomplete character syntax"); return 0; } - if (isspace(c)) { + if (ISSPACE(c)) { if (!IS_ARG()) { int c2; switch (c) { @@ -4378,7 +4377,7 @@ parser_yylex(parser_state *p) p->lstate = EXPR_VALUE; return '?'; } - token_column = newtok(p); + newtok(p); /* need support UTF-8 if configured */ if ((isalnum(c) || c == '_')) { int c2 = nextc(p); @@ -4542,7 +4541,7 @@ parser_yylex(parser_state *p) is_float = seen_point = seen_e = nondigit = 0; p->lstate = EXPR_END; - token_column = newtok(p); + newtok(p); if (c == '-' || c == '+') { tokadd(p, c); c = nextc(p); @@ -5164,7 +5163,6 @@ parser_yylex(parser_state *p) { int result = 0; - last_state = p->lstate; switch (tok(p)[0]) { case '$': p->lstate = EXPR_END; @@ -5194,7 +5192,7 @@ parser_yylex(parser_state *p) pushback(p, c); } } - if (result == 0 && isupper((int)(unsigned char)tok(p)[0])) { + if (result == 0 && ISUPPER(tok(p)[0])) { result = tCONSTANT; } else { diff --git a/src/state.c b/src/state.c index 9dd798f92..c0a9c14c2 100644 --- a/src/state.c +++ b/src/state.c @@ -14,7 +14,6 @@ void mrb_init_heap(mrb_state*); void mrb_init_core(mrb_state*); -void mrb_final_core(mrb_state*); static mrb_value inspect_main(mrb_state *mrb, mrb_value mod) @@ -30,7 +29,7 @@ mrb_open_allocf(mrb_allocf f, void *ud) mrb_state *mrb; #ifdef MRB_NAN_BOXING - mrb_assert(sizeof(void*) == 4); + mrb_static_assert(sizeof(void*) == 4, "when using NaN boxing sizeof pointer must be 4 byte"); #endif mrb = (mrb_state *)(f)(NULL, NULL, sizeof(mrb_state), ud); @@ -40,6 +39,7 @@ mrb_open_allocf(mrb_allocf f, void *ud) mrb->ud = ud; mrb->allocf = f; mrb->current_white_part = MRB_GC_WHITE_A; + mrb->atexit_stack_len = 0; #ifndef MRB_GC_FIXED_ARENA mrb->arena = (struct RBasic**)mrb_malloc(mrb, sizeof(struct RBasic*)*MRB_GC_ARENA_SIZE); @@ -221,7 +221,13 @@ mrb_free_context(mrb_state *mrb, struct mrb_context *c) void mrb_close(mrb_state *mrb) { - mrb_final_core(mrb); + if (mrb->atexit_stack_len > 0) { + mrb_int i; + for (i = mrb->atexit_stack_len; i > 0; --i) { + mrb->atexit_stack[i - 1](mrb); + } + mrb_free(mrb, mrb->atexit_stack); + } /* free */ mrb_gc_free_gv(mrb); @@ -258,3 +264,18 @@ mrb_top_self(mrb_state *mrb) } return mrb_obj_value(mrb->top_self); } + +void +mrb_state_atexit(mrb_state *mrb, mrb_atexit_func f) +{ + size_t stack_size; + + stack_size = sizeof(mrb_atexit_func) * (mrb->atexit_stack_len + 1); + if (mrb->atexit_stack_len == 0) { + mrb->atexit_stack = (mrb_atexit_func*)mrb_malloc(mrb, stack_size); + } else { + mrb->atexit_stack = (mrb_atexit_func*)mrb_realloc(mrb, mrb->atexit_stack, stack_size); + } + + mrb->atexit_stack[mrb->atexit_stack_len++] = f; +} diff --git a/src/string.c b/src/string.c index 1572cab14..9b5707dc0 100644 --- a/src/string.c +++ b/src/string.c @@ -17,28 +17,6 @@ #include "mruby/string.h" #include "mruby/re.h" -#define STR_EMBED_P(s) ((s)->flags & MRB_STR_EMBED) -#define STR_SET_EMBED_FLAG(s) ((s)->flags |= MRB_STR_EMBED) -#define STR_UNSET_EMBED_FLAG(s) ((s)->flags &= ~(MRB_STR_EMBED|MRB_STR_EMBED_LEN_MASK)) -#define STR_SET_EMBED_LEN(s, n) do {\ - size_t tmp_n = (n);\ - s->flags &= ~MRB_STR_EMBED_LEN_MASK;\ - s->flags |= (tmp_n) << MRB_STR_EMBED_LEN_SHIFT;\ -} while (0) -#define STR_SET_LEN(s, n) do {\ - if (STR_EMBED_P(s)) {\ - STR_SET_EMBED_LEN((s),(n));\ - } else {\ - s->as.heap.len = (mrb_int)(n);\ - }\ -} while (0) -#define RSTRING_EMBED_LEN(s) \ - (mrb_int)((RSTRING(s)->flags & MRB_STR_EMBED_LEN_MASK) >> MRB_STR_EMBED_LEN_SHIFT) -#define STR_EMBED_LEN(s)\ - (mrb_int)(((s)->flags & MRB_STR_EMBED_LEN_MASK) >> MRB_STR_EMBED_LEN_SHIFT) -#define STR_PTR(s) ((STR_EMBED_P(s)) ? (s)->as.ary : (s)->as.heap.ptr) -#define STR_LEN(s) ((STR_EMBED_P(s)) ? STR_EMBED_LEN(s) : (s)->as.heap.len) - const char mrb_digitmap[] = "0123456789abcdefghijklmnopqrstuvwxyz"; typedef struct mrb_shared_string { @@ -48,18 +26,14 @@ typedef struct mrb_shared_string { mrb_int len; } mrb_shared_string; -#define STR_SHARED_P(s) ((s)->flags & MRB_STR_SHARED) -#define STR_SET_SHARED_FLAG(s) ((s)->flags |= MRB_STR_SHARED) -#define STR_UNSET_SHARED_FLAG(s) ((s)->flags &= ~MRB_STR_SHARED) - static mrb_value str_replace(mrb_state *mrb, struct RString *s1, struct RString *s2); static mrb_value mrb_str_subseq(mrb_state *mrb, mrb_value str, mrb_int beg, mrb_int len); mrb_int mrb_str_strlen(mrb_state *mrb, struct RString *s) { - mrb_int i, max = STR_LEN(s); - char *p = STR_PTR(s); + mrb_int i, max = RSTR_LEN(s); + char *p = RSTR_PTR(s); if (!p) return 0; for (i=0; i<max; i++) { @@ -73,19 +47,19 @@ mrb_str_strlen(mrb_state *mrb, struct RString *s) static inline void resize_capa(mrb_state *mrb, struct RString *s, mrb_int capacity) { - if (STR_EMBED_P(s)) { + if (RSTR_EMBED_P(s)) { if (RSTRING_EMBED_LEN_MAX < capacity) { char *const tmp = (char *)mrb_malloc(mrb, capacity+1); - const mrb_int len = STR_EMBED_LEN(s); + const mrb_int len = RSTR_EMBED_LEN(s); memcpy(tmp, s->as.ary, len); - STR_UNSET_EMBED_FLAG(s); + RSTR_UNSET_EMBED_FLAG(s); s->as.heap.ptr = tmp; s->as.heap.len = len; s->as.heap.aux.capa = capacity; } } else { - s->as.heap.ptr = (char *)mrb_realloc(mrb, STR_PTR(s), capacity+1); + s->as.heap.ptr = (char *)mrb_realloc(mrb, RSTR_PTR(s), capacity+1); s->as.heap.aux.capa = capacity; } } @@ -105,20 +79,20 @@ str_decref(mrb_state *mrb, mrb_shared_string *shared) void mrb_str_modify(mrb_state *mrb, struct RString *s) { - if (STR_SHARED_P(s)) { + if (RSTR_SHARED_P(s)) { mrb_shared_string *shared = s->as.heap.aux.shared; if (shared->refcnt == 1 && s->as.heap.ptr == shared->ptr) { s->as.heap.ptr = shared->ptr; s->as.heap.aux.capa = shared->len; - STR_PTR(s)[s->as.heap.len] = '\0'; + RSTR_PTR(s)[s->as.heap.len] = '\0'; mrb_free(mrb, shared); } else { char *ptr, *p; mrb_int len; - p = STR_PTR(s); + p = RSTR_PTR(s); len = s->as.heap.len; ptr = (char *)mrb_malloc(mrb, (size_t)len + 1); if (p) { @@ -129,19 +103,19 @@ mrb_str_modify(mrb_state *mrb, struct RString *s) s->as.heap.aux.capa = len; str_decref(mrb, shared); } - STR_UNSET_SHARED_FLAG(s); + RSTR_UNSET_SHARED_FLAG(s); return; } - if (s->flags & MRB_STR_NOFREE) { + if (RSTR_NOFREE_P(s)) { char *p = s->as.heap.ptr; s->as.heap.ptr = (char *)mrb_malloc(mrb, (size_t)s->as.heap.len+1); if (p) { - memcpy(STR_PTR(s), p, s->as.heap.len); + memcpy(RSTR_PTR(s), p, s->as.heap.len); } - STR_PTR(s)[s->as.heap.len] = '\0'; + RSTR_PTR(s)[s->as.heap.len] = '\0'; s->as.heap.aux.capa = s->as.heap.len; - s->flags &= ~MRB_STR_NOFREE; + RSTR_UNSET_NOFREE_FLAG(s); return; } } @@ -153,13 +127,13 @@ mrb_str_resize(mrb_state *mrb, mrb_value str, mrb_int len) struct RString *s = mrb_str_ptr(str); mrb_str_modify(mrb, s); - slen = STR_LEN(s); + slen = RSTR_LEN(s); if (len != slen) { if (slen < len || slen - len > 256) { resize_capa(mrb, s, len); } - STR_SET_LEN(s, len); - STR_PTR(s)[len] = '\0'; /* sentinel */ + RSTR_SET_LEN(s, len); + RSTR_PTR(s)[len] = '\0'; /* sentinel */ } return str; } @@ -173,8 +147,8 @@ str_new(mrb_state *mrb, const char *p, size_t len) s = mrb_obj_alloc_string(mrb); if (len < RSTRING_EMBED_LEN_MAX) { - STR_SET_EMBED_FLAG(s); - STR_SET_EMBED_LEN(s,len); + RSTR_SET_EMBED_FLAG(s); + RSTR_SET_EMBED_LEN(s,len); if (p) { memcpy(s->as.ary, p, len); } @@ -189,7 +163,7 @@ str_new(mrb_state *mrb, const char *p, size_t len) memcpy(s->as.heap.ptr, p, len); } } - STR_PTR(s)[len] = '\0'; + RSTR_PTR(s)[len] = '\0'; return s; } @@ -228,7 +202,7 @@ mrb_str_buf_new(mrb_state *mrb, size_t capa) s->as.heap.len = 0; s->as.heap.aux.capa = capa; s->as.heap.ptr = (char *)mrb_malloc(mrb, capa+1); - STR_PTR(s)[0] = '\0'; + RSTR_PTR(s)[0] = '\0'; return mrb_obj_value(s); } @@ -242,19 +216,19 @@ str_buf_cat(mrb_state *mrb, struct RString *s, const char *ptr, size_t len) if (len == 0) return; mrb_str_modify(mrb, s); - if (ptr >= STR_PTR(s) && ptr <= STR_PTR(s) + (size_t)STR_LEN(s)) { - off = ptr - STR_PTR(s); + if (ptr >= RSTR_PTR(s) && ptr <= RSTR_PTR(s) + (size_t)RSTR_LEN(s)) { + off = ptr - RSTR_PTR(s); } - if (STR_EMBED_P(s)) + if (RSTR_EMBED_P(s)) capa = RSTRING_EMBED_LEN_MAX; else capa = s->as.heap.aux.capa; - if (STR_LEN(s) >= MRB_INT_MAX - (mrb_int)len) { + if (RSTR_LEN(s) >= MRB_INT_MAX - (mrb_int)len) { mrb_raise(mrb, E_ARGUMENT_ERROR, "string size too big"); } - total = STR_LEN(s)+len; + total = RSTR_LEN(s)+len; if (capa <= total) { while (total > capa) { if (capa + 1 >= MRB_INT_MAX / 2) { @@ -266,12 +240,12 @@ str_buf_cat(mrb_state *mrb, struct RString *s, const char *ptr, size_t len) resize_capa(mrb, s, capa); } if (off != -1) { - ptr = STR_PTR(s) + off; + ptr = RSTR_PTR(s) + off; } - memcpy(STR_PTR(s) + STR_LEN(s), ptr, len); + memcpy(RSTR_PTR(s) + RSTR_LEN(s), ptr, len); mrb_assert_int_fit(size_t, total, mrb_int, MRB_INT_MAX); - STR_SET_LEN(s, total); - STR_PTR(s)[total] = '\0'; /* sentinel */ + RSTR_SET_LEN(s, total); + RSTR_PTR(s)[total] = '\0'; /* sentinel */ } mrb_value @@ -324,11 +298,11 @@ mrb_str_new_static(mrb_state *mrb, const char *p, size_t len) void mrb_gc_free_str(mrb_state *mrb, struct RString *str) { - if (STR_EMBED_P(str)) + if (RSTR_EMBED_P(str)) /* no code */; - else if (STR_SHARED_P(str)) + else if (RSTR_SHARED_P(str)) str_decref(mrb, str->as.heap.aux.shared); - else if ((str->flags & MRB_STR_NOFREE) == 0) + else if (!RSTR_NOFREE_P(str)) mrb_free(mrb, str->as.heap.ptr); } @@ -342,34 +316,34 @@ mrb_str_to_cstr(mrb_state *mrb, mrb_value str0) } s = str_new(mrb, RSTRING_PTR(str0), RSTRING_LEN(str0)); - if ((strlen(STR_PTR(s)) ^ STR_LEN(s)) != 0) { + if ((strlen(RSTR_PTR(s)) ^ RSTR_LEN(s)) != 0) { mrb_raise(mrb, E_ARGUMENT_ERROR, "string contains null byte"); } - return STR_PTR(s); + return RSTR_PTR(s); } static void str_make_shared(mrb_state *mrb, struct RString *s) { - if (!STR_SHARED_P(s)) { + if (!RSTR_SHARED_P(s)) { mrb_shared_string *shared = (mrb_shared_string *)mrb_malloc(mrb, sizeof(mrb_shared_string)); shared->refcnt = 1; - if (STR_EMBED_P(s)) { - const mrb_int len = STR_EMBED_LEN(s); + if (RSTR_EMBED_P(s)) { + const mrb_int len = RSTR_EMBED_LEN(s); char *const tmp = (char *)mrb_malloc(mrb, len+1); memcpy(tmp, s->as.ary, len); tmp[len] = '\0'; - STR_UNSET_EMBED_FLAG(s); + RSTR_UNSET_EMBED_FLAG(s); s->as.heap.ptr = tmp; s->as.heap.len = len; shared->nofree = FALSE; shared->ptr = s->as.heap.ptr; } - else if (s->flags & MRB_STR_NOFREE) { + else if (RSTR_NOFREE_P(s)) { shared->nofree = TRUE; shared->ptr = s->as.heap.ptr; - s->flags &= ~MRB_STR_NOFREE; + RSTR_UNSET_NOFREE_FLAG(s); } else { shared->nofree = FALSE; @@ -382,7 +356,7 @@ str_make_shared(mrb_state *mrb, struct RString *s) } shared->len = s->as.heap.len; s->as.heap.aux.shared = shared; - STR_SET_SHARED_FLAG(s); + RSTR_SET_SHARED_FLAG(s); } } @@ -397,8 +371,8 @@ mrb_str_body(mrb_value str, int *len_p) { struct RString *s = mrb_str_ptr(str); - *len_p = STR_LEN(s); - return STR_PTR(s); + *len_p = RSTR_LEN(s); + return RSTR_PTR(s); } /* @@ -418,14 +392,14 @@ mrb_str_concat(mrb_state *mrb, mrb_value self, mrb_value other) other = mrb_str_to_str(mrb, other); } s2 = mrb_str_ptr(other); - len = STR_LEN(s1) + STR_LEN(s2); + len = RSTR_LEN(s1) + RSTR_LEN(s2); if (RSTRING_CAPA(self) < len) { resize_capa(mrb, s1, len); } - memcpy(STR_PTR(s1)+STR_LEN(s1), STR_PTR(s2), STR_LEN(s2)); - STR_SET_LEN(s1, len); - STR_PTR(s1)[len] = '\0'; + memcpy(RSTR_PTR(s1)+RSTR_LEN(s1), RSTR_PTR(s2), RSTR_LEN(s2)); + RSTR_SET_LEN(s1, len); + RSTR_PTR(s1)[len] = '\0'; } /* @@ -441,9 +415,9 @@ mrb_str_plus(mrb_state *mrb, mrb_value a, mrb_value b) struct RString *s2 = mrb_str_ptr(b); struct RString *t; - t = str_new(mrb, 0, STR_LEN(s) + STR_LEN(s2)); - memcpy(STR_PTR(t), STR_PTR(s), STR_LEN(s)); - memcpy(STR_PTR(t) + STR_LEN(s), STR_PTR(s2), STR_LEN(s2)); + t = str_new(mrb, 0, RSTR_LEN(s) + RSTR_LEN(s2)); + memcpy(RSTR_PTR(t), RSTR_PTR(s), RSTR_LEN(s)); + memcpy(RSTR_PTR(t) + RSTR_LEN(s), RSTR_PTR(s2), RSTR_LEN(s2)); return mrb_obj_value(t); } @@ -475,7 +449,7 @@ static mrb_value mrb_str_bytesize(mrb_state *mrb, mrb_value self) { struct RString *s = mrb_str_ptr(self); - return mrb_fixnum_value(STR_LEN(s)); + return mrb_fixnum_value(RSTR_LEN(s)); } /* 15.2.10.5.26 */ @@ -490,7 +464,7 @@ mrb_value mrb_str_size(mrb_state *mrb, mrb_value self) { struct RString *s = mrb_str_ptr(self); - return mrb_fixnum_value(STR_LEN(s)); + return mrb_fixnum_value(RSTR_LEN(s)); } /* 15.2.10.5.1 */ @@ -521,7 +495,7 @@ mrb_str_times(mrb_state *mrb, mrb_value self) len = RSTRING_LEN(self)*times; str2 = str_new(mrb, 0, len); str_with_class(mrb, str2, self); - p = STR_PTR(str2); + p = RSTR_PTR(str2); if (len > 0) { n = RSTRING_LEN(self); memcpy(p, RSTRING_PTR(self), n); @@ -531,7 +505,7 @@ mrb_str_times(mrb_state *mrb, mrb_value self) } memcpy(p + n, p, len-n); } - p[STR_LEN(str2)] = '\0'; + p[RSTR_LEN(str2)] = '\0'; return mrb_obj_value(str2); } @@ -555,11 +529,11 @@ mrb_str_cmp(mrb_state *mrb, mrb_value str1, mrb_value str2) struct RString *s1 = mrb_str_ptr(str1); struct RString *s2 = mrb_str_ptr(str2); - len = lesser(STR_LEN(s1), STR_LEN(s2)); - retval = memcmp(STR_PTR(s1), STR_PTR(s2), len); + len = lesser(RSTR_LEN(s1), RSTR_LEN(s2)); + retval = memcmp(RSTR_PTR(s1), RSTR_PTR(s2), len); if (retval == 0) { - if (STR_LEN(s1) == STR_LEN(s2)) return 0; - if (STR_LEN(s1) > STR_LEN(s2)) return 1; + if (RSTR_LEN(s1) == RSTR_LEN(s2)) return 0; + if (RSTR_LEN(s1) > RSTR_LEN(s2)) return 1; return -1; } if (retval > 0) return 1; @@ -913,8 +887,8 @@ mrb_str_capitalize_bang(mrb_state *mrb, mrb_value str) struct RString *s = mrb_str_ptr(str); mrb_str_modify(mrb, s); - if (STR_LEN(s) == 0 || !STR_PTR(s)) return mrb_nil_value(); - p = STR_PTR(s); pend = STR_PTR(s) + STR_LEN(s); + if (RSTR_LEN(s) == 0 || !RSTR_PTR(s)) return mrb_nil_value(); + p = RSTR_PTR(s); pend = RSTR_PTR(s) + RSTR_LEN(s); if (ISLOWER(*p)) { *p = TOUPPER(*p); modify = TRUE; @@ -970,29 +944,29 @@ mrb_str_chomp_bang(mrb_state *mrb, mrb_value str) struct RString *s = mrb_str_ptr(str); mrb_str_modify(mrb, s); - len = STR_LEN(s); + len = RSTR_LEN(s); if (mrb_get_args(mrb, "|S", &rs) == 0) { if (len == 0) return mrb_nil_value(); smart_chomp: - if (STR_PTR(s)[len-1] == '\n') { - STR_SET_LEN(s, STR_LEN(s) - 1); - if (STR_LEN(s) > 0 && - STR_PTR(s)[STR_LEN(s)-1] == '\r') { - STR_SET_LEN(s, STR_LEN(s) - 1); + if (RSTR_PTR(s)[len-1] == '\n') { + RSTR_SET_LEN(s, RSTR_LEN(s) - 1); + if (RSTR_LEN(s) > 0 && + RSTR_PTR(s)[RSTR_LEN(s)-1] == '\r') { + RSTR_SET_LEN(s, RSTR_LEN(s) - 1); } } - else if (STR_PTR(s)[len-1] == '\r') { - STR_SET_LEN(s, STR_LEN(s) - 1); + else if (RSTR_PTR(s)[len-1] == '\r') { + RSTR_SET_LEN(s, RSTR_LEN(s) - 1); } else { return mrb_nil_value(); } - STR_PTR(s)[STR_LEN(s)] = '\0'; + RSTR_PTR(s)[RSTR_LEN(s)] = '\0'; return str; } if (len == 0 || mrb_nil_p(rs)) return mrb_nil_value(); - p = STR_PTR(s); + p = RSTR_PTR(s); rslen = RSTRING_LEN(rs); if (rslen == 0) { while (len>0 && p[len-1] == '\n') { @@ -1000,8 +974,8 @@ mrb_str_chomp_bang(mrb_state *mrb, mrb_value str) if (len>0 && p[len-1] == '\r') len--; } - if (len < STR_LEN(s)) { - STR_SET_LEN(s, len); + if (len < RSTR_LEN(s)) { + RSTR_SET_LEN(s, len); p[len] = '\0'; return str; } @@ -1018,8 +992,8 @@ mrb_str_chomp_bang(mrb_state *mrb, mrb_value str) if (p[len-1] == newline && (rslen <= 1 || memcmp(RSTRING_PTR(rs), pp, rslen) == 0)) { - STR_SET_LEN(s, len - rslen); - p[STR_LEN(s)] = '\0'; + RSTR_SET_LEN(s, len - rslen); + p[RSTR_LEN(s)] = '\0'; return str; } return mrb_nil_value(); @@ -1069,17 +1043,17 @@ mrb_str_chop_bang(mrb_state *mrb, mrb_value str) struct RString *s = mrb_str_ptr(str); mrb_str_modify(mrb, s); - if (STR_LEN(s) > 0) { + if (RSTR_LEN(s) > 0) { mrb_int len; - len = STR_LEN(s) - 1; - if (STR_PTR(s)[len] == '\n') { + len = RSTR_LEN(s) - 1; + if (RSTR_PTR(s)[len] == '\n') { if (len > 0 && - STR_PTR(s)[len-1] == '\r') { + RSTR_PTR(s)[len-1] == '\r') { len--; } } - STR_SET_LEN(s, len); - STR_PTR(s)[len] = '\0'; + RSTR_SET_LEN(s, len); + RSTR_PTR(s)[len] = '\0'; return str; } return mrb_nil_value(); @@ -1127,8 +1101,8 @@ mrb_str_downcase_bang(mrb_state *mrb, mrb_value str) struct RString *s = mrb_str_ptr(str); mrb_str_modify(mrb, s); - p = STR_PTR(s); - pend = STR_PTR(s) + STR_LEN(s); + p = RSTR_PTR(s); + pend = RSTR_PTR(s) + RSTR_LEN(s); while (p < pend) { if (ISUPPER(*p)) { *p = TOLOWER(*p); @@ -1177,7 +1151,7 @@ mrb_str_empty_p(mrb_state *mrb, mrb_value self) { struct RString *s = mrb_str_ptr(self); - return mrb_bool_value(STR_LEN(s) == 0); + return mrb_bool_value(RSTR_LEN(s) == 0); } /* 15.2.10.5.17 */ @@ -1206,7 +1180,7 @@ mrb_str_subseq(mrb_state *mrb, mrb_value str, mrb_int beg, mrb_int len) mrb_shared_string *shared; orig = mrb_str_ptr(str); - if (STR_EMBED_P(orig)) { + if (RSTR_EMBED_P(orig)) { s = str_new(mrb, orig->as.ary+beg, len); } else { str_make_shared(mrb, orig); @@ -1215,7 +1189,7 @@ mrb_str_subseq(mrb_state *mrb, mrb_value str, mrb_int beg, mrb_int len) s->as.heap.ptr = orig->as.heap.ptr + beg; s->as.heap.len = len; s->as.heap.aux.shared = shared; - STR_SET_SHARED_FLAG(s); + RSTR_SET_SHARED_FLAG(s); shared->refcnt++; } @@ -1247,8 +1221,8 @@ mrb_str_hash(mrb_state *mrb, mrb_value str) { /* 1-8-7 */ struct RString *s = mrb_str_ptr(str); - mrb_int len = STR_LEN(s); - char *p = STR_PTR(s); + mrb_int len = RSTR_LEN(s); + char *p = RSTR_PTR(s); mrb_int key = 0; while (len--) { @@ -1393,27 +1367,31 @@ str_replace(mrb_state *mrb, struct RString *s1, struct RString *s2) { long len; - len = STR_LEN(s2); - if (STR_SHARED_P(s2)) { - L_SHARE: - if (STR_SHARED_P(s1)) { - str_decref(mrb, s1->as.heap.aux.shared); - } - else if (!STR_EMBED_P(s1) && !(s1->flags & MRB_STR_NOFREE)) { - mrb_free(mrb, s1->as.heap.ptr); - } - STR_UNSET_EMBED_FLAG(s1); + len = RSTR_LEN(s2); + if (RSTR_SHARED_P(s1)) { + str_decref(mrb, s1->as.heap.aux.shared); + } + else if (!RSTR_EMBED_P(s1) && !RSTR_NOFREE_P(s1)) { + mrb_free(mrb, s1->as.heap.ptr); + } + + RSTR_UNSET_NOFREE_FLAG(s1); + + if (RSTR_SHARED_P(s2)) { +L_SHARE: + RSTR_UNSET_EMBED_FLAG(s1); s1->as.heap.ptr = s2->as.heap.ptr; s1->as.heap.len = len; s1->as.heap.aux.shared = s2->as.heap.aux.shared; - STR_SET_SHARED_FLAG(s1); + RSTR_SET_SHARED_FLAG(s1); s1->as.heap.aux.shared->refcnt++; } else { if (len <= RSTRING_EMBED_LEN_MAX) { - STR_SET_EMBED_FLAG(s1); - memcpy(s1->as.ary, STR_PTR(s2), len); - STR_SET_EMBED_LEN(s1, len); + RSTR_UNSET_SHARED_FLAG(s1); + RSTR_SET_EMBED_FLAG(s1); + memcpy(s1->as.ary, RSTR_PTR(s2), len); + RSTR_SET_EMBED_LEN(s1, len); } else { str_make_shared(mrb, s2); @@ -1510,7 +1488,7 @@ mrb_ptr_to_str(mrb_state *mrb, void *p) uintptr_t n = (uintptr_t)p; p_str = str_new(mrb, NULL, 2 + sizeof(uintptr_t) * CHAR_BIT / 4); - p1 = STR_PTR(p_str); + p1 = RSTR_PTR(p_str); *p1++ = '0'; *p1++ = 'x'; p2 = p1; @@ -1520,7 +1498,7 @@ mrb_ptr_to_str(mrb_state *mrb, void *p) n /= 16; } while (n > 0); *p2 = '\0'; - STR_SET_LEN(p_str, (mrb_int)(p2 - STR_PTR(p_str))); + RSTR_SET_LEN(p_str, (mrb_int)(p2 - RSTR_PTR(p_str))); while (p1 < p2) { const char c = *p1; @@ -1564,7 +1542,7 @@ mrb_str_reverse(mrb_state *mrb, mrb_value str) s2 = str_new(mrb, 0, RSTRING_LEN(str)); str_with_class(mrb, s2, str); s = RSTRING_PTR(str); e = RSTRING_END(str) - 1; - p = STR_PTR(s2); + p = RSTR_PTR(s2); while (e >= s) { *p++ = *e--; @@ -1587,9 +1565,9 @@ mrb_str_reverse_bang(mrb_state *mrb, mrb_value str) char c; mrb_str_modify(mrb, s); - if (STR_LEN(s) > 1) { - p = STR_PTR(s); - e = p + STR_LEN(s) - 1; + if (RSTR_LEN(s) > 1) { + p = RSTR_PTR(s); + e = p + RSTR_LEN(s) - 1; while (p < e) { c = *p; *p++ = *e; @@ -1625,17 +1603,17 @@ mrb_str_rindex(mrb_state *mrb, mrb_value str, mrb_value sub, mrb_int pos) mrb_int len = RSTRING_LEN(sub); /* substring longer than string */ - if (STR_LEN(ps) < len) return -1; - if (STR_LEN(ps) - pos < len) { - pos = STR_LEN(ps) - len; + if (RSTR_LEN(ps) < len) return -1; + if (RSTR_LEN(ps) - pos < len) { + pos = RSTR_LEN(ps) - len; } - sbeg = STR_PTR(ps); - s = STR_PTR(ps) + pos; + sbeg = RSTR_PTR(ps); + s = RSTR_PTR(ps) + pos; t = RSTRING_PTR(sub); if (len) { while (sbeg <= s) { if (memcmp(s, t, len) == 0) { - return s - STR_PTR(ps); + return s - RSTR_PTR(ps); } s--; } @@ -1728,27 +1706,6 @@ mrb_str_rindex_m(mrb_state *mrb, mrb_value str) return mrb_nil_value(); } -static const char isspacetable[256] = { - 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 -}; - -#define ascii_isspace(c) isspacetable[(unsigned char)(c)] - /* 15.2.10.5.35 */ /* @@ -1844,7 +1801,7 @@ mrb_str_split_m(mrb_state *mrb, mrb_value str) int ai = mrb_gc_arena_save(mrb); c = (unsigned char)*ptr++; if (skip) { - if (ascii_isspace(c)) { + if (ISSPACE(c)) { beg = ptr - bptr; } else { @@ -1853,7 +1810,7 @@ mrb_str_split_m(mrb_state *mrb, mrb_value str) if (lim_p && lim <= i) break; } } - else if (ascii_isspace(c)) { + else if (ISSPACE(c)) { mrb_ary_push(mrb, result, mrb_str_subseq(mrb, str, beg, end-beg)); mrb_gc_arena_restore(mrb, ai); skip = TRUE; @@ -2068,11 +2025,11 @@ mrb_string_value_cstr(mrb_state *mrb, mrb_value *ptr) { struct RString *ps = mrb_str_ptr(*ptr); mrb_int len = mrb_str_strlen(mrb, ps); - char *p = STR_PTR(ps); + char *p = RSTR_PTR(ps); if (!p || p[len] != '\0') { mrb_str_modify(mrb, ps); - return STR_PTR(ps); + return RSTR_PTR(ps); } return p; } @@ -2094,7 +2051,7 @@ mrb_str_to_inum(mrb_state *mrb, mrb_value str, mrb_int base, mrb_bool badcheck) len = RSTRING_LEN(str); if (s[len]) { /* no sentinel somehow */ struct RString *temp_str = str_new(mrb, s, len); - s = STR_PTR(temp_str); + s = RSTR_PTR(temp_str); } } return mrb_cstr_to_inum(mrb, s, base, badcheck); @@ -2214,7 +2171,7 @@ mrb_str_to_dbl(mrb_state *mrb, mrb_value str, mrb_bool badcheck) } if (s[len]) { /* no sentinel somehow */ struct RString *temp_str = str_new(mrb, s, len); - s = STR_PTR(temp_str); + s = RSTR_PTR(temp_str); } } return mrb_cstr_to_dbl(mrb, s, badcheck); @@ -2308,6 +2265,8 @@ mrb_str_upcase(mrb_state *mrb, mrb_value self) return str; } +#define IS_EVSTR(p,e) ((p) < (e) && (*(p) == '$' || *(p) == '@' || *(p) == '{')) + /* * call-seq: * str.dump -> new_str @@ -2353,7 +2312,7 @@ mrb_str_dump(mrb_state *mrb, mrb_value str) result = str_new(mrb, 0, len); str_with_class(mrb, result, str); p = RSTRING_PTR(str); pend = p + RSTRING_LEN(str); - q = STR_PTR(result); + q = RSTR_PTR(result); *q++ = '"'; while (p < pend) { unsigned char c = *p++; @@ -2532,8 +2491,8 @@ static mrb_value mrb_str_bytes(mrb_state *mrb, mrb_value str) { struct RString *s = mrb_str_ptr(str); - mrb_value a = mrb_ary_new_capa(mrb, STR_LEN(s)); - unsigned char *p = (unsigned char *)(STR_PTR(s)), *pend = p + STR_LEN(s); + mrb_value a = mrb_ary_new_capa(mrb, RSTR_LEN(s)); + unsigned char *p = (unsigned char *)(RSTR_PTR(s)), *pend = p + RSTR_LEN(s); while (p < pend) { mrb_ary_push(mrb, a, mrb_fixnum_value(p[0])); @@ -2548,6 +2507,8 @@ mrb_init_string(mrb_state *mrb) { struct RClass *s; + mrb_static_assert(RSTRING_EMBED_LEN_MAX < (1 << 5), "pointer size too big for embedded string"); + s = mrb->string_class = mrb_define_class(mrb, "String", mrb->object_class); /* 15.2.10 */ MRB_SET_INSTANCE_TT(s, MRB_TT_STRING); @@ -625,7 +625,7 @@ mrb_yield_with_class(mrb_state *mrb, mrb_value b, mrb_int argc, const mrb_value stack_extend(mrb, ci->nregs, 0); } else { - ci->nregs = p->body.irep->nregs + 1; + ci->nregs = p->body.irep->nregs; stack_extend(mrb, ci->nregs, argc+2); } @@ -793,7 +793,7 @@ RETRY_TRY_BLOCK: } stack_extend(mrb, irep->nregs, stack_keep); mrb->c->ci->proc = proc; - mrb->c->ci->nregs = irep->nregs + 1; + mrb->c->ci->nregs = irep->nregs; regs = mrb->c->stack; regs[0] = self; @@ -811,7 +811,7 @@ RETRY_TRY_BLOCK: CASE(OP_LOADL) { /* A Bx R(A) := Pool(Bx) */ - regs[GETARG_A(i)] = pool[GETARG_Bx(i)]; + regs[GETARG_A(i)] = pool[GETARG_Bx(i)]; NEXT; } diff --git a/tasks/libmruby.rake b/tasks/libmruby.rake index 887cc69aa..095bedd52 100644 --- a/tasks/libmruby.rake +++ b/tasks/libmruby.rake @@ -1,7 +1,10 @@ MRuby.each_target do file libfile("#{build_dir}/lib/libmruby") => libmruby.flatten do |t| archiver.run t.name, t.prerequisites - open("#{build_dir}/lib/libmruby.flags.mak", 'w') do |f| + end + + file "#{build_dir}/lib/libmruby.flags.mak" => [__FILE__, libfile("#{build_dir}/lib/libmruby")] do |t| + open(t.name, 'w') do |f| f.puts "MRUBY_CFLAGS = #{cc.all_flags.gsub('"', '\\"')}" gem_flags = gems.map { |g| g.linker.flags } @@ -15,4 +18,5 @@ MRuby.each_target do f.puts "MRUBY_LIBS = #{linker.option_library % 'mruby'} #{linker.library_flags(gem_libraries).gsub('"', '\\"')}" end end + task :all => "#{build_dir}/lib/libmruby.flags.mak" end diff --git a/tasks/mrbgem_spec.rake b/tasks/mrbgem_spec.rake index 67edffbc5..2a15a1f46 100644 --- a/tasks/mrbgem_spec.rake +++ b/tasks/mrbgem_spec.rake @@ -32,7 +32,9 @@ module MRuby attr_accessor :bins attr_accessor :requirements - attr_reader :dependencies + attr_reader :dependencies, :conflicts + + attr_accessor :export_include_paths attr_block MRuby::Build::COMMANDS @@ -44,27 +46,24 @@ module MRuby end def run_test_in_other_mrb_state? - not test_preload.nil? or not test_objs.empty? + not test_preload.nil? or not test_objs.empty? or not test_args.empty? end def setup MRuby::Gem.current = self - @build.compilers.each do |compiler| - compiler.include_paths << "#{dir}/include" - end if File.directory? "#{dir}/include" MRuby::Build::COMMANDS.each do |command| instance_variable_set("@#{command}", @build.send(command).clone) end @linker = LinkerConfig.new([], [], [], []) @rbfiles = Dir.glob("#{dir}/mrblib/*.rb").sort - @objs = Dir.glob("#{dir}/src/*.{c,cpp,cxx,cc,m,asm,S}").map do |f| + @objs = Dir.glob("#{dir}/src/*.{c,cpp,cxx,cc,m,asm,s,S}").map do |f| objfile(f.relative_path_from(@dir).to_s.pathmap("#{build_dir}/%X")) end @objs << objfile("#{build_dir}/gem_init") @test_rbfiles = Dir.glob("#{dir}/test/*.rb") - @test_objs = Dir.glob("#{dir}/test/*.{c,cpp,cxx,cc,m,asm,S}").map do |f| + @test_objs = Dir.glob("#{dir}/test/*.{c,cpp,cxx,cc,m,asm,s,S}").map do |f| objfile(f.relative_path_from(dir).to_s.pathmap("#{build_dir}/%X")) end @test_preload = nil # 'test/assert.rb' @@ -73,7 +72,9 @@ module MRuby @bins = [] @requirements = [] - @dependencies = [] + @dependencies, @conflicts = [], [] + @export_include_paths = [] + @export_include_paths << "#{dir}/include" if File.directory? "#{dir}/include" instance_eval(&@initializer) @@ -88,6 +89,7 @@ module MRuby compilers.each do |compiler| compiler.define_rules build_dir, "#{dir}" compiler.defines << %Q[MRBGEM_#{funcname.upcase}_VERSION=#{version}] + compiler.include_paths << "#{dir}/include" if File.directory? "#{dir}/include" end define_gem_init_builder @@ -100,6 +102,10 @@ module MRuby @dependencies << {:gem => name, :requirements => requirements, :default => default_gem} end + def add_conflict(name, *req) + @conflicts << {:gem => name, :requirements => req.empty? ? nil : req} + end + def self.bin=(bin) @bins = [bin].flatten end @@ -124,7 +130,7 @@ module MRuby def define_gem_init_builder file objfile("#{build_dir}/gem_init") => "#{build_dir}/gem_init.c" - file "#{build_dir}/gem_init.c" => [build.mrbcfile] + [rbfiles].flatten do |t| + file "#{build_dir}/gem_init.c" => [build.mrbcfile, __FILE__] + [rbfiles].flatten do |t| FileUtils.mkdir_p build_dir generate_gem_init("#{build_dir}/gem_init.c") end @@ -182,6 +188,7 @@ module MRuby f.puts %Q[#include "mruby/irep.h"] f.puts %Q[#include "mruby/string.h"] f.puts %Q[#include "mruby/variable.h"] + f.puts %Q[#include "mruby/hash.h"] unless test_args.empty? end def version_ok?(req_versions) @@ -320,6 +327,12 @@ module MRuby fail "#{name} version should be #{req_versions.join(' and ')} but was '#{dep_g.version}'" end end + + cfls = g.conflicts.select { |c| + cfl_g = gem_table[c[:gem]] + cfl_g and cfl_g.version_ok?(c[:requirements] || ['>= 0.0.0']) + }.map { |c| "#{c[:gem]}(#{gem_table[c[:gem]].version})" } + fail "Conflicts of gem `#{g.name}` found: #{cfls.join ', '}" unless cfls.empty? end class << gem_table @@ -337,6 +350,25 @@ module MRuby rescue TSort::Cyclic => e fail "Circular mrbgem dependency found: #{e.message}" end + + each do |g| + import_include_paths(g) + end + end + + def import_include_paths(g) + gem_table = @ary.reduce({}) { |res,v| res[v.name] = v; res } + g.dependencies.each do |dep| + dep_g = gem_table[dep[:gem]] + # We can do recursive call safely + # as circular dependency has already detected in the caller. + import_include_paths(dep_g) + + g.compilers.each do |compiler| + compiler.include_paths += dep_g.export_include_paths + g.export_include_paths += dep_g.export_include_paths + end + end end end # List end # Gem diff --git a/tasks/mrbgems.rake b/tasks/mrbgems.rake index b57f318e0..2d17be931 100644 --- a/tasks/mrbgems.rake +++ b/tasks/mrbgems.rake @@ -7,7 +7,7 @@ MRuby.each_target do # loader all gems self.libmruby << objfile("#{build_dir}/mrbgems/gem_init") file objfile("#{build_dir}/mrbgems/gem_init") => ["#{build_dir}/mrbgems/gem_init.c", "#{build_dir}/LEGAL"] - file "#{build_dir}/mrbgems/gem_init.c" => [MRUBY_CONFIG] do |t| + file "#{build_dir}/mrbgems/gem_init.c" => [MRUBY_CONFIG, __FILE__] do |t| FileUtils.mkdir_p "#{build_dir}/mrbgems" open(t.name, 'w') do |f| f.puts %Q[/*] @@ -26,21 +26,22 @@ MRuby.each_target do f.puts %Q[] f.puts %Q[#{gems.map{|g| "void GENERATED_TMP_mrb_%s_gem_final(mrb_state* mrb);" % g.funcname}.join("\n")}] f.puts %Q[] - f.puts %Q[void] - f.puts %Q[mrb_init_mrbgems(mrb_state *mrb) {] - f.puts %Q[#{gems.map{|g| "GENERATED_TMP_mrb_%s_gem_init(mrb);" % g.funcname}.join("\n")}] + f.puts %Q[static void] + f.puts %Q[mrb_final_mrbgems(mrb_state *mrb) {] + f.puts %Q[#{gems.map{|g| "GENERATED_TMP_mrb_%s_gem_final(mrb);" % g.funcname}.join("\n")}] f.puts %Q[}] f.puts %Q[] f.puts %Q[void] - f.puts %Q[mrb_final_mrbgems(mrb_state *mrb) {] - f.puts %Q[#{gems.map{|g| "GENERATED_TMP_mrb_%s_gem_final(mrb);" % g.funcname}.join("\n")}] + f.puts %Q[mrb_init_mrbgems(mrb_state *mrb) {] + f.puts %Q[#{gems.map{|g| "GENERATED_TMP_mrb_%s_gem_init(mrb);" % g.funcname}.join("\n")}] + f.puts %Q[mrb_state_atexit(mrb, mrb_final_mrbgems);] f.puts %Q[}] end end end # legal documents - file "#{build_dir}/LEGAL" => [MRUBY_CONFIG] do |t| + file "#{build_dir}/LEGAL" => [MRUBY_CONFIG, __FILE__] do |t| open(t.name, 'w+') do |f| f.puts <<LEGAL Copyright (c) #{Time.now.year} mruby developers diff --git a/tasks/mrbgems_test.rake b/tasks/mrbgems_test.rake index d06b7a24c..a6e9eb99e 100644 --- a/tasks/mrbgems_test.rake +++ b/tasks/mrbgems_test.rake @@ -10,7 +10,7 @@ MRuby.each_target do test_rbobj = g.test_rbireps.ext(exts.object) file test_rbobj => g.test_rbireps - file g.test_rbireps => [g.test_rbfiles].flatten + [g.build.mrbcfile] do |t| + file g.test_rbireps => [g.test_rbfiles].flatten + [g.build.mrbcfile, __FILE__] do |t| open(t.name, 'w') do |f| g.print_gem_test_header(f) test_preload = g.test_preload and [g.dir, MRUBY_ROOT].map {|dir| @@ -117,7 +117,7 @@ MRuby.each_target do no_mrb_open_test_lib = no_mrb_open_test.ext(exts.object) file no_mrb_open_test_lib => "#{no_mrb_open_test}.c" - file "#{no_mrb_open_test}.c" => no_mrb_open_test_rbfiles + [MRUBY_CONFIG] do |t| + file "#{no_mrb_open_test}.c" => no_mrb_open_test_rbfiles + [MRUBY_CONFIG, __FILE__] do |t| open(t.name, 'w') do |f| f.puts %Q[/*] f.puts %Q[ * This file contains a test code for following gems:] diff --git a/tasks/mruby_build.rake b/tasks/mruby_build.rake index 966c602a4..09175d533 100644 --- a/tasks/mruby_build.rake +++ b/tasks/mruby_build.rake @@ -209,6 +209,7 @@ module MRuby def run_bintest targets = @gems.select { |v| File.directory? "#{v.dir}/bintest" }.map { |v| filename v.dir } + targets << filename(".") if File.directory? "./bintest" sh "ruby test/bintest.rb #{targets.join ' '}" end diff --git a/tasks/mruby_build_gem.rake b/tasks/mruby_build_gem.rake index e58dc5c71..5d2dc030c 100644 --- a/tasks/mruby_build_gem.rake +++ b/tasks/mruby_build_gem.rake @@ -35,8 +35,9 @@ module MRuby Gem.current.build_config_initializer = block gems << Gem.current - cxx_srcs = Dir.glob("#{Gem.current.dir}/src/*.{cpp,cxx,cc}") - cxx_srcs += Dir.glob("#{Gem.current.dir}/test/*.{cpp,cxx,cc}") + cxx_srcs = ['src', 'test', 'tools'].map do |subdir| + Dir.glob("#{Gem.current.dir}/#{subdir}/*.{cpp,cxx,cc}") + end.flatten enable_cxx_abi unless cxx_srcs.empty? Gem.current diff --git a/tasks/toolchains/gcc.rake b/tasks/toolchains/gcc.rake index 821100748..7edf93642 100644 --- a/tasks/toolchains/gcc.rake +++ b/tasks/toolchains/gcc.rake @@ -2,7 +2,6 @@ MRuby::Toolchain.new(:gcc) do |conf| [conf.cc, conf.objc, conf.asm].each do |cc| cc.command = ENV['CC'] || 'gcc' cc.flags = [ENV['CFLAGS'] || %w(-g -std=gnu99 -O3 -Wall -Werror-implicit-function-declaration -Wdeclaration-after-statement)] - cc.include_paths = ["#{MRUBY_ROOT}/include"] cc.defines = %w(DISABLE_GEMS) cc.option_include_path = '-I%s' cc.option_define = '-D%s' @@ -12,7 +11,6 @@ MRuby::Toolchain.new(:gcc) do |conf| [conf.cxx].each do |cxx| cxx.command = ENV['CXX'] || 'g++' cxx.flags = [ENV['CXXFLAGS'] || ENV['CFLAGS'] || %w(-g -O3 -Wall -Werror-implicit-function-declaration)] - cxx.include_paths = ["#{MRUBY_ROOT}/include"] cxx.defines = %w(DISABLE_GEMS) cxx.option_include_path = '-I%s' cxx.option_define = '-D%s' diff --git a/tasks/toolchains/visualcpp.rake b/tasks/toolchains/visualcpp.rake index a5726dce7..8838f8a41 100644 --- a/tasks/toolchains/visualcpp.rake +++ b/tasks/toolchains/visualcpp.rake @@ -3,7 +3,6 @@ MRuby::Toolchain.new(:visualcpp) do |conf| cc.command = ENV['CC'] || 'cl.exe' # C4013: implicit function declaration cc.flags = [ENV['CFLAGS'] || %w(/c /nologo /W3 /we4013 /Zi /MD /O2 /D_CRT_SECURE_NO_WARNINGS)] - cc.include_paths = ["#{MRUBY_ROOT}/include"] cc.defines = %w(DISABLE_GEMS MRB_STACK_EXTEND_DOUBLING) cc.option_include_path = '/I%s' cc.option_define = '/D%s' @@ -13,7 +12,6 @@ MRuby::Toolchain.new(:visualcpp) do |conf| [conf.cxx].each do |cxx| cxx.command = ENV['CXX'] || 'cl.exe' cxx.flags = [ENV['CXXFLAGS'] || ENV['CFLAGS'] || %w(/c /nologo /W3 /Zi /MD /O2 /EHsc /D_CRT_SECURE_NO_WARNINGS)] - cxx.include_paths = ["#{MRUBY_ROOT}/include"] cxx.defines = %w(DISABLE_GEMS MRB_STACK_EXTEND_DOUBLING) cxx.option_include_path = '/I%s' cxx.option_define = '/D%s' diff --git a/test/mrbtest.rake b/test/mrbtest.rake index e8eb6addd..c28cf3577 100644 --- a/test/mrbtest.rake +++ b/test/mrbtest.rake @@ -31,15 +31,15 @@ MRuby.each_target do end file ass_lib => ass_c - file ass_c => "#{current_dir}/assert.rb" do |t| + file ass_c => ["#{current_dir}/assert.rb", __FILE__] do |t| FileUtils.mkdir_p File.dirname t.name open(t.name, 'w') do |f| - mrbc.run f, [t.prerequisites], 'mrbtest_assert_irep' + mrbc.run f, [t.prerequisites.first], 'mrbtest_assert_irep' end end file mlib => clib - file clib => [mrbcfile, init] + mrbs do |t| + file clib => [mrbcfile, init, __FILE__] + mrbs do |t| _pp "GEN", "*.rb", "#{clib.relative_path}" FileUtils.mkdir_p File.dirname(clib) open(clib, 'w') do |f| diff --git a/test/t/nomethoderror.rb b/test/t/nomethoderror.rb index 561e545f9..709d31165 100644 --- a/test/t/nomethoderror.rb +++ b/test/t/nomethoderror.rb @@ -11,3 +11,16 @@ end assert('NoMethodError superclass', '15.2.32.2') do assert_equal NameError, NoMethodError.superclass end + +assert('NoMethodError#args', '15.2.32.2.1') do + a = NoMethodError.new 'test', :test, [1, 2] + assert_equal [1, 2], a.args + + assert_nothing_raised do + begin + doesNotExistAsAMethodNameForVerySure 3, 1, 4 + rescue NoMethodError => e + assert_equal [3, 1, 4], e.args + end + end +end diff --git a/test/t/string.rb b/test/t/string.rb index 5ecb51530..00e98f671 100644 --- a/test/t/string.rb +++ b/test/t/string.rb @@ -320,6 +320,13 @@ assert('String#replace', '15.2.10.5.28') do b.replace(c); c.replace(b); assert_equal c, b + + # shared string + s = "foo" * 100 + a = s[10, 90] # create shared string + assert_equal("", s.replace("")) # clear + assert_equal("", s) # s is cleared + assert_not_equal("", a) # a should not be affected end assert('String#reverse', '15.2.10.5.29') do diff --git a/tools/mrbc/mrbc.c b/tools/mrbc/mrbc.c index e5858e54a..52e762a50 100644 --- a/tools/mrbc/mrbc.c +++ b/tools/mrbc/mrbc.c @@ -270,7 +270,7 @@ main(int argc, char **argv) fprintf(stderr, "%s: no program file given\n", args.prog); return EXIT_FAILURE; } - if (args.outfile == NULL) { + if (args.outfile == NULL && !args.check_syntax) { if (n + 1 == argc) { args.outfile = get_outfilename(mrb, argv[n], args.initname ? C_EXT : RITEBIN_EXT); } |
