summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorFelix Jones <[email protected]>2017-02-16 13:33:46 +0000
committerFelix Jones <[email protected]>2017-02-16 13:33:46 +0000
commitd83aad8d570e4bbffa3bd3ce64e210f78afa425f (patch)
tree5389a87c135b1bdf3e23a1ba02e02400b7cf80fc
parent70aa6dc38d75dd6b1e2c76f290bc576e36e36ea3 (diff)
parentb165708c8deba00685f9a27926c554aaa7f3b0fb (diff)
downloadmruby-d83aad8d570e4bbffa3bd3ce64e210f78afa425f.tar.gz
mruby-d83aad8d570e4bbffa3bd3ce64e210f78afa425f.zip
Merge branch 'master' into android.rake-ndk-clang
-rw-r--r--.travis.yml8
-rw-r--r--AUTHORS1
-rw-r--r--MITL2
-rw-r--r--build_config.rb6
-rw-r--r--examples/targets/build_config_RX630.rb82
-rw-r--r--examples/targets/build_config_android_arm64-v8a.rb26
-rw-r--r--examples/targets/build_config_android_armeabi.rb26
-rw-r--r--include/mrbconf.h17
-rw-r--r--include/mruby.h121
-rw-r--r--include/mruby/array.h136
-rw-r--r--include/mruby/boxing_nan.h28
-rw-r--r--include/mruby/boxing_word.h10
-rw-r--r--include/mruby/class.h1
-rw-r--r--include/mruby/common.h5
-rw-r--r--include/mruby/compile.h2
-rw-r--r--include/mruby/hash.h88
-rw-r--r--include/mruby/irep.h1
-rw-r--r--include/mruby/istruct.h47
-rw-r--r--include/mruby/object.h4
-rw-r--r--include/mruby/range.h5
-rw-r--r--include/mruby/re.h8
-rw-r--r--include/mruby/string.h19
-rw-r--r--include/mruby/throw.h4
-rw-r--r--include/mruby/value.h10
-rw-r--r--include/mruby/variable.h58
-rwxr-xr-xminirake1
-rw-r--r--mrbgems/default.gembox5
-rw-r--r--mrbgems/mruby-array-ext/src/array.c2
-rw-r--r--mrbgems/mruby-array-ext/test/array.rb11
-rw-r--r--mrbgems/mruby-bin-debugger/tools/mrdb/apibreak.c4
-rw-r--r--mrbgems/mruby-bin-debugger/tools/mrdb/apilist.c2
-rw-r--r--mrbgems/mruby-bin-mruby/bintest/mruby.rb14
-rw-r--r--mrbgems/mruby-bin-mruby/tools/mruby/mruby.c105
-rw-r--r--mrbgems/mruby-class-ext/mrbgem.rake5
-rw-r--r--mrbgems/mruby-class-ext/src/class.c23
-rw-r--r--mrbgems/mruby-class-ext/test/module.rb10
-rw-r--r--mrbgems/mruby-compiler/bintest/mrbc.rb9
-rw-r--r--mrbgems/mruby-compiler/core/codegen.c133
-rw-r--r--mrbgems/mruby-compiler/core/parse.y282
-rw-r--r--mrbgems/mruby-enum-ext/mrblib/enum.rb83
-rw-r--r--mrbgems/mruby-enum-ext/test/enum.rb7
-rw-r--r--mrbgems/mruby-enum-lazy/mrbgem.rake2
-rw-r--r--mrbgems/mruby-enum-lazy/mrblib/lazy.rb15
-rw-r--r--mrbgems/mruby-enum-lazy/test/lazy.rb14
-rw-r--r--mrbgems/mruby-enumerator/mrblib/enumerator.rb15
-rw-r--r--mrbgems/mruby-enumerator/test/enumerator.rb3
-rw-r--r--mrbgems/mruby-eval/src/eval.c17
-rw-r--r--mrbgems/mruby-inline-struct/mrbgem.rake5
-rw-r--r--mrbgems/mruby-inline-struct/test/inline.c83
-rw-r--r--mrbgems/mruby-inline-struct/test/inline.rb151
-rw-r--r--mrbgems/mruby-kernel-ext/src/kernel.c59
-rw-r--r--mrbgems/mruby-kernel-ext/test/kernel.rb29
-rw-r--r--mrbgems/mruby-object-ext/src/object.c2
-rw-r--r--mrbgems/mruby-object-ext/test/object.rb28
-rw-r--r--mrbgems/mruby-objectspace/src/mruby_objectspace.c2
-rw-r--r--mrbgems/mruby-print/src/print.c2
-rw-r--r--mrbgems/mruby-proc-ext/src/proc.c3
-rw-r--r--mrbgems/mruby-random/src/random.c6
-rw-r--r--mrbgems/mruby-random/test/random.rb12
-rw-r--r--mrbgems/mruby-range-ext/mrblib/range.rb31
-rw-r--r--mrbgems/mruby-range-ext/src/range.c66
-rw-r--r--mrbgems/mruby-range-ext/test/range.rb3
-rw-r--r--mrbgems/mruby-sprintf/src/sprintf.c92
-rw-r--r--mrbgems/mruby-sprintf/test/sprintf.rb25
-rw-r--r--mrbgems/mruby-string-ext/mrblib/string.rb50
-rw-r--r--mrbgems/mruby-string-ext/src/string.c65
-rw-r--r--mrbgems/mruby-string-ext/test/string.rb58
-rw-r--r--mrbgems/mruby-struct/src/struct.c32
-rw-r--r--mrbgems/mruby-struct/test/struct.rb39
-rw-r--r--mrbgems/mruby-test/driver.c6
-rw-r--r--mrbgems/mruby-time/src/time.c109
-rw-r--r--mrblib/numeric.rb39
-rw-r--r--mrblib/string.rb14
-rw-r--r--src/array.c168
-rw-r--r--src/backtrace.c11
-rw-r--r--src/class.c234
-rw-r--r--src/codedump.c3
-rw-r--r--src/dump.c2
-rw-r--r--src/error.c45
-rw-r--r--src/etc.c1
-rw-r--r--src/gc.c94
-rw-r--r--src/hash.c68
-rw-r--r--src/kernel.c223
-rw-r--r--src/numeric.c179
-rw-r--r--src/object.c12
-rw-r--r--src/proc.c32
-rw-r--r--src/range.c67
-rw-r--r--src/state.c5
-rw-r--r--src/string.c379
-rw-r--r--src/symbol.c3
-rw-r--r--src/variable.c7
-rw-r--r--src/vm.c235
-rw-r--r--tasks/libmruby.rake8
-rw-r--r--tasks/mrbgem_spec.rake20
-rw-r--r--tasks/mruby_build.rake5
-rw-r--r--tasks/mruby_build_commands.rake1
-rw-r--r--tasks/toolchains/android.rake51
-rw-r--r--tasks/toolchains/gcc.rake2
-rw-r--r--tasks/toolchains/visualcpp.rake2
-rw-r--r--test/assert.rb7
-rw-r--r--test/t/array.rb35
-rw-r--r--test/t/class.rb21
-rw-r--r--test/t/codegen.rb93
-rw-r--r--test/t/hash.rb18
-rw-r--r--test/t/kernel.rb38
-rw-r--r--test/t/literals.rb2
-rw-r--r--test/t/module.rb12
-rw-r--r--test/t/nomethoderror.rb49
-rw-r--r--test/t/proc.rb12
-rw-r--r--test/t/range.rb9
-rw-r--r--test/t/string.rb20
-rw-r--r--test/t/syntax.rb20
-rw-r--r--test/t/unicode.rb32
113 files changed, 3600 insertions, 1113 deletions
diff --git a/.travis.yml b/.travis.yml
index 50feac2e0..8d201515c 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -16,11 +16,3 @@ addons:
env: MRUBY_CONFIG=travis_config.rb
script: "./minirake all test"
-
-notifications:
- # Update mruby-head installed on Travis CI so other projects can test against it.
- webhooks:
- urls:
- - "https://rubies.travis-ci.org/rebuild/mruby-head"
- on_success: always
- on_failure: never
diff --git a/AUTHORS b/AUTHORS
index 180fd752e..e3b95d728 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -34,3 +34,4 @@ Original Authors "mruby developers" are:
Yuichi Osawa
Terence Lee
Zachary Scott
+ Tomasz DÄ…browski
diff --git a/MITL b/MITL
index 38fdbe231..d02b8fe1c 100644
--- a/MITL
+++ b/MITL
@@ -1,4 +1,4 @@
-Copyright (c) 2016 mruby developers
+Copyright (c) 2017 mruby developers
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"),
diff --git a/build_config.rb b/build_config.rb
index bc9e69e42..8293092ab 100644
--- a/build_config.rb
+++ b/build_config.rb
@@ -16,8 +16,10 @@ MRuby::Build.new do |conf|
# g.cc.flags << '-g' # append cflags in this gem
# end
# conf.gem 'examples/mrbgems/c_and_ruby_extension_example'
- # conf.gem :github => 'masuidrive/mrbgems-example', :checksum_hash => '76518e8aecd131d047378448ac8055fa29d974a9'
- # conf.gem :git => '[email protected]:masuidrive/mrbgems-example.git', :branch => 'master', :options => '-v'
+ # conf.gem :core => 'mruby-eval'
+ # conf.gem :mgem => 'mruby-io'
+ # conf.gem :github => 'iij/mruby-io'
+ # conf.gem :git => '[email protected]:iij/mruby-io.git', :branch => 'master', :options => '-v'
# include the default GEMs
conf.gembox 'default'
diff --git a/examples/targets/build_config_RX630.rb b/examples/targets/build_config_RX630.rb
new file mode 100644
index 000000000..8e387c29d
--- /dev/null
+++ b/examples/targets/build_config_RX630.rb
@@ -0,0 +1,82 @@
+MRuby::Build.new do |conf|
+
+ # Gets set by the VS command prompts.
+ if ENV['VisualStudioVersion'] || ENV['VSINSTALLDIR']
+ toolchain :visualcpp
+ else
+ toolchain :gcc
+ end
+
+ enable_debug
+
+ # include the default GEMs
+ conf.gembox 'default'
+
+end
+
+# Cross Compiling configuration for RX630
+# http://gadget.renesas.com/
+#
+# Requires gnurx_v14.03
+MRuby::CrossBuild.new("RX630") do |conf|
+ toolchain :gcc
+
+ # Linux
+ BIN_PATH = "/usr/share/gnurx_v14.03_elf-1/bin"
+
+ conf.cc do |cc|
+ cc.command = "#{BIN_PATH}/rx-elf-gcc"
+ cc.flags = "-Wall -g -O2 -flto -mcpu=rx600 -m64bit-doubles"
+ cc.compile_options = "%{flags} -o %{outfile} -c %{infile}"
+
+ #configuration for low memory environment
+ cc.defines << %w(MRB_USE_FLOAT)
+ cc.defines << %w(MRB_HEAP_PAGE_SIZE=64)
+ cc.defines << %w(MRB_USE_IV_SEGLIST)
+ cc.defines << %w(KHASH_DEFAULT_SIZE=8)
+ cc.defines << %w(MRB_STR_BUF_MIN_SIZE=20)
+ cc.defines << %w(MRB_GC_STRESS)
+ cc.defines << %w(MRB_DISABLE_STDIO) #if you dont need stdio.
+ #cc.defines << %w(POOL_PAGE_SIZE=1000) #effective only for use with mruby-eval
+ end
+
+ conf.cxx do |cxx|
+ cxx.command = conf.cc.command.dup
+ cxx.include_paths = conf.cc.include_paths.dup
+ cxx.flags = conf.cc.flags.dup
+ cxx.defines = conf.cc.defines.dup
+ cxx.compile_options = conf.cc.compile_options.dup
+ end
+
+ conf.linker do |linker|
+ linker.command="#{BIN_PATH}/rx-elf-ld"
+ end
+
+ conf.archiver do |archiver|
+ archiver.command = "#{BIN_PATH}/rx-elf-ar"
+ archiver.archive_options = 'rcs %{outfile} %{objs}'
+ end
+
+ #no executables
+ conf.bins = []
+
+ #do not build executable test
+ conf.build_mrbtest_lib_only
+
+ #disable C++ exception
+ conf.disable_cxx_exception
+
+ #gems from core
+ conf.gem :core => "mruby-sprintf"
+ conf.gem :core => "mruby-print"
+ conf.gem :core => "mruby-math"
+ conf.gem :core => "mruby-enum-ext"
+ conf.gem :core => "mruby-numeric-ext"
+
+ #light-weight regular expression
+ #conf.gem :github => "masamitsu-murase/mruby-hs-regexp", :branch => "master"
+
+ #Arduino API
+ #conf.gem :github =>"kyab/mruby-arduino", :branch => "master"
+
+end
diff --git a/examples/targets/build_config_android_arm64-v8a.rb b/examples/targets/build_config_android_arm64-v8a.rb
new file mode 100644
index 000000000..6188c13ec
--- /dev/null
+++ b/examples/targets/build_config_android_arm64-v8a.rb
@@ -0,0 +1,26 @@
+MRuby::Build.new do |conf|
+
+ # Gets set by the VS command prompts.
+ if ENV['VisualStudioVersion'] || ENV['VSINSTALLDIR']
+ toolchain :visualcpp
+ else
+ toolchain :gcc
+ end
+
+ enable_debug
+
+ # include the default GEMs
+ conf.gembox 'default'
+end
+
+# Requires Android NDK r13 or later.
+MRuby::CrossBuild.new('android-arm64-v8a') do |conf|
+ params = {
+ :arch => 'arm64-v8a',
+ :platform => 'android-24',
+ :toolchain => :clang,
+ }
+ toolchain :android, params
+
+ conf.gembox 'default'
+end
diff --git a/examples/targets/build_config_android_armeabi.rb b/examples/targets/build_config_android_armeabi.rb
new file mode 100644
index 000000000..b7eb33a92
--- /dev/null
+++ b/examples/targets/build_config_android_armeabi.rb
@@ -0,0 +1,26 @@
+MRuby::Build.new do |conf|
+
+ # Gets set by the VS command prompts.
+ if ENV['VisualStudioVersion'] || ENV['VSINSTALLDIR']
+ toolchain :visualcpp
+ else
+ toolchain :gcc
+ end
+
+ enable_debug
+
+ # include the default GEMs
+ conf.gembox 'default'
+end
+
+# Requires Android NDK r13 or later.
+MRuby::CrossBuild.new('android-armeabi') do |conf|
+ params = {
+ :arch => 'armeabi',
+ :platform => 'android-24',
+ :toolchain => :clang,
+ }
+ toolchain :android, params
+
+ conf.gembox 'default'
+end
diff --git a/include/mrbconf.h b/include/mrbconf.h
index ab5dd1a03..4796919c2 100644
--- a/include/mrbconf.h
+++ b/include/mrbconf.h
@@ -7,6 +7,23 @@
#ifndef MRUBYCONF_H
#define MRUBYCONF_H
+#include <limits.h>
+#include <stdint.h>
+
+/* architecture selection: */
+/* specify -DMRB_32BIT or -DMRB_64BIT to override */
+#if !defined(MRB_32BIT) && !defined(MRB_64BIT)
+#if UINT64_MAX == SIZE_MAX
+#define MRB_64BIT
+#else
+#define MRB_32BIT
+#endif
+#endif
+
+#if defined(MRB_32BIT) && defined(MRB_64BIT)
+#error Cannot build for 32 and 64 bit architecture at the same time
+#endif
+
/* configuration options: */
/* add -DMRB_USE_FLOAT to use float instead of double for floating point numbers */
//#define MRB_USE_FLOAT
diff --git a/include/mruby.h b/include/mruby.h
index 886b15e50..8adce289b 100644
--- a/include/mruby.h
+++ b/include/mruby.h
@@ -1,7 +1,7 @@
/*
** mruby - An embeddable Ruby implementation
**
-** Copyright (c) mruby developers 2010-2016
+** Copyright (c) mruby developers 2010-2017
**
** Permission is hereby granted, free of charge, to any person obtaining
** a copy of this software and associated documentation files (the
@@ -28,11 +28,49 @@
#ifndef MRUBY_H
#define MRUBY_H
+#ifdef __cplusplus
+#define __STDC_LIMIT_MACROS
+#define __STDC_CONSTANT_MACROS
+#define __STDC_FORMAT_MACROS
+#endif
+
#include <stdint.h>
#include <stddef.h>
#include <limits.h>
+#ifdef __cplusplus
+#ifndef SIZE_MAX
+#ifdef __SIZE_MAX__
+#define SIZE_MAX __SIZE_MAX__
+#else
+#define SIZE_MAX std::numeric_limits<size_t>::max()
+#endif
+#endif
+#endif
+
+#ifdef MRB_DEBUG
+#include <assert.h>
+#define mrb_assert(p) assert(p)
+#define mrb_assert_int_fit(t1,n,t2,max) assert((n)>=0 && ((sizeof(n)<=sizeof(t2))||(n<=(t1)(max))))
+#else
+#define mrb_assert(p) ((void)0)
+#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
+
#include "mrbconf.h"
+
+#ifdef MRB_USE_FLOAT
+#define MRB_FLOAT_EPSILON FLT_EPSILON
+#else
+#define MRB_FLOAT_EPSILON DBL_EPSILON
+#endif
+
#include "mruby/common.h"
#include <mruby/value.h>
#include <mruby/gc.h>
@@ -183,6 +221,10 @@ typedef struct mrb_state {
struct RClass *eException_class;
struct RClass *eStandardError_class;
struct RObject *nomem_err; /* pre-allocated NoMemoryError */
+ struct RObject *stack_err; /* pre-allocated SysStackError */
+#ifdef MRB_GC_FIXED_ARENA
+ struct RObject *arena_err; /* pre-allocated arena overfow error */
+#endif
void *ud; /* auxiliary data */
@@ -392,7 +434,7 @@ MRB_API void mrb_define_const(mrb_state*, struct RClass*, const char *name, mrb_
*
* mrb_value
* mrb_example_method(mrb_state *mrb){
- * return mrb_str_new_cstr(mrb, "example");
+ * return mrb_str_new_lit(mrb, "example");
* }
*
* void
@@ -435,7 +477,7 @@ MRB_API void mrb_undef_method(mrb_state*, struct RClass*, const char*);
*
* mrb_value
* mrb_example_method(mrb_state *mrb){
- * return mrb_str_new_cstr(mrb, "example");
+ * return mrb_str_new_lit(mrb, "example");
* }
*
* void
@@ -567,6 +609,14 @@ MRB_API mrb_bool mrb_class_defined(mrb_state *mrb, const char *name);
MRB_API struct RClass * mrb_class_get(mrb_state *mrb, const char *name);
/**
+ * Gets a exception class.
+ * @param [mrb_state*] mrb The current mruby state.
+ * @param [const char *] name The name of the class.
+ * @return [struct RClass *] A reference to the class.
+*/
+MRB_API struct RClass * mrb_exc_get(mrb_state *mrb, const char *name);
+
+/**
* Returns an mrb_bool. True if inner class was defined, and false if the inner class was not defined.
*
* Example:
@@ -578,10 +628,10 @@ MRB_API struct RClass * mrb_class_get(mrb_state *mrb, const char *name);
* example_outer = mrb_define_module(mrb, "ExampleOuter");
*
* example_inner = mrb_define_class_under(mrb, example_outer, "ExampleInner", mrb->object_class);
- * cd = mrb_class_under_defined(mrb, example_outer, "ExampleInner");
+ * cd = mrb_class_defined_under(mrb, example_outer, "ExampleInner");
*
- * // If mrb_class_under_defined returns 1 then puts "True"
- * // If mrb_class_under_defined returns 0 then puts "False"
+ * // If mrb_class_defined_under returns 1 then puts "True"
+ * // If mrb_class_defined_under returns 0 then puts "False"
* if (cd == 1){
* puts("True");
* }
@@ -595,7 +645,7 @@ MRB_API struct RClass * mrb_class_get(mrb_state *mrb, const char *name);
* @param [const char *] name A string representing the name of the inner class.
* @return [mrb_bool] A boolean value.
*/
-MRB_API mrb_bool mrb_class_under_defined(mrb_state *mrb, struct RClass *outer, const char *name);
+MRB_API mrb_bool mrb_class_defined_under(mrb_state *mrb, struct RClass *outer, const char *name);
/**
* Gets a child class.
@@ -658,7 +708,7 @@ MRB_API mrb_value mrb_check_to_integer(mrb_state *mrb, mrb_value val, const char
*
* example_class = mrb_define_class(mrb, "ExampleClass", mrb->object_class);
* mrb_define_method(mrb, example_class, "example_method", exampleMethod, MRB_ARGS_NONE());
- * mid = mrb_intern_str(mrb, mrb_str_new_cstr(mrb, "example_method" ));
+ * mid = mrb_intern_str(mrb, mrb_str_new_lit(mrb, "example_method" ));
* obj_resp = mrb_obj_respond_to(mrb, example_class, mid); // => 1(true in Ruby world)
*
* // If mrb_obj_respond_to returns 1 then puts "True"
@@ -846,7 +896,7 @@ MRB_API mrb_value mrb_funcall(mrb_state*, mrb_value, const char*, mrb_int,...);
* mrb_state *mrb = mrb_open();
*
* if (!mrb) { }
- * mrb_sym m_sym = mrb_intern_cstr(mrb, "method_name"); // Symbol for method.
+ * mrb_sym m_sym = mrb_intern_lit(mrb, "method_name"); // Symbol for method.
*
* FILE *fp = fopen("test.rb","r");
* mrb_value obj = mrb_load_file(mrb,fp);
@@ -874,7 +924,7 @@ MRB_API mrb_value mrb_funcall_with_block(mrb_state*, mrb_value, mrb_sym, mrb_int
* :pizza # => :pizza
*
* // C style:
- * mrb_sym m_sym = mrb_intern_cstr(mrb, "pizza"); // => :pizza
+ * mrb_sym m_sym = mrb_intern_lit(mrb, "pizza"); // => :pizza
* @param [mrb_state*] mrb_state* The current mruby state.
* @param [const char*] const char* The name of the method.
* @return [mrb_sym] mrb_sym A symbol.
@@ -1050,23 +1100,22 @@ MRB_API void mrb_print_error(mrb_state *mrb);
+ those E_* macros requires mrb_state* variable named mrb.
+ exception objects obtained from those macros are local to mrb
*/
-#define E_RUNTIME_ERROR (mrb_class_get(mrb, "RuntimeError"))
-#define E_TYPE_ERROR (mrb_class_get(mrb, "TypeError"))
-#define E_ARGUMENT_ERROR (mrb_class_get(mrb, "ArgumentError"))
-#define E_INDEX_ERROR (mrb_class_get(mrb, "IndexError"))
-#define E_RANGE_ERROR (mrb_class_get(mrb, "RangeError"))
-#define E_NAME_ERROR (mrb_class_get(mrb, "NameError"))
-#define E_NOMETHOD_ERROR (mrb_class_get(mrb, "NoMethodError"))
-#define E_SCRIPT_ERROR (mrb_class_get(mrb, "ScriptError"))
-#define E_SYNTAX_ERROR (mrb_class_get(mrb, "SyntaxError"))
-#define E_LOCALJUMP_ERROR (mrb_class_get(mrb, "LocalJumpError"))
-#define E_REGEXP_ERROR (mrb_class_get(mrb, "RegexpError"))
-#define E_SYSSTACK_ERROR (mrb_class_get(mrb, "SystemStackError"))
-
-#define E_NOTIMP_ERROR (mrb_class_get(mrb, "NotImplementedError"))
-#define E_FLOATDOMAIN_ERROR (mrb_class_get(mrb, "FloatDomainError"))
-
-#define E_KEY_ERROR (mrb_class_get(mrb, "KeyError"))
+#define E_RUNTIME_ERROR (mrb_exc_get(mrb, "RuntimeError"))
+#define E_TYPE_ERROR (mrb_exc_get(mrb, "TypeError"))
+#define E_ARGUMENT_ERROR (mrb_exc_get(mrb, "ArgumentError"))
+#define E_INDEX_ERROR (mrb_exc_get(mrb, "IndexError"))
+#define E_RANGE_ERROR (mrb_exc_get(mrb, "RangeError"))
+#define E_NAME_ERROR (mrb_exc_get(mrb, "NameError"))
+#define E_NOMETHOD_ERROR (mrb_exc_get(mrb, "NoMethodError"))
+#define E_SCRIPT_ERROR (mrb_exc_get(mrb, "ScriptError"))
+#define E_SYNTAX_ERROR (mrb_exc_get(mrb, "SyntaxError"))
+#define E_LOCALJUMP_ERROR (mrb_exc_get(mrb, "LocalJumpError"))
+#define E_REGEXP_ERROR (mrb_exc_get(mrb, "RegexpError"))
+
+#define E_NOTIMP_ERROR (mrb_exc_get(mrb, "NotImplementedError"))
+#define E_FLOATDOMAIN_ERROR (mrb_exc_get(mrb, "FloatDomainError"))
+
+#define E_KEY_ERROR (mrb_exc_get(mrb, "KeyError"))
MRB_API mrb_value mrb_yield(mrb_state *mrb, mrb_value b, mrb_value arg);
MRB_API mrb_value mrb_yield_argv(mrb_state *mrb, mrb_value b, mrb_int argc, const mrb_value *argv);
@@ -1098,6 +1147,7 @@ MRB_API mrb_value mrb_attr_get(mrb_state *mrb, mrb_value obj, mrb_sym id);
MRB_API mrb_bool mrb_respond_to(mrb_state *mrb, mrb_value obj, mrb_sym mid);
MRB_API mrb_bool mrb_obj_is_instance_of(mrb_state *mrb, mrb_value obj, struct RClass* c);
+MRB_API mrb_bool mrb_func_basic_p(mrb_state *mrb, mrb_value obj, mrb_sym mid, mrb_func_t func);
/*
@@ -1119,7 +1169,7 @@ MRB_API mrb_value mrb_fiber_yield(mrb_state *mrb, mrb_int argc, const mrb_value
*
* @mrbgem mruby-fiber
*/
-#define E_FIBER_ERROR (mrb_class_get(mrb, "FiberError"))
+#define E_FIBER_ERROR (mrb_exc_get(mrb, "FiberError"))
/* memory pool implementation */
typedef struct mrb_pool mrb_pool;
@@ -1135,21 +1185,6 @@ MRB_API void mrb_state_atexit(mrb_state *mrb, mrb_atexit_func func);
MRB_API void mrb_show_version(mrb_state *mrb);
MRB_API void mrb_show_copyright(mrb_state *mrb);
-#ifdef MRB_DEBUG
-#include <assert.h>
-#define mrb_assert(p) assert(p)
-#define mrb_assert_int_fit(t1,n,t2,max) assert((n)>=0 && ((sizeof(n)<=sizeof(t2))||(n<=(t1)(max))))
-#else
-#define mrb_assert(p) ((void)0)
-#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_API mrb_value mrb_format(mrb_state *mrb, const char *format, ...);
MRB_END_DECL
diff --git a/include/mruby/array.h b/include/mruby/array.h
index bd78db066..e553faf92 100644
--- a/include/mruby/array.h
+++ b/include/mruby/array.h
@@ -54,14 +54,60 @@ MRB_API mrb_value mrb_ary_new_capa(mrb_state*, mrb_int);
* Array.new
*
* @param mrb The mruby state reference.
- * @return The initialized array
+ * @return The initialized array.
*/
MRB_API mrb_value mrb_ary_new(mrb_state *mrb);
+/*
+ * Initializes a new array with initial values
+ *
+ * Equivalent to:
+ *
+ * Array[value1, value2, ...]
+ *
+ * @param mrb The mruby state reference.
+ * @param size The numer of values.
+ * @param vals The actual values.
+ * @return The initialized array.
+ */
MRB_API mrb_value mrb_ary_new_from_values(mrb_state *mrb, mrb_int size, const mrb_value *vals);
+
+/*
+ * Initializes a new array with two initial values
+ *
+ * Equivalent to:
+ *
+ * Array[car, cdr]
+ *
+ * @param mrb The mruby state reference.
+ * @param car The first value.
+ * @param cdr The second value.
+ * @return The initialized array.
+ */
MRB_API mrb_value mrb_assoc_new(mrb_state *mrb, mrb_value car, mrb_value cdr);
-MRB_API void mrb_ary_concat(mrb_state*, mrb_value, mrb_value);
-MRB_API mrb_value mrb_ary_splat(mrb_state*, mrb_value);
+
+/*
+ * Concatenate two arrays. The target array will be modified
+ *
+ * Equivalent to:
+ * ary.concat(other)
+ *
+ * @param mrb The mruby state reference.
+ * @param self The target array.
+ * @param other The array that will be concatenated to self.
+ */
+MRB_API void mrb_ary_concat(mrb_state *mrb, mrb_value self, mrb_value other);
+
+/*
+ * Create an array from the input. It tries calling to_a on the
+ * value. If value does not respond to that, it creates a new
+ * array with just this value.
+ *
+ * @param mrb The mruby state reference.
+ * @param value The value to change into an array.
+ * @return An array representation of value.
+ */
+MRB_API mrb_value mrb_ary_splat(mrb_state *mrb, mrb_value value);
/*
* Pushes value into array.
@@ -84,8 +130,8 @@ MRB_API void mrb_ary_push(mrb_state *mrb, mrb_value array, mrb_value value);
* ary.pop
*
* @param mrb The mruby state reference.
- * @param ary The array from which the value will be poped.
- * @return The poped value.
+ * @param ary The array from which the value will be popped.
+ * @return The popped value.
*/
MRB_API mrb_value mrb_ary_pop(mrb_state *mrb, mrb_value ary);
@@ -117,14 +163,81 @@ MRB_API mrb_value mrb_ary_ref(mrb_state *mrb, mrb_value ary, mrb_int n);
*/
MRB_API void mrb_ary_set(mrb_state *mrb, mrb_value ary, mrb_int n, mrb_value val);
-MRB_API void mrb_ary_replace(mrb_state *mrb, mrb_value a, mrb_value b);
+/*
+ * Replace the array with another array
+ *
+ * Equivalent to:
+ *
+ * ary.replace(other)
+ *
+ * @param mrb The mruby state reference
+ * @param self The target array.
+ * @param other The array to replace it with.
+ */
+MRB_API void mrb_ary_replace(mrb_state *mrb, mrb_value self, mrb_value other);
MRB_API mrb_value mrb_check_array_type(mrb_state *mrb, mrb_value self);
+
+/*
+ * Unshift an element into an array
+ *
+ * Equivalent to:
+ *
+ * ary.unshift(item)
+ *
+ * @param mrb The mruby state reference.
+ * @param self The target array.
+ * @param item The item to unshift.
+ */
MRB_API mrb_value mrb_ary_unshift(mrb_state *mrb, mrb_value self, mrb_value item);
MRB_API mrb_value mrb_ary_entry(mrb_value ary, mrb_int offset);
+
+/*
+ * Shifts the first element from the array.
+ *
+ * Equivalent to:
+ *
+ * ary.shift
+ *
+ * @param mrb The mruby state reference.
+ * @param self The array from which the value will be shifted.
+ * @return The shifted value.
+ */
MRB_API mrb_value mrb_ary_shift(mrb_state *mrb, mrb_value self);
+
+/*
+ * Removes all elements from this array
+ *
+ * Equivalent to:
+ *
+ * ary.clear
+ *
+ * @param mrb The mruby state reference.
+ * @param self The target array.
+ * @return self
+ */
MRB_API mrb_value mrb_ary_clear(mrb_state *mrb, mrb_value self);
+
+/*
+ * Join the array elements together in a string
+ *
+ * Equivalent to:
+ *
+ * ary.join(sep="")
+ *
+ * @param mrb The mruby state reference.
+ * @param ary The target array
+ * @param sep The separater, can be NULL
+ */
MRB_API mrb_value mrb_ary_join(mrb_state *mrb, mrb_value ary, mrb_value sep);
-MRB_API mrb_value mrb_ary_resize(mrb_state *mrb, mrb_value ary, mrb_int len);
+
+/*
+ * Update the capacity of the array
+ *
+ * @param mrb The mruby state reference.
+ * @param ary The target array.
+ * @param new_len The new capacity of the array
+ */
+MRB_API mrb_value mrb_ary_resize(mrb_state *mrb, mrb_value ary, mrb_int new_len);
static inline mrb_int
mrb_ary_len(mrb_state *mrb, mrb_value ary)
@@ -134,6 +247,15 @@ mrb_ary_len(mrb_state *mrb, mrb_value ary)
return RARRAY_LEN(ary);
}
+static inline mrb_value
+ary_elt(mrb_value ary, mrb_int offset)
+{
+ if (offset < 0 || RARRAY_LEN(ary) <= offset) {
+ return mrb_nil_value();
+ }
+ return RARRAY_PTR(ary)[offset];
+}
+
MRB_END_DECL
#endif /* MRUBY_ARRAY_H */
diff --git a/include/mruby/boxing_nan.h b/include/mruby/boxing_nan.h
index b71c4b746..4cb82bf14 100644
--- a/include/mruby/boxing_nan.h
+++ b/include/mruby/boxing_nan.h
@@ -53,23 +53,27 @@ typedef struct mrb_value {
#define mrb_float_pool(mrb,f) mrb_float_value(mrb,f)
#define mrb_tt(o) ((enum mrb_vtype)(((o).value.ttt & 0xfc000)>>14)-1)
-#define mrb_type(o) ((uint32_t)0xfff00000 < (o).value.ttt ? mrb_tt(o) : MRB_TT_FLOAT)
+#define mrb_type(o) (enum mrb_vtype)((uint32_t)0xfff00000 < (o).value.ttt ? mrb_tt(o) : MRB_TT_FLOAT)
#define mrb_ptr(o) ((void*)((((uintptr_t)0x3fffffffffff)&((uintptr_t)((o).value.p)))<<2))
#define mrb_float(o) (o).f
#define mrb_cptr(o) mrb_ptr(o)
#define mrb_fixnum(o) (o).value.i
#define mrb_symbol(o) (o).value.sym
+#ifdef MRB_64BIT
+#define BOXNAN_SHIFT_LONG_POINTER(v) (((uintptr_t)(v)>>34)&0x3fff)
+#else
+#define BOXNAN_SHIFT_LONG_POINTER(v) 0
+#endif
+
#define BOXNAN_SET_VALUE(o, tt, attr, v) do {\
- switch (tt) {\
- case MRB_TT_FALSE:\
- case MRB_TT_TRUE:\
- case MRB_TT_UNDEF:\
- case MRB_TT_FIXNUM:\
- case MRB_TT_SYMBOL: (o).attr = (v); break;\
- default: (o).value.i = 0; (o).value.p = (void*)((uintptr_t)(o).value.p | (((uintptr_t)(v))>>2)); break;\
- }\
- (o).value.ttt = (0xfff00000|(((tt)+1)<<14));\
+ (o).attr = (v);\
+ (o).value.ttt = 0xfff00000 | (((tt)+1)<<14);\
+} while (0)
+
+#define BOXNAN_SET_OBJ_VALUE(o, tt, v) do {\
+ (o).value.p = (void*)((uintptr_t)(v)>>2);\
+ (o).value.ttt = (0xfff00000|(((tt)+1)<<14)|BOXNAN_SHIFT_LONG_POINTER(v));\
} while (0)
#define SET_FLOAT_VALUE(mrb,r,v) do { \
@@ -86,8 +90,8 @@ typedef struct mrb_value {
#define SET_BOOL_VALUE(r,b) BOXNAN_SET_VALUE(r, b ? MRB_TT_TRUE : MRB_TT_FALSE, value.i, 1)
#define SET_INT_VALUE(r,n) BOXNAN_SET_VALUE(r, MRB_TT_FIXNUM, value.i, (n))
#define SET_SYM_VALUE(r,v) BOXNAN_SET_VALUE(r, MRB_TT_SYMBOL, value.sym, (v))
-#define SET_OBJ_VALUE(r,v) BOXNAN_SET_VALUE(r, (((struct RObject*)(v))->tt), value.p, (v))
-#define SET_CPTR_VALUE(mrb,r,v) BOXNAN_SET_VALUE(r, MRB_TT_CPTR, value.p, v)
+#define SET_OBJ_VALUE(r,v) BOXNAN_SET_OBJ_VALUE(r, (((struct RObject*)(v))->tt), (v))
+#define SET_CPTR_VALUE(mrb,r,v) BOXNAN_SET_OBJ_VALUE(r, MRB_TT_CPTR, v)
#define SET_UNDEF_VALUE(r) BOXNAN_SET_VALUE(r, MRB_TT_UNDEF, value.i, 0)
#endif /* MRUBY_BOXING_NAN_H */
diff --git a/include/mruby/boxing_word.h b/include/mruby/boxing_word.h
index 8754087a3..6ca491585 100644
--- a/include/mruby/boxing_word.h
+++ b/include/mruby/boxing_word.h
@@ -11,6 +11,10 @@
# error MRB_INT16 is too small for MRB_WORD_BOXING.
#endif
+#if defined(MRB_INT64) && !defined(MRB_64BIT)
+#error MRB_INT64 cannot be used with MRB_WORD_BOXING in 32-bit mode.
+#endif
+
struct RFloat {
MRB_OBJECT_HEADER;
mrb_float f;
@@ -91,13 +95,13 @@ mrb_type(mrb_value o)
#define mrb_undef_p(o) ((o).w == MRB_Qundef)
#define mrb_nil_p(o) ((o).w == MRB_Qnil)
-#define BOXWORD_SET_VALUE(o, ttt, attr, v) do {\
+#define BOXWORD_SET_VALUE(o, ttt, attr, v) do { \
switch (ttt) {\
case MRB_TT_FALSE: (o).w = (v) ? MRB_Qfalse : MRB_Qnil; break;\
case MRB_TT_TRUE: (o).w = MRB_Qtrue; break;\
case MRB_TT_UNDEF: (o).w = MRB_Qundef; break;\
- case MRB_TT_FIXNUM: (o).value.i_flag = MRB_FIXNUM_FLAG; (o).attr = (v); break;\
- case MRB_TT_SYMBOL: (o).value.sym_flag = MRB_SYMBOL_FLAG; (o).attr = (v); break;\
+ case MRB_TT_FIXNUM: (o).w = 0;(o).value.i_flag = MRB_FIXNUM_FLAG; (o).attr = (v); break;\
+ case MRB_TT_SYMBOL: (o).w = 0;(o).value.sym_flag = MRB_SYMBOL_FLAG; (o).attr = (v); break;\
default: (o).w = 0; (o).attr = (v); if ((o).value.bp) (o).value.bp->tt = ttt; break;\
}\
} while (0)
diff --git a/include/mruby/class.h b/include/mruby/class.h
index 246e82e59..ce953af3b 100644
--- a/include/mruby/class.h
+++ b/include/mruby/class.h
@@ -52,6 +52,7 @@ mrb_class(mrb_state *mrb, mrb_value v)
}
// TODO: figure out where to put user flags
+#define MRB_FLAG_IS_FROZEN (1 << 18)
#define MRB_FLAG_IS_PREPENDED (1 << 19)
#define MRB_FLAG_IS_ORIGIN (1 << 20)
#define MRB_CLASS_ORIGIN(c) do {\
diff --git a/include/mruby/common.h b/include/mruby/common.h
index 3745c58ea..338044c2f 100644
--- a/include/mruby/common.h
+++ b/include/mruby/common.h
@@ -9,8 +9,13 @@
#ifdef __cplusplus
+#ifdef MRB_ENABLE_CXX_EXCEPTION
+#define MRB_BEGIN_DECL
+#define MRB_END_DECL
+#else
# define MRB_BEGIN_DECL extern "C" {
# define MRB_END_DECL }
+#endif
#else
/** Start declarations in C mode */
# define MRB_BEGIN_DECL
diff --git a/include/mruby/compile.h b/include/mruby/compile.h
index 3ccaf9f6a..ad3f19db1 100644
--- a/include/mruby/compile.h
+++ b/include/mruby/compile.h
@@ -163,6 +163,7 @@ struct mrb_parser_state {
MRB_API struct mrb_parser_state* mrb_parser_new(mrb_state*);
MRB_API void mrb_parser_free(struct mrb_parser_state*);
MRB_API void mrb_parser_parse(struct mrb_parser_state*,mrbc_context*);
+MRB_API double mrb_float_read(const char*, char**);
MRB_API void mrb_parser_set_filename(struct mrb_parser_state*, char const*);
MRB_API char const* mrb_parser_get_filename(struct mrb_parser_state*, uint16_t idx);
@@ -174,6 +175,7 @@ MRB_API struct mrb_parser_state* mrb_parse_file(mrb_state*,FILE*,mrbc_context*);
MRB_API struct mrb_parser_state* mrb_parse_string(mrb_state*,const char*,mrbc_context*);
MRB_API struct mrb_parser_state* mrb_parse_nstring(mrb_state*,const char*,int,mrbc_context*);
MRB_API struct RProc* mrb_generate_code(mrb_state*, struct mrb_parser_state*);
+MRB_API mrb_value mrb_load_exec(mrb_state *mrb, struct mrb_parser_state *p, mrbc_context *c);
/* program load functions */
#ifndef MRB_DISABLE_STDIO
diff --git a/include/mruby/hash.h b/include/mruby/hash.h
index 922353322..55ad6a921 100644
--- a/include/mruby/hash.h
+++ b/include/mruby/hash.h
@@ -28,40 +28,126 @@ MRB_API mrb_value mrb_hash_new_capa(mrb_state*, int);
/*
* Initializes a new hash.
+ *
+ * Equivalent to:
+ *
+ * Hash.new
+ *
+ * @param mrb The mruby state reference.
+ * @return The initialized hash.
*/
MRB_API mrb_value mrb_hash_new(mrb_state *mrb);
/*
* Sets a keys and values to hashes.
+ *
+ * Equivalent to:
+ *
+ * hash[key] = val
+ *
+ * @param mrb The mruby state reference.
+ * @param hash The target hash.
+ * @param key The key to set.
+ * @param val The value to set.
+ * @return The value.
*/
MRB_API void mrb_hash_set(mrb_state *mrb, mrb_value hash, mrb_value key, mrb_value val);
/*
- * Gets a value from a key.
+ * Gets a value from a key. If the key is not found, the default of the
+ * hash is used.
+ *
+ * Equivalent to:
+ *
+ * hash[key]
+ *
+ * @param mrb The mruby state reference.
+ * @param hash The target hash.
+ * @param key The key to get.
+ * @return The found value.
*/
MRB_API mrb_value mrb_hash_get(mrb_state *mrb, mrb_value hash, mrb_value key);
+/*
+ * Gets a value from a key. If the key is not found, the default parameter is
+ * used.
+ *
+ * Equivalent to:
+ *
+ * hash.hash_key?(key) ? hash[key] : def
+ *
+ * @param mrb The mruby state reference.
+ * @param hash The target hash.
+ * @param key The key to get.
+ * @param def The default value.
+ * @return The found value.
+ */
MRB_API mrb_value mrb_hash_fetch(mrb_state *mrb, mrb_value hash, mrb_value key, mrb_value def);
/*
* Deletes hash key and value pair.
+ *
+ * Equivalent to:
+ *
+ * hash.delete(key)
+ *
+ * @param mrb The mruby state reference.
+ * @param hash The target hash.
+ * @param key The key to delete.
+ * @return The deleted value.
*/
MRB_API mrb_value mrb_hash_delete_key(mrb_state *mrb, mrb_value hash, mrb_value key);
/*
* Gets an array of keys.
+ *
+ * Equivalent to:
+ *
+ * hash.keys
+ *
+ * @param mrb The mruby state reference.
+ * @param hash The target hash.
+ * @return An array with the keys of the hash.
*/
MRB_API mrb_value mrb_hash_keys(mrb_state *mrb, mrb_value hash);
MRB_API mrb_value mrb_check_hash_type(mrb_state *mrb, mrb_value hash);
+
+/*
+ * Check if the hash is empty
+ *
+ * Equivalent to:
+ *
+ * hash.empty?
+ *
+ * @param mrb The mruby state reference.
+ * @param self The target hash.
+ * @return True if the hash is empty, false otherwise.
+ */
MRB_API mrb_value mrb_hash_empty_p(mrb_state *mrb, mrb_value self);
/*
* Gets an array of values.
+ *
+ * Equivalent to:
+ *
+ * hash.values
+ *
+ * @param mrb The mruby state reference.
+ * @param hash The target hash.
+ * @return An array with the values of the hash.
*/
MRB_API mrb_value mrb_hash_values(mrb_state *mrb, mrb_value hash);
/*
* Clears the hash.
+ *
+ * Equivalent to:
+ *
+ * hash.clear
+ *
+ * @param mrb The mruby state reference.
+ * @param hash The target hash.
+ * @return The hash
*/
MRB_API mrb_value mrb_hash_clear(mrb_state *mrb, mrb_value hash);
diff --git a/include/mruby/irep.h b/include/mruby/irep.h
index 8922f4b76..35ae2bbaa 100644
--- a/include/mruby/irep.h
+++ b/include/mruby/irep.h
@@ -39,6 +39,7 @@ typedef struct mrb_irep {
struct mrb_locals *lv;
/* debug info */
+ mrb_bool own_filename;
const char *filename;
uint16_t *lines;
struct mrb_irep_debug_info* debug_info;
diff --git a/include/mruby/istruct.h b/include/mruby/istruct.h
new file mode 100644
index 000000000..4d2393ccd
--- /dev/null
+++ b/include/mruby/istruct.h
@@ -0,0 +1,47 @@
+/*
+** mruby/istruct.h - Inline structures
+**
+** See Copyright Notice in mruby.h
+*/
+
+#ifndef MRUBY_ISTRUCT_H
+#define MRUBY_ISTRUCT_H
+
+#include "common.h"
+#include <string.h>
+
+/**
+ * Inline structures that fit in RVALUE
+ *
+ * They cannot have finalizer, and cannot have instance variables.
+ */
+MRB_BEGIN_DECL
+
+#define ISTRUCT_DATA_SIZE (sizeof(void*) * 3)
+
+struct RIstruct {
+ MRB_OBJECT_HEADER;
+ char inline_data[ISTRUCT_DATA_SIZE];
+};
+
+#define RISTRUCT(obj) ((struct RIstruct*)(mrb_ptr(obj)))
+#define ISTRUCT_PTR(obj) (RISTRUCT(obj)->inline_data)
+
+MRB_INLINE mrb_int mrb_istruct_size()
+{
+ return ISTRUCT_DATA_SIZE;
+}
+
+MRB_INLINE void* mrb_istruct_ptr(mrb_value object)
+{
+ return ISTRUCT_PTR(object);
+}
+
+MRB_INLINE void mrb_istruct_copy(mrb_value dest, mrb_value src)
+{
+ memcpy(ISTRUCT_PTR(dest), ISTRUCT_PTR(src), ISTRUCT_DATA_SIZE);
+}
+
+MRB_END_DECL
+
+#endif /* MRUBY_ISTRUCT_H */
diff --git a/include/mruby/object.h b/include/mruby/object.h
index 9fbfe34f3..9347981d4 100644
--- a/include/mruby/object.h
+++ b/include/mruby/object.h
@@ -22,6 +22,10 @@ struct RBasic {
};
#define mrb_basic_ptr(v) ((struct RBasic*)(mrb_ptr(v)))
+#define MRB_FROZEN_P(o) ((o)->flags & MRB_FLAG_IS_FROZEN)
+#define MRB_SET_FROZEN_FLAG(o) ((o)->flags |= MRB_FLAG_IS_FROZEN)
+#define MRB_UNSET_FROZEN_FLAG(o) ((o)->flags &= ~MRB_FLAG_IS_FROZEN)
+
struct RObject {
MRB_OBJECT_HEADER;
struct iv_tbl *iv;
diff --git a/include/mruby/range.h b/include/mruby/range.h
index cf42ce133..b166e586b 100644
--- a/include/mruby/range.h
+++ b/include/mruby/range.h
@@ -25,7 +25,8 @@ struct RRange {
mrb_bool excl : 1;
};
-#define mrb_range_ptr(v) ((struct RRange*)(mrb_ptr(v)))
+MRB_API struct RRange* mrb_range_ptr(mrb_state *mrb, mrb_value v);
+#define mrb_range_raw_ptr(v) ((struct RRange*)mrb_ptr(v))
#define mrb_range_value(p) mrb_obj_value((void*)(p))
/*
@@ -40,7 +41,7 @@ struct RRange {
*/
MRB_API mrb_value mrb_range_new(mrb_state *mrb, mrb_value start, mrb_value end, mrb_bool exclude);
-MRB_API mrb_bool mrb_range_beg_len(mrb_state *mrb, mrb_value range, mrb_int *begp, mrb_int *lenp, mrb_int len);
+MRB_API mrb_int mrb_range_beg_len(mrb_state *mrb, mrb_value range, mrb_int *begp, mrb_int *lenp, mrb_int len, mrb_bool trunc);
mrb_value mrb_get_values_at(mrb_state *mrb, mrb_value obj, mrb_int olen, mrb_int argc, const mrb_value *argv, mrb_value (*func)(mrb_state*, mrb_value, mrb_int));
MRB_END_DECL
diff --git a/include/mruby/re.h b/include/mruby/re.h
index dfb3b0e2d..1d09d06c9 100644
--- a/include/mruby/re.h
+++ b/include/mruby/re.h
@@ -7,14 +7,10 @@
#ifndef MRUBY_RE_H
#define MRUBY_RE_H
-#ifdef __cplusplus
-extern "C" {
-#endif
+MRB_BEGIN_DECL
#define REGEXP_CLASS "Regexp"
-#ifdef __cplusplus
-}
-#endif
+MRB_END_DECL
#endif /* RE_H */
diff --git a/include/mruby/string.h b/include/mruby/string.h
index e45846e87..5a5a6ffd2 100644
--- a/include/mruby/string.h
+++ b/include/mruby/string.h
@@ -62,17 +62,13 @@ struct RString {
#define RSTR_SET_NOFREE_FLAG(s) ((s)->flags |= MRB_STR_NOFREE)
#define RSTR_UNSET_NOFREE_FLAG(s) ((s)->flags &= ~MRB_STR_NOFREE)
-#define RSTR_FROZEN_P(s) ((s)->flags & MRB_STR_FROZEN)
-#define RSTR_SET_FROZEN_FLAG(s) ((s)->flags |= MRB_STR_FROZEN)
-#define RSTR_UNSET_FROZEN_FLAG(s) ((s)->flags &= ~MRB_STR_FROZEN)
-
/*
* Returns a pointer from a Ruby string
*/
#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_EMBED_LEN(s) RSTR_EMBED_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))
@@ -80,7 +76,6 @@ MRB_API mrb_int mrb_str_strlen(mrb_state*, struct RString*);
#define MRB_STR_SHARED 1
#define MRB_STR_NOFREE 2
-#define MRB_STR_FROZEN 4
#define MRB_STR_NO_UTF 8
#define MRB_STR_EMBED 16
#define MRB_STR_EMBED_LEN_MASK 0x3e0
@@ -110,8 +105,8 @@ MRB_API void mrb_str_modify(mrb_state*, struct RString*);
* }
*
* // Creates new Ruby strings.
- * str1 = mrb_str_new_cstr(mrb, "abc");
- * str2 = mrb_str_new_cstr(mrb, "def");
+ * str1 = mrb_str_new_lit(mrb, "abc");
+ * str2 = mrb_str_new_lit(mrb, "def");
*
* // Concatnates str2 to str1.
* mrb_str_concat(mrb, str1, str2);
@@ -158,8 +153,8 @@ MRB_API void mrb_str_concat(mrb_state*, mrb_value, mrb_value);
* }
*
* // Creates two Ruby strings from the passed in C strings.
- * a = mrb_str_new_cstr(mrb, "abc");
- * b = mrb_str_new_cstr(mrb, "def");
+ * a = mrb_str_new_lit(mrb, "abc");
+ * b = mrb_str_new_lit(mrb, "def");
*
* // Prints both C strings.
* mrb_p(mrb, a);
@@ -227,7 +222,7 @@ MRB_API mrb_value mrb_obj_as_string(mrb_state *mrb, mrb_value obj);
* // handle error
* }
* // Creates a new string.
- * str = mrb_str_new_cstr(mrb, "Hello, world!");
+ * str = mrb_str_new_lit(mrb, "Hello, world!");
* // Returns 5 characters of
* mrb_str_resize(mrb, str, 5);
* mrb_p(mrb, str);
@@ -267,7 +262,7 @@ MRB_API mrb_value mrb_str_resize(mrb_state *mrb, mrb_value str, mrb_int len);
* // handle error
* }
* // Creates new string.
- * str1 = mrb_str_new_cstr(mrb, "Hello, world!");
+ * str1 = mrb_str_new_lit(mrb, "Hello, world!");
* // Returns a sub-string within the range of 0..2
* str2 = mrb_str_substr(mrb, str1, 0, 2);
*
diff --git a/include/mruby/throw.h b/include/mruby/throw.h
index 5dd1d0a9f..010793027 100644
--- a/include/mruby/throw.h
+++ b/include/mruby/throw.h
@@ -7,6 +7,10 @@
#ifndef MRB_THROW_H
#define MRB_THROW_H
+#if defined(MRB_ENABLE_CXX_EXCEPTION) && !defined(__cplusplus)
+#error Trying to use C++ exception handling in C code
+#endif
+
#if defined(MRB_ENABLE_CXX_EXCEPTION) && defined(__cplusplus)
#define MRB_TRY(buf) do { try {
diff --git a/include/mruby/value.h b/include/mruby/value.h
index 4330b9441..54d197f8f 100644
--- a/include/mruby/value.h
+++ b/include/mruby/value.h
@@ -62,12 +62,12 @@ struct mrb_state;
# define MRB_PRIx PRIx32
#endif
+
+MRB_API double mrb_float_read(const char*, char**);
#ifdef MRB_USE_FLOAT
typedef float mrb_float;
-# define str_to_mrb_float(buf) strtof(buf, NULL)
#else
typedef double mrb_float;
-# define str_to_mrb_float(buf) strtod(buf, NULL)
#endif
#if defined _MSC_VER && _MSC_VER < 1900
@@ -85,7 +85,6 @@ MRB_API int mrb_msvc_snprintf(char *s, size_t n, const char *format, ...);
# define isnan _isnan
# define isinf(n) (!_finite(n) && !_isnan(n))
# define signbit(n) (_copysign(1.0, (n)) < 0.0)
-# define strtof (float)strtod
static const unsigned int IEEE754_INFINITY_BITS_SINGLE = 0x7F800000;
# define INFINITY (*(float *)&IEEE754_INFINITY_BITS_SINGLE)
# define NAN ((float)(INFINITY - INFINITY))
@@ -116,7 +115,8 @@ enum mrb_vtype {
MRB_TT_ENV, /* 20 */
MRB_TT_DATA, /* 21 */
MRB_TT_FIBER, /* 22 */
- MRB_TT_MAXDEFINE /* 23 */
+ MRB_TT_ISTRUCT, /* 23 */
+ MRB_TT_MAXDEFINE /* 24 */
};
#include <mruby/object.h>
@@ -210,6 +210,8 @@ mrb_obj_value(void *p)
{
mrb_value v;
SET_OBJ_VALUE(v, (struct RBasic*)p);
+ mrb_assert(p == mrb_ptr(v));
+ mrb_assert(((struct RBasic*)p)->tt == mrb_type(v));
return v;
}
diff --git a/include/mruby/variable.h b/include/mruby/variable.h
index 15068039a..2f2bbbf98 100644
--- a/include/mruby/variable.h
+++ b/include/mruby/variable.h
@@ -54,9 +54,67 @@ MRB_API mrb_bool mrb_iv_defined(mrb_state*, mrb_value, mrb_sym);
MRB_API mrb_value mrb_iv_remove(mrb_state *mrb, mrb_value obj, mrb_sym sym);
MRB_API void mrb_iv_copy(mrb_state *mrb, mrb_value dst, mrb_value src);
MRB_API mrb_bool mrb_const_defined_at(mrb_state *mrb, mrb_value mod, mrb_sym id);
+
+/**
+ * Get a global variable. Will return nil if the var does not exist
+ *
+ * Example:
+ *
+ * !!!ruby
+ * # Ruby style
+ * var = $value
+ *
+ * !!!c
+ * // C style
+ * mrb_sym sym = mrb_intern_lit(mrb, "$value");
+ * mrb_value var = mrb_gv_get(mrb, sym);
+ *
+ * @param mrb The mruby state reference
+ * @param sym The name of the global variable
+ * @return The value of that global variable. May be nil
+ */
MRB_API mrb_value mrb_gv_get(mrb_state *mrb, mrb_sym sym);
+
+/**
+ * Set a global variable
+ *
+ * Example:
+ *
+ * !!!ruby
+ * # Ruby style
+ * $value = "foo"
+ *
+ * !!!c
+ * // C style
+ * mrb_sym sym = mrb_intern_lit(mrb, "$value");
+ * mrb_gv_set(mrb, sym, mrb_str_new_lit("foo"));
+ *
+ * @param mrb The mruby state reference
+ * @param sym The name of the global variable
+ * @param val The value of the global variable
+ */
MRB_API void mrb_gv_set(mrb_state *mrb, mrb_sym sym, mrb_value val);
+
+/**
+ * Remove a global variable.
+ *
+ * Example:
+ *
+ * !!!ruby
+ * # Ruby style
+ * $value = nil
+ *
+ * !!!c
+ * // C style
+ * mrb_sym sym = mrb_intern_lit(mrb, "$value");
+ * mrb_gv_remove(mrb, sym);
+ *
+ * @param mrb The mruby state reference
+ * @param sym The name of the global variable
+ * @param val The value of the global variable
+ */
MRB_API void mrb_gv_remove(mrb_state *mrb, mrb_sym sym);
+
MRB_API mrb_value mrb_cv_get(mrb_state *mrb, mrb_value mod, mrb_sym sym);
MRB_API void mrb_mod_cv_set(mrb_state *mrb, struct RClass * c, mrb_sym sym, mrb_value v);
MRB_API void mrb_cv_set(mrb_state *mrb, mrb_value mod, mrb_sym sym, mrb_value v);
diff --git a/minirake b/minirake
index eb219b934..6e63cbb1e 100755
--- a/minirake
+++ b/minirake
@@ -237,6 +237,7 @@ module MiniRake
# Time stamp for file task.
def timestamp
+ return Time.at(0) unless File.exist?(name)
stat = File::stat(name.to_s)
stat.directory? ? Time.at(0) : stat.mtime
end
diff --git a/mrbgems/default.gembox b/mrbgems/default.gembox
index 0960ba979..64f05de10 100644
--- a/mrbgems/default.gembox
+++ b/mrbgems/default.gembox
@@ -53,7 +53,7 @@ MRuby::GemBox.new do |conf|
# Use Enumerator class (require mruby-fiber)
conf.gem :core => "mruby-enumerator"
- # Use Enumerable::Lazy class (require mruby-enumerator)
+ # Use Enumerator::Lazy class (require mruby-enumerator)
conf.gem :core => "mruby-enum-lazy"
# Use toplevel object (main) methods extension
@@ -71,6 +71,9 @@ MRuby::GemBox.new do |conf|
# Use Kernel module extension
conf.gem :core => "mruby-kernel-ext"
+ # Use class/module extension
+ conf.gem :core => "mruby-class-ext"
+
# Use mruby-compiler to build other mrbgems
conf.gem :core => "mruby-compiler"
end
diff --git a/mrbgems/mruby-array-ext/src/array.c b/mrbgems/mruby-array-ext/src/array.c
index d5c96e2cc..af947303b 100644
--- a/mrbgems/mruby-array-ext/src/array.c
+++ b/mrbgems/mruby-array-ext/src/array.c
@@ -131,7 +131,7 @@ mrb_ary_to_h(mrb_state *mrb, mrb_value ary)
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_str_new_cstr(mrb, mrb_obj_classname(mrb, ary_elt(ary, i))),
mrb_fixnum_value(i)
);
}
diff --git a/mrbgems/mruby-array-ext/test/array.rb b/mrbgems/mruby-array-ext/test/array.rb
index ec1528fc3..09ec8d9e7 100644
--- a/mrbgems/mruby-array-ext/test/array.rb
+++ b/mrbgems/mruby-array-ext/test/array.rb
@@ -301,6 +301,17 @@ assert('Array#to_h') do
assert_raise(ArgumentError) { [[1]].to_h }
end
+assert('Array#to_h (Modified)') do
+ class A
+ def to_ary
+ $a.clear
+ nil
+ end
+ end
+ $a = [A.new]
+ assert_raise(TypeError) { $a.to_h }
+end
+
assert("Array#index (block)") do
assert_nil (1..10).to_a.index { |i| i % 5 == 0 and i % 7 == 0 }
assert_equal 34, (1..100).to_a.index { |i| i % 5 == 0 and i % 7 == 0 }
diff --git a/mrbgems/mruby-bin-debugger/tools/mrdb/apibreak.c b/mrbgems/mruby-bin-debugger/tools/mrdb/apibreak.c
index 218aeda90..27f27a05a 100644
--- a/mrbgems/mruby-bin-debugger/tools/mrdb/apibreak.c
+++ b/mrbgems/mruby-bin-debugger/tools/mrdb/apibreak.c
@@ -43,7 +43,7 @@ check_lineno( mrb_irep_debug_info_file *info_file, uint16_t lineno )
}
static int32_t
-get_break_index( mrb_debug_context *dbg, int32_t bpno )
+get_break_index( mrb_debug_context *dbg, uint32_t bpno )
{
uint32_t i;
int32_t index;
@@ -296,7 +296,7 @@ mrb_debug_get_break_all( mrb_state *mrb, mrb_debug_context *dbg, uint32_t size,
int32_t
mrb_debug_get_break( mrb_state *mrb, mrb_debug_context *dbg, uint32_t bpno, mrb_debug_breakpoint *bp )
{
- uint32_t index;
+ int32_t index;
if((mrb == NULL) || (dbg == NULL) || (bp == NULL)) {
return MRB_DEBUG_INVALID_ARGUMENT;
diff --git a/mrbgems/mruby-bin-debugger/tools/mrdb/apilist.c b/mrbgems/mruby-bin-debugger/tools/mrdb/apilist.c
index 1c77d1ef8..33e9575bf 100644
--- a/mrbgems/mruby-bin-debugger/tools/mrdb/apilist.c
+++ b/mrbgems/mruby-bin-debugger/tools/mrdb/apilist.c
@@ -71,7 +71,7 @@ dirname(mrb_state *mrb, const char *path)
}
p = strrchr(path, '/');
- len = p != NULL ? p - path : strlen(path);
+ len = p != NULL ? (size_t)(p - path) : strlen(path);
dir = mrb_malloc(mrb, len + 1);
strncpy(dir, path, len);
diff --git a/mrbgems/mruby-bin-mruby/bintest/mruby.rb b/mrbgems/mruby-bin-mruby/bintest/mruby.rb
index 01fc94632..72ac6586d 100644
--- a/mrbgems/mruby-bin-mruby/bintest/mruby.rb
+++ b/mrbgems/mruby-bin-mruby/bintest/mruby.rb
@@ -44,3 +44,17 @@ EOS
script.flush
assert_equal "\"test\"\n\"fin\"\n", `#{cmd('mruby')} #{script.path}`
end
+
+assert('garbage collecting built-in classes') do
+ script = Tempfile.new('test.rb')
+
+ script.write <<RUBY
+NilClass = nil
+GC.start
+Array.dup
+print nil.class.to_s
+RUBY
+ script.flush
+ assert_equal "NilClass", `#{cmd('mruby')} #{script.path}`
+ assert_equal 0, $?.exitstatus
+end
diff --git a/mrbgems/mruby-bin-mruby/tools/mruby/mruby.c b/mrbgems/mruby-bin-mruby/tools/mruby/mruby.c
index ab6c6688f..d10535140 100644
--- a/mrbgems/mruby-bin-mruby/tools/mruby/mruby.c
+++ b/mrbgems/mruby-bin-mruby/tools/mruby/mruby.c
@@ -6,6 +6,7 @@
#include <mruby/compile.h>
#include <mruby/dump.h>
#include <mruby/variable.h>
+#include <mruby/throw.h>
#ifdef MRB_DISABLE_STDIO
static void
@@ -176,6 +177,8 @@ main(int argc, char **argv)
mrbc_context *c;
mrb_value v;
mrb_sym zero_sym;
+ struct mrb_jmpbuf c_jmp;
+ int ai;
if (mrb == NULL) {
fputs("Invalid mrb_state, exiting mruby\n", stderr);
@@ -189,59 +192,67 @@ main(int argc, char **argv)
return n;
}
- ARGV = mrb_ary_new_capa(mrb, args.argc);
- for (i = 0; i < args.argc; i++) {
- char* utf8 = mrb_utf8_from_locale(args.argv[i], -1);
- if (utf8) {
- mrb_ary_push(mrb, ARGV, mrb_str_new_cstr(mrb, utf8));
- mrb_utf8_free(utf8);
+ ai = mrb_gc_arena_save(mrb);
+ MRB_TRY(&c_jmp) {
+ mrb->jmp = &c_jmp;
+ ARGV = mrb_ary_new_capa(mrb, args.argc);
+ for (i = 0; i < args.argc; i++) {
+ char* utf8 = mrb_utf8_from_locale(args.argv[i], -1);
+ if (utf8) {
+ mrb_ary_push(mrb, ARGV, mrb_str_new_cstr(mrb, utf8));
+ mrb_utf8_free(utf8);
+ }
}
- }
- mrb_define_global_const(mrb, "ARGV", ARGV);
-
- c = mrbc_context_new(mrb);
- if (args.verbose)
- c->dump_result = TRUE;
- if (args.check_syntax)
- c->no_exec = TRUE;
-
- /* Set $0 */
- zero_sym = mrb_intern_lit(mrb, "$0");
- if (args.rfp) {
- const char *cmdline;
- cmdline = args.cmdline ? args.cmdline : "-";
- mrbc_filename(mrb, c, cmdline);
- mrb_gv_set(mrb, zero_sym, mrb_str_new_cstr(mrb, cmdline));
- }
- else {
- mrbc_filename(mrb, c, "-e");
- mrb_gv_set(mrb, zero_sym, mrb_str_new_lit(mrb, "-e"));
- }
+ mrb_define_global_const(mrb, "ARGV", ARGV);
- /* Load program */
- if (args.mrbfile) {
- v = mrb_load_irep_file_cxt(mrb, args.rfp, c);
- }
- else if (args.rfp) {
- v = mrb_load_file_cxt(mrb, args.rfp, c);
- }
- else {
- char* utf8 = mrb_utf8_from_locale(args.cmdline, -1);
- if (!utf8) abort();
- v = mrb_load_string_cxt(mrb, utf8, c);
- mrb_utf8_free(utf8);
- }
+ c = mrbc_context_new(mrb);
+ if (args.verbose)
+ c->dump_result = TRUE;
+ if (args.check_syntax)
+ c->no_exec = TRUE;
+
+ /* Set $0 */
+ zero_sym = mrb_intern_lit(mrb, "$0");
+ if (args.rfp) {
+ const char *cmdline;
+ cmdline = args.cmdline ? args.cmdline : "-";
+ mrbc_filename(mrb, c, cmdline);
+ mrb_gv_set(mrb, zero_sym, mrb_str_new_cstr(mrb, cmdline));
+ }
+ else {
+ mrbc_filename(mrb, c, "-e");
+ mrb_gv_set(mrb, zero_sym, mrb_str_new_lit(mrb, "-e"));
+ }
+
+ /* Load program */
+ if (args.mrbfile) {
+ v = mrb_load_irep_file_cxt(mrb, args.rfp, c);
+ }
+ else if (args.rfp) {
+ v = mrb_load_file_cxt(mrb, args.rfp, c);
+ }
+ else {
+ char* utf8 = mrb_utf8_from_locale(args.cmdline, -1);
+ if (!utf8) abort();
+ v = mrb_load_string_cxt(mrb, utf8, c);
+ mrb_utf8_free(utf8);
+ }
- mrbc_context_free(mrb, c);
- if (mrb->exc) {
- if (!mrb_undef_p(v)) {
- mrb_print_error(mrb);
+ mrb_gc_arena_restore(mrb, ai);
+ mrbc_context_free(mrb, c);
+ if (mrb->exc) {
+ if (!mrb_undef_p(v)) {
+ mrb_print_error(mrb);
+ }
+ n = -1;
+ }
+ else if (args.check_syntax) {
+ printf("Syntax OK\n");
}
- n = -1;
}
- else if (args.check_syntax) {
- printf("Syntax OK\n");
+ MRB_CATCH(&c_jmp) { /* error */
}
+ MRB_END_EXC(&c_jmp);
cleanup(mrb, &args);
return n == 0 ? EXIT_SUCCESS : EXIT_FAILURE;
diff --git a/mrbgems/mruby-class-ext/mrbgem.rake b/mrbgems/mruby-class-ext/mrbgem.rake
new file mode 100644
index 000000000..a384b1eef
--- /dev/null
+++ b/mrbgems/mruby-class-ext/mrbgem.rake
@@ -0,0 +1,5 @@
+MRuby::Gem::Specification.new('mruby-class-ext') do |spec|
+ spec.license = 'MIT'
+ spec.author = 'mruby developers'
+ spec.summary = 'class/module extension'
+end
diff --git a/mrbgems/mruby-class-ext/src/class.c b/mrbgems/mruby-class-ext/src/class.c
new file mode 100644
index 000000000..8ca7d66c2
--- /dev/null
+++ b/mrbgems/mruby-class-ext/src/class.c
@@ -0,0 +1,23 @@
+#include "mruby.h"
+#include "mruby/class.h"
+#include "mruby/string.h"
+
+static mrb_value
+mrb_mod_name(mrb_state *mrb, mrb_value self)
+{
+ mrb_value name = mrb_class_path(mrb, mrb_class_ptr(self));
+ return mrb_nil_p(name)? name : mrb_str_dup(mrb, name);
+}
+
+void
+mrb_mruby_class_ext_gem_init(mrb_state *mrb)
+{
+ struct RClass *mod = mrb->module_class;
+
+ mrb_define_method(mrb, mod, "name", mrb_mod_name, MRB_ARGS_NONE());
+}
+
+void
+mrb_mruby_class_ext_gem_final(mrb_state *mrb)
+{
+}
diff --git a/mrbgems/mruby-class-ext/test/module.rb b/mrbgems/mruby-class-ext/test/module.rb
new file mode 100644
index 000000000..f721ad0c6
--- /dev/null
+++ b/mrbgems/mruby-class-ext/test/module.rb
@@ -0,0 +1,10 @@
+assert 'Module#name' do
+ module A
+ class B
+ end
+ end
+
+ assert_nil A::B.singleton_class.name
+ assert_equal 'Fixnum', Fixnum.name
+ assert_equal 'A::B', A::B.name
+end
diff --git a/mrbgems/mruby-compiler/bintest/mrbc.rb b/mrbgems/mruby-compiler/bintest/mrbc.rb
index e4dc6a9a8..e27365edb 100644
--- a/mrbgems/mruby-compiler/bintest/mrbc.rb
+++ b/mrbgems/mruby-compiler/bintest/mrbc.rb
@@ -10,3 +10,12 @@ assert('Compiling multiple files without new line in last line. #2361') do
assert_equal "#{cmd('mrbc')}:#{a.path}:Syntax OK", result.chomp
assert_equal 0, $?.exitstatus
end
+
+assert('parsing function with void argument') do
+ a, out = Tempfile.new('a.rb'), Tempfile.new('out.mrb')
+ a.write('f ()')
+ a.flush
+ result = `#{cmd('mrbc')} -c -o #{out.path} #{a.path} 2>&1`
+ assert_equal "#{cmd('mrbc')}:#{a.path}:Syntax OK", result.chomp
+ assert_equal 0, $?.exitstatus
+end
diff --git a/mrbgems/mruby-compiler/core/codegen.c b/mrbgems/mruby-compiler/core/codegen.c
index 9b064b867..23e036d49 100644
--- a/mrbgems/mruby-compiler/core/codegen.c
+++ b/mrbgems/mruby-compiler/core/codegen.c
@@ -93,6 +93,7 @@ codegen_error(codegen_scope *s, const char *message)
if (!s) return;
while (s->prev) {
codegen_scope *tmp = s->prev;
+ mrb_free(s->mrb, s->iseq);
mrb_pool_close(s->mpool);
s = tmp;
}
@@ -387,6 +388,9 @@ dispatch(codegen_scope *s, int pc)
scope_error(s);
break;
}
+ if (diff > MAXARG_sBx) {
+ codegen_error(s, "too distant jump address");
+ }
s->iseq[pc] = MKOP_AsBx(c, GETARG_A(i), diff);
}
@@ -500,7 +504,12 @@ new_lit(codegen_scope *s, mrb_value val)
return i;
}
-static inline int
+/* method symbols should be fit in 9 bits */
+#define MAXMSYMLEN 512
+/* maximum symbol numbers */
+#define MAXSYMLEN 65536
+
+static int
new_msym(codegen_scope *s, mrb_sym sym)
{
size_t i, len;
@@ -508,20 +517,20 @@ new_msym(codegen_scope *s, mrb_sym sym)
mrb_assert(s->irep);
len = s->irep->slen;
- if (len > 256) len = 256;
+ if (len > MAXMSYMLEN) len = MAXMSYMLEN;
for (i=0; i<len; i++) {
if (s->irep->syms[i] == sym) return i;
if (s->irep->syms[i] == 0) break;
}
- if (i == 256) {
- codegen_error(s, "too many symbols (max 256)");
+ if (i == MAXMSYMLEN) {
+ codegen_error(s, "too many symbols (max " MRB_STRINGIZE(MAXMSYMLEN) ")");
}
s->irep->syms[i] = sym;
if (i == s->irep->slen) s->irep->slen++;
return i;
}
-static inline int
+static int
new_sym(codegen_scope *s, mrb_sym sym)
{
size_t i;
@@ -529,13 +538,18 @@ new_sym(codegen_scope *s, mrb_sym sym)
for (i=0; i<s->irep->slen; i++) {
if (s->irep->syms[i] == sym) return i;
}
- if (s->irep->slen > 125 && s->irep->slen < 256) {
- s->irep->syms = (mrb_sym *)codegen_realloc(s, s->irep->syms, sizeof(mrb_sym)*65536);
- for (i = 0; i < 256 - s->irep->slen; i++) {
+ if (s->irep->slen == MAXSYMLEN) {
+ codegen_error(s, "too many symbols (max " MRB_STRINGIZE(MAXSYMLEN) ")");
+ }
+
+ if (s->irep->slen > MAXMSYMLEN/2 && s->scapa == MAXMSYMLEN) {
+ s->scapa = MAXSYMLEN;
+ s->irep->syms = (mrb_sym *)codegen_realloc(s, s->irep->syms, sizeof(mrb_sym)*MAXSYMLEN);
+ for (i = s->irep->slen; i < MAXMSYMLEN; i++) {
static const mrb_sym mrb_sym_zero = { 0 };
- s->irep->syms[i + s->irep->slen] = mrb_sym_zero;
+ s->irep->syms[i] = mrb_sym_zero;
}
- s->irep->slen = 256;
+ s->irep->slen = MAXMSYMLEN;
}
s->irep->syms[s->irep->slen] = sym;
return s->irep->slen++;
@@ -588,9 +602,6 @@ for_body(codegen_scope *s, node *tree)
push(); /* push for a block parameter */
- lp = loop_push(s, LOOP_FOR);
- lp->pc1 = new_label(s);
-
/* generate loop variable */
n2 = tree->car;
genop(s, MKOP_Ax(OP_ENTER, 0x40000));
@@ -600,6 +611,11 @@ for_body(codegen_scope *s, node *tree)
else {
gen_vmassignment(s, n2, 1, VAL);
}
+ /* construct loop */
+ lp = loop_push(s, LOOP_FOR);
+ lp->pc2 = new_label(s);
+
+ /* loop body */
codegen(s, tree->cdr->cdr->car, VAL);
pop();
if (s->pc > 0) {
@@ -650,6 +666,9 @@ lambda_body(codegen_scope *s, node *tree, int blk)
ka = kd = 0;
ba = tree->car->cdr->cdr->cdr->cdr ? 1 : 0;
+ if (ma > 0x1f || oa > 0x1f || pa > 0x1f || ka > 0x1f) {
+ codegen_error(s, "too many formal arguments");
+ }
a = ((mrb_aspec)(ma & 0x1f) << 18)
| ((mrb_aspec)(oa & 0x1f) << 13)
| ((ra & 1) << 12)
@@ -770,6 +789,8 @@ attrsym(codegen_scope *s, mrb_sym a)
return mrb_intern(s->mrb, name2, len+1);
}
+#define CALL_MAXARGS 127
+
static int
gen_values(codegen_scope *s, node *t, int val)
{
@@ -778,7 +799,9 @@ gen_values(codegen_scope *s, node *t, int val)
while (t) {
is_splat = (intptr_t)t->car->car == NODE_SPLAT; /* splat mode */
- if (n >= 127 || is_splat) {
+ if (
+ n >= CALL_MAXARGS - 1 /* need to subtract one because vm.c expects an array if n == CALL_MAXARGS */
+ || is_splat) {
if (val) {
if (is_splat && n == 0 && (intptr_t)t->car->cdr->car == NODE_ARRAY) {
codegen(s, t->car->cdr, VAL);
@@ -812,8 +835,6 @@ gen_values(codegen_scope *s, node *t, int val)
}
}
else {
- codegen(s, t->car->cdr, NOVAL);
- t = t->cdr;
while (t) {
codegen(s, t->car, NOVAL);
t = t->cdr;
@@ -829,8 +850,6 @@ gen_values(codegen_scope *s, node *t, int val)
return n;
}
-#define CALL_MAXARGS 127
-
static void
gen_call(codegen_scope *s, node *tree, mrb_sym name, int sp, int val, int safe)
{
@@ -1057,7 +1076,9 @@ gen_vmassignment(codegen_scope *s, node *tree, int rhs, int val)
n++;
}
}
- push();
+ if (!val) {
+ push();
+ }
}
}
@@ -1360,6 +1381,10 @@ codegen(codegen_scope *s, node *tree, int val)
int pos1, pos2;
node *e = tree->cdr->cdr->car;
+ if (!tree->car) {
+ codegen(s, e, val);
+ return;
+ }
switch ((intptr_t)tree->car->car) {
case NODE_TRUE:
case NODE_INT:
@@ -1510,7 +1535,9 @@ codegen(codegen_scope *s, node *tree, int val)
genop(s, MKOP_A(OP_LOADNIL, cursp()));
if (pos3) dispatch_linked(s, pos3);
if (head) pop();
- genop(s, MKOP_AB(OP_MOVE, cursp(), pos));
+ if (cursp() != pos) {
+ genop(s, MKOP_AB(OP_MOVE, cursp(), pos));
+ }
push();
}
else {
@@ -1655,7 +1682,6 @@ codegen(codegen_scope *s, node *tree, int val)
}
tree = tree->car;
if (tree->car) { /* pre */
- int first = TRUE;
t = tree->car;
n = 0;
while (t) {
@@ -1664,10 +1690,7 @@ codegen(codegen_scope *s, node *tree, int val)
n++;
}
else {
- if (first) {
- genop(s, MKOP_A(OP_LOADNIL, rhs+n));
- first = FALSE;
- }
+ genop(s, MKOP_A(OP_LOADNIL, rhs+n));
gen_assignment(s, t->car, rhs+n, NOVAL);
}
t = t->cdr;
@@ -1746,6 +1769,7 @@ codegen(codegen_scope *s, node *tree, int val)
genop(s, MKOP_A(OP_RESCUE, exc));
genop(s, MKOP_A(OP_LOADF, exc));
dispatch(s, noexc);
+ loop_pop(s, NOVAL);
}
else if ((intptr_t)tree->car->car == NODE_CALL) {
node *n = tree->car->cdr;
@@ -1795,8 +1819,10 @@ codegen(codegen_scope *s, node *tree, int val)
int pos;
pop();
- if (val && vsp >= 0) {
- genop(s, MKOP_AB(OP_MOVE, vsp, cursp()));
+ if (val) {
+ if (vsp >= 0) {
+ genop(s, MKOP_AB(OP_MOVE, vsp, cursp()));
+ }
pos = genop(s, MKOP_AsBx(name[0]=='|'?OP_JMPIF:OP_JMPNOT, cursp(), 0));
}
else {
@@ -2012,7 +2038,7 @@ codegen(codegen_scope *s, node *tree, int val)
break;
case NODE_REDO:
- if (!s->loop) {
+ if (!s->loop || s->loop->type == LOOP_BEGIN || s->loop->type == LOOP_RESCUE) {
raise_error(s, "unexpected redo");
}
else {
@@ -2021,6 +2047,7 @@ codegen(codegen_scope *s, node *tree, int val)
}
genop(s, MKOP_sBx(OP_JMP, s->loop->pc2 - s->pc));
}
+ if (val) push();
break;
case NODE_RETRY:
@@ -2055,6 +2082,7 @@ codegen(codegen_scope *s, node *tree, int val)
genop(s, MKOP_sBx(OP_JMP, lp->pc1 - s->pc));
}
}
+ if (val) push();
}
break;
@@ -2194,7 +2222,7 @@ codegen(codegen_scope *s, node *tree, int val)
case NODE_FLOAT:
if (val) {
char *p = (char*)tree;
- mrb_float f = str_to_mrb_float(p);
+ mrb_float f = mrb_float_read(p, NULL);
int off = new_lit(s, mrb_float_value(s->mrb, f));
genop(s, MKOP_ABx(OP_LOADL, cursp(), off));
@@ -2208,9 +2236,9 @@ codegen(codegen_scope *s, node *tree, int val)
tree = tree->cdr;
switch (nt) {
case NODE_FLOAT:
- {
+ if (val) {
char *p = (char*)tree;
- mrb_float f = str_to_mrb_float(p);
+ mrb_float f = mrb_float_read(p, NULL);
int off = new_lit(s, mrb_float_value(s->mrb, -f));
genop(s, MKOP_ABx(OP_LOADL, cursp(), off));
@@ -2219,7 +2247,7 @@ codegen(codegen_scope *s, node *tree, int val)
break;
case NODE_INT:
- {
+ if (val) {
char *p = (char*)tree->car;
int base = (intptr_t)tree->cdr->car;
mrb_int i;
@@ -2248,7 +2276,7 @@ codegen(codegen_scope *s, node *tree, int val)
break;
default:
- {
+ if (val) {
int sym = new_msym(s, mrb_intern_lit(s->mrb, "-"));
genop(s, MKOP_ABx(OP_LOADI, cursp(), 0));
@@ -2257,6 +2285,9 @@ codegen(codegen_scope *s, node *tree, int val)
pop(); pop();
genop(s, MKOP_ABC(OP_SUB, cursp(), sym, 2));
}
+ else {
+ codegen(s, tree, NOVAL);
+ }
break;
}
}
@@ -2282,7 +2313,11 @@ codegen(codegen_scope *s, node *tree, int val)
if (val) {
node *n = tree;
- if (!n) break;
+ if (!n) {
+ genop(s, MKOP_A(OP_LOADNIL, cursp()));
+ push();
+ break;
+ }
codegen(s, n->car, VAL);
n = n->cdr;
while (n) {
@@ -2539,13 +2574,31 @@ codegen(codegen_scope *s, node *tree, int val)
genop(s, MKOP_A(OP_TCLASS, cursp()));
push();
while (t) {
- int symbol = new_msym(s, sym(t->car));
+ int symbol;
+ if (num >= CALL_MAXARGS - 1) {
+ pop_n(num);
+ genop(s, MKOP_ABC(OP_ARRAY, cursp(), cursp(), num));
+ while (t) {
+ symbol = new_msym(s, sym(t->car));
+ push();
+ genop(s, MKOP_ABx(OP_LOADSYM, cursp(), symbol));
+ pop();
+ genop(s, MKOP_AB(OP_ARYPUSH, cursp(), cursp()+1));
+ t = t->cdr;
+ }
+ num = CALL_MAXARGS;
+ break;
+ }
+ symbol = new_msym(s, sym(t->car));
genop(s, MKOP_ABx(OP_LOADSYM, cursp(), symbol));
push();
t = t->cdr;
num++;
}
- pop_n(num + 1);
+ pop();
+ if (num < CALL_MAXARGS) {
+ pop_n(num);
+ }
genop(s, MKOP_ABC(OP_SEND, cursp(), undef, num));
if (val) {
push();
@@ -2713,13 +2766,13 @@ scope_new(mrb_state *mrb, codegen_scope *prev, node *lv)
p->icapa = 1024;
p->iseq = (mrb_code*)mrb_malloc(mrb, sizeof(mrb_code)*p->icapa);
- p->irep->iseq = p->iseq;
+ p->irep->iseq = NULL;
p->pcapa = 32;
p->irep->pool = (mrb_value*)mrb_malloc(mrb, sizeof(mrb_value)*p->pcapa);
p->irep->plen = 0;
- p->scapa = 256;
+ p->scapa = MAXMSYMLEN;
p->irep->syms = (mrb_sym*)mrb_malloc(mrb, sizeof(mrb_sym)*p->scapa);
p->irep->slen = 0;
@@ -2797,6 +2850,7 @@ scope_finish(codegen_scope *s)
memcpy(fname, s->filename, fname_len);
fname[fname_len] = '\0';
irep->filename = fname;
+ irep->own_filename = TRUE;
}
irep->nlocals = s->nlocals;
@@ -2904,9 +2958,6 @@ mrb_generate_code(mrb_state *mrb, parser_state *p)
return proc;
}
MRB_CATCH(&scope->jmp) {
- if (scope->filename == scope->irep->filename) {
- scope->irep->filename = NULL;
- }
mrb_irep_decref(mrb, scope->irep);
mrb_pool_close(scope->mpool);
return NULL;
diff --git a/mrbgems/mruby-compiler/core/parse.y b/mrbgems/mruby-compiler/core/parse.y
index 426c262fb..f8e4eb72f 100644
--- a/mrbgems/mruby-compiler/core/parse.y
+++ b/mrbgems/mruby-compiler/core/parse.y
@@ -41,6 +41,7 @@ static void yyerror(parser_state *p, const char *s);
static void yywarn(parser_state *p, const char *s);
static void yywarning(parser_state *p, const char *s);
static void backref_error(parser_state *p, node *n);
+static void void_expr_error(parser_state *p, node *n);
static void tokadd(parser_state *p, int32_t c);
#define identchar(c) (ISALNUM(c) || (c) == '_' || !ISASCII(c))
@@ -870,6 +871,7 @@ call_with_block(parser_state *p, node *a, node *b)
break;
case NODE_CALL:
case NODE_FCALL:
+ case NODE_SCALL:
n = a->cdr->cdr->cdr;
if (!n->car) n->car = cons(0, b);
else {
@@ -1097,7 +1099,7 @@ heredoc_end(parser_state *p)
%type <nd> literal numeric cpath symbol
%type <nd> top_compstmt top_stmts top_stmt
%type <nd> bodystmt compstmt stmts stmt expr arg primary command command_call method_call
-%type <nd> expr_value arg_value arg_rhs primary_value
+%type <nd> expr_value arg_rhs primary_value
%type <nd> if_tail opt_else case_body cases opt_rescue exc_list exc_var opt_ensure
%type <nd> args call_args opt_call_args
%type <nd> paren_args opt_paren_args variable
@@ -1322,7 +1324,7 @@ stmt : keyword_alias fsym {p->lstate = EXPR_FNAME;} fsym
{
$$ = new_asgn(p, $1, new_array(p, $3));
}
- | mlhs '=' arg_value
+ | mlhs '=' arg
{
$$ = new_masgn(p, $1, $3);
}
@@ -1401,7 +1403,10 @@ expr : command_call
expr_value : expr
{
if (!$1) $$ = new_nil(p);
- else $$ = $1;
+ else {
+ void_expr_error(p, $1);
+ $$ = $1;
+ }
}
;
@@ -1907,20 +1912,13 @@ arg : lhs '=' arg_rhs
}
;
-arg_value : arg
- {
- $$ = $1;
- if (!$$) $$ = new_nil(p);
- }
- ;
-
aref_args : none
| args trailer
{
$$ = $1;
NODE_LINENO($$, $1);
}
- | args ',' assocs trailer
+ | args comma assocs trailer
{
$$ = push($1, new_hash(p, $3));
}
@@ -1931,9 +1929,14 @@ aref_args : none
}
;
-arg_rhs : arg %prec tOP_ASGN
+arg_rhs : arg %prec tOP_ASGN
+ {
+ void_expr_error(p, $1);
+ $$ = $1;
+ }
| arg modifier_rescue arg
{
+ void_expr_error(p, $1);
$$ = new_mod_rescue(p, $1, $3);
}
;
@@ -1955,7 +1958,7 @@ opt_call_args : none
$$ = cons($1,0);
NODE_LINENO($$, $1);
}
- | args ',' assocs ','
+ | args comma assocs ','
{
$$ = cons(push($1, new_hash(p, $3)), 0);
NODE_LINENO($$, $1);
@@ -1982,7 +1985,7 @@ call_args : command
$$ = cons(list1(new_hash(p, $1)), $2);
NODE_LINENO($$, $1);
}
- | args ',' assocs opt_block_arg
+ | args comma assocs opt_block_arg
{
$$ = cons(push($1, new_hash(p, $3)), $4);
NODE_LINENO($$, $1);
@@ -2005,13 +2008,13 @@ command_args : {
}
;
-block_arg : tAMPER arg_value
+block_arg : tAMPER arg
{
$$ = new_block_arg(p, $2);
}
;
-opt_block_arg : ',' block_arg
+opt_block_arg : comma block_arg
{
$$ = $2;
}
@@ -2021,44 +2024,47 @@ opt_block_arg : ',' block_arg
}
;
-args : arg_value
+comma : ','
+ | ',' heredoc_bodies
+ ;
+
+args : arg
{
+ void_expr_error(p, $1);
$$ = cons($1, 0);
NODE_LINENO($$, $1);
}
- | tSTAR arg_value
+ | tSTAR arg
{
+ void_expr_error(p, $2);
$$ = cons(new_splat(p, $2), 0);
NODE_LINENO($$, $2);
}
- | args ',' arg_value
+ | args comma arg
{
+ void_expr_error(p, $3);
$$ = push($1, $3);
}
- | args ',' tSTAR arg_value
+ | args comma tSTAR arg
{
+ void_expr_error(p, $4);
$$ = push($1, new_splat(p, $4));
}
- | args ',' heredoc_bodies arg_value
- {
- $$ = push($1, $4);
- }
- | args ',' heredoc_bodies tSTAR arg_value
- {
- $$ = push($1, new_splat(p, $5));
- }
;
-mrhs : args ',' arg_value
+mrhs : args comma arg
{
+ void_expr_error(p, $3);
$$ = push($1, $3);
}
- | args ',' tSTAR arg_value
+ | args comma tSTAR arg
{
+ void_expr_error(p, $4);
$$ = push($1, new_splat(p, $4));
}
- | tSTAR arg_value
+ | tSTAR arg
{
+ void_expr_error(p, $2);
$$ = list1(new_splat(p, $2));
}
;
@@ -2097,7 +2103,7 @@ primary : literal
}
| tLPAREN_ARG {p->lstate = EXPR_ENDARG;} rparen
{
- $$ = 0;
+ $$ = new_nil(p);
}
| tLPAREN compstmt ')'
{
@@ -2125,17 +2131,9 @@ primary : literal
{
$$ = new_return(p, 0);
}
- | keyword_yield '(' call_args rparen
+ | keyword_yield opt_paren_args
{
- $$ = new_yield(p, $3);
- }
- | keyword_yield '(' rparen
- {
- $$ = new_yield(p, 0);
- }
- | keyword_yield
- {
- $$ = new_yield(p, 0);
+ $$ = new_yield(p, $2);
}
| keyword_not '(' expr rparen
{
@@ -2450,7 +2448,7 @@ block_param : f_arg ',' f_block_optarg ',' f_rest_arg opt_f_block_arg
}
| f_arg ','
{
- $$ = new_args(p, $1, 0, 1, 0, 0);
+ $$ = new_args(p, $1, 0, 0, 0, 0);
}
| f_arg ',' f_rest_arg ',' f_arg opt_f_block_arg
{
@@ -2688,7 +2686,7 @@ opt_rescue : keyword_rescue exc_list exc_var then
| none
;
-exc_list : arg_value
+exc_list : arg
{
$$ = list1($1);
}
@@ -2783,10 +2781,6 @@ regexp : tREGEXP_BEG tREGEXP
heredoc : tHEREDOC_BEG
;
-opt_heredoc_bodies : /* none */
- | heredoc_bodies
- ;
-
heredoc_bodies : heredoc_body
| heredoc_bodies heredoc_body
;
@@ -3115,7 +3109,7 @@ f_opt_asgn : tIDENTIFIER '='
}
;
-f_opt : f_opt_asgn arg_value
+f_opt : f_opt_asgn arg
{
$$ = cons(nsym($1), $2);
}
@@ -3233,23 +3227,23 @@ assocs : assoc
}
;
-assoc : arg_value tASSOC arg_value
+assoc : arg tASSOC arg
{
$$ = cons($1, $3);
}
- | tLABEL arg_value
+ | tLABEL arg
{
$$ = cons(new_sym(p, $1), $2);
}
- | tLABEL_END arg_value
+ | tLABEL_END arg
{
$$ = cons(new_sym(p, new_strsym(p, $1)), $2);
}
- | tSTRING_BEG tLABEL_END arg_value
+ | tSTRING_BEG tLABEL_END arg
{
$$ = cons(new_sym(p, new_strsym(p, $2)), $3);
}
- | tSTRING_BEG string_rep tLABEL_END arg_value
+ | tSTRING_BEG string_rep tLABEL_END arg
{
$$ = cons(new_dsym(p, push($2, $3)), $4);
}
@@ -3308,11 +3302,12 @@ rbracket : opt_nl ']'
trailer : /* none */
| nl
- | ','
+ | comma
;
term : ';' {yyerrok;}
| nl
+ | heredoc_body
;
nl : '\n'
@@ -3320,10 +3315,10 @@ nl : '\n'
p->lineno++;
p->column = 0;
}
- opt_heredoc_bodies
+ ;
terms : term
- | terms ';' {yyerrok;}
+ | terms term
;
none : /* none */
@@ -3430,6 +3425,26 @@ backref_error(parser_state *p, node *n)
}
}
+static void
+void_expr_error(parser_state *p, node *n)
+{
+ int c;
+
+ if (n == NULL) return;
+ c = (int)(intptr_t)n->car;
+ switch (c) {
+ case NODE_BREAK:
+ case NODE_RETURN:
+ case NODE_NEXT:
+ case NODE_REDO:
+ case NODE_RETRY:
+ yyerror(p, "void value expression");
+ break;
+ default:
+ break;
+ }
+}
+
static void pushback(parser_state *p, int c);
static mrb_bool peeks(parser_state *p, const char *s);
static mrb_bool skips(parser_state *p, const char *s);
@@ -3730,6 +3745,44 @@ scan_hex(const int *start, int len, int *retlen)
return retval;
}
+static int32_t
+read_escape_unicode(parser_state *p, int limit)
+{
+ int32_t c;
+ int buf[9];
+ int i;
+
+ /* Look for opening brace */
+ i = 0;
+ buf[0] = nextc(p);
+ if (buf[0] < 0) goto eof;
+ if (ISXDIGIT(buf[0])) {
+ /* \uxxxx form */
+ for (i=1; i<limit; i++) {
+ buf[i] = nextc(p);
+ if (buf[i] < 0) goto eof;
+ if (!ISXDIGIT(buf[i])) {
+ pushback(p, buf[i]);
+ break;
+ }
+ }
+ }
+ else {
+ pushback(p, buf[0]);
+ }
+ c = scan_hex(buf, i, &i);
+ if (i == 0) {
+ eof:
+ yyerror(p, "Invalid escape character syntax");
+ return -1;
+ }
+ if (c < 0 || c > 0x10FFFF || (c & 0xFFFFF800) == 0xD800) {
+ yyerror(p, "Invalid Unicode code point");
+ return -1;
+ }
+ return c;
+}
+
/* Return negative to indicate Unicode code point */
static int32_t
read_escape(parser_state *p)
@@ -3802,53 +3855,17 @@ read_escape(parser_state *p)
return c;
case 'u': /* Unicode */
- {
- int buf[9];
- int i;
-
- /* Look for opening brace */
- i = 0;
- buf[0] = nextc(p);
- if (buf[0] < 0) goto eof;
- if (buf[0] == '{') {
+ if (peek(p, '{')) {
/* \u{xxxxxxxx} form */
- for (i=0; i<9; i++) {
- buf[i] = nextc(p);
- if (buf[i] < 0) goto eof;
- if (buf[i] == '}') {
- break;
- }
- else if (!ISXDIGIT(buf[i])) {
- yyerror(p, "Invalid escape character syntax");
- pushback(p, buf[i]);
- return 0;
- }
- }
- }
- else if (ISXDIGIT(buf[0])) {
- /* \uxxxx form */
- for (i=1; i<4; i++) {
- buf[i] = nextc(p);
- if (buf[i] < 0) goto eof;
- if (!ISXDIGIT(buf[i])) {
- pushback(p, buf[i]);
- break;
- }
- }
+ nextc(p);
+ c = read_escape_unicode(p, 8);
+ if (c < 0) return 0;
+ if (nextc(p) != '}') goto eof;
}
else {
- pushback(p, buf[0]);
+ c = read_escape_unicode(p, 4);
+ if (c < 0) return 0;
}
- c = scan_hex(buf, i, &i);
- if (i == 0) {
- yyerror(p, "Invalid escape character syntax");
- return 0;
- }
- if (c < 0 || c > 0x10FFFF || (c & 0xFFFFF800) == 0xD800) {
- yyerror(p, "Invalid Unicode code point");
- return 0;
- }
- }
return -c;
case 'b':/* backspace */
@@ -3906,7 +3923,10 @@ parse_string(parser_state *p)
int beg = (intptr_t)p->lex_strterm->cdr->cdr->car;
int end = (intptr_t)p->lex_strterm->cdr->cdr->cdr;
parser_heredoc_info *hinf = (type & STR_FUNC_HEREDOC) ? parsing_heredoc_inf(p) : NULL;
+ int cmd_state = p->cmd_start;
+ if (beg == 0) beg = -3; /* should never happen */
+ if (end == 0) end = -3;
newtok(p);
while ((c = nextc(p)) != end || nest_level != 0) {
if (hinf && (c == '\n' || c < 0)) {
@@ -3928,7 +3948,12 @@ parse_string(parser_state *p)
}
}
if ((len-1 == hinf->term_len) && (strncmp(s, hinf->term, len-1) == 0)) {
- return tHEREDOC_END;
+ if (c < 0) {
+ p->parsing_heredoc = NULL;
+ }
+ else {
+ return tHEREDOC_END;
+ }
}
}
if (c < 0) {
@@ -3969,6 +3994,20 @@ parse_string(parser_state *p)
tokadd(p, '\\');
tokadd(p, c);
}
+ else if (c == 'u' && peek(p, '{')) {
+ /* \u{xxxx xxxx xxxx} form */
+ nextc(p);
+ while (1) {
+ do c = nextc(p); while (ISSPACE(c));
+ if (c == '}') break;
+ pushback(p, c);
+ c = read_escape_unicode(p, 8);
+ if (c < 0) break;
+ tokadd(p, -c);
+ }
+ if (hinf)
+ hinf->line_head = FALSE;
+ }
else {
pushback(p, c);
tokadd(p, read_escape(p));
@@ -4093,10 +4132,12 @@ parse_string(parser_state *p)
return tREGEXP;
}
yylval.nd = new_str(p, tok(p), toklen(p));
- if (IS_LABEL_SUFFIX(0)) {
- p->lstate = EXPR_BEG;
- nextc(p);
- return tLABEL_END;
+ if (IS_LABEL_POSSIBLE()) {
+ if (IS_LABEL_SUFFIX(0)) {
+ p->lstate = EXPR_BEG;
+ nextc(p);
+ return tLABEL_END;
+ }
}
return tSTRING;
@@ -4871,7 +4912,7 @@ parser_yylex(parser_state *p)
char *endp;
errno = 0;
- d = strtod(tok(p), &endp);
+ d = mrb_float_read(tok(p), &endp);
if (d == 0 && endp == tok(p)) {
yywarning_s(p, "corrupted float value %s", tok(p));
}
@@ -5477,7 +5518,10 @@ mrb_parser_parse(parser_state *p, mrbc_context *c)
p->lex_strterm = NULL;
parser_init_cxt(p, c);
- yyparse(p);
+ if (yyparse(p) != 0 || p->nerr > 0) {
+ p->tree = 0;
+ return;
+ }
if (!p->tree) {
p->tree = new_nil(p);
}
@@ -5657,8 +5701,8 @@ mrb_parse_string(mrb_state *mrb, const char *s, mrbc_context *c)
return mrb_parse_nstring(mrb, s, strlen(s), c);
}
-static mrb_value
-load_exec(mrb_state *mrb, parser_state *p, mrbc_context *c)
+MRB_API mrb_value
+mrb_load_exec(mrb_state *mrb, struct mrb_parser_state *p, mrbc_context *c)
{
struct RClass *target = mrb->object_class;
struct RProc *proc;
@@ -5717,7 +5761,7 @@ load_exec(mrb_state *mrb, parser_state *p, mrbc_context *c)
MRB_API mrb_value
mrb_load_file_cxt(mrb_state *mrb, FILE *f, mrbc_context *c)
{
- return load_exec(mrb, mrb_parse_file(mrb, f, c), c);
+ return mrb_load_exec(mrb, mrb_parse_file(mrb, f, c), c);
}
MRB_API mrb_value
@@ -5730,7 +5774,7 @@ mrb_load_file(mrb_state *mrb, FILE *f)
MRB_API mrb_value
mrb_load_nstring_cxt(mrb_state *mrb, const char *s, int len, mrbc_context *c)
{
- return load_exec(mrb, mrb_parse_nstring(mrb, s, len, c), c);
+ return mrb_load_exec(mrb, mrb_parse_nstring(mrb, s, len, c), c);
}
MRB_API mrb_value
@@ -6024,7 +6068,17 @@ mrb_parser_dump(mrb_state *mrb, node *tree, int offset)
case NODE_FCALL:
case NODE_CALL:
- printf("NODE_CALL:\n");
+ case NODE_SCALL:
+ switch (nodetype) {
+ case NODE_FCALL:
+ printf("NODE_FCALL:\n"); break;
+ case NODE_CALL:
+ printf("NODE_CALL(.):\n"); break;
+ case NODE_SCALL:
+ printf("NODE_SCALL(&.):\n"); break;
+ default:
+ break;
+ }
mrb_parser_dump(mrb, tree->car, offset+1);
dump_prefix(tree, offset+1);
printf("method='%s' (%d)\n",
@@ -6504,8 +6558,8 @@ mrb_parser_dump(mrb_state *mrb, node *tree, int offset)
break;
case NODE_HEREDOC:
- printf("NODE_HEREDOC:\n");
- mrb_parser_dump(mrb, ((parser_heredoc_info*)tree)->doc, offset+1);
+ printf("NODE_HEREDOC (<<%s):\n", ((parser_heredoc_info*)tree)->term);
+ dump_recur(mrb, ((parser_heredoc_info*)tree)->doc, offset+1);
break;
default:
diff --git a/mrbgems/mruby-enum-ext/mrblib/enum.rb b/mrbgems/mruby-enum-ext/mrblib/enum.rb
index a5f661ce6..113b470a9 100644
--- a/mrbgems/mruby-enum-ext/mrblib/enum.rb
+++ b/mrbgems/mruby-enum-ext/mrblib/enum.rb
@@ -58,13 +58,14 @@ module Enumerable
def take(n)
raise TypeError, "no implicit conversion of #{n.class} into Integer" unless n.respond_to?(:to_int)
- raise ArgumentError, "attempt to take negative size" if n < 0
-
- n = n.to_int
+ i = n.to_int
+ raise ArgumentError, "attempt to take negative size" if i < 0
ary = []
+ return ary if i == 0
self.each do |*val|
- break if ary.size >= n
ary << val.__svalue
+ i -= 1
+ break if i == 0
end
ary
end
@@ -214,21 +215,28 @@ module Enumerable
# Returns the first element, or the first +n+ elements, of the enumerable.
# If the enumerable is empty, the first form returns <code>nil</code>, and the
# second form returns an empty array.
- def first(n=NONE)
- if n == NONE
+ def first(*args)
+ case args.length
+ when 0
self.each do |*val|
return val.__svalue
end
return nil
- else
- a = []
- i = 0
+ when 1
+ n = args[0]
+ raise TypeError, "no implicit conversion of #{n.class} into Integer" unless n.respond_to?(:to_int)
+ i = n.to_int
+ raise ArgumentError, "attempt to take negative size" if i < 0
+ ary = []
+ return ary if i == 0
self.each do |*val|
- break if n<=i
- a.push val.__svalue
- i += 1
+ ary << val.__svalue
+ i -= 1
+ break if i == 0
end
- a
+ ary
+ else
+ raise ArgumentError, "wrong number of arguments (given #{args.length}, expected 0..1)"
end
end
@@ -573,35 +581,38 @@ module Enumerable
# a.cycle(2) { |x| puts x } # print, a, b, c, a, b, c.
#
- def cycle(n=nil, &block)
- return to_enum(:cycle, n) if !block && n.nil?
+ def cycle(nv = nil, &block)
+ return to_enum(:cycle, nv) unless block
- ary = []
- if n.nil?
- self.each do|*val|
- ary.push val
- block.call(*val)
+ n = nil
+
+ if nv.nil?
+ n = -1
+ else
+ unless nv.respond_to?(:to_int)
+ raise TypeError, "no implicit conversion of #{nv.class} into Integer"
end
- loop do
- ary.each do|e|
- block.call(*e)
- end
+ n = nv.to_int
+ unless n.kind_of?(Integer)
+ raise TypeError, "no implicit conversion of #{nv.class} into Integer"
end
- else
- raise TypeError, "no implicit conversion of #{n.class} into Integer" unless n.respond_to?(:to_int)
+ return nil if n <= 0
+ end
- n = n.to_int
- self.each do|*val|
- ary.push val
- end
- count = 0
- while count < n
- ary.each do|e|
- block.call(*e)
- end
- count += 1
+ ary = []
+ each do |*i|
+ ary.push(i)
+ yield(*i)
+ end
+ return nil if ary.empty?
+
+ while n < 0 || 0 < (n -= 1)
+ ary.each do |i|
+ yield(*i)
end
end
+
+ nil
end
##
diff --git a/mrbgems/mruby-enum-ext/test/enum.rb b/mrbgems/mruby-enum-ext/test/enum.rb
index 08b553fe5..076562f45 100644
--- a/mrbgems/mruby-enum-ext/test/enum.rb
+++ b/mrbgems/mruby-enum-ext/test/enum.rb
@@ -128,6 +128,13 @@ assert("Enumerable#cycle") do
["a", "b", "c"].cycle(2) { |v| a << v }
assert_equal ["a", "b", "c", "a", "b", "c"], a
assert_raise(TypeError) { ["a", "b", "c"].cycle("a") { |v| a << v } }
+
+ empty = Class.new do
+ include Enumerable
+ def each
+ end
+ end
+ assert_nil empty.new.cycle { break :nope }
end
assert("Enumerable#find_index") do
diff --git a/mrbgems/mruby-enum-lazy/mrbgem.rake b/mrbgems/mruby-enum-lazy/mrbgem.rake
index 219141e98..682134c41 100644
--- a/mrbgems/mruby-enum-lazy/mrbgem.rake
+++ b/mrbgems/mruby-enum-lazy/mrbgem.rake
@@ -1,7 +1,7 @@
MRuby::Gem::Specification.new('mruby-enum-lazy') do |spec|
spec.license = 'MIT'
spec.author = 'mruby developers'
- spec.summary = 'Enumerable::Lazy class'
+ spec.summary = 'Enumerator::Lazy class'
spec.add_dependency('mruby-enumerator', :core => 'mruby-enumerator')
spec.add_dependency('mruby-enum-ext', :core => 'mruby-enum-ext')
end
diff --git a/mrbgems/mruby-enum-lazy/mrblib/lazy.rb b/mrbgems/mruby-enum-lazy/mrblib/lazy.rb
index 8ce363c6d..b650072e2 100644
--- a/mrbgems/mruby-enum-lazy/mrblib/lazy.rb
+++ b/mrbgems/mruby-enum-lazy/mrblib/lazy.rb
@@ -2,7 +2,7 @@ module Enumerable
# = Enumerable#lazy implementation
#
- # Enumerable#lazy returns an instance of Enumerable::Lazy.
+ # Enumerable#lazy returns an instance of Enumerator::Lazy.
# You can use it just like as normal Enumerable object,
# except these methods act as 'lazy':
#
@@ -16,9 +16,11 @@ module Enumerable
# - flat_map collect_concat
# - zip
def lazy
- Lazy.new(self)
+ Enumerator::Lazy.new(self)
end
+end
+class Enumerator
# == Acknowledgements
#
# Based on https://github.com/yhara/enumerable-lazy
@@ -40,6 +42,15 @@ module Enumerable
}
end
+ def to_enum(meth=:each, *args, &block)
+ lz = Lazy.new(self, &block)
+ lz.obj = self
+ lz.meth = meth
+ lz.args = args
+ lz
+ end
+ alias enum_for to_enum
+
def map(&block)
Lazy.new(self){|yielder, val|
yielder << block.call(val)
diff --git a/mrbgems/mruby-enum-lazy/test/lazy.rb b/mrbgems/mruby-enum-lazy/test/lazy.rb
index ca009d34c..940d070e8 100644
--- a/mrbgems/mruby-enum-lazy/test/lazy.rb
+++ b/mrbgems/mruby-enum-lazy/test/lazy.rb
@@ -1,9 +1,9 @@
-assert("Enumerable::Lazy") do
+assert("Enumerator::Lazy") do
a = [1, 2]
- assert_equal Enumerable::Lazy, a.lazy.class
+ assert_equal Enumerator::Lazy, a.lazy.class
end
-assert("Enumerable::Lazy laziness") do
+assert("Enumerator::Lazy laziness") do
a = Object.new
def a.each
return to_enum :each unless block_given?
@@ -40,7 +40,13 @@ assert("Enumerable::Lazy laziness") do
assert_equal [10,20], a.b
end
-assert("Enumerable::Lazy#zip with cycle") do
+assert("Enumrator::Lazy#to_enum") do
+ lazy_enum = (0..Float::INFINITY).lazy.to_enum(:each_slice, 2)
+ assert_kind_of Enumerator::Lazy, lazy_enum
+ assert_equal [0*1, 2*3, 4*5, 6*7], lazy_enum.map { |a| a.first * a.last }.first(4)
+end
+
+assert("Enumerator::Lazy#zip with cycle") do
e1 = [1, 2, 3].cycle
e2 = [:a, :b].cycle
assert_equal [[1,:a],[2,:b],[3,:a]], e1.lazy.zip(e2).first(3)
diff --git a/mrbgems/mruby-enumerator/mrblib/enumerator.rb b/mrbgems/mruby-enumerator/mrblib/enumerator.rb
index 9abaca38a..89b66cd45 100644
--- a/mrbgems/mruby-enumerator/mrblib/enumerator.rb
+++ b/mrbgems/mruby-enumerator/mrblib/enumerator.rb
@@ -153,12 +153,18 @@ class Enumerator
#
def with_index(offset=0)
return to_enum :with_index, offset unless block_given?
- raise TypeError, "no implicit conversion of #{offset.class} into Integer" unless offset.respond_to?(:to_int)
+ offset = if offset.nil?
+ 0
+ elsif offset.respond_to?(:to_int)
+ offset.to_int
+ else
+ raise TypeError, "no implicit conversion of #{offset.class} into Integer"
+ end
- n = offset.to_int - 1
- enumerator_block_call do |i|
+ n = offset - 1
+ enumerator_block_call do |*i|
n += 1
- yield [i,n]
+ yield i.__svalue, n
end
end
@@ -516,6 +522,7 @@ class Enumerator
# just for internal
class Generator
+ include Enumerable
def initialize(&block)
raise TypeError, "wrong argument type #{self.class} (expected Proc)" unless block.kind_of? Proc
diff --git a/mrbgems/mruby-enumerator/test/enumerator.rb b/mrbgems/mruby-enumerator/test/enumerator.rb
index 2e45dae4b..e86e874f0 100644
--- a/mrbgems/mruby-enumerator/test/enumerator.rb
+++ b/mrbgems/mruby-enumerator/test/enumerator.rb
@@ -50,6 +50,9 @@ end
assert 'Enumerator#with_index' do
assert_equal([[1,0],[2,1],[3,2]], @obj.to_enum(:foo, 1, 2, 3).with_index.to_a)
assert_equal([[1,5],[2,6],[3,7]], @obj.to_enum(:foo, 1, 2, 3).with_index(5).to_a)
+ a = []
+ @obj.to_enum(:foo, 1, 2, 3).with_index(10).with_index(20) { |*i| a << i }
+ assert_equal [[[1, 10], 20], [[2, 11], 21], [[3, 12], 22]], a
end
assert 'Enumerator#with_index nonnum offset' do
diff --git a/mrbgems/mruby-eval/src/eval.c b/mrbgems/mruby-eval/src/eval.c
index 26dd728ba..81bc80280 100644
--- a/mrbgems/mruby-eval/src/eval.c
+++ b/mrbgems/mruby-eval/src/eval.c
@@ -5,6 +5,9 @@
#include <mruby/proc.h>
#include <mruby/opcode.h>
+mrb_value mrb_exec_irep(mrb_state *mrb, mrb_value self, struct RProc *p);
+mrb_value mrb_obj_instance_eval(mrb_state *mrb, mrb_value self);
+
static struct mrb_irep *
get_closure_irep(mrb_state *mrb, int level)
{
@@ -209,22 +212,15 @@ f_eval(mrb_state *mrb, mrb_value self)
mrb_value binding = mrb_nil_value();
char *file = NULL;
mrb_int line = 1;
- mrb_value ret;
struct RProc *proc;
mrb_get_args(mrb, "s|ozi", &s, &len, &binding, &file, &line);
proc = create_proc_from_string(mrb, s, len, binding, file, line);
- ret = mrb_top_run(mrb, proc, mrb->c->stack[0], 0);
- if (mrb->exc) {
- mrb_exc_raise(mrb, mrb_obj_value(mrb->exc));
- }
-
- return ret;
+ mrb_assert(!MRB_PROC_CFUNC_P(proc));
+ return mrb_exec_irep(mrb, self, proc);
}
-mrb_value mrb_obj_instance_eval(mrb_state *mrb, mrb_value self);
-
#define CI_ACC_SKIP -1
static mrb_value
@@ -250,7 +246,8 @@ f_instance_eval(mrb_state *mrb, mrb_value self)
c->ci->target_class = mrb_class_ptr(cv);
proc = create_proc_from_string(mrb, s, len, mrb_nil_value(), file, line);
mrb->c->ci->env = NULL;
- return mrb_vm_run(mrb, proc, mrb->c->stack[0], 0);
+ mrb_assert(!MRB_PROC_CFUNC_P(proc));
+ return mrb_exec_irep(mrb, self, proc);
}
else {
mrb_get_args(mrb, "&", &b);
diff --git a/mrbgems/mruby-inline-struct/mrbgem.rake b/mrbgems/mruby-inline-struct/mrbgem.rake
new file mode 100644
index 000000000..91ad9f44b
--- /dev/null
+++ b/mrbgems/mruby-inline-struct/mrbgem.rake
@@ -0,0 +1,5 @@
+MRuby::Gem::Specification.new('mruby-inline-struct') do |spec|
+ spec.license = 'MIT'
+ spec.author = 'mruby developers'
+ spec.summary = 'inline structure'
+end
diff --git a/mrbgems/mruby-inline-struct/test/inline.c b/mrbgems/mruby-inline-struct/test/inline.c
new file mode 100644
index 000000000..0baaab617
--- /dev/null
+++ b/mrbgems/mruby-inline-struct/test/inline.c
@@ -0,0 +1,83 @@
+#include <mruby.h>
+#include <mruby/class.h>
+#include <mruby/string.h>
+#include <mruby/istruct.h>
+
+static mrb_value
+istruct_test_initialize(mrb_state *mrb, mrb_value self)
+{
+ char *string = (char*)mrb_istruct_ptr(self);
+ mrb_int size = mrb_istruct_size();
+ mrb_value object;
+ mrb_get_args(mrb, "o", &object);
+
+ if (mrb_float_p(object))
+ {
+ snprintf(string, size, "float(%.3f)", mrb_float(object));
+ }
+ else if (mrb_fixnum_p(object))
+ {
+ snprintf(string, size, "fixnum(%" MRB_PRId ")", mrb_fixnum(object));
+ }
+ else if (mrb_string_p(object))
+ {
+ snprintf(string, size, "string(%s)", mrb_string_value_cstr(mrb, &object));
+ }
+
+ string[size - 1] = 0; // force NULL at the end
+ return self;
+}
+
+static mrb_value
+istruct_test_to_s(mrb_state *mrb, mrb_value self)
+{
+ return mrb_str_new_cstr(mrb, (const char*)mrb_istruct_ptr(self));
+}
+
+static mrb_value
+istruct_test_length(mrb_state *mrb, mrb_value self)
+{
+ return mrb_fixnum_value(mrb_istruct_size());
+}
+
+static mrb_value
+istruct_test_test_receive(mrb_state *mrb, mrb_value self)
+{
+ mrb_value object;
+ mrb_get_args(mrb, "o", &object);
+ if (mrb_obj_class(mrb, object) != mrb_class_get(mrb, "InlineStructTest"))
+ {
+ mrb_raisef(mrb, E_TYPE_ERROR, "Expected InlineStructTest");
+ }
+ return mrb_bool_value(((char*)mrb_istruct_ptr(object))[0] == 's');
+}
+
+static mrb_value
+istruct_test_test_receive_direct(mrb_state *mrb, mrb_value self)
+{
+ char *ptr;
+ mrb_get_args(mrb, "I", &ptr);
+ return mrb_bool_value(ptr[0] == 's');
+}
+
+static mrb_value
+istruct_test_mutate(mrb_state *mrb, mrb_value self)
+{
+ char *ptr = (char*)mrb_istruct_ptr(self);
+ memcpy(ptr, "mutate", 6);
+ return mrb_nil_value();
+}
+
+void mrb_mruby_inline_struct_gem_test(mrb_state *mrb)
+{
+ struct RClass *cls;
+
+ cls = mrb_define_class(mrb, "InlineStructTest", mrb->object_class);
+ MRB_SET_INSTANCE_TT(cls, MRB_TT_ISTRUCT);
+ mrb_define_method(mrb, cls, "initialize", istruct_test_initialize, MRB_ARGS_REQ(1));
+ mrb_define_method(mrb, cls, "to_s", istruct_test_to_s, MRB_ARGS_NONE());
+ mrb_define_method(mrb, cls, "mutate", istruct_test_mutate, MRB_ARGS_NONE());
+ mrb_define_class_method(mrb, cls, "length", istruct_test_length, MRB_ARGS_NONE());
+ mrb_define_class_method(mrb, cls, "test_receive", istruct_test_test_receive, MRB_ARGS_REQ(1));
+ mrb_define_class_method(mrb, cls, "test_receive_direct", istruct_test_test_receive_direct, MRB_ARGS_REQ(1));
+}
diff --git a/mrbgems/mruby-inline-struct/test/inline.rb b/mrbgems/mruby-inline-struct/test/inline.rb
new file mode 100644
index 000000000..495859232
--- /dev/null
+++ b/mrbgems/mruby-inline-struct/test/inline.rb
@@ -0,0 +1,151 @@
+##
+# InlineStruct Test
+
+class InlineStructTest
+ def extra_method
+ :ok
+ end
+
+ def test_ivar_set
+ @var = :ivar
+ end
+
+ def test_ivar_get
+ @vat
+ end
+end
+
+assert('InlineStructTest#dup') do
+ obj = InlineStructTest.new(1)
+ assert_equal obj.to_s, 'fixnum(1)'
+ assert_equal obj.dup.to_s, 'fixnum(1)'
+end
+
+assert('InlineStructTest#clone') do
+ obj = InlineStructTest.new(1)
+ assert_equal obj.to_s, 'fixnum(1)'
+ assert_equal obj.clone.to_s, 'fixnum(1)'
+end
+
+assert('InlineStruct#object_id') do
+ obj1 = InlineStructTest.new(1)
+ obj2 = InlineStructTest.new(1)
+ assert_not_equal obj1, obj2
+ assert_not_equal obj1.object_id, obj2.object_id
+ assert_not_equal obj1.object_id, obj1.dup.object_id
+ assert_not_equal obj1.object_id, obj1.clone.object_id
+end
+
+assert('InlineStructTest#mutate (dup)') do
+ obj1 = InlineStructTest.new("foo")
+ assert_equal obj1.to_s, "string(foo)"
+ obj2 = obj1.dup
+ assert_equal obj2.to_s, "string(foo)"
+ obj1.mutate
+ assert_equal obj1.to_s, "mutate(foo)"
+ assert_equal obj2.to_s, "string(foo)"
+end
+
+assert('InlineStructTest#mutate (clone)') do
+ obj1 = InlineStructTest.new("foo")
+ assert_equal obj1.to_s, "string(foo)"
+ obj2 = obj1.clone
+ assert_equal obj2.to_s, "string(foo)"
+ obj1.mutate
+ assert_equal obj1.to_s, "mutate(foo)"
+ assert_equal obj2.to_s, "string(foo)"
+end
+
+assert('InlineStructTest#test_receive(string)') do
+ assert_equal InlineStructTest.test_receive(InlineStructTest.new('a')), true
+end
+
+assert('InlineStructTest#test_receive(float)') do
+ assert_equal InlineStructTest.test_receive(InlineStructTest.new(1.25)), false
+end
+
+assert('InlineStructTest#test_receive(invalid object)') do
+ assert_raise(TypeError) do
+ InlineStructTest.test_receive([])
+ end
+end
+
+assert('InlineStructTest#test_receive(string)') do
+ assert_equal InlineStructTest.test_receive_direct(InlineStructTest.new('a')), true
+end
+
+assert('InlineStructTest#test_receive(float)') do
+ assert_equal InlineStructTest.test_receive_direct(InlineStructTest.new(1.25)), false
+end
+
+assert('InlineStructTest#test_receive(invalid object)') do
+ assert_raise(TypeError) do
+ InlineStructTest.test_receive_direct([])
+ end
+end
+
+assert('InlineStructTest#extra_method') do
+ assert_equal InlineStructTest.new(1).extra_method, :ok
+end
+
+assert('InlineStructTest instance variable') do
+ obj = InlineStructTest.new(1)
+ assert_raise(ArgumentError) do
+ obj.test_ivar_set
+ end
+ assert_equal obj.test_ivar_get, nil
+end
+
+# 64-bit mode
+if InlineStructTest.length == 24
+ assert('InlineStructTest length [64 bit]') do
+ assert_equal InlineStructTest.length, 3 * 8
+ end
+
+ assert('InlineStructTest w/float [64 bit]') do
+ obj = InlineStructTest.new(1.25)
+ assert_equal obj.to_s, "float(1.250)"
+ end
+
+ assert('InlineStructTest w/fixnum [64 bit]') do
+ obj = InlineStructTest.new(42)
+ assert_equal obj.to_s, "fixnum(42)"
+ end
+
+ assert('InlineStructTest w/string [64 bit]') do
+ obj = InlineStructTest.new("hello")
+ assert_equal obj.to_s, "string(hello)"
+ end
+
+ assert('InlineStructTest w/long string [64 bit]') do
+ obj = InlineStructTest.new("this won't fit in 3 * 8 bytes available for the structure")
+ assert_equal obj.to_s, "string(this won't fit i"
+ end
+end
+
+# 32-bit mode
+if InlineStructTest.length == 12
+ assert('InlineStructTest length [32 bit]') do
+ assert_equal InlineStructTest.length, 3 * 4
+ end
+
+ assert('InlineStructTest w/float [32 bit]') do
+ obj = InlineStructTest.new(1.25)
+ assert_equal obj.to_s, "float(1.250"
+ end
+
+ assert('InlineStructTest w/fixnum [32 bit]') do
+ obj = InlineStructTest.new(42)
+ assert_equal obj.to_s, "fixnum(42)"
+ end
+
+ assert('InlineStructTest w/string [32 bit]') do
+ obj = InlineStructTest.new("hello")
+ assert_equal obj.to_s, "string(hell"
+ end
+
+ assert('InlineStructTest w/long string [32 bit]') do
+ obj = InlineStructTest.new("this won't fit in 3 * 4 bytes available for the structure")
+ assert_equal obj.to_s, "string(this"
+ end
+end
diff --git a/mrbgems/mruby-kernel-ext/src/kernel.c b/mrbgems/mruby-kernel-ext/src/kernel.c
index d2153be4a..7e6fa28bd 100644
--- a/mrbgems/mruby-kernel-ext/src/kernel.c
+++ b/mrbgems/mruby-kernel-ext/src/kernel.c
@@ -2,6 +2,64 @@
#include <mruby/error.h>
#include <mruby/array.h>
#include <mruby/hash.h>
+#include <mruby/range.h>
+
+static mrb_value
+mrb_f_caller(mrb_state *mrb, mrb_value self)
+{
+ mrb_value bt, v, length;
+ mrb_int bt_len, argc, lev, n;
+
+ bt = mrb_get_backtrace(mrb);
+ bt_len = RARRAY_LEN(bt);
+ argc = mrb_get_args(mrb, "|oo", &v, &length);
+
+ switch (argc) {
+ case 0:
+ lev = 1;
+ n = bt_len - lev;
+ break;
+ case 1:
+ if (mrb_type(v) == MRB_TT_RANGE) {
+ mrb_int beg, len;
+ if (mrb_range_beg_len(mrb, v, &beg, &len, bt_len, TRUE) == 1) {
+ lev = beg;
+ n = len;
+ }
+ else {
+ return mrb_nil_value();
+ }
+ }
+ else {
+ v = mrb_to_int(mrb, v);
+ lev = mrb_fixnum(v);
+ if (lev < 0) {
+ mrb_raisef(mrb, E_ARGUMENT_ERROR, "negative level (%S)", v);
+ }
+ n = bt_len - lev;
+ }
+ break;
+ case 2:
+ lev = mrb_fixnum(mrb_to_int(mrb, v));
+ n = mrb_fixnum(mrb_to_int(mrb, length));
+ if (lev < 0) {
+ mrb_raisef(mrb, E_ARGUMENT_ERROR, "negative level (%S)", v);
+ }
+ if (n < 0) {
+ mrb_raisef(mrb, E_ARGUMENT_ERROR, "negative size (%S)", length);
+ }
+ break;
+ default:
+ lev = n = 0;
+ break;
+ }
+
+ if (n == 0) {
+ return mrb_ary_new(mrb);
+ }
+
+ return mrb_funcall(mrb, bt, "[]", 2, mrb_fixnum_value(lev), mrb_fixnum_value(n));
+}
/*
* call-seq:
@@ -170,6 +228,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_OPT(2));
+ mrb_define_module_function(mrb, krn, "caller", mrb_f_caller, MRB_ARGS_OPT(2));
mrb_define_method(mrb, krn, "__method__", mrb_f_method, MRB_ARGS_NONE());
mrb_define_module_function(mrb, krn, "Integer", mrb_f_integer, MRB_ARGS_ANY());
mrb_define_module_function(mrb, krn, "Float", mrb_f_float, MRB_ARGS_REQ(1));
diff --git a/mrbgems/mruby-kernel-ext/test/kernel.rb b/mrbgems/mruby-kernel-ext/test/kernel.rb
index cc6af13a3..dd7cea86a 100644
--- a/mrbgems/mruby-kernel-ext/test/kernel.rb
+++ b/mrbgems/mruby-kernel-ext/test/kernel.rb
@@ -3,6 +3,35 @@ assert('Kernel.fail, Kernel#fail') do
assert_raise(RuntimeError) { Kernel.fail }
end
+assert('Kernel.caller, Kernel#caller') do
+ skip "backtrace isn't available" if caller(0).empty?
+
+ c = Class.new do
+ def foo(*args)
+ caller(*args)
+ end
+
+ def bar(*args)
+ foo(*args)
+ end
+
+ def baz(*args)
+ bar(*args)
+ end
+ end
+ assert_equal "#bar", c.new.baz[0][-4..-1]
+ assert_equal "#foo", c.new.baz(0)[0][-4..-1]
+ assert_equal "#bar", c.new.baz(1)[0][-4..-1]
+ assert_equal "#baz", c.new.baz(2)[0][-4..-1]
+ assert_equal ["#foo", "#bar"], c.new.baz(0, 2).map { |i| i[-4..-1] }
+ assert_equal ["#bar", "#baz"], c.new.baz(1..2).map { |i| i[-4..-1] }
+ assert_nil c.new.baz(10..20)
+ assert_raise(ArgumentError) { c.new.baz(-1) }
+ assert_raise(ArgumentError) { c.new.baz(-1, 1) }
+ assert_raise(ArgumentError) { c.new.baz(1, -1) }
+ assert_raise(TypeError) { c.new.baz(nil) }
+end
+
assert('Kernel#__method__') do
assert_equal(:m, Class.new {def m; __method__; end}.new.m)
assert_equal(:m, Class.new {define_method(:m) {__method__}}.new.m)
diff --git a/mrbgems/mruby-object-ext/src/object.c b/mrbgems/mruby-object-ext/src/object.c
index 3eba95633..e4fe328e9 100644
--- a/mrbgems/mruby-object-ext/src/object.c
+++ b/mrbgems/mruby-object-ext/src/object.c
@@ -96,7 +96,7 @@ mrb_mruby_object_ext_gem_init(mrb_state* mrb)
mrb_define_method(mrb, n, "to_f", nil_to_f, MRB_ARGS_NONE());
mrb_define_method(mrb, n, "to_i", nil_to_i, MRB_ARGS_NONE());
- mrb_define_method(mrb, mrb->object_class, "instance_exec", mrb_obj_instance_exec, MRB_ARGS_ANY() | MRB_ARGS_BLOCK());
+ mrb_define_method(mrb, mrb->kernel_module, "instance_exec", mrb_obj_instance_exec, MRB_ARGS_ANY() | MRB_ARGS_BLOCK());
}
void
diff --git a/mrbgems/mruby-object-ext/test/object.rb b/mrbgems/mruby-object-ext/test/object.rb
index fe56f1ec5..f0742f8ce 100644
--- a/mrbgems/mruby-object-ext/test/object.rb
+++ b/mrbgems/mruby-object-ext/test/object.rb
@@ -23,3 +23,31 @@ assert('Object#tap') do
], ret
assert_equal(:tap_ok, Class.new {def m; tap{return :tap_ok}; end}.new.m)
end
+
+assert('instance_exec on primitives with class and module definition') do
+ begin
+ class A
+ 1.instance_exec do
+ class B
+ end
+ end
+ end
+
+ assert_kind_of Class, A::B
+ ensure
+ Object.remove_const :A
+ end
+
+ begin
+ class A
+ 1.instance_exec do
+ module B
+ end
+ end
+ end
+
+ assert_kind_of Module, A::B
+ ensure
+ Object.remove_const :A
+ end
+end
diff --git a/mrbgems/mruby-objectspace/src/mruby_objectspace.c b/mrbgems/mruby-objectspace/src/mruby_objectspace.c
index d5cd4f5a1..d0a8effd0 100644
--- a/mrbgems/mruby-objectspace/src/mruby_objectspace.c
+++ b/mrbgems/mruby-objectspace/src/mruby_objectspace.c
@@ -49,7 +49,7 @@ static mrb_value
os_count_objects(mrb_state *mrb, mrb_value self)
{
struct os_count_struct obj_count = { 0 };
- enum mrb_vtype i;
+ mrb_int i;
mrb_value hash;
if (mrb_get_args(mrb, "|H", &hash) == 0) {
diff --git a/mrbgems/mruby-print/src/print.c b/mrbgems/mruby-print/src/print.c
index da5f36789..fd8d15241 100644
--- a/mrbgems/mruby-print/src/print.c
+++ b/mrbgems/mruby-print/src/print.c
@@ -22,7 +22,7 @@ printstr(mrb_state *mrb, mrb_value obj)
int mlen = RSTRING_LEN(obj);
char* utf8 = RSTRING_PTR(obj);
int wlen = MultiByteToWideChar(CP_UTF8, 0, utf8, mlen, NULL, 0);
- wchar_t* utf16 = mrb_malloc(mrb, (wlen+1) * sizeof(wchar_t));
+ 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;
diff --git a/mrbgems/mruby-proc-ext/src/proc.c b/mrbgems/mruby-proc-ext/src/proc.c
index c8c8f1aa1..34f6230dc 100644
--- a/mrbgems/mruby-proc-ext/src/proc.c
+++ b/mrbgems/mruby-proc-ext/src/proc.c
@@ -114,6 +114,9 @@ mrb_proc_parameters(mrb_state *mrb, mrb_value self)
// TODO cfunc aspec is not implemented yet
return mrb_ary_new(mrb);
}
+ if (!irep) {
+ return mrb_ary_new(mrb);
+ }
if (!irep->lv) {
return mrb_ary_new(mrb);
}
diff --git a/mrbgems/mruby-random/src/random.c b/mrbgems/mruby-random/src/random.c
index 3b8d0dd2f..f81d38fe6 100644
--- a/mrbgems/mruby-random/src/random.c
+++ b/mrbgems/mruby-random/src/random.c
@@ -124,6 +124,8 @@ mrb_random_init(mrb_state *mrb, mrb_value self)
mrb_value seed;
mt_state *t;
+ seed = get_opt(mrb);
+
/* avoid memory leaks */
t = (mt_state*)DATA_PTR(self);
if (t) {
@@ -134,7 +136,6 @@ mrb_random_init(mrb_state *mrb, mrb_value self)
t = (mt_state *)mrb_malloc(mrb, sizeof(mt_state));
t->mti = N + 1;
- seed = get_opt(mrb);
seed = mrb_random_mt_srand(mrb, t, seed);
if (mrb_nil_p(seed)) {
t->has_seed = FALSE;
@@ -266,7 +267,7 @@ mrb_ary_sample(mrb_state *mrb, mrb_value ary)
mrb_int n = 0;
mrb_bool given;
mt_state *random = NULL;
- mrb_int len = RARRAY_LEN(ary);
+ mrb_int len;
mrb_get_args(mrb, "|i?d", &n, &given, &random, &mt_state_type);
if (random == NULL) {
@@ -274,6 +275,7 @@ mrb_ary_sample(mrb_state *mrb, mrb_value ary)
}
mrb_random_rand_seed(mrb, random);
mt_rand(random);
+ len = RARRAY_LEN(ary);
if (!given) { /* pick one element */
switch (len) {
case 0:
diff --git a/mrbgems/mruby-random/test/random.rb b/mrbgems/mruby-random/test/random.rb
index 1653ae4a6..1c59be3a6 100644
--- a/mrbgems/mruby-random/test/random.rb
+++ b/mrbgems/mruby-random/test/random.rb
@@ -74,3 +74,15 @@ assert('Array#shuffle!(random)') do
ary1 != [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] and 10.times { |x| ary1.include? x } and ary1 == ary2
end
+
+assert('Array#sample checks input length after reading arguments') do
+ $ary = [1, 2, 3]
+ class ArrayChange
+ def to_i
+ $ary << 4
+ 4
+ end
+ end
+
+ assert_equal [1, 2, 3, 4], $ary.sample(ArrayChange.new).sort
+end
diff --git a/mrbgems/mruby-range-ext/mrblib/range.rb b/mrbgems/mruby-range-ext/mrblib/range.rb
new file mode 100644
index 000000000..e5d1fb079
--- /dev/null
+++ b/mrbgems/mruby-range-ext/mrblib/range.rb
@@ -0,0 +1,31 @@
+class Range
+ ##
+ # call-seq:
+ # rng.first -> obj
+ # rng.first(n) -> an_array
+ #
+ # Returns the first object in the range, or an array of the first +n+
+ # elements.
+ #
+ # (10..20).first #=> 10
+ # (10..20).first(3) #=> [10, 11, 12]
+ #
+ def first(*args)
+ return self.begin if args.empty?
+
+ raise ArgumentError, "wrong number of arguments (given #{args.length}, expected 1)" unless args.length == 1
+ nv = args[0]
+ raise TypeError, "no implicit conversion from nil to integer" if nv.nil?
+ raise TypeError, "no implicit conversion of #{nv.class} into Integer" unless nv.respond_to?(:to_int)
+ n = nv.to_int
+ raise TypeError, "no implicit conversion of #{nv.class} into Integer" unless n.kind_of?(Integer)
+ raise ArgumentError, "negative array size (or size too big)" unless 0 <= n
+ ary = []
+ each do |i|
+ break if n <= 0
+ ary.push(i)
+ n -= 1
+ end
+ ary
+ end
+end
diff --git a/mrbgems/mruby-range-ext/src/range.c b/mrbgems/mruby-range-ext/src/range.c
index 4d6f1684f..3131192ff 100644
--- a/mrbgems/mruby-range-ext/src/range.c
+++ b/mrbgems/mruby-range-ext/src/range.c
@@ -1,6 +1,7 @@
#include <mruby.h>
#include <mruby/range.h>
#include <math.h>
+#include <float.h>
static mrb_bool
r_le(mrb_state *mrb, mrb_value a, mrb_value b)
@@ -43,7 +44,7 @@ static mrb_value
mrb_range_cover(mrb_state *mrb, mrb_value range)
{
mrb_value val;
- struct RRange *r = mrb_range_ptr(range);
+ struct RRange *r = mrb_range_ptr(mrb, range);
mrb_value beg, end;
mrb_get_args(mrb, "o", &val);
@@ -67,32 +68,6 @@ mrb_range_cover(mrb_state *mrb, mrb_value range)
/*
* call-seq:
- * rng.first -> obj
- * rng.first(n) -> an_array
- *
- * Returns the first object in the range, or an array of the first +n+
- * elements.
- *
- * (10..20).first #=> 10
- * (10..20).first(3) #=> [10, 11, 12]
- */
-static mrb_value
-mrb_range_first(mrb_state *mrb, mrb_value range)
-{
- mrb_int num;
- mrb_value array;
- struct RRange *r = mrb_range_ptr(range);
-
- if (mrb_get_args(mrb, "|i", &num) == 0) {
- return r->edges->beg;
- }
-
- array = mrb_funcall(mrb, range, "to_a", 0);
- return mrb_funcall(mrb, array, "first", 1, mrb_fixnum_value(num));
-}
-
-/*
- * call-seq:
* rng.last -> obj
* rng.last(n) -> an_array
*
@@ -112,7 +87,7 @@ mrb_range_last(mrb_state *mrb, mrb_value range)
{
mrb_value num;
mrb_value array;
- struct RRange *r = mrb_range_ptr(range);
+ struct RRange *r = mrb_range_ptr(mrb, range);
if (mrb_get_args(mrb, "|o", &num) == 0) {
return r->edges->end;
@@ -136,15 +111,17 @@ mrb_range_last(mrb_state *mrb, mrb_value range)
static mrb_value
mrb_range_size(mrb_state *mrb, mrb_value range)
{
- struct RRange *r = mrb_range_ptr(range);
+ struct RRange *r = mrb_range_ptr(mrb, range);
mrb_value beg, end;
- double beg_f, end_f;
+ mrb_float beg_f, end_f;
mrb_bool num_p = TRUE;
+ mrb_bool excl;
beg = r->edges->beg;
end = r->edges->end;
+ excl = r->excl;
if (mrb_fixnum_p(beg)) {
- beg_f = (double)mrb_fixnum(beg);
+ beg_f = (mrb_float)mrb_fixnum(beg);
}
else if (mrb_float_p(beg)) {
beg_f = mrb_float(beg);
@@ -153,7 +130,7 @@ mrb_range_size(mrb_state *mrb, mrb_value range)
num_p = FALSE;
}
if (mrb_fixnum_p(end)) {
- end_f = (double)mrb_fixnum(end);
+ end_f = (mrb_float)mrb_fixnum(end);
}
else if (mrb_float_p(end)) {
end_f = mrb_float(end);
@@ -162,14 +139,24 @@ mrb_range_size(mrb_state *mrb, mrb_value range)
num_p = FALSE;
}
if (num_p) {
- double f;
-
- if (beg_f > end_f) return mrb_fixnum_value(0);
- f = end_f - beg_f;
- if (!r->excl) {
- return mrb_fixnum_value((mrb_int)ceil(f + 1));
+ mrb_float n = end_f - beg_f;
+ mrb_float err = (fabs(beg_f) + fabs(end_f) + fabs(end_f-beg_f)) * MRB_FLOAT_EPSILON;
+
+ if (err>0.5) err=0.5;
+ if (excl) {
+ if (n<=0) return mrb_fixnum_value(0);
+ if (n<1)
+ n = 0;
+ else
+ n = floor(n - err);
+ }
+ else {
+ if (n<0) return mrb_fixnum_value(0);
+ n = floor(n + err);
}
- return mrb_fixnum_value((mrb_int)ceil(f));
+ if (isinf(n+1))
+ return mrb_float_value(mrb, INFINITY);
+ return mrb_fixnum_value((mrb_int)n+1);
}
return mrb_nil_value();
}
@@ -180,7 +167,6 @@ mrb_mruby_range_ext_gem_init(mrb_state* mrb)
struct RClass * s = mrb_class_get(mrb, "Range");
mrb_define_method(mrb, s, "cover?", mrb_range_cover, MRB_ARGS_REQ(1));
- mrb_define_method(mrb, s, "first", mrb_range_first, MRB_ARGS_OPT(1));
mrb_define_method(mrb, s, "last", mrb_range_last, MRB_ARGS_OPT(1));
mrb_define_method(mrb, s, "size", mrb_range_size, MRB_ARGS_NONE());
}
diff --git a/mrbgems/mruby-range-ext/test/range.rb b/mrbgems/mruby-range-ext/test/range.rb
index 18e7dafe4..efcbdabe4 100644
--- a/mrbgems/mruby-range-ext/test/range.rb
+++ b/mrbgems/mruby-range-ext/test/range.rb
@@ -10,6 +10,7 @@ end
assert('Range#first') do
assert_equal 10, (10..20).first
assert_equal [10, 11, 12], (10..20).first(3)
+ assert_equal [0, 1, 2], (0..Float::INFINITY).first(3)
end
assert('Range#last') do
@@ -25,5 +26,7 @@ assert('Range#size') do
assert_equal 6, (1...6.3).size
assert_equal 5, (1...6.0).size
assert_equal 5, (1.1...6).size
+ assert_equal 15, (1.0..15.9).size
+ assert_equal Float::INFINITY, (0..Float::INFINITY).size
assert_nil ('a'..'z').size
end
diff --git a/mrbgems/mruby-sprintf/src/sprintf.c b/mrbgems/mruby-sprintf/src/sprintf.c
index 8d14b0fc5..d02a2aa4d 100644
--- a/mrbgems/mruby-sprintf/src/sprintf.c
+++ b/mrbgems/mruby-sprintf/src/sprintf.c
@@ -116,8 +116,9 @@ mrb_fix2binstr(mrb_state *mrb, mrb_value x, int base)
#define CHECK(l) do {\
/* int cr = ENC_CODERANGE(result);*/\
- while (blen + (l) >= bsiz) {\
+ while ((l) >= bsiz - blen) {\
bsiz*=2;\
+ if (bsiz < 0) mrb_raise(mrb, E_ARGUMENT_ERROR, "too big specifier"); \
}\
mrb_str_resize(mrb, result, bsiz);\
/* ENC_CODERANGE_SET(result, cr);*/\
@@ -136,29 +137,63 @@ mrb_fix2binstr(mrb_state *mrb, mrb_value x, int base)
blen += (l);\
} while (0)
-#define GETARG() (!mrb_undef_p(nextvalue) ? nextvalue : \
- posarg == -1 ? \
- (mrb_raisef(mrb, E_ARGUMENT_ERROR, "unnumbered(%S) mixed with numbered", mrb_fixnum_value(nextarg)), mrb_undef_value()) : \
- posarg == -2 ? \
- (mrb_raisef(mrb, E_ARGUMENT_ERROR, "unnumbered(%S) mixed with named", mrb_fixnum_value(nextarg)), mrb_undef_value()) : \
+static void
+check_next_arg(mrb_state *mrb, int posarg, int nextarg)
+{
+ switch (posarg) {
+ case -1:
+ mrb_raisef(mrb, E_ARGUMENT_ERROR, "unnumbered(%S) mixed with numbered", mrb_fixnum_value(nextarg));
+ break;
+ case -2:
+ mrb_raisef(mrb, E_ARGUMENT_ERROR, "unnumbered(%S) mixed with named", mrb_fixnum_value(nextarg));
+ break;
+ default:
+ break;
+ }
+}
+
+static void
+check_pos_arg(mrb_state *mrb, int posarg, int n)
+{
+ if (posarg > 0) {
+ mrb_raisef(mrb, E_ARGUMENT_ERROR, "numbered(%S) after unnumbered(%S)",
+ mrb_fixnum_value(n), mrb_fixnum_value(posarg));
+ }
+ if (posarg == -2) {
+ mrb_raisef(mrb, E_ARGUMENT_ERROR, "numbered(%S) after named", mrb_fixnum_value(n));
+ }
+ if (n < 1) {
+ mrb_raisef(mrb, E_ARGUMENT_ERROR, "invalid index - %S$", mrb_fixnum_value(n));
+ }
+}
+
+static void
+check_name_arg(mrb_state *mrb, int posarg, const char *name, int len)
+{
+ if (posarg > 0) {
+ mrb_raisef(mrb, E_ARGUMENT_ERROR, "named%S after unnumbered(%S)",
+ mrb_str_new(mrb, (name), (len)), mrb_fixnum_value(posarg));
+ }
+ if (posarg == -1) {
+ mrb_raisef(mrb, E_ARGUMENT_ERROR, "named%S after numbered", mrb_str_new(mrb, (name), (len)));
+ }
+}
+
+#define GETNEXTARG() (\
+ check_next_arg(mrb, posarg, nextarg),\
(posarg = nextarg++, GETNTHARG(posarg)))
-#define GETPOSARG(n) (posarg > 0 ? \
- (mrb_raisef(mrb, E_ARGUMENT_ERROR, "numbered(%S) after unnumbered(%S)", mrb_fixnum_value(n), mrb_fixnum_value(posarg)), mrb_undef_value()) : \
- posarg == -2 ? \
- (mrb_raisef(mrb, E_ARGUMENT_ERROR, "numbered(%S) after named", mrb_fixnum_value(n)), mrb_undef_value()) : \
- ((n < 1) ? \
- (mrb_raisef(mrb, E_ARGUMENT_ERROR, "invalid index - %S$", mrb_fixnum_value(n)), mrb_undef_value()) : \
- (posarg = -1, GETNTHARG(n))))
+#define GETARG() (!mrb_undef_p(nextvalue) ? nextvalue : GETNEXTARG())
+
+#define GETPOSARG(n) (\
+ check_pos_arg(mrb, posarg, n),\
+ (posarg = -1, GETNTHARG(n)))
#define GETNTHARG(nth) \
((nth >= argc) ? (mrb_raise(mrb, E_ARGUMENT_ERROR, "too few arguments"), mrb_undef_value()) : argv[nth])
-#define GETNAMEARG(id, name, len) ( \
- posarg > 0 ? \
- (mrb_raisef(mrb, E_ARGUMENT_ERROR, "named%S after unnumbered(%S)", mrb_str_new(mrb, (name), (len)), mrb_fixnum_value(posarg)), mrb_undef_value()) : \
- posarg == -1 ? \
- (mrb_raisef(mrb, E_ARGUMENT_ERROR, "named%S after numbered", mrb_str_new(mrb, (name), (len))), mrb_undef_value()) : \
+#define GETNAMEARG(id, name, len) (\
+ check_name_arg(mrb, posarg, name, len),\
(posarg = -2, mrb_hash_fetch(mrb, get_hash(mrb, &hash, argc, argv), id, mrb_undef_value())))
#define GETNUM(n, val) \
@@ -182,7 +217,7 @@ mrb_fix2binstr(mrb_state *mrb, mrb_value x, int base)
tmp_v = GETPOSARG(n); \
} \
else { \
- tmp_v = GETARG(); \
+ tmp_v = GETNEXTARG(); \
p = t; \
} \
num = mrb_fixnum(tmp_v); \
@@ -675,6 +710,7 @@ retry:
else {
mrb_raise(mrb, E_ARGUMENT_ERROR, "invalid character");
}
+ mrb_check_type(mrb, tmp, MRB_TT_STRING);
c = RSTRING_PTR(tmp);
n = RSTRING_LEN(tmp);
if (!(flags & FWIDTH)) {
@@ -686,10 +722,10 @@ retry:
CHECK(n);
memcpy(buf+blen, c, n);
blen += n;
- FILL(' ', width-1);
+ if (width>0) FILL(' ', width-1);
}
else {
- FILL(' ', width-1);
+ if (width>0) FILL(' ', width-1);
CHECK(n);
memcpy(buf+blen, c, n);
blen += n;
@@ -843,20 +879,20 @@ retry:
strncpy(nbuf, RSTRING_PTR(val), sizeof(nbuf));
break;
case 8:
- snprintf(nbuf, sizeof(nbuf), "%"MRB_PRIo, v);
+ snprintf(nbuf, sizeof(nbuf), "%" MRB_PRIo, v);
break;
case 10:
- snprintf(nbuf, sizeof(nbuf), "%"MRB_PRId, v);
+ snprintf(nbuf, sizeof(nbuf), "%" MRB_PRId, v);
break;
case 16:
- snprintf(nbuf, sizeof(nbuf), "%"MRB_PRIx, v);
+ snprintf(nbuf, sizeof(nbuf), "%" MRB_PRIx, v);
break;
}
s = nbuf;
}
else {
s = nbuf;
- if (v < 0) {
+ if (base != 10 && v < 0) {
dots = 1;
}
switch (base) {
@@ -864,13 +900,13 @@ retry:
strncpy(++s, RSTRING_PTR(val), sizeof(nbuf)-1);
break;
case 8:
- snprintf(++s, sizeof(nbuf)-1, "%"MRB_PRIo, v);
+ snprintf(++s, sizeof(nbuf)-1, "%" MRB_PRIo, v);
break;
case 10:
- snprintf(++s, sizeof(nbuf)-1, "%"MRB_PRId, v);
+ snprintf(++s, sizeof(nbuf)-1, "%" MRB_PRId, v);
break;
case 16:
- snprintf(++s, sizeof(nbuf)-1, "%"MRB_PRIx, v);
+ snprintf(++s, sizeof(nbuf)-1, "%" MRB_PRIx, v);
break;
}
if (v < 0) {
diff --git a/mrbgems/mruby-sprintf/test/sprintf.rb b/mrbgems/mruby-sprintf/test/sprintf.rb
index 454c226e1..ccbd95d51 100644
--- a/mrbgems/mruby-sprintf/test/sprintf.rb
+++ b/mrbgems/mruby-sprintf/test/sprintf.rb
@@ -5,5 +5,28 @@ 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" }
- assert_equal 16, ("%b" % (1<<15)).size
+ assert_equal 15, ("%b" % (1<<14)).size
+end
+
+assert("String#% with invalid chr") do
+ begin
+ class Fixnum
+ alias_method :chr_, :chr if method_defined?(:chr)
+
+ def chr
+ nil
+ end
+ end
+
+ assert_raise TypeError do
+ "%c" % 0
+ end
+ ensure
+ class Fixnum
+ if method_defined?(:chr_)
+ alias_method :chr, :chr_
+ remove_method :chr_
+ end
+ end
+ end
end
diff --git a/mrbgems/mruby-string-ext/mrblib/string.rb b/mrbgems/mruby-string-ext/mrblib/string.rb
index c3b765a5f..8895b7ad3 100644
--- a/mrbgems/mruby-string-ext/mrblib/string.rb
+++ b/mrbgems/mruby-string-ext/mrblib/string.rb
@@ -45,7 +45,7 @@ class String
def lstrip
a = 0
z = self.size - 1
- a += 1 while " \f\n\r\t\v".include?(self[a]) and a <= z
+ a += 1 while a <= z and " \f\n\r\t\v".include?(self[a])
(z >= 0) ? self[a..z] : ""
end
@@ -62,7 +62,7 @@ class String
def rstrip
a = 0
z = self.size - 1
- z -= 1 while " \f\n\r\t\v\0".include?(self[z]) and a <= z
+ z -= 1 while a <= z and " \f\n\r\t\v\0".include?(self[z])
(z >= 0) ? self[a..z] : ""
end
@@ -78,8 +78,8 @@ class String
def strip
a = 0
z = self.size - 1
- a += 1 while " \f\n\r\t\v".include?(self[a]) and a <= z
- z -= 1 while " \f\n\r\t\v\0".include?(self[z]) and a <= z
+ a += 1 while a <= z and " \f\n\r\t\v".include?(self[a])
+ z -= 1 while a <= z and " \f\n\r\t\v\0".include?(self[z])
(z >= 0) ? self[a..z] : ""
end
@@ -275,15 +275,11 @@ class String
# "hello".ljust(20) #=> "hello "
# "hello".ljust(20, '1234') #=> "hello123412341234123"
def ljust(idx, padstr = ' ')
- if idx <= self.size
- return self
- end
- newstr = self.dup
- newstr << padstr
- while newstr.size <= idx
- newstr << padstr
- end
- return newstr.slice(0,idx)
+ raise ArgumentError, 'zero width padding' if padstr == ''
+ return self if idx <= self.size
+ pad_repetitions = (idx / padstr.length).ceil
+ padding = (padstr * pad_repetitions)[0...(idx - self.length)]
+ self + padding
end
##
@@ -298,15 +294,11 @@ class String
# "hello".rjust(20) #=> " hello"
# "hello".rjust(20, '1234') #=> "123412341234123hello"
def rjust(idx, padstr = ' ')
- if idx <= self.size
- return self
- end
- padsize = idx - self.size
- newstr = padstr.dup
- while newstr.size <= padsize
- newstr << padstr
- end
- return newstr.slice(0,padsize) + self
+ raise ArgumentError, 'zero width padding' if padstr == ''
+ return self if idx <= self.size
+ pad_repetitions = (idx / padstr.length).ceil
+ padding = (padstr * pad_repetitions)[0...(idx - self.length)]
+ padding + self
end
# str.upto(other_str, exclusive=false) {|s| block } -> str
@@ -385,4 +377,18 @@ class String
end
end
alias each_codepoint codepoints
+
+ ##
+ # call-seq:
+ # str.prepend(other_str) -> str
+ #
+ # Prepend---Prepend the given string to <i>str</i>.
+ #
+ # a = "world"
+ # a.prepend("hello ") #=> "hello world"
+ # a #=> "hello world"
+ def prepend(arg)
+ self[0, 0] = arg
+ self
+ end
end
diff --git a/mrbgems/mruby-string-ext/src/string.c b/mrbgems/mruby-string-ext/src/string.c
index 2a52d53b3..7e87b3db4 100644
--- a/mrbgems/mruby-string-ext/src/string.c
+++ b/mrbgems/mruby-string-ext/src/string.c
@@ -23,10 +23,11 @@ static mrb_value
mrb_str_setbyte(mrb_state *mrb, mrb_value str)
{
mrb_int pos, byte;
- long len = RSTRING_LEN(str);
+ long len;
mrb_get_args(mrb, "ii", &pos, &byte);
+ len = RSTRING_LEN(str);
if (pos < -len || len <= pos)
mrb_raisef(mrb, E_INDEX_ERROR, "index %S is out of array", mrb_fixnum_value(pos));
if (pos < 0)
@@ -55,8 +56,14 @@ mrb_str_byteslice(mrb_state *mrb, mrb_value str)
mrb_int beg;
len = RSTRING_LEN(str);
- if (mrb_range_beg_len(mrb, a1, &beg, &len, len)) {
+ switch (mrb_range_beg_len(mrb, a1, &beg, &len, len, TRUE)) {
+ case 0: /* not range */
+ break;
+ case 1: /* range */
return mrb_str_substr(mrb, str, beg, len);
+ case 2: /* out of range */
+ mrb_raisef(mrb, E_RANGE_ERROR, "%S out of range", a1);
+ break;
}
return mrb_nil_value();
}
@@ -307,13 +314,14 @@ mrb_str_lines(mrb_state *mrb, mrb_value self)
int ai;
mrb_int len;
mrb_value arg;
- char *p = RSTRING_PTR(self), *t;
- char *e = p + RSTRING_LEN(self);
+ char *b = RSTRING_PTR(self);
+ char *p = b, *t;
+ char *e = b + RSTRING_LEN(self);
mrb_get_args(mrb, "&", &blk);
result = mrb_ary_new(mrb);
-
+ ai = mrb_gc_arena_save(mrb);
if (!mrb_nil_p(blk)) {
while (p < e) {
t = p;
@@ -322,11 +330,17 @@ mrb_str_lines(mrb_state *mrb, mrb_value self)
len = (mrb_int) (p - t);
arg = mrb_str_new(mrb, t, len);
mrb_yield_argv(mrb, blk, 1, &arg);
+ mrb_gc_arena_restore(mrb, ai);
+ if (b != RSTRING_PTR(self)) {
+ ptrdiff_t diff = p - b;
+ b = RSTRING_PTR(self);
+ p = b + diff;
+ }
+ e = b + RSTRING_LEN(self);
}
return self;
}
while (p < e) {
- ai = mrb_gc_arena_save(mrb);
t = p;
while (p < e && *p != '\n') p++;
if (*p == '\n') p++;
@@ -429,44 +443,6 @@ mrb_str_succ(mrb_state *mrb, mrb_value self)
return str;
}
-/*
- * call-seq:
- * str.prepend(other_str) -> str
- *
- * Prepend---Prepend the given string to <i>str</i>.
- *
- * a = "world"
- * a.prepend("hello ") #=> "hello world"
- * a #=> "hello world"
- */
-static mrb_value
-mrb_str_prepend(mrb_state *mrb, mrb_value self)
-{
- struct RString *s1 = mrb_str_ptr(self), *s2, *temp_s;
- mrb_int len;
- mrb_value other, temp_str;
-
- mrb_get_args(mrb, "S", &other);
-
- mrb_str_modify(mrb, s1);
- if (!mrb_string_p(other)) {
- other = mrb_str_to_str(mrb, other);
- }
- s2 = mrb_str_ptr(other);
- len = RSTR_LEN(s1) + RSTR_LEN(s2);
- temp_str = mrb_str_new(mrb, NULL, RSTR_LEN(s1));
- temp_s = mrb_str_ptr(temp_str);
- memcpy(RSTR_PTR(temp_s), RSTR_PTR(s1), RSTR_LEN(s1));
- if (RSTRING_CAPA(self) < len) {
- mrb_str_resize(mrb, self, len);
- }
- memcpy(RSTR_PTR(s1), RSTR_PTR(s2), RSTR_LEN(s2));
- memcpy(RSTR_PTR(s1) + RSTR_LEN(s2), RSTR_PTR(temp_s), RSTR_LEN(temp_s));
- RSTR_SET_LEN(s1, len);
- RSTR_PTR(s1)[len] = '\0';
- return self;
-}
-
#ifdef MRB_UTF8_STRING
static const char utf8len_codepage_zero[256] =
{
@@ -554,7 +530,6 @@ mrb_mruby_string_ext_gem_init(mrb_state* mrb)
mrb_define_method(mrb, s, "lines", mrb_str_lines, MRB_ARGS_NONE());
mrb_define_method(mrb, s, "succ", mrb_str_succ, MRB_ARGS_NONE());
mrb_define_method(mrb, s, "succ!", mrb_str_succ_bang, MRB_ARGS_NONE());
- mrb_define_method(mrb, s, "prepend", mrb_str_prepend, MRB_ARGS_REQ(1));
mrb_alias_method(mrb, s, mrb_intern_lit(mrb, "next"), mrb_intern_lit(mrb, "succ"));
mrb_alias_method(mrb, s, mrb_intern_lit(mrb, "next!"), mrb_intern_lit(mrb, "succ!"));
mrb_define_method(mrb, s, "ord", mrb_str_ord, MRB_ARGS_NONE());
diff --git a/mrbgems/mruby-string-ext/test/string.rb b/mrbgems/mruby-string-ext/test/string.rb
index a5d55a7ee..24bc859d8 100644
--- a/mrbgems/mruby-string-ext/test/string.rb
+++ b/mrbgems/mruby-string-ext/test/string.rb
@@ -30,6 +30,18 @@ assert('String#setbyte') do
assert_equal("Hello", str1)
end
+assert("String#setbyte raises IndexError if arg conversion resizes String") do
+ $s = "01234\n"
+ class Tmp
+ def to_i
+ $s.chomp! ''
+ 95
+ end
+ end
+ tmp = Tmp.new
+ assert_raise(IndexError) { $s.setbyte(5, tmp) }
+end
+
assert('String#byteslice') do
str1 = "hello"
assert_equal("e", str1.byteslice(1))
@@ -421,6 +433,7 @@ end
assert('String#ljust') do
assert_equal "hello", "hello".ljust(4)
assert_equal "hello ", "hello".ljust(20)
+ assert_equal 20, "hello".ljust(20).length
assert_equal "hello123412341234123", "hello".ljust(20, '1234')
assert_equal "hello", "hello".ljust(-3)
end
@@ -428,10 +441,55 @@ end
assert('String#rjust') do
assert_equal "hello", "hello".rjust(4)
assert_equal " hello", "hello".rjust(20)
+ assert_equal 20, "hello".rjust(20).length
assert_equal "123412341234123hello", "hello".rjust(20, '1234')
assert_equal "hello", "hello".rjust(-3)
end
+if UTF8STRING
+ assert('String#ljust with UTF8') do
+ assert_equal "helloã‚“ ", "helloã‚“".ljust(20)
+ assert_equal "helloó ", "helloó".ljust(34)
+ assert_equal 34, "helloó".ljust(34).length
+ assert_equal "helloã‚“ã‚“ã‚“ã‚“ã‚“ã‚“ã‚“ã‚“ã‚“ã‚“ã‚“ã‚“ã‚“ã‚“", "hello".ljust(19, 'ã‚“')
+ assert_equal "helloã‚“ã‚“ã‚“ã‚“ã‚“ã‚“ã‚“ã‚“ã‚“ã‚“ã‚“ã‚“ã‚“ã‚“ã‚“", "hello".ljust(20, 'ã‚“')
+ end
+
+ assert('String#rjust with UTF8') do
+ assert_equal " helloã‚“", "helloã‚“".rjust(20)
+ assert_equal " helloó", "helloó".rjust(34)
+ # assert_equal 34, "helloó".rjust(34).length
+ assert_equal "ã‚“ã‚“ã‚“ã‚“ã‚“ã‚“ã‚“ã‚“ã‚“ã‚“ã‚“ã‚“ã‚“ã‚“hello", "hello".rjust(19, 'ã‚“')
+ assert_equal "ã‚“ã‚“ã‚“ã‚“ã‚“ã‚“ã‚“ã‚“ã‚“ã‚“ã‚“ã‚“ã‚“ã‚“ã‚“hello", "hello".rjust(20, 'ã‚“')
+ end
+
+ assert('UTF8 byte counting') do
+ ret = ' '
+ ret[-6..-1] = "helloó"
+ assert_equal 34, ret.length
+ end
+end
+
+assert('String#ljust should not change string') do
+ a = "hello"
+ a.ljust(20)
+ assert_equal "hello", a
+end
+
+assert('String#rjust should not change string') do
+ a = "hello"
+ a.rjust(20)
+ assert_equal "hello", a
+end
+
+assert('String#ljust should raise on zero width padding') do
+ assert_raise(ArgumentError) { "foo".ljust(10, '') }
+end
+
+assert('String#rjust should raise on zero width padding') do
+ assert_raise(ArgumentError) { "foo".rjust(10, '') }
+end
+
assert('String#upto') do
a = "aa"
start = "aa"
diff --git a/mrbgems/mruby-struct/src/struct.c b/mrbgems/mruby-struct/src/struct.c
index 1a06e0c12..93bd1e2b2 100644
--- a/mrbgems/mruby-struct/src/struct.c
+++ b/mrbgems/mruby-struct/src/struct.c
@@ -203,7 +203,7 @@ make_struct(mrb_state *mrb, mrb_value name, mrb_value members, struct RClass * k
}
if (mrb_const_defined_at(mrb, mrb_obj_value(klass), id)) {
mrb_warn(mrb, "redefining constant Struct::%S", name);
- /* ?rb_mod_remove_const(klass, mrb_sym2name(mrb, id)); */
+ mrb_const_remove(mrb, mrb_obj_value(klass), id);
}
c = mrb_define_class_under(mrb, klass, RSTRING_PTR(name), klass);
}
@@ -273,31 +273,21 @@ mrb_struct_s_def(mrb_state *mrb, mrb_value klass)
}
else {
if (argc > 0) name = argv[0];
- if (argc > 1) rest = argv[1];
- if (mrb_array_p(rest)) {
- if (!mrb_nil_p(name) && mrb_symbol_p(name)) {
- /* 1stArgument:symbol -> name=nil rest=argv[0]-[n] */
- mrb_ary_unshift(mrb, rest, name);
- name = mrb_nil_value();
- }
- }
- else {
- pargv = &argv[1];
- argcnt = argc-1;
- if (!mrb_nil_p(name) && mrb_symbol_p(name)) {
- /* 1stArgument:symbol -> name=nil rest=argv[0]-[n] */
- name = mrb_nil_value();
- pargv = &argv[0];
- argcnt++;
- }
- rest = mrb_ary_new_from_values(mrb, argcnt, pargv);
+ pargv = &argv[1];
+ argcnt = argc-1;
+ if (!mrb_nil_p(name) && mrb_symbol_p(name)) {
+ /* 1stArgument:symbol -> name=nil rest=argv[0]-[n] */
+ name = mrb_nil_value();
+ pargv = &argv[0];
+ argcnt++;
}
+ rest = mrb_ary_new_from_values(mrb, argcnt, pargv);
for (i=0; i<RARRAY_LEN(rest); i++) {
id = mrb_obj_to_sym(mrb, RARRAY_PTR(rest)[i]);
mrb_ary_set(mrb, rest, i, mrb_symbol_value(id));
}
}
- st = make_struct(mrb, name, rest, struct_class(mrb));
+ st = make_struct(mrb, name, rest, mrb_class_ptr(klass));
if (!mrb_nil_p(b)) {
mrb_yield_with_class(mrb, b, 1, &st, st, mrb_class_ptr(st));
}
@@ -703,7 +693,7 @@ mrb_mruby_struct_gem_init(mrb_state* mrb)
mrb_define_method(mrb, st, "to_a", mrb_struct_to_a, MRB_ARGS_NONE());
mrb_define_method(mrb, st, "values", mrb_struct_to_a, MRB_ARGS_NONE());
mrb_define_method(mrb, st, "to_h", mrb_struct_to_h, MRB_ARGS_NONE());
- mrb_define_method(mrb, st, "values_at", mrb_struct_values_at, MRB_ARGS_NONE());
+ mrb_define_method(mrb, st, "values_at", mrb_struct_values_at, MRB_ARGS_ANY());
}
void
diff --git a/mrbgems/mruby-struct/test/struct.rb b/mrbgems/mruby-struct/test/struct.rb
index 02ecf69e4..1c0939e7f 100644
--- a/mrbgems/mruby-struct/test/struct.rb
+++ b/mrbgems/mruby-struct/test/struct.rb
@@ -158,3 +158,42 @@ assert("Struct#dig") do
assert_equal 1, a.dig(:purple, :red)
assert_equal 1, a.dig(1, 0)
end
+
+assert("Struct.new removes existing constant") do
+ skip "redefining Struct with same name cause warnings"
+ begin
+ assert_not_equal Struct.new("Test", :a), Struct.new("Test", :a, :b)
+ ensure
+ Struct.remove_const :Test
+ end
+end
+
+assert("Struct#initialize_copy requires struct to be the same type") do
+ begin
+ Struct.new("Test", :a)
+ a = Struct::Test.new("a")
+ Struct.remove_const :Test
+ Struct.new("Test", :a, :b)
+ assert_raise(TypeError) do
+ a.initialize_copy(Struct::Test.new("a", "b"))
+ end
+ ensure
+ Struct.remove_const :Test
+ end
+end
+
+assert("Struct.new does not allow array") do
+ assert_raise(TypeError) do
+ Struct.new("Test", [:a])
+ end
+end
+
+assert("Struct.new generates subclass of Struct") do
+ begin
+ original_struct = Struct
+ Struct = String
+ assert_equal original_struct, original_struct.new.superclass
+ ensure
+ Struct = original_struct
+ end
+end
diff --git a/mrbgems/mruby-test/driver.c b/mrbgems/mruby-test/driver.c
index 7cc5211f0..14e93ff33 100644
--- a/mrbgems/mruby-test/driver.c
+++ b/mrbgems/mruby-test/driver.c
@@ -94,6 +94,12 @@ mrb_init_test_driver(mrb_state *mrb, mrb_bool verbose)
mrb_define_const(mrb, mrbtest, "FIXNUM_MIN", mrb_fixnum_value(MRB_INT_MIN));
mrb_define_const(mrb, mrbtest, "FIXNUM_BIT", mrb_fixnum_value(MRB_INT_BIT));
+#ifdef MRB_USE_FLOAT
+ mrb_define_const(mrb, mrbtest, "FLOAT_TOLERANCE", mrb_float_value(mrb, 1e-6));
+#else
+ mrb_define_const(mrb, mrbtest, "FLOAT_TOLERANCE", mrb_float_value(mrb, 1e-12));
+#endif
+
if (verbose) {
mrb_gv_set(mrb, mrb_intern_lit(mrb, "$mrbtest_verbose"), mrb_true_value());
}
diff --git a/mrbgems/mruby-time/src/time.c b/mrbgems/mruby-time/src/time.c
index 19ce32832..43d87e5ff 100644
--- a/mrbgems/mruby-time/src/time.c
+++ b/mrbgems/mruby-time/src/time.c
@@ -183,7 +183,7 @@ static const struct mrb_data_type mrb_time_type = { "Time", mrb_free };
/** Updates the datetime of a mrb_time based on it's timezone and
seconds setting. Returns self on success, NULL of failure. */
static struct mrb_time*
-mrb_time_update_datetime(struct mrb_time *self)
+time_update_datetime(mrb_state *mrb, struct mrb_time *self)
{
struct tm *aid;
@@ -193,7 +193,11 @@ mrb_time_update_datetime(struct mrb_time *self)
else {
aid = localtime_r(&self->sec, &self->datetime);
}
- if (!aid) return NULL;
+ if (!aid) {
+ mrb_raisef(mrb, E_ARGUMENT_ERROR, "%S out of Time range", mrb_float_value(mrb, (mrb_float)self->sec));
+ /* not reached */
+ return NULL;
+ }
#ifdef NO_GMTIME_R
self->datetime = *aid; /* copy data */
#endif
@@ -213,7 +217,7 @@ static struct mrb_time*
time_alloc(mrb_state *mrb, double sec, double usec, enum mrb_timezone timezone)
{
struct mrb_time *tm;
- time_t tsec;
+ time_t tsec = 0;
if (sizeof(time_t) == 4 && (sec > (double)INT32_MAX || (double)INT32_MIN > sec)) {
goto out_of_range;
@@ -238,7 +242,7 @@ time_alloc(mrb_state *mrb, double sec, double usec, enum mrb_timezone timezone)
tm->usec -= 1000000;
}
tm->timezone = timezone;
- mrb_time_update_datetime(tm);
+ time_update_datetime(mrb, tm);
return tm;
}
@@ -290,7 +294,7 @@ current_mrb_time(mrb_state *mrb)
}
#endif
tm->timezone = MRB_TIMEZONE_LOCAL;
- mrb_time_update_datetime(tm);
+ time_update_datetime(mrb, tm);
return tm;
}
@@ -328,6 +332,15 @@ time_mktime(mrb_state *mrb, mrb_int ayear, mrb_int amonth, mrb_int aday,
nowtime.tm_min = (int)amin;
nowtime.tm_sec = (int)asec;
nowtime.tm_isdst = -1;
+
+ if (nowtime.tm_mon < 0 || nowtime.tm_mon > 11
+ || nowtime.tm_mday < 1 || nowtime.tm_mday > 31
+ || nowtime.tm_hour < 0 || nowtime.tm_hour > 24
+ || (nowtime.tm_hour == 24 && (nowtime.tm_min > 0 || nowtime.tm_sec > 0))
+ || nowtime.tm_min < 0 || nowtime.tm_min > 59
+ || nowtime.tm_sec < 0 || nowtime.tm_sec > 60)
+ mrb_raise(mrb, E_RUNTIME_ERROR, "argument out of range");
+
if (timezone == MRB_TIMEZONE_UTC) {
nowsecs = timegm(&nowtime);
}
@@ -368,6 +381,17 @@ mrb_time_local(mrb_state *mrb, mrb_value self)
time_mktime(mrb, ayear, amonth, aday, ahour, amin, asec, ausec, MRB_TIMEZONE_LOCAL));
}
+static struct mrb_time*
+time_get_ptr(mrb_state *mrb, mrb_value time)
+{
+ struct mrb_time *tm;
+
+ tm = DATA_GET_PTR(mrb, time, &mrb_time_type, struct mrb_time);
+ if (!tm) {
+ mrb_raise(mrb, E_ARGUMENT_ERROR, "uninitialized time");
+ }
+ return tm;
+}
static mrb_value
mrb_time_eq(mrb_state *mrb, mrb_value self)
@@ -377,7 +401,7 @@ mrb_time_eq(mrb_state *mrb, mrb_value self)
mrb_bool eq_p;
mrb_get_args(mrb, "o", &other);
- tm1 = DATA_CHECK_GET_PTR(mrb, self, &mrb_time_type, struct mrb_time);
+ tm1 = DATA_GET_PTR(mrb, self, &mrb_time_type, struct mrb_time);
tm2 = DATA_CHECK_GET_PTR(mrb, other, &mrb_time_type, struct mrb_time);
eq_p = tm1 && tm2 && tm1->sec == tm2->sec && tm1->usec == tm2->usec;
@@ -391,7 +415,7 @@ mrb_time_cmp(mrb_state *mrb, mrb_value self)
struct mrb_time *tm1, *tm2;
mrb_get_args(mrb, "o", &other);
- tm1 = DATA_CHECK_GET_PTR(mrb, self, &mrb_time_type, struct mrb_time);
+ tm1 = DATA_GET_PTR(mrb, self, &mrb_time_type, struct mrb_time);
tm2 = DATA_CHECK_GET_PTR(mrb, other, &mrb_time_type, struct mrb_time);
if (!tm1 || !tm2) return mrb_nil_value();
if (tm1->sec > tm2->sec) {
@@ -417,7 +441,7 @@ mrb_time_plus(mrb_state *mrb, mrb_value self)
struct mrb_time *tm;
mrb_get_args(mrb, "f", &f);
- tm = DATA_GET_PTR(mrb, self, &mrb_time_type, struct mrb_time);
+ tm = time_get_ptr(mrb, self);
return mrb_time_make(mrb, mrb_obj_class(mrb, self), (double)tm->sec+f, (double)tm->usec, tm->timezone);
}
@@ -429,8 +453,7 @@ mrb_time_minus(mrb_state *mrb, mrb_value self)
struct mrb_time *tm, *tm2;
mrb_get_args(mrb, "o", &other);
- tm = DATA_GET_PTR(mrb, self, &mrb_time_type, struct mrb_time);
-
+ tm = time_get_ptr(mrb, self);
tm2 = DATA_CHECK_GET_PTR(mrb, other, &mrb_time_type, struct mrb_time);
if (tm2) {
f = (mrb_float)(tm->sec - tm2->sec)
@@ -450,7 +473,7 @@ mrb_time_wday(mrb_state *mrb, mrb_value self)
{
struct mrb_time *tm;
- tm = DATA_GET_PTR(mrb, self, &mrb_time_type, struct mrb_time);
+ tm = time_get_ptr(mrb, self);
return mrb_fixnum_value(tm->datetime.tm_wday);
}
@@ -461,7 +484,7 @@ mrb_time_yday(mrb_state *mrb, mrb_value self)
{
struct mrb_time *tm;
- tm = DATA_GET_PTR(mrb, self, &mrb_time_type, struct mrb_time);
+ tm = time_get_ptr(mrb, self);
return mrb_fixnum_value(tm->datetime.tm_yday + 1);
}
@@ -472,7 +495,7 @@ mrb_time_year(mrb_state *mrb, mrb_value self)
{
struct mrb_time *tm;
- tm = DATA_GET_PTR(mrb, self, &mrb_time_type, struct mrb_time);
+ tm = time_get_ptr(mrb, self);
return mrb_fixnum_value(tm->datetime.tm_year + 1900);
}
@@ -483,7 +506,7 @@ mrb_time_zone(mrb_state *mrb, mrb_value self)
{
struct mrb_time *tm;
- tm = DATA_GET_PTR(mrb, self, &mrb_time_type, struct mrb_time);
+ tm = time_get_ptr(mrb, self);
if (tm->timezone <= MRB_TIMEZONE_NONE) return mrb_nil_value();
if (tm->timezone >= MRB_TIMEZONE_LAST) return mrb_nil_value();
return mrb_str_new_static(mrb,
@@ -501,7 +524,7 @@ mrb_time_asctime(mrb_state *mrb, mrb_value self)
char buf[256];
int len;
- tm = DATA_GET_PTR(mrb, self, &mrb_time_type, struct mrb_time);
+ tm = time_get_ptr(mrb, self);
d = &tm->datetime;
len = snprintf(buf, sizeof(buf), "%s %s %02d %02d:%02d:%02d %s%d",
wday_names[d->tm_wday], mon_names[d->tm_mon], d->tm_mday,
@@ -518,8 +541,7 @@ mrb_time_day(mrb_state *mrb, mrb_value self)
{
struct mrb_time *tm;
- tm = DATA_GET_PTR(mrb, self, &mrb_time_type, struct mrb_time);
- if (!tm) return mrb_nil_value();
+ tm = time_get_ptr(mrb, self);
return mrb_fixnum_value(tm->datetime.tm_mday);
}
@@ -531,7 +553,7 @@ mrb_time_dst_p(mrb_state *mrb, mrb_value self)
{
struct mrb_time *tm;
- tm = DATA_GET_PTR(mrb, self, &mrb_time_type, struct mrb_time);
+ tm = time_get_ptr(mrb, self);
return mrb_bool_value(tm->datetime.tm_isdst);
}
@@ -543,11 +565,11 @@ mrb_time_getutc(mrb_state *mrb, mrb_value self)
{
struct mrb_time *tm, *tm2;
- tm = DATA_GET_PTR(mrb, self, &mrb_time_type, struct mrb_time);
+ tm = time_get_ptr(mrb, self);
tm2 = (struct mrb_time *)mrb_malloc(mrb, sizeof(*tm));
*tm2 = *tm;
tm2->timezone = MRB_TIMEZONE_UTC;
- mrb_time_update_datetime(tm2);
+ time_update_datetime(mrb, tm2);
return mrb_time_wrap(mrb, mrb_obj_class(mrb, self), tm2);
}
@@ -558,11 +580,11 @@ mrb_time_getlocal(mrb_state *mrb, mrb_value self)
{
struct mrb_time *tm, *tm2;
- tm = DATA_GET_PTR(mrb, self, &mrb_time_type, struct mrb_time);
+ tm = time_get_ptr(mrb, self);
tm2 = (struct mrb_time *)mrb_malloc(mrb, sizeof(*tm));
*tm2 = *tm;
tm2->timezone = MRB_TIMEZONE_LOCAL;
- mrb_time_update_datetime(tm2);
+ time_update_datetime(mrb, tm2);
return mrb_time_wrap(mrb, mrb_obj_class(mrb, self), tm2);
}
@@ -573,7 +595,7 @@ mrb_time_hour(mrb_state *mrb, mrb_value self)
{
struct mrb_time *tm;
- tm = DATA_GET_PTR(mrb, self, &mrb_time_type, struct mrb_time);
+ tm = time_get_ptr(mrb, self);
return mrb_fixnum_value(tm->datetime.tm_hour);
}
@@ -587,14 +609,14 @@ mrb_time_initialize(mrb_state *mrb, mrb_value self)
int n;
struct mrb_time *tm;
+ n = mrb_get_args(mrb, "|iiiiiii",
+ &ayear, &amonth, &aday, &ahour, &amin, &asec, &ausec);
tm = (struct mrb_time*)DATA_PTR(self);
if (tm) {
mrb_free(mrb, tm);
}
mrb_data_init(self, NULL, &mrb_time_type);
- n = mrb_get_args(mrb, "|iiiiiii",
- &ayear, &amonth, &aday, &ahour, &amin, &asec, &ausec);
if (n == 0) {
tm = current_mrb_time(mrb);
}
@@ -611,16 +633,23 @@ static mrb_value
mrb_time_initialize_copy(mrb_state *mrb, mrb_value copy)
{
mrb_value src;
+ struct mrb_time *t1, *t2;
mrb_get_args(mrb, "o", &src);
if (mrb_obj_equal(mrb, copy, src)) return copy;
if (!mrb_obj_is_instance_of(mrb, src, mrb_obj_class(mrb, copy))) {
mrb_raise(mrb, E_TYPE_ERROR, "wrong argument class");
}
- if (!DATA_PTR(copy)) {
- mrb_data_init(copy, mrb_malloc(mrb, sizeof(struct mrb_time)), &mrb_time_type);
+ t1 = (struct mrb_time *)DATA_PTR(copy);
+ t2 = (struct mrb_time *)DATA_PTR(src);
+ if (!t2) {
+ mrb_raise(mrb, E_ARGUMENT_ERROR, "uninitialized time");
+ }
+ if (!t1) {
+ t1 = (struct mrb_time *)mrb_malloc(mrb, sizeof(struct mrb_time));
+ mrb_data_init(copy, t1, &mrb_time_type);
}
- *(struct mrb_time *)DATA_PTR(copy) = *(struct mrb_time *)DATA_PTR(src);
+ *t1 = *t2;
return copy;
}
@@ -631,9 +660,9 @@ mrb_time_localtime(mrb_state *mrb, mrb_value self)
{
struct mrb_time *tm;
- tm = DATA_GET_PTR(mrb, self, &mrb_time_type, struct mrb_time);
+ tm = time_get_ptr(mrb, self);
tm->timezone = MRB_TIMEZONE_LOCAL;
- mrb_time_update_datetime(tm);
+ time_update_datetime(mrb, tm);
return self;
}
@@ -644,7 +673,7 @@ mrb_time_mday(mrb_state *mrb, mrb_value self)
{
struct mrb_time *tm;
- tm = DATA_GET_PTR(mrb, self, &mrb_time_type, struct mrb_time);
+ tm = time_get_ptr(mrb, self);
return mrb_fixnum_value(tm->datetime.tm_mday);
}
@@ -655,7 +684,7 @@ mrb_time_min(mrb_state *mrb, mrb_value self)
{
struct mrb_time *tm;
- tm = DATA_GET_PTR(mrb, self, &mrb_time_type, struct mrb_time);
+ tm = time_get_ptr(mrb, self);
return mrb_fixnum_value(tm->datetime.tm_min);
}
@@ -666,7 +695,7 @@ mrb_time_mon(mrb_state *mrb, mrb_value self)
{
struct mrb_time *tm;
- tm = DATA_GET_PTR(mrb, self, &mrb_time_type, struct mrb_time);
+ tm = time_get_ptr(mrb, self);
return mrb_fixnum_value(tm->datetime.tm_mon + 1);
}
@@ -677,7 +706,7 @@ mrb_time_sec(mrb_state *mrb, mrb_value self)
{
struct mrb_time *tm;
- tm = DATA_GET_PTR(mrb, self, &mrb_time_type, struct mrb_time);
+ tm = time_get_ptr(mrb, self);
return mrb_fixnum_value(tm->datetime.tm_sec);
}
@@ -689,7 +718,7 @@ mrb_time_to_f(mrb_state *mrb, mrb_value self)
{
struct mrb_time *tm;
- tm = DATA_GET_PTR(mrb, self, &mrb_time_type, struct mrb_time);
+ tm = time_get_ptr(mrb, self);
return mrb_float_value(mrb, (mrb_float)tm->sec + (mrb_float)tm->usec/1.0e6);
}
@@ -700,7 +729,7 @@ mrb_time_to_i(mrb_state *mrb, mrb_value self)
{
struct mrb_time *tm;
- tm = DATA_GET_PTR(mrb, self, &mrb_time_type, struct mrb_time);
+ tm = time_get_ptr(mrb, self);
if (tm->sec > MRB_INT_MAX || tm->sec < MRB_INT_MIN) {
return mrb_float_value(mrb, (mrb_float)tm->sec);
}
@@ -714,7 +743,7 @@ mrb_time_usec(mrb_state *mrb, mrb_value self)
{
struct mrb_time *tm;
- tm = DATA_GET_PTR(mrb, self, &mrb_time_type, struct mrb_time);
+ tm = time_get_ptr(mrb, self);
if (tm->usec > MRB_INT_MAX || tm->usec < MRB_INT_MIN) {
return mrb_float_value(mrb, (mrb_float)tm->usec);
}
@@ -728,9 +757,9 @@ mrb_time_utc(mrb_state *mrb, mrb_value self)
{
struct mrb_time *tm;
- tm = DATA_GET_PTR(mrb, self, &mrb_time_type, struct mrb_time);
+ tm = time_get_ptr(mrb, self);
tm->timezone = MRB_TIMEZONE_UTC;
- mrb_time_update_datetime(tm);
+ time_update_datetime(mrb, tm);
return self;
}
@@ -741,7 +770,7 @@ mrb_time_utc_p(mrb_state *mrb, mrb_value self)
{
struct mrb_time *tm;
- tm = DATA_GET_PTR(mrb, self, &mrb_time_type, struct mrb_time);
+ tm = time_get_ptr(mrb, self);
return mrb_bool_value(tm->timezone == MRB_TIMEZONE_UTC);
}
diff --git a/mrblib/numeric.rb b/mrblib/numeric.rb
index 6e4c5027f..975ad973f 100644
--- a/mrblib/numeric.rb
+++ b/mrblib/numeric.rb
@@ -100,11 +100,18 @@ module Integral
# Calls the given block from +self+ to +num+
# incremented by +step+ (default 1).
#
- def step(num, step = 1, &block)
+ def step(num=nil, step=1, &block)
raise ArgumentError, "step can't be 0" if step == 0
return to_enum(:step, num, step) unless block_given?
i = if num.kind_of? Float then self.to_f else self end
+ if num == nil
+ while true
+ block.call(i)
+ i+=step
+ end
+ return self
+ end
if step > 0
while i <= num
block.call(i)
@@ -160,35 +167,7 @@ end
#
# ISO 15.2.9
class Float
- include Integral
# mruby special - since mruby integers may be upgraded to floats,
# floats should be compatible to integers.
- def >> other
- n = self.to_i
- other = other.to_i
- if other < 0
- n << -other
- else
- other.times { n /= 2 }
- if n.abs < 1
- if n >= 0
- 0
- else
- -1
- end
- else
- n.to_i
- end
- end
- end
- def << other
- n = self.to_i
- other = other.to_i
- if other < 0
- n >> -other
- else
- other.times { n *= 2 }
- n
- end
- end
+ include Integral
end
diff --git a/mrblib/string.rb b/mrblib/string.rb
index 37441ec98..aa2ca9973 100644
--- a/mrblib/string.rb
+++ b/mrblib/string.rb
@@ -159,16 +159,24 @@ class String
anum = args.size
if anum == 2
pos, value = args
- if pos.kind_of? String
+ case pos
+ when String
posnum = self.index(pos)
if posnum
b = self[0, posnum.to_i]
a = self[(posnum + pos.length)..-1]
self.replace([b, value, a].join(''))
- return value
else
raise IndexError, "string not matched"
end
+ when Range
+ head = pos.begin
+ tail = pos.end
+ tail += self.length if tail < 0
+ unless pos.exclude_end?
+ tail += 1
+ end
+ return self[head, tail-head]=value
else
pos += self.length if pos < 0
if pos < 0 || pos > self.length
@@ -177,8 +185,8 @@ class String
b = self[0, pos.to_i]
a = self[pos + 1..-1]
self.replace([b, value, a].join(''))
- return value
end
+ return value
elsif anum == 3
pos, len, value = args
pos += self.length if pos < 0
diff --git a/src/array.c b/src/array.c
index df953832b..c7499ab36 100644
--- a/src/array.c
+++ b/src/array.c
@@ -16,28 +16,16 @@
#define ARY_C_MAX_SIZE (SIZE_MAX / sizeof(mrb_value))
#define ARY_MAX_SIZE ((ARY_C_MAX_SIZE < (size_t)MRB_INT_MAX) ? (mrb_int)ARY_C_MAX_SIZE : MRB_INT_MAX-1)
-static inline mrb_value
-ary_elt(mrb_value ary, mrb_int offset)
-{
- if (offset < 0 || RARRAY_LEN(ary) <= offset) {
- return mrb_nil_value();
- }
- return RARRAY_PTR(ary)[offset];
-}
-
static struct RArray*
ary_new_capa(mrb_state *mrb, mrb_int capa)
{
struct RArray *a;
- mrb_int blen;
+ size_t blen;
if (capa > ARY_MAX_SIZE) {
mrb_raise(mrb, E_ARGUMENT_ERROR, "array size too big");
}
blen = capa * sizeof(mrb_value);
- if (blen < capa) {
- mrb_raise(mrb, E_ARGUMENT_ERROR, "array size too big");
- }
a = (struct RArray*)mrb_obj_alloc(mrb, MRB_TT_ARRAY, mrb->array_class);
a->ptr = (mrb_value *)mrb_malloc(mrb, blen);
@@ -120,6 +108,10 @@ ary_fill_with_nil(mrb_value *ptr, mrb_int size)
static void
ary_modify(mrb_state *mrb, struct RArray *a)
{
+ if (MRB_FROZEN_P(a)) {
+ mrb_raise(mrb, E_RUNTIME_ERROR, "can't modify frozen array");
+ }
+
if (ARY_SHARED_P(a)) {
mrb_shared_array *shared = a->aux.shared;
@@ -130,7 +122,7 @@ ary_modify(mrb_state *mrb, struct RArray *a)
}
else {
mrb_value *ptr, *p;
- mrb_int len;
+ size_t len;
p = a->ptr;
len = a->len * sizeof(mrb_value);
@@ -173,11 +165,12 @@ ary_make_shared(mrb_state *mrb, struct RArray *a)
}
static void
-ary_expand_capa(mrb_state *mrb, struct RArray *a, mrb_int len)
+ary_expand_capa(mrb_state *mrb, struct RArray *a, size_t len)
{
- mrb_int capa = a->aux.capa;
+ size_t capa = a->aux.capa;
if (len > ARY_MAX_SIZE) {
+ size_error:
mrb_raise(mrb, E_ARGUMENT_ERROR, "array size too big");
}
@@ -185,12 +178,18 @@ ary_expand_capa(mrb_state *mrb, struct RArray *a, mrb_int len)
capa = ARY_DEFAULT_LEN;
}
while (capa < len) {
- capa *= 2;
+ if (capa <= ARY_MAX_SIZE / 2) {
+ capa *= 2;
+ }
+ else {
+ capa = len;
+ }
+ }
+ if (capa < len || capa > ARY_MAX_SIZE) {
+ goto size_error;
}
- if (capa > ARY_MAX_SIZE) capa = ARY_MAX_SIZE; /* len <= capa <= ARY_MAX_SIZE */
-
- if (capa > a->aux.capa) {
+ if (capa > (size_t)a->aux.capa) {
mrb_value *expanded_ptr = (mrb_value *)mrb_realloc(mrb, a->ptr, sizeof(mrb_value)*capa);
a->aux.capa = capa;
@@ -254,13 +253,20 @@ mrb_ary_s_create(mrb_state *mrb, mrb_value self)
}
static void
-ary_concat(mrb_state *mrb, struct RArray *a, mrb_value *ptr, mrb_int blen)
+ary_concat(mrb_state *mrb, struct RArray *a, struct RArray *a2)
{
- mrb_int len = a->len + blen;
+ mrb_int len;
+
+ if (a2->len > ARY_MAX_SIZE - a->len) {
+ mrb_raise(mrb, E_ARGUMENT_ERROR, "array size too big");
+ }
+ len = a->len + a2->len;
ary_modify(mrb, a);
- if (a->aux.capa < len) ary_expand_capa(mrb, a, len);
- array_copy(a->ptr+a->len, ptr, blen);
+ if (a->aux.capa < len) {
+ ary_expand_capa(mrb, a, len);
+ }
+ array_copy(a->ptr+a->len, a2->ptr, a2->len);
mrb_write_barrier(mrb, (struct RBasic*)a);
a->len = len;
}
@@ -270,17 +276,16 @@ mrb_ary_concat(mrb_state *mrb, mrb_value self, mrb_value other)
{
struct RArray *a2 = mrb_ary_ptr(other);
- ary_concat(mrb, mrb_ary_ptr(self), a2->ptr, a2->len);
+ ary_concat(mrb, mrb_ary_ptr(self), a2);
}
static mrb_value
mrb_ary_concat_m(mrb_state *mrb, mrb_value self)
{
- mrb_value *ptr;
- mrb_int blen;
+ mrb_value ary;
- mrb_get_args(mrb, "a", &ptr, &blen);
- ary_concat(mrb, mrb_ary_ptr(self), ptr, blen);
+ mrb_get_args(mrb, "A", &ary);
+ mrb_ary_concat(mrb, self, ary);
return self;
}
@@ -318,9 +323,12 @@ ary_replace(mrb_state *mrb, struct RArray *a, mrb_value *argv, mrb_int len)
MRB_API void
mrb_ary_replace(mrb_state *mrb, mrb_value self, mrb_value other)
{
+ struct RArray *a1 = mrb_ary_ptr(self);
struct RArray *a2 = mrb_ary_ptr(other);
- ary_replace(mrb, mrb_ary_ptr(self), a2->ptr, a2->len);
+ if (a1 != a2) {
+ ary_replace(mrb, a1, a2->ptr, a2->len);
+ }
}
static mrb_value
@@ -432,6 +440,7 @@ mrb_ary_pop(mrb_state *mrb, mrb_value ary)
{
struct RArray *a = mrb_ary_ptr(ary);
+ ary_modify(mrb, a);
if (a->len == 0) return mrb_nil_value();
return a->ptr[--a->len];
}
@@ -444,6 +453,7 @@ mrb_ary_shift(mrb_state *mrb, mrb_value self)
struct RArray *a = mrb_ary_ptr(self);
mrb_value val;
+ ary_modify(mrb, a);
if (a->len == 0) return mrb_nil_value();
if (ARY_SHARED_P(a)) {
L_SHIFT:
@@ -506,6 +516,9 @@ mrb_ary_unshift_m(mrb_state *mrb, mrb_value self)
mrb_int len;
mrb_get_args(mrb, "*", &vals, &len);
+ if (len > ARY_MAX_SIZE - a->len) {
+ mrb_raise(mrb, E_ARGUMENT_ERROR, "array size too big");
+ }
if (ARY_SHARED_P(a)
&& a->aux.shared->refcnt == 1 /* shared only referenced from this array */
&& a->ptr - a->aux.shared->ptr >= len) /* there's room for unshifted item */ {
@@ -563,13 +576,22 @@ mrb_ary_set(mrb_state *mrb, mrb_value ary, mrb_int n, mrb_value val)
mrb_field_write_barrier_value(mrb, (struct RBasic*)a, val);
}
+static struct RArray*
+ary_dup(mrb_state *mrb, struct RArray *a)
+{
+ struct RArray *d = ary_new_capa(mrb, a->len);
+
+ ary_replace(mrb, d, a->ptr, a->len);
+ return d;
+}
+
MRB_API mrb_value
mrb_ary_splice(mrb_state *mrb, mrb_value ary, mrb_int head, mrb_int len, mrb_value rpl)
{
struct RArray *a = mrb_ary_ptr(ary);
- mrb_int tail, size;
const mrb_value *argv;
- mrb_int i, argc;
+ mrb_int argc;
+ size_t tail;
ary_modify(mrb, a);
@@ -583,40 +605,59 @@ mrb_ary_splice(mrb_state *mrb, mrb_value ary, mrb_int head, mrb_int len, mrb_val
mrb_raise(mrb, E_INDEX_ERROR, "index is out of array");
}
}
- if (a->len < len || a->len < head + len) {
+ tail = head + len;
+ if (a->len < len || (size_t)a->len < tail) {
len = a->len - head;
}
- tail = head + len;
/* size check */
if (mrb_array_p(rpl)) {
argc = RARRAY_LEN(rpl);
argv = RARRAY_PTR(rpl);
+ if (argv == a->ptr) {
+ struct RArray *r = ary_dup(mrb, a);
+ argv = r->ptr;
+ }
}
else {
argc = 1;
argv = &rpl;
}
- size = head + argc;
-
- if (tail < a->len) size += a->len - tail;
- if (size > a->aux.capa)
- ary_expand_capa(mrb, a, size);
-
- if (head > a->len) {
+ if (head >= a->len) {
+ if (head > ARY_MAX_SIZE - argc) {
+ mrb_raisef(mrb, E_INDEX_ERROR, "index %S too big", mrb_fixnum_value(head));
+ }
+ len = head + argc;
+ if (len > a->aux.capa) {
+ ary_expand_capa(mrb, a, head + argc);
+ }
ary_fill_with_nil(a->ptr + a->len, head - a->len);
+ if (argc > 0) {
+ array_copy(a->ptr + head, argv, argc);
+ }
+ a->len = len;
}
- else if (head < a->len) {
- value_move(a->ptr + head + argc, a->ptr + tail, a->len - tail);
- }
-
- for (i = 0; i < argc; i++) {
- *(a->ptr + head + i) = *(argv + i);
- mrb_field_write_barrier_value(mrb, (struct RBasic*)a, argv[i]);
- }
+ else {
+ mrb_int alen;
- a->len = size;
+ if (a->len - len > ARY_MAX_SIZE - argc) {
+ mrb_raisef(mrb, E_INDEX_ERROR, "index %S too big", mrb_fixnum_value(a->len + argc - len));
+ }
+ alen = a->len + argc - len;
+ if (alen > a->aux.capa) {
+ ary_expand_capa(mrb, a, alen);
+ }
+ if (len != argc) {
+ tail = head + len;
+ value_move(a->ptr + head + argc, a->ptr + tail, a->len - tail);
+ a->len = alen;
+ }
+ if (argc > 0) {
+ value_move(a->ptr + head, argv, argc);
+ }
+ }
+ mrb_write_barrier(mrb, (struct RBasic*)a);
return ary;
}
@@ -702,7 +743,7 @@ mrb_ary_aget(mrb_state *mrb, mrb_value self)
switch (mrb_type(index)) {
/* a[n..m] */
case MRB_TT_RANGE:
- if (mrb_range_beg_len(mrb, index, &i, &len, a->len)) {
+ if (mrb_range_beg_len(mrb, index, &i, &len, a->len, TRUE) == 1) {
return ary_subseq(mrb, a, i, len);
}
else {
@@ -766,20 +807,18 @@ mrb_ary_aset(mrb_state *mrb, mrb_value self)
mrb_value v1, v2, v3;
mrb_int i, len;
+ mrb_ary_modify(mrb, mrb_ary_ptr(self));
if (mrb_get_args(mrb, "oo|o", &v1, &v2, &v3) == 2) {
- switch (mrb_type(v1)) {
/* a[n..m] = v */
- case MRB_TT_RANGE:
- if (mrb_range_beg_len(mrb, v1, &i, &len, RARRAY_LEN(self))) {
- mrb_ary_splice(mrb, self, i, len, v2);
- }
+ switch (mrb_range_beg_len(mrb, v1, &i, &len, RARRAY_LEN(self), FALSE)) {
+ case 0: /* not range */
+ mrb_ary_set(mrb, self, aget_index(mrb, v1), v2);
break;
- /* a[n] = v */
- case MRB_TT_FIXNUM:
- mrb_ary_set(mrb, self, mrb_fixnum(v1), v2);
+ case 1: /* range */
+ mrb_ary_splice(mrb, self, i, len, v2);
break;
- default:
- mrb_ary_set(mrb, self, aget_index(mrb, v1), v2);
+ case 2: /* out of range */
+ mrb_raisef(mrb, E_RANGE_ERROR, "%S out of range", v1);
break;
}
return v2;
@@ -877,13 +916,16 @@ static mrb_value
mrb_ary_rindex_m(mrb_state *mrb, mrb_value self)
{
mrb_value obj;
- mrb_int i;
+ mrb_int i, len;
mrb_get_args(mrb, "o", &obj);
for (i = RARRAY_LEN(self) - 1; i >= 0; i--) {
if (mrb_equal(mrb, RARRAY_PTR(self)[i], obj)) {
return mrb_fixnum_value(i);
}
+ if (i > (len = RARRAY_LEN(self))) {
+ i = len;
+ }
}
return mrb_nil_value();
}
@@ -915,6 +957,7 @@ mrb_ary_splat(mrb_state *mrb, mrb_value v)
recv_class,
mrb_obj_value(mrb_obj_class(mrb, a))
);
+ /* not reached */
return mrb_undef_value();
}
}
@@ -932,6 +975,7 @@ mrb_ary_clear(mrb_state *mrb, mrb_value self)
{
struct RArray *a = mrb_ary_ptr(self);
+ ary_modify(mrb, a);
if (ARY_SHARED_P(a)) {
mrb_ary_decref(mrb, a->aux.shared);
ARY_UNSET_SHARED_FLAG(a);
diff --git a/src/backtrace.c b/src/backtrace.c
index 11082b705..529b0b1c9 100644
--- a/src/backtrace.c
+++ b/src/backtrace.c
@@ -118,6 +118,7 @@ each_backtrace(mrb_state *mrb, mrb_int ciidx, mrb_code *pc0, each_backtrace_func
if (MRB_PROC_CFUNC_P(ci->proc)) continue;
irep = ci->proc->body.irep;
+ if (!irep) continue;
if (mrb->c->cibase[i].err) {
pc = mrb->c->cibase[i].err;
@@ -160,7 +161,7 @@ static void
output_backtrace_i(mrb_state *mrb, struct backtrace_location_raw *loc_raw, void *data)
{
struct backtrace_location loc;
- struct output_backtrace_args *args = data;
+ struct output_backtrace_args *args = (struct output_backtrace_args *)data;
loc.i = loc_raw->i;
loc.lineno = loc_raw->lineno;
@@ -219,7 +220,9 @@ print_backtrace(mrb_state *mrb, mrb_value backtrace)
for (i = 0; i < n; i++) {
mrb_value entry = RARRAY_PTR(backtrace)[i];
- fprintf(stream, "\t[%d] %.*s\n", i, (int)RSTRING_LEN(entry), RSTRING_PTR(entry));
+ if (mrb_string_p(entry)) {
+ fprintf(stream, "\t[%d] %.*s\n", i, (int)RSTRING_LEN(entry), RSTRING_PTR(entry));
+ }
}
}
@@ -260,7 +263,7 @@ mrb_print_backtrace(mrb_state *mrb)
{
mrb_value backtrace;
- if (!mrb->exc || mrb_obj_is_kind_of(mrb, mrb_obj_value(mrb->exc), E_SYSSTACK_ERROR)) {
+ if (!mrb->exc) {
return;
}
@@ -338,7 +341,7 @@ save_backtrace_i(mrb_state *mrb,
else {
new_n_allocated = mrb->backtrace.n_allocated * 2;
}
- mrb->backtrace.entries =
+ mrb->backtrace.entries = (mrb_backtrace_entry *)
mrb_realloc(mrb,
mrb->backtrace.entries,
sizeof(mrb_backtrace_entry) * new_n_allocated);
diff --git a/src/class.c b/src/class.c
index ed2e5d5ba..0922b3cff 100644
--- a/src/class.c
+++ b/src/class.c
@@ -14,6 +14,7 @@
#include <mruby/variable.h>
#include <mruby/error.h>
#include <mruby/data.h>
+#include <mruby/istruct.h>
KHASH_DEFINE(mt, mrb_sym, struct RProc*, TRUE, kh_int_hash_func, kh_int_hash_equal)
@@ -174,6 +175,14 @@ MRB_API struct RClass*
mrb_vm_define_module(mrb_state *mrb, mrb_value outer, mrb_sym id)
{
check_if_class_or_module(mrb, outer);
+ if (mrb_const_defined_at(mrb, outer, id)) {
+ mrb_value old = mrb_const_get(mrb, outer, id);
+
+ if (mrb_type(old) != MRB_TT_MODULE) {
+ mrb_raisef(mrb, E_TYPE_ERROR, "%S is not a module", mrb_inspect(mrb, old));
+ }
+ return mrb_class_ptr(old);
+ }
return define_module(mrb, id, mrb_class_ptr(outer));
}
@@ -231,12 +240,22 @@ mrb_define_class(mrb_state *mrb, const char *name, struct RClass *super)
return mrb_define_class_id(mrb, mrb_intern_cstr(mrb, name), super);
}
+static mrb_value mrb_bob_init(mrb_state *mrb, mrb_value cv);
+
static void
mrb_class_inherited(mrb_state *mrb, struct RClass *super, struct RClass *klass)
{
+ mrb_value s;
+ mrb_sym mid;
+
if (!super)
super = mrb->object_class;
- mrb_funcall(mrb, mrb_obj_value(super), "inherited", 1, mrb_obj_value(klass));
+ s = mrb_obj_value(super);
+ mid = mrb_intern_lit(mrb, "inherited");
+ if (!mrb_func_basic_p(mrb, s, mid, mrb_bob_init)) {
+ mrb_value c = mrb_obj_value(klass);
+ mrb_funcall_argv(mrb, mrb_obj_value(super), mid, 1, &c);
+ }
}
MRB_API struct RClass*
@@ -255,6 +274,21 @@ mrb_vm_define_class(mrb_state *mrb, mrb_value outer, mrb_value super, mrb_sym id
s = 0;
}
check_if_class_or_module(mrb, outer);
+ if (mrb_const_defined_at(mrb, outer, id)) {
+ mrb_value old = mrb_const_get(mrb, outer, id);
+
+ if (mrb_type(old) != MRB_TT_CLASS) {
+ mrb_raisef(mrb, E_TYPE_ERROR, "%S is not a class", mrb_inspect(mrb, old));
+ }
+ c = mrb_class_ptr(old);
+ if (s) {
+ /* check super class */
+ if (mrb_class_real(c->super) != s) {
+ mrb_raisef(mrb, E_TYPE_ERROR, "superclass mismatch for class %S", old);
+ }
+ }
+ return c;
+ }
c = define_class(mrb, id, s, mrb_class_ptr(outer));
mrb_class_inherited(mrb, mrb_class_real(c->super), c);
@@ -272,7 +306,7 @@ mrb_class_defined(mrb_state *mrb, const char *name)
}
MRB_API mrb_bool
-mrb_class_under_defined(mrb_state *mrb, struct RClass *outer, const char *name)
+mrb_class_defined_under(mrb_state *mrb, struct RClass *outer, const char *name)
{
mrb_value sym = mrb_check_intern_cstr(mrb, name);
if (mrb_nil_p(sym)) {
@@ -294,6 +328,20 @@ mrb_class_get(mrb_state *mrb, const char *name)
}
MRB_API struct RClass *
+mrb_exc_get(mrb_state *mrb, const char *name)
+{
+ struct RClass *exc = mrb_class_get_under(mrb, mrb->object_class, name);
+ struct RClass *e = exc;
+
+ while (e) {
+ if (e == mrb->eException_class)
+ return exc;
+ e = e->super;
+ }
+ return mrb->eException_class;
+}
+
+MRB_API struct RClass *
mrb_module_get_under(mrb_state *mrb, struct RClass *outer, const char *name)
{
return module_from_sym(mrb, outer, mrb_intern_cstr(mrb, name));
@@ -346,6 +394,12 @@ mrb_define_method_raw(mrb_state *mrb, struct RClass *c, mrb_sym mid, struct RPro
MRB_CLASS_ORIGIN(c);
h = c->mt;
+ if (MRB_FROZEN_P(c)) {
+ if (c->tt == MRB_TT_MODULE)
+ mrb_raise(mrb, E_RUNTIME_ERROR, "can't modify frozen module");
+ else
+ mrb_raise(mrb, E_RUNTIME_ERROR, "can't modify frozen class");
+ }
if (!h) h = c->mt = kh_init(mt, mrb);
k = kh_put(mt, mrb, h, mid);
kh_value(h, k) = p;
@@ -468,6 +522,7 @@ to_sym(mrb_state *mrb, mrb_value ss)
b: Boolean [mrb_bool]
n: Symbol [mrb_sym]
d: Data [void*,mrb_data_type const] 2nd argument will be used to check data type so it won't be modified
+ I: Inline struct [void*]
&: Block [mrb_value]
*: rest argument [mrb_value*,mrb_int] Receive the rest of the arguments as an array.
|: optional Next argument of '|' and later are optional.
@@ -679,6 +734,24 @@ mrb_get_args(mrb_state *mrb, const char *format, ...)
}
}
break;
+ case 'I':
+ {
+ void* *p;
+ mrb_value ss;
+
+ p = va_arg(ap, void**);
+ if (i < argc) {
+ ss = ARGV[arg_i];
+ if (mrb_type(ss) != MRB_TT_ISTRUCT)
+ {
+ mrb_raisef(mrb, E_TYPE_ERROR, "%S is not inline struct", ss);
+ }
+ *p = mrb_istruct_ptr(ss);
+ arg_i++;
+ i++;
+ }
+ }
+ break;
case 'f':
{
mrb_float *p;
@@ -852,7 +925,9 @@ boot_defclass(mrb_state *mrb, struct RClass *super)
static void
boot_initmod(mrb_state *mrb, struct RClass *mod)
{
- mod->mt = kh_init(mt, mrb);
+ if (!mod->mt) {
+ mod->mt = kh_init(mt, mrb);
+ }
}
static struct RClass*
@@ -1305,6 +1380,9 @@ mrb_instance_alloc(mrb_state *mrb, mrb_value cv)
mrb_raise(mrb, E_TYPE_ERROR, "can't create instance of singleton class");
if (ttype == 0) ttype = MRB_TT_OBJECT;
+ if (ttype <= MRB_TT_CPTR) {
+ mrb_raisef(mrb, E_TYPE_ERROR, "can't create instance of %S", cv);
+ }
o = (struct RObject*)mrb_obj_alloc(mrb, ttype, c);
return mrb_obj_value(o);
}
@@ -1339,10 +1417,13 @@ MRB_API mrb_value
mrb_obj_new(mrb_state *mrb, struct RClass *c, mrb_int argc, const mrb_value *argv)
{
mrb_value obj;
+ mrb_sym mid;
obj = mrb_instance_alloc(mrb, mrb_obj_value(c));
- mrb_funcall_argv(mrb, obj, mrb_intern_lit(mrb, "initialize"), argc, argv);
-
+ mid = mrb_intern_lit(mrb, "initialize");
+ if (!mrb_func_basic_p(mrb, obj, mid, mrb_bob_init)) {
+ mrb_funcall_argv(mrb, obj, mid, argc, argv);
+ }
return obj;
}
@@ -1364,13 +1445,17 @@ mrb_class_new_class(mrb_state *mrb, mrb_value cv)
mrb_int n;
mrb_value super, blk;
mrb_value new_class;
+ mrb_sym mid;
n = mrb_get_args(mrb, "|C&", &super, &blk);
if (n == 0) {
super = mrb_obj_value(mrb->object_class);
}
new_class = mrb_obj_value(mrb_class_new(mrb, mrb_class_ptr(super)));
- mrb_funcall_with_block(mrb, new_class, mrb_intern_lit(mrb, "initialize"), n, &super, blk);
+ mid = mrb_intern_lit(mrb, "initialize");
+ if (!mrb_func_basic_p(mrb, new_class, mid, mrb_bob_init)) {
+ mrb_funcall_with_block(mrb, new_class, mid, n, &super, blk);
+ }
mrb_class_inherited(mrb, mrb_class_ptr(super), mrb_class_ptr(new_class));
return new_class;
}
@@ -1401,75 +1486,53 @@ mrb_bob_not(mrb_state *mrb, mrb_value cv)
return mrb_bool_value(!mrb_test(cv));
}
-void
-mrb_method_missing(mrb_state *mrb, mrb_sym name, mrb_value self, mrb_value args)
-{
- mrb_sym inspect;
- mrb_value repr;
-
- inspect = mrb_intern_lit(mrb, "inspect");
- if (mrb->c->ci > mrb->c->cibase && mrb->c->ci[-1].mid == inspect) {
- /* method missing in inspect; avoid recursion */
- repr = mrb_any_to_s(mrb, self);
- }
- else if (mrb_respond_to(mrb, self, inspect) && mrb->c->ci - mrb->c->cibase < 64) {
- repr = mrb_funcall_argv(mrb, self, inspect, 0, 0);
- if (mrb_string_p(repr) && RSTRING_LEN(repr) > 64) {
- repr = mrb_any_to_s(mrb, self);
- }
- }
- else {
- repr = mrb_any_to_s(mrb, self);
- }
-
- mrb_no_method_error(mrb, name, args, "undefined method '%S' for %S",
- mrb_sym2str(mrb, name), repr);
-}
-
-/* 15.3.1.3.30 */
+/* 15.3.1.3.1 */
+/* 15.3.1.3.10 */
+/* 15.3.1.3.11 */
/*
* call-seq:
- * obj.method_missing(symbol [, *args] ) -> result
+ * obj == other -> true or false
+ * obj.equal?(other) -> true or false
+ * obj.eql?(other) -> true or false
*
- * Invoked by Ruby when <i>obj</i> is sent a message it cannot handle.
- * <i>symbol</i> is the symbol for the method called, and <i>args</i>
- * are any arguments that were passed to it. By default, the interpreter
- * raises an error when this method is called. However, it is possible
- * to override the method to provide more dynamic behavior.
- * If it is decided that a particular method should not be handled, then
- * <i>super</i> should be called, so that ancestors can pick up the
- * missing method.
- * The example below creates
- * a class <code>Roman</code>, which responds to methods with names
- * consisting of roman numerals, returning the corresponding integer
- * values.
+ * Equality---At the <code>Object</code> level, <code>==</code> returns
+ * <code>true</code> only if <i>obj</i> and <i>other</i> are the
+ * same object. Typically, this method is overridden in descendant
+ * classes to provide class-specific meaning.
*
- * class Roman
- * def romanToInt(str)
- * # ...
- * end
- * def method_missing(methId)
- * str = methId.id2name
- * romanToInt(str)
- * end
- * end
+ * Unlike <code>==</code>, the <code>equal?</code> method should never be
+ * overridden by subclasses: it is used to determine object identity
+ * (that is, <code>a.equal?(b)</code> iff <code>a</code> is the same
+ * object as <code>b</code>).
*
- * r = Roman.new
- * r.iv #=> 4
- * r.xxiii #=> 23
- * r.mm #=> 2000
+ * The <code>eql?</code> method returns <code>true</code> if
+ * <i>obj</i> and <i>anObject</i> have the same value. Used by
+ * <code>Hash</code> to test members for equality. For objects of
+ * class <code>Object</code>, <code>eql?</code> is synonymous with
+ * <code>==</code>. Subclasses normally continue this tradition, but
+ * there are exceptions. <code>Numeric</code> types, for example,
+ * perform type conversion across <code>==</code>, but not across
+ * <code>eql?</code>, so:
+ *
+ * 1 == 1.0 #=> true
+ * 1.eql? 1.0 #=> false
*/
+mrb_value
+mrb_obj_equal_m(mrb_state *mrb, mrb_value self)
+{
+ mrb_value arg;
+
+ mrb_get_args(mrb, "o", &arg);
+ return mrb_bool_value(mrb_obj_equal(mrb, self, arg));
+}
+
static mrb_value
-mrb_bob_missing(mrb_state *mrb, mrb_value mod)
+mrb_obj_not_equal_m(mrb_state *mrb, mrb_value self)
{
- mrb_sym name;
- mrb_value *a;
- mrb_int alen;
+ mrb_value arg;
- mrb_get_args(mrb, "n*", &name, &a, &alen);
- mrb_method_missing(mrb, name, mod, mrb_ary_new_from_values(mrb, alen, a));
- /* not reached */
- return mrb_nil_value();
+ mrb_get_args(mrb, "o", &arg);
+ return mrb_bool_value(!mrb_equal(mrb, self, arg));
}
MRB_API mrb_bool
@@ -1518,7 +1581,7 @@ mrb_class_path(mrb_state *mrb, struct RClass *c)
if (sym == 0) {
return mrb_nil_value();
}
- else if (outer && outer != mrb->object_class) {
+ else if (outer && outer != c && outer != mrb->object_class) {
mrb_value base = mrb_class_path(mrb, outer);
path = mrb_str_buf_new(mrb, 0);
if (mrb_nil_p(base)) {
@@ -1537,9 +1600,11 @@ mrb_class_path(mrb_state *mrb, struct RClass *c)
name = mrb_sym2name_len(mrb, sym, &len);
path = mrb_str_new(mrb, name, len);
}
- mrb_obj_iv_set(mrb, (struct RObject*)c, classpath, path);
+ if (!MRB_FROZEN_P(c)) {
+ mrb_obj_iv_set(mrb, (struct RObject*)c, classpath, path);
+ }
}
- return path;
+ return mrb_str_dup(mrb, path);
}
MRB_API struct RClass *
@@ -1771,7 +1836,7 @@ mrb_mod_undef(mrb_state *mrb, mrb_value mod)
mrb_get_args(mrb, "*", &argv, &argc);
while (argc--) {
- undef_method(mrb, c, mrb_symbol(*argv));
+ undef_method(mrb, c, to_sym(mrb, *argv));
argv++;
}
return mrb_nil_value();
@@ -1783,9 +1848,21 @@ mod_define_method(mrb_state *mrb, mrb_value self)
struct RClass *c = mrb_class_ptr(self);
struct RProc *p;
mrb_sym mid;
+ mrb_value proc = mrb_undef_value();
mrb_value blk;
- mrb_get_args(mrb, "n&", &mid, &blk);
+ mrb_get_args(mrb, "n|o&", &mid, &proc, &blk);
+ switch (mrb_type(proc)) {
+ case MRB_TT_PROC:
+ blk = proc;
+ break;
+ case MRB_TT_UNDEF:
+ /* ignored */
+ break;
+ default:
+ mrb_raisef(mrb, E_TYPE_ERROR, "wrong argument type %S (expected Proc)", mrb_obj_value(mrb_obj_class(mrb, proc)));
+ break;
+ }
if (mrb_nil_p(blk)) {
mrb_raise(mrb, E_ARGUMENT_ERROR, "no block given");
}
@@ -2013,7 +2090,7 @@ mrb_mod_remove_method(mrb_state *mrb, mrb_value mod)
mrb_get_args(mrb, "*", &argv, &argc);
while (argc--) {
- remove_method(mrb, mod, mrb_symbol(*argv));
+ remove_method(mrb, mod, to_sym(mrb, *argv));
argv++;
}
return mod;
@@ -2168,6 +2245,11 @@ mrb_mod_module_function(mrb_state *mrb, mrb_value mod)
return mod;
}
+/* implementation of __id__ */
+mrb_value mrb_obj_id_m(mrb_state *mrb, mrb_value self);
+/* implementation of instance_eval */
+mrb_value mrb_obj_instance_eval(mrb_state*, mrb_value);
+
void
mrb_init_class(mrb_state *mrb)
{
@@ -2207,7 +2289,11 @@ mrb_init_class(mrb_state *mrb)
MRB_SET_INSTANCE_TT(cls, MRB_TT_CLASS);
mrb_define_method(mrb, bob, "initialize", mrb_bob_init, MRB_ARGS_NONE());
mrb_define_method(mrb, bob, "!", mrb_bob_not, MRB_ARGS_NONE());
- mrb_define_method(mrb, bob, "method_missing", mrb_bob_missing, MRB_ARGS_ANY()); /* 15.3.1.3.30 */
+ mrb_define_method(mrb, bob, "==", mrb_obj_equal_m, MRB_ARGS_REQ(1)); /* 15.3.1.3.1 */
+ mrb_define_method(mrb, bob, "!=", mrb_obj_not_equal_m, MRB_ARGS_REQ(1));
+ mrb_define_method(mrb, bob, "__id__", mrb_obj_id_m, MRB_ARGS_NONE()); /* 15.3.1.3.3 */
+ mrb_define_method(mrb, bob, "__send__", mrb_f_send, MRB_ARGS_ANY()); /* 15.3.1.3.4 */
+ mrb_define_method(mrb, bob, "instance_eval", mrb_obj_instance_eval, MRB_ARGS_ANY()); /* 15.3.1.3.18 */
mrb_define_class_method(mrb, cls, "new", mrb_class_new_class, MRB_ARGS_OPT(1));
mrb_define_method(mrb, cls, "superclass", mrb_class_superclass, MRB_ARGS_NONE()); /* 15.2.3.3.4 */
@@ -2252,7 +2338,7 @@ mrb_init_class(mrb_state *mrb)
mrb_define_method(mrb, mod, "constants", mrb_mod_constants, MRB_ARGS_OPT(1)); /* 15.2.2.4.24 */
mrb_define_method(mrb, mod, "remove_const", mrb_mod_remove_const, MRB_ARGS_REQ(1)); /* 15.2.2.4.40 */
mrb_define_method(mrb, mod, "const_missing", mrb_mod_const_missing, MRB_ARGS_REQ(1));
- mrb_define_method(mrb, mod, "define_method", mod_define_method, MRB_ARGS_REQ(1));
+ mrb_define_method(mrb, mod, "define_method", mod_define_method, MRB_ARGS_ARG(1,1));
mrb_define_method(mrb, mod, "class_variables", mrb_mod_class_variables, MRB_ARGS_NONE()); /* 15.2.2.4.19 */
mrb_define_method(mrb, mod, "===", mrb_mod_eqq, MRB_ARGS_REQ(1));
mrb_define_class_method(mrb, mod, "constants", mrb_mod_s_constants, MRB_ARGS_ANY()); /* 15.2.2.3.1 */
diff --git a/src/codedump.c b/src/codedump.c
index 4b13d4361..59ba4fdae 100644
--- a/src/codedump.c
+++ b/src/codedump.c
@@ -416,8 +416,7 @@ codedump(mrb_state *mrb, mrb_irep *irep)
print_lv(mrb, irep, c, RA);
break;
case OP_POPERR:
- printf("OP_POPERR\t%d\t\t", GETARG_A(c));
- print_lv(mrb, irep, c, RA);
+ printf("OP_POPERR\t%d\t\t\n", GETARG_A(c));
break;
case OP_EPOP:
printf("OP_EPOP\t%d\n", GETARG_A(c));
diff --git a/src/dump.c b/src/dump.c
index ea1cd5596..45c595d3b 100644
--- a/src/dump.c
+++ b/src/dump.c
@@ -1059,6 +1059,7 @@ mrb_dump_irep_cfunc(mrb_state *mrb, mrb_irep *irep, uint8_t flags, FILE *fp, con
return MRB_DUMP_WRITE_FAULT;
}
if (fprintf(fp,
+ "extern const uint8_t %s[];\n"
"const uint8_t\n"
"#if defined __GNUC__\n"
"__attribute__((aligned(%u)))\n"
@@ -1066,6 +1067,7 @@ mrb_dump_irep_cfunc(mrb_state *mrb, mrb_irep *irep, uint8_t flags, FILE *fp, con
"__declspec(align(%u))\n"
"#endif\n"
"%s[] = {",
+ initname,
(uint16_t)MRB_DUMP_ALIGNMENT, (uint16_t)MRB_DUMP_ALIGNMENT, initname) < 0) {
mrb_free(mrb, bin);
return MRB_DUMP_WRITE_FAULT;
diff --git a/src/error.c b/src/error.c
index 13032b136..3fa18fcb3 100644
--- a/src/error.c
+++ b/src/error.c
@@ -44,8 +44,10 @@ static mrb_value
exc_initialize(mrb_state *mrb, mrb_value exc)
{
mrb_value mesg;
+ mrb_int argc;
+ mrb_value *argv;
- if (mrb_get_args(mrb, "|o", &mesg) == 1) {
+ if (mrb_get_args(mrb, "|o*", &mesg, &argv, &argc) >= 1) {
mrb_iv_set(mrb, exc, mrb_intern_lit(mrb, "mesg"), mesg);
}
return exc;
@@ -200,14 +202,32 @@ exc_get_backtrace(mrb_state *mrb, mrb_value exc)
return backtrace;
}
+static void
+set_backtrace(mrb_state *mrb, mrb_value exc, mrb_value backtrace)
+{
+ if (!mrb_array_p(backtrace)) {
+ type_err:
+ mrb_raise(mrb, E_TYPE_ERROR, "backtrace must be Array of String");
+ }
+ else {
+ const mrb_value *p = RARRAY_PTR(backtrace);
+ const mrb_value *pend = p + RARRAY_LEN(backtrace);
+
+ while (p < pend) {
+ if (!mrb_string_p(*p)) goto type_err;
+ p++;
+ }
+ }
+ mrb_iv_set(mrb, exc, mrb_intern_lit(mrb, "backtrace"), backtrace);
+}
+
static mrb_value
exc_set_backtrace(mrb_state *mrb, mrb_value exc)
{
mrb_value backtrace;
mrb_get_args(mrb, "o", &backtrace);
- mrb_iv_set(mrb, exc, mrb_intern_lit(mrb, "backtrace"), backtrace);
-
+ set_backtrace(mrb, exc, backtrace);
return backtrace;
}
@@ -238,12 +258,6 @@ exc_debug_info(mrb_state *mrb, struct RObject *exc)
}
}
-static void
-set_backtrace(mrb_state *mrb, mrb_value info, mrb_value bt)
-{
- mrb_funcall(mrb, info, "set_backtrace", 1, bt);
-}
-
static mrb_bool
have_backtrace(mrb_state *mrb, struct RObject *exc)
{
@@ -285,6 +299,9 @@ mrb_exc_set(mrb_state *mrb, mrb_value exc)
MRB_API mrb_noreturn void
mrb_exc_raise(mrb_state *mrb, mrb_value exc)
{
+ if (!mrb_obj_is_kind_of(mrb, exc, mrb->eException_class)) {
+ mrb_raise(mrb, E_TYPE_ERROR, "exception object expected");
+ }
mrb_exc_set(mrb, exc);
if (!mrb->gc.out_of_memory) {
exc_debug_info(mrb, mrb->exc);
@@ -463,7 +480,7 @@ exception_call:
}
if (argc > 0) {
if (!mrb_obj_is_kind_of(mrb, mesg, mrb->eException_class))
- mrb_raise(mrb, E_TYPE_ERROR, "exception object expected");
+ mrb_raise(mrb, mrb->eException_class, "exception object expected");
if (argc > 2)
set_backtrace(mrb, mesg, argv[2]);
}
@@ -514,7 +531,7 @@ mrb_no_method_error(mrb_state *mrb, mrb_sym id, mrb_value args, char const* fmt,
void
mrb_init_exception(mrb_state *mrb)
{
- struct RClass *exception, *runtime_error, *script_error;
+ struct RClass *exception, *runtime_error, *script_error, *stack_error;
mrb->eException_class = exception = mrb_define_class(mrb, "Exception", mrb->object_class); /* 15.2.22 */
MRB_SET_INSTANCE_TT(exception, MRB_TT_EXCEPTION);
@@ -530,7 +547,11 @@ mrb_init_exception(mrb_state *mrb)
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_lit(mrb, runtime_error, "Out of memory"));
+#ifdef MRB_GC_FIXED_ARENA
+ mrb->arena_err = mrb_obj_ptr(mrb_exc_new_str_lit(mrb, runtime_error, "arena overflow error"));
+#endif
script_error = mrb_define_class(mrb, "ScriptError", mrb->eException_class); /* 15.2.37 */
mrb_define_class(mrb, "SyntaxError", script_error); /* 15.2.38 */
- mrb_define_class(mrb, "SystemStackError", exception);
+ stack_error = mrb_define_class(mrb, "SystemStackError", exception);
+ mrb->stack_err = mrb_obj_ptr(mrb_exc_new_str_lit(mrb, stack_error, "stack level too deep"));
}
diff --git a/src/etc.c b/src/etc.c
index 183e2f070..e0810d589 100644
--- a/src/etc.c
+++ b/src/etc.c
@@ -139,6 +139,7 @@ mrb_obj_id(mrb_value obj)
case MRB_TT_EXCEPTION:
case MRB_TT_FILE:
case MRB_TT_DATA:
+ case MRB_TT_ISTRUCT:
default:
return MakeID(mrb_ptr(obj));
}
diff --git a/src/gc.c b/src/gc.c
index b29df1f02..63eab8e00 100644
--- a/src/gc.c
+++ b/src/gc.c
@@ -367,7 +367,7 @@ mrb_gc_init(mrb_state *mrb, mrb_gc *gc)
#endif
}
-static void obj_free(mrb_state *mrb, struct RBasic *obj);
+static void obj_free(mrb_state *mrb, struct RBasic *obj, int end);
void
free_heap(mrb_state *mrb, mrb_gc *gc)
@@ -381,7 +381,7 @@ free_heap(mrb_state *mrb, mrb_gc *gc)
page = page->next;
for (p = objects(tmp), e=p+MRB_HEAP_PAGE_SIZE; p<e; p++) {
if (p->as.free.tt != MRB_TT_FREE)
- obj_free(mrb, &p->as.basic);
+ obj_free(mrb, &p->as.basic, TRUE);
}
mrb_free(mrb, tmp);
}
@@ -403,7 +403,7 @@ gc_protect(mrb_state *mrb, mrb_gc *gc, struct RBasic *p)
if (gc->arena_idx >= MRB_GC_ARENA_SIZE) {
/* arena overflow error */
gc->arena_idx = MRB_GC_ARENA_SIZE - 4; /* force room in arena */
- mrb_raise(mrb, E_RUNTIME_ERROR, "arena overflow error");
+ mrb_exc_raise(mrb, mrb_obj_value(mrb->arena_err));
}
#else
if (gc->arena_idx >= gc->arena_capa) {
@@ -453,7 +453,7 @@ mrb_gc_unregister(mrb_state *mrb, mrb_value obj)
mrb_sym root = mrb_intern_lit(mrb, GC_ROOT_NAME);
mrb_value table = mrb_gv_get(mrb, root);
struct RArray *a;
- mrb_int i, len;
+ mrb_int i;
if (mrb_nil_p(table)) return;
if (mrb_type(table) != MRB_TT_ARRAY) {
@@ -462,14 +462,13 @@ mrb_gc_unregister(mrb_state *mrb, mrb_value obj)
}
a = mrb_ary_ptr(table);
mrb_ary_modify(mrb, a);
- len = a->len-1;
- for (i=0; i<len; i++) {
+ for (i = 0; i < a->len; i++) {
if (mrb_obj_eq(mrb, a->ptr[i], obj)) {
- memmove(&a->ptr[i], &a->ptr[i+1], len-i);
+ a->len--;
+ memmove(&a->ptr[i], &a->ptr[i + 1], (a->len - i) * sizeof(a->ptr[i]));
break;
}
}
- a->len--;
}
MRB_API struct RBasic*
@@ -479,6 +478,28 @@ mrb_obj_alloc(mrb_state *mrb, enum mrb_vtype ttype, struct RClass *cls)
static const RVALUE RVALUE_zero = { { { MRB_TT_FALSE } } };
mrb_gc *gc = &mrb->gc;
+ if (cls) {
+ enum mrb_vtype tt;
+
+ switch (cls->tt) {
+ case MRB_TT_CLASS:
+ case MRB_TT_SCLASS:
+ case MRB_TT_MODULE:
+ case MRB_TT_ENV:
+ break;
+ default:
+ mrb_raise(mrb, E_TYPE_ERROR, "allocation failure");
+ }
+ tt = MRB_INSTANCE_TT(cls);
+ if (tt != MRB_TT_FALSE &&
+ ttype != MRB_TT_SCLASS &&
+ ttype != MRB_TT_ICLASS &&
+ ttype != MRB_TT_ENV &&
+ ttype != tt) {
+ mrb_raisef(mrb, E_TYPE_ERROR, "allocation failure of %S", mrb_obj_value(cls));
+ }
+ }
+
#ifdef MRB_GC_STRESS
mrb_full_gc(mrb);
#endif
@@ -678,7 +699,7 @@ mrb_gc_mark(mrb_state *mrb, struct RBasic *obj)
}
static void
-obj_free(mrb_state *mrb, struct RBasic *obj)
+obj_free(mrb_state *mrb, struct RBasic *obj, int end)
{
DEBUG(printf("obj_free(%p,tt=%d)\n",obj,obj->tt));
switch (obj->tt) {
@@ -720,17 +741,19 @@ obj_free(mrb_state *mrb, struct RBasic *obj)
{
struct REnv *e = (struct REnv*)obj;
- if (!MRB_ENV_STACK_SHARED_P(e)) {
- mrb_free(mrb, e->stack);
- e->stack = NULL;
+ if (MRB_ENV_STACK_SHARED_P(e)) {
+ /* cannot be freed */
+ return;
}
+ mrb_free(mrb, e->stack);
+ e->stack = NULL;
}
break;
case MRB_TT_FIBER:
{
struct mrb_context *c = ((struct RFiber*)obj)->cxt;
- if (c && c != mrb->root_c) {
+ if (!end && c && c != mrb->root_c) {
mrb_callinfo *ci = c->ci;
mrb_callinfo *ce = c->cibase;
@@ -810,12 +833,42 @@ root_scan_phase(mrb_state *mrb, mrb_gc *gc)
}
/* mark class hierarchy */
mrb_gc_mark(mrb, (struct RBasic*)mrb->object_class);
+
+ /* mark built-in classes */
+ mrb_gc_mark(mrb, (struct RBasic*)mrb->class_class);
+ mrb_gc_mark(mrb, (struct RBasic*)mrb->module_class);
+ mrb_gc_mark(mrb, (struct RBasic*)mrb->proc_class);
+ mrb_gc_mark(mrb, (struct RBasic*)mrb->string_class);
+ mrb_gc_mark(mrb, (struct RBasic*)mrb->array_class);
+ mrb_gc_mark(mrb, (struct RBasic*)mrb->hash_class);
+
+ mrb_gc_mark(mrb, (struct RBasic*)mrb->float_class);
+ mrb_gc_mark(mrb, (struct RBasic*)mrb->fixnum_class);
+ mrb_gc_mark(mrb, (struct RBasic*)mrb->true_class);
+ mrb_gc_mark(mrb, (struct RBasic*)mrb->false_class);
+ mrb_gc_mark(mrb, (struct RBasic*)mrb->nil_class);
+ mrb_gc_mark(mrb, (struct RBasic*)mrb->symbol_class);
+ mrb_gc_mark(mrb, (struct RBasic*)mrb->kernel_module);
+
+ mrb_gc_mark(mrb, (struct RBasic*)mrb->eException_class);
+ mrb_gc_mark(mrb, (struct RBasic*)mrb->eStandardError_class);
+
/* mark top_self */
mrb_gc_mark(mrb, (struct RBasic*)mrb->top_self);
/* mark exception */
mrb_gc_mark(mrb, (struct RBasic*)mrb->exc);
+ /* mark backtrace */
+ mrb_gc_mark(mrb, (struct RBasic*)mrb->backtrace.exc);
+ e = (size_t)mrb->backtrace.n;
+ for (i=0; i<e; i++) {
+ mrb_gc_mark(mrb, (struct RBasic*)mrb->backtrace.entries[i].klass);
+ }
/* mark pre-allocated exception */
mrb_gc_mark(mrb, (struct RBasic*)mrb->nomem_err);
+ mrb_gc_mark(mrb, (struct RBasic*)mrb->stack_err);
+#ifdef MRB_GC_FIXED_ARENA
+ mrb_gc_mark(mrb, (struct RBasic*)mrb->arena_err);
+#endif
mark_context(mrb, mrb->root_c);
if (mrb->root_c->fib) {
@@ -973,16 +1026,21 @@ incremental_sweep_phase(mrb_state *mrb, mrb_gc *gc, size_t limit)
while (p<e) {
if (is_dead(gc, &p->as.basic)) {
if (p->as.basic.tt != MRB_TT_FREE) {
- obj_free(mrb, &p->as.basic);
- p->as.free.next = page->freelist;
- page->freelist = (struct RBasic*)p;
- freed++;
+ obj_free(mrb, &p->as.basic, FALSE);
+ if (p->as.basic.tt == MRB_TT_FREE) {
+ p->as.free.next = page->freelist;
+ page->freelist = (struct RBasic*)p;
+ freed++;
+ }
+ else {
+ dead_slot = FALSE;
+ }
}
}
else {
if (!is_generational(gc))
paint_partial_white(gc, &p->as.basic); /* next gc target */
- dead_slot = 0;
+ dead_slot = FALSE;
}
p++;
}
diff --git a/src/hash.c b/src/hash.c
index 2ad6a9642..d15faa206 100644
--- a/src/hash.c
+++ b/src/hash.c
@@ -98,9 +98,9 @@ static void mrb_hash_modify(mrb_state *mrb, mrb_value hash);
static inline mrb_value
mrb_hash_ht_key(mrb_state *mrb, mrb_value key)
{
- if (mrb_string_p(key) && !RSTR_FROZEN_P(mrb_str_ptr(key))) {
+ if (mrb_string_p(key) && !MRB_FROZEN_P(mrb_str_ptr(key))) {
key = mrb_str_dup(mrb, key);
- RSTR_SET_FROZEN_FLAG(mrb_str_ptr(key));
+ MRB_SET_FROZEN_FLAG(mrb_str_ptr(key));
}
return key;
}
@@ -159,11 +159,15 @@ mrb_hash_new(mrb_state *mrb)
return mrb_hash_new_capa(mrb, 0);
}
+static mrb_value mrb_hash_default(mrb_state *mrb, mrb_value hash);
+static mrb_value hash_default(mrb_state *mrb, mrb_value hash, mrb_value key);
+
MRB_API mrb_value
mrb_hash_get(mrb_state *mrb, mrb_value hash, mrb_value key)
{
khash_t(ht) *h = RHASH_TBL(hash);
khiter_t k;
+ mrb_sym mid;
if (h) {
k = kh_get(ht, mrb, h, key);
@@ -171,14 +175,12 @@ mrb_hash_get(mrb_state *mrb, mrb_value hash, mrb_value key)
return kh_value(h, k).v;
}
- /* not found */
- if (MRB_RHASH_DEFAULT_P(hash)) {
- if (MRB_RHASH_PROCDEFAULT_P(hash)) {
- return mrb_funcall(mrb, RHASH_PROCDEFAULT(hash), "call", 2, hash, key);
- }
- return RHASH_IFNONE(hash);
+ mid = mrb_intern_lit(mrb, "default");
+ if (mrb_func_basic_p(mrb, hash, mid, mrb_hash_default)) {
+ return hash_default(mrb, hash, key);
}
- return mrb_nil_value();
+ /* xxx mrb_funcall_tailcall(mrb, hash, "default", 1, key); */
+ return mrb_funcall_argv(mrb, hash, mid, 1, &key);
}
MRB_API mrb_value
@@ -230,6 +232,7 @@ mrb_hash_dup(mrb_state *mrb, mrb_value hash)
struct RHash* ret;
khash_t(ht) *h, *ret_h;
khiter_t k, ret_k;
+ mrb_value ifnone, vret;
h = RHASH_TBL(hash);
ret = (struct RHash*)mrb_obj_alloc(mrb, MRB_TT_HASH, mrb->hash_class);
@@ -248,7 +251,18 @@ mrb_hash_dup(mrb_state *mrb, mrb_value hash)
}
}
- return mrb_obj_value(ret);
+ if (MRB_RHASH_DEFAULT_P(hash)) {
+ ret->flags |= MRB_HASH_DEFAULT;
+ }
+ if (MRB_RHASH_PROCDEFAULT_P(hash)) {
+ ret->flags |= MRB_HASH_PROC_DEFAULT;
+ }
+ vret = mrb_obj_value(ret);
+ ifnone = RHASH_IFNONE(hash);
+ if (!mrb_nil_p(ifnone)) {
+ mrb_iv_set(mrb, vret, mrb_intern_lit(mrb, "ifnone"), ifnone);
+ }
+ return vret;
}
MRB_API mrb_value
@@ -271,6 +285,9 @@ mrb_hash_tbl(mrb_state *mrb, mrb_value hash)
static void
mrb_hash_modify(mrb_state *mrb, mrb_value hash)
{
+ if (MRB_FROZEN_P(mrb_hash_ptr(hash))) {
+ mrb_raise(mrb, E_RUNTIME_ERROR, "can't modify frozen hash");
+ }
mrb_hash_tbl(mrb, hash);
}
@@ -356,6 +373,20 @@ mrb_hash_aget(mrb_state *mrb, mrb_value self)
return mrb_hash_get(mrb, self, key);
}
+static mrb_value
+hash_default(mrb_state *mrb, mrb_value hash, mrb_value key)
+{
+ if (MRB_RHASH_DEFAULT_P(hash)) {
+ if (MRB_RHASH_PROCDEFAULT_P(hash)) {
+ return mrb_funcall(mrb, RHASH_PROCDEFAULT(hash), "call", 2, hash, key);
+ }
+ else {
+ return RHASH_IFNONE(hash);
+ }
+ }
+ return mrb_nil_value();
+}
+
/* 15.2.13.4.5 */
/*
* call-seq:
@@ -385,13 +416,16 @@ mrb_hash_default(mrb_state *mrb, mrb_value hash)
mrb_bool given;
mrb_get_args(mrb, "|o?", &key, &given);
- if (MRB_RHASH_PROCDEFAULT_P(hash)) {
- if (!given) return mrb_nil_value();
- return mrb_funcall(mrb, RHASH_PROCDEFAULT(hash), "call", 2, hash, key);
- }
- else {
- return RHASH_IFNONE(hash);
+ if (MRB_RHASH_DEFAULT_P(hash)) {
+ if (MRB_RHASH_PROCDEFAULT_P(hash)) {
+ if (!given) return mrb_nil_value();
+ return mrb_funcall(mrb, RHASH_PROCDEFAULT(hash), "call", 2, hash, key);
+ }
+ else {
+ return RHASH_IFNONE(hash);
+ }
}
+ return mrb_nil_value();
}
/* 15.2.13.4.6 */
@@ -541,6 +575,7 @@ mrb_hash_delete(mrb_state *mrb, mrb_value self)
mrb_value key;
mrb_get_args(mrb, "o", &key);
+ mrb_hash_modify(mrb, self);
return mrb_hash_delete_key(mrb, self, key);
}
@@ -607,6 +642,7 @@ mrb_hash_clear(mrb_state *mrb, mrb_value hash)
{
khash_t(ht) *h = RHASH_TBL(hash);
+ mrb_hash_modify(mrb, hash);
if (h) kh_clear(ht, mrb, h);
return hash;
}
diff --git a/src/kernel.c b/src/kernel.c
index df237cd46..6cb2e3ad5 100644
--- a/src/kernel.c
+++ b/src/kernel.c
@@ -6,11 +6,13 @@
#include <mruby.h>
#include <mruby/array.h>
+#include <mruby/hash.h>
#include <mruby/class.h>
#include <mruby/proc.h>
#include <mruby/string.h>
#include <mruby/variable.h>
#include <mruby/error.h>
+#include <mruby/istruct.h>
typedef enum {
NOEX_PUBLIC = 0x00,
@@ -26,15 +28,21 @@ typedef enum {
NOEX_RESPONDS = 0x80
} mrb_method_flag_t;
-static mrb_bool
-mrb_obj_basic_to_s_p(mrb_state *mrb, mrb_value obj)
+MRB_API mrb_bool
+mrb_func_basic_p(mrb_state *mrb, mrb_value obj, mrb_sym mid, mrb_func_t func)
{
- struct RProc *me = mrb_method_search(mrb, mrb_class(mrb, obj), mrb_intern_lit(mrb, "to_s"));
- if (MRB_PROC_CFUNC_P(me) && (me->body.func == mrb_any_to_s))
+ struct RProc *me = mrb_method_search(mrb, mrb_class(mrb, obj), mid);
+ if (MRB_PROC_CFUNC_P(me) && (me->body.func == func))
return TRUE;
return FALSE;
}
+static mrb_bool
+mrb_obj_basic_to_s_p(mrb_state *mrb, mrb_value obj)
+{
+ return mrb_func_basic_p(mrb, obj, mrb_intern_lit(mrb, "to_s"), mrb_any_to_s);
+}
+
/* 15.3.1.3.17 */
/*
* call-seq:
@@ -58,55 +66,6 @@ mrb_obj_inspect(mrb_state *mrb, mrb_value obj)
return mrb_any_to_s(mrb, obj);
}
-/* 15.3.1.3.1 */
-/* 15.3.1.3.10 */
-/* 15.3.1.3.11 */
-/*
- * call-seq:
- * obj == other -> true or false
- * obj.equal?(other) -> true or false
- * obj.eql?(other) -> true or false
- *
- * Equality---At the <code>Object</code> level, <code>==</code> returns
- * <code>true</code> only if <i>obj</i> and <i>other</i> are the
- * same object. Typically, this method is overridden in descendant
- * classes to provide class-specific meaning.
- *
- * Unlike <code>==</code>, the <code>equal?</code> method should never be
- * overridden by subclasses: it is used to determine object identity
- * (that is, <code>a.equal?(b)</code> iff <code>a</code> is the same
- * object as <code>b</code>).
- *
- * The <code>eql?</code> method returns <code>true</code> if
- * <i>obj</i> and <i>anObject</i> have the same value. Used by
- * <code>Hash</code> to test members for equality. For objects of
- * class <code>Object</code>, <code>eql?</code> is synonymous with
- * <code>==</code>. Subclasses normally continue this tradition, but
- * there are exceptions. <code>Numeric</code> types, for example,
- * perform type conversion across <code>==</code>, but not across
- * <code>eql?</code>, so:
- *
- * 1 == 1.0 #=> true
- * 1.eql? 1.0 #=> false
- */
-static mrb_value
-mrb_obj_equal_m(mrb_state *mrb, mrb_value self)
-{
- mrb_value arg;
-
- mrb_get_args(mrb, "o", &arg);
- return mrb_bool_value(mrb_obj_equal(mrb, self, arg));
-}
-
-static mrb_value
-mrb_obj_not_equal_m(mrb_state *mrb, mrb_value self)
-{
- mrb_value arg;
-
- mrb_get_args(mrb, "o", &arg);
- return mrb_bool_value(!mrb_equal(mrb, self, arg));
-}
-
/* 15.3.1.3.2 */
/*
* call-seq:
@@ -142,7 +101,7 @@ mrb_equal_m(mrb_state *mrb, mrb_value self)
* <code>:name</code> notation, which returns the symbol id of
* <code>name</code>. Replaces the deprecated <code>Object#id</code>.
*/
-static mrb_value
+mrb_value
mrb_obj_id_m(mrb_state *mrb, mrb_value self)
{
return mrb_fixnum_value(mrb_obj_id(self));
@@ -284,6 +243,7 @@ copy_class(mrb_state *mrb, mrb_value dst, mrb_value src)
}
dc->mt = kh_copy(mt, mrb, sc->mt);
dc->super = sc->super;
+ MRB_SET_INSTANCE_TT(dc, MRB_INSTANCE_TT(sc));
}
static void
@@ -301,6 +261,9 @@ init_copy(mrb_state *mrb, mrb_value dest, mrb_value obj)
case MRB_TT_EXCEPTION:
mrb_iv_copy(mrb, dest, obj);
break;
+ case MRB_TT_ISTRUCT:
+ mrb_istruct_copy(dest, obj);
+ break;
default:
break;
@@ -440,11 +403,60 @@ mrb_obj_extend_m(mrb_state *mrb, mrb_value self)
{
mrb_value *argv;
mrb_int argc;
+ mrb_value args;
mrb_get_args(mrb, "*", &argv, &argc);
+ args = mrb_ary_new_from_values(mrb, argc, argv);
+ argv = (mrb_value*)RARRAY_PTR(args);
return mrb_obj_extend(mrb, argc, argv, self);
}
+static mrb_value
+mrb_obj_freeze(mrb_state *mrb, mrb_value self)
+{
+ struct RBasic *b;
+
+ switch (mrb_type(self)) {
+ case MRB_TT_FALSE:
+ case MRB_TT_TRUE:
+ case MRB_TT_FIXNUM:
+ case MRB_TT_SYMBOL:
+ case MRB_TT_FLOAT:
+ return self;
+ default:
+ break;
+ }
+
+ b = mrb_basic_ptr(self);
+ if (!MRB_FROZEN_P(b)) {
+ MRB_SET_FROZEN_FLAG(b);
+ }
+ return self;
+}
+
+static mrb_value
+mrb_obj_frozen(mrb_state *mrb, mrb_value self)
+{
+ struct RBasic *b;
+
+ switch (mrb_type(self)) {
+ case MRB_TT_FALSE:
+ case MRB_TT_TRUE:
+ case MRB_TT_FIXNUM:
+ case MRB_TT_SYMBOL:
+ case MRB_TT_FLOAT:
+ return mrb_true_value();
+ default:
+ break;
+ }
+
+ b = mrb_basic_ptr(self);
+ if (!MRB_FROZEN_P(b)) {
+ return mrb_false_value();
+ }
+ return mrb_true_value();
+}
+
/* 15.3.1.3.15 */
/*
* call-seq:
@@ -477,9 +489,6 @@ mrb_obj_init_copy(mrb_state *mrb, mrb_value self)
}
-/* implementation of instance_eval */
-mrb_value mrb_obj_instance_eval(mrb_state*, mrb_value);
-
MRB_API mrb_bool
mrb_obj_is_instance_of(mrb_state *mrb, mrb_value obj, struct RClass* c)
{
@@ -902,6 +911,77 @@ mrb_obj_remove_instance_variable(mrb_state *mrb, mrb_value self)
return val;
}
+void
+mrb_method_missing(mrb_state *mrb, mrb_sym name, mrb_value self, mrb_value args)
+{
+ mrb_sym inspect;
+ mrb_value repr;
+
+ inspect = mrb_intern_lit(mrb, "inspect");
+ if (mrb->c->ci > mrb->c->cibase && mrb->c->ci[-1].mid == inspect) {
+ /* method missing in inspect; avoid recursion */
+ repr = mrb_any_to_s(mrb, self);
+ }
+ else if (mrb_respond_to(mrb, self, inspect) && mrb->c->ci - mrb->c->cibase < 64) {
+ repr = mrb_funcall_argv(mrb, self, inspect, 0, 0);
+ if (mrb_string_p(repr) && RSTRING_LEN(repr) > 64) {
+ repr = mrb_any_to_s(mrb, self);
+ }
+ }
+ else {
+ repr = mrb_any_to_s(mrb, self);
+ }
+
+ mrb_no_method_error(mrb, name, args, "undefined method '%S' for %S",
+ mrb_sym2str(mrb, name), repr);
+}
+
+/* 15.3.1.3.30 */
+/*
+ * call-seq:
+ * obj.method_missing(symbol [, *args] ) -> result
+ *
+ * Invoked by Ruby when <i>obj</i> is sent a message it cannot handle.
+ * <i>symbol</i> is the symbol for the method called, and <i>args</i>
+ * are any arguments that were passed to it. By default, the interpreter
+ * raises an error when this method is called. However, it is possible
+ * to override the method to provide more dynamic behavior.
+ * If it is decided that a particular method should not be handled, then
+ * <i>super</i> should be called, so that ancestors can pick up the
+ * missing method.
+ * The example below creates
+ * a class <code>Roman</code>, which responds to methods with names
+ * consisting of roman numerals, returning the corresponding integer
+ * values.
+ *
+ * class Roman
+ * def romanToInt(str)
+ * # ...
+ * end
+ * def method_missing(methId)
+ * str = methId.id2name
+ * romanToInt(str)
+ * end
+ * end
+ *
+ * r = Roman.new
+ * r.iv #=> 4
+ * r.xxiii #=> 23
+ * r.mm #=> 2000
+ */
+static mrb_value
+mrb_obj_missing(mrb_state *mrb, mrb_value mod)
+{
+ mrb_sym name;
+ mrb_value *a;
+ mrb_int alen;
+
+ mrb_get_args(mrb, "n*", &name, &a, &alen);
+ mrb_method_missing(mrb, name, mod, mrb_ary_new_from_values(mrb, alen, a));
+ /* not reached */
+ return mrb_nil_value();
+}
+
static inline mrb_bool
basic_obj_respond_to(mrb_state *mrb, mrb_value obj, mrb_sym id, int pub)
{
@@ -937,14 +1017,17 @@ obj_respond_to(mrb_state *mrb, mrb_value self)
}
else {
mrb_value tmp;
- if (!mrb_string_p(mid)) {
+ if (mrb_string_p(mid)) {
+ tmp = mrb_check_intern_str(mrb, mid);
+ }
+ else {
tmp = mrb_check_string_type(mrb, mid);
if (mrb_nil_p(tmp)) {
tmp = mrb_inspect(mrb, mid);
mrb_raisef(mrb, E_TYPE_ERROR, "%S is not a symbol", tmp);
}
+ tmp = mrb_check_intern_str(mrb, tmp);
}
- tmp = mrb_check_intern_str(mrb, mid);
if (mrb_nil_p(tmp)) {
respond_to_p = FALSE;
}
@@ -1049,8 +1132,8 @@ mrb_obj_ceqq(mrb_state *mrb, mrb_value self)
static mrb_value
mrb_local_variables(mrb_state *mrb, mrb_value self)
{
- mrb_value ret;
struct RProc *proc;
+ mrb_value vars;
struct mrb_irep *irep;
size_t i;
@@ -1064,22 +1147,23 @@ mrb_local_variables(mrb_state *mrb, mrb_value self)
if (!irep->lv) {
return mrb_ary_new(mrb);
}
- ret = mrb_ary_new_capa(mrb, irep->nlocals - 1);
+ vars = mrb_hash_new(mrb);
for (i = 0; i + 1 < irep->nlocals; ++i) {
if (irep->lv[i].name) {
- mrb_ary_push(mrb, ret, mrb_symbol_value(irep->lv[i].name));
+ mrb_hash_set(mrb, vars, mrb_symbol_value(irep->lv[i].name), mrb_true_value());
}
}
if (proc->env) {
struct REnv *e = proc->env;
while (e) {
- if (!MRB_PROC_CFUNC_P(mrb->c->cibase[e->cioff].proc)) {
+ if (MRB_ENV_STACK_SHARED_P(e) &&
+ !MRB_PROC_CFUNC_P(mrb->c->cibase[e->cioff].proc)) {
irep = mrb->c->cibase[e->cioff].proc->body.irep;
if (irep->lv) {
for (i = 0; i + 1 < irep->nlocals; ++i) {
if (irep->lv[i].name) {
- mrb_ary_push(mrb, ret, mrb_symbol_value(irep->lv[i].name));
+ mrb_hash_set(mrb, vars, mrb_symbol_value(irep->lv[i].name), mrb_true_value());
}
}
}
@@ -1088,9 +1172,10 @@ mrb_local_variables(mrb_state *mrb, mrb_value self)
}
}
- return ret;
+ return mrb_hash_keys(mrb, vars);
}
+mrb_value mrb_obj_equal_m(mrb_state *mrb, mrb_value);
void
mrb_init_kernel(mrb_state *mrb)
{
@@ -1106,11 +1191,7 @@ mrb_init_kernel(mrb_state *mrb)
mrb_define_method(mrb, krn, "singleton_class", mrb_singleton_class, MRB_ARGS_NONE());
- mrb_define_method(mrb, krn, "==", mrb_obj_equal_m, MRB_ARGS_REQ(1)); /* 15.3.1.3.1 */
- mrb_define_method(mrb, krn, "!=", mrb_obj_not_equal_m, MRB_ARGS_REQ(1));
mrb_define_method(mrb, krn, "===", mrb_equal_m, MRB_ARGS_REQ(1)); /* 15.3.1.3.2 */
- mrb_define_method(mrb, krn, "__id__", mrb_obj_id_m, MRB_ARGS_NONE()); /* 15.3.1.3.3 */
- mrb_define_method(mrb, krn, "__send__", mrb_f_send, MRB_ARGS_ANY()); /* 15.3.1.3.4 */
mrb_define_method(mrb, krn, "block_given?", mrb_f_block_given_p_m, MRB_ARGS_NONE()); /* 15.3.1.3.6 */
mrb_define_method(mrb, krn, "class", mrb_obj_class_m, MRB_ARGS_NONE()); /* 15.3.1.3.7 */
mrb_define_method(mrb, krn, "clone", mrb_obj_clone, MRB_ARGS_NONE()); /* 15.3.1.3.8 */
@@ -1118,11 +1199,12 @@ mrb_init_kernel(mrb_state *mrb)
mrb_define_method(mrb, krn, "eql?", mrb_obj_equal_m, MRB_ARGS_REQ(1)); /* 15.3.1.3.10 */
mrb_define_method(mrb, krn, "equal?", mrb_obj_equal_m, MRB_ARGS_REQ(1)); /* 15.3.1.3.11 */
mrb_define_method(mrb, krn, "extend", mrb_obj_extend_m, MRB_ARGS_ANY()); /* 15.3.1.3.13 */
+ mrb_define_method(mrb, krn, "freeze", mrb_obj_freeze, MRB_ARGS_NONE());
+ mrb_define_method(mrb, krn, "frozen?", mrb_obj_frozen, MRB_ARGS_NONE());
mrb_define_method(mrb, krn, "global_variables", mrb_f_global_variables, MRB_ARGS_NONE()); /* 15.3.1.3.14 */
mrb_define_method(mrb, krn, "hash", mrb_obj_hash, MRB_ARGS_NONE()); /* 15.3.1.3.15 */
mrb_define_method(mrb, krn, "initialize_copy", mrb_obj_init_copy, MRB_ARGS_REQ(1)); /* 15.3.1.3.16 */
mrb_define_method(mrb, krn, "inspect", mrb_obj_inspect, MRB_ARGS_NONE()); /* 15.3.1.3.17 */
- mrb_define_method(mrb, krn, "instance_eval", mrb_obj_instance_eval, MRB_ARGS_ANY()); /* 15.3.1.3.18 */
mrb_define_method(mrb, krn, "instance_of?", obj_is_instance_of, MRB_ARGS_REQ(1)); /* 15.3.1.3.19 */
mrb_define_method(mrb, krn, "instance_variable_defined?", mrb_obj_ivar_defined, MRB_ARGS_REQ(1)); /* 15.3.1.3.20 */
mrb_define_method(mrb, krn, "instance_variable_get", mrb_obj_ivar_get, MRB_ARGS_REQ(1)); /* 15.3.1.3.21 */
@@ -1132,6 +1214,7 @@ mrb_init_kernel(mrb_state *mrb)
mrb_define_method(mrb, krn, "iterator?", mrb_f_block_given_p_m, MRB_ARGS_NONE()); /* 15.3.1.3.25 */
mrb_define_method(mrb, krn, "kind_of?", mrb_obj_is_kind_of_m, MRB_ARGS_REQ(1)); /* 15.3.1.3.26 */
mrb_define_method(mrb, krn, "local_variables", mrb_local_variables, MRB_ARGS_NONE()); /* 15.3.1.3.28 */
+ mrb_define_method(mrb, krn, "method_missing", mrb_obj_missing, MRB_ARGS_ANY()); /* 15.3.1.3.30 */
mrb_define_method(mrb, krn, "methods", mrb_obj_methods_m, MRB_ARGS_OPT(1)); /* 15.3.1.3.31 */
mrb_define_method(mrb, krn, "nil?", mrb_false, MRB_ARGS_NONE()); /* 15.3.1.3.32 */
mrb_define_method(mrb, krn, "object_id", mrb_obj_id_m, MRB_ARGS_NONE()); /* 15.3.1.3.33 */
diff --git a/src/numeric.c b/src/numeric.c
index 327d54177..0306b1363 100644
--- a/src/numeric.c
+++ b/src/numeric.c
@@ -13,8 +13,10 @@
#include <mruby/array.h>
#include <mruby/numeric.h>
#include <mruby/string.h>
+#include <mruby/class.h>
#ifdef MRB_USE_FLOAT
+#define trunc(f) truncf(f)
#define floor(f) floorf(f)
#define ceil(f) ceilf(f)
#define fmod(x,y) fmodf(x,y)
@@ -271,6 +273,128 @@ flo_eq(mrb_state *mrb, mrb_value x)
}
}
+static int64_t
+value_int64(mrb_state *mrb, mrb_value x)
+{
+ switch (mrb_type(x)) {
+ case MRB_TT_FIXNUM:
+ return (int64_t)mrb_fixnum(x);
+ break;
+ case MRB_TT_FLOAT:
+ return (int64_t)mrb_float(x);
+ default:
+ mrb_raise(mrb, E_TYPE_ERROR, "cannot convert to Integer");
+ break;
+ }
+ /* not reached */
+ return 0;
+}
+
+static mrb_value
+int64_value(mrb_state *mrb, int64_t v)
+{
+ if (FIXABLE(v)) {
+ return mrb_fixnum_value((mrb_int)v);
+ }
+ return mrb_float_value(mrb, (mrb_float)v);
+}
+
+static mrb_value
+flo_rev(mrb_state *mrb, mrb_value x)
+{
+ int64_t v1;
+ mrb_get_args(mrb, "");
+ v1 = (int64_t)mrb_float(x);
+ return int64_value(mrb, ~v1);
+}
+
+static mrb_value
+flo_and(mrb_state *mrb, mrb_value x)
+{
+ mrb_value y;
+ int64_t v1, v2;
+ mrb_get_args(mrb, "o", &y);
+
+ v1 = (int64_t)mrb_float(x);
+ v2 = value_int64(mrb, y);
+ return int64_value(mrb, v1 & v2);
+}
+
+static mrb_value
+flo_or(mrb_state *mrb, mrb_value x)
+{
+ mrb_value y;
+ int64_t v1, v2;
+ mrb_get_args(mrb, "o", &y);
+
+ v1 = (int64_t)mrb_float(x);
+ v2 = value_int64(mrb, y);
+ return int64_value(mrb, v1 | v2);
+}
+
+static mrb_value
+flo_xor(mrb_state *mrb, mrb_value x)
+{
+ mrb_value y;
+ int64_t v1, v2;
+ mrb_get_args(mrb, "o", &y);
+
+ v1 = (int64_t)mrb_float(x);
+ v2 = value_int64(mrb, y);
+ return int64_value(mrb, v1 ^ v2);
+}
+
+static mrb_value
+flo_shift(mrb_state *mrb, mrb_value x, mrb_int width)
+{
+ mrb_float val;
+
+ if (width == 0) {
+ return x;
+ }
+ val = mrb_float(x);
+ if (width < 0) {
+ while (width++) {
+ val /= 2;
+ }
+#if defined(_ISOC99_SOURCE)
+ val = trunc(val);
+#else
+ val = val > 0 ? floor(val) : ceil(val);
+#endif
+ if (val == 0 && mrb_float(x) < 0) {
+ return mrb_fixnum_value(-1);
+ }
+ }
+ else {
+ while (width--) {
+ val *= 2;
+ }
+ }
+ if (FIXABLE(val)) {
+ return mrb_fixnum_value(val);
+ }
+ return mrb_float_value(mrb, val);
+}
+
+static mrb_value
+flo_lshift(mrb_state *mrb, mrb_value x)
+{
+ mrb_int width;
+
+ mrb_get_args(mrb, "i", &width);
+ return flo_shift(mrb, x, -width);
+}
+
+static mrb_value
+flo_rshift(mrb_state *mrb, mrb_value x)
+{
+ mrb_int width;
+
+ mrb_get_args(mrb, "i", &width);
+ return flo_shift(mrb, x, width);
+}
+
/* 15.2.8.3.18 */
/*
* call-seq:
@@ -738,17 +862,13 @@ fix_rev(mrb_state *mrb, mrb_value num)
return mrb_fixnum_value(~val);
}
-static mrb_value
-bit_coerce(mrb_state *mrb, mrb_value x)
-{
- while (!mrb_fixnum_p(x)) {
- if (mrb_float_p(x)) {
- mrb_raise(mrb, E_TYPE_ERROR, "can't convert Float into Integer");
- }
- x = mrb_to_int(mrb, x);
- }
- return x;
-}
+static mrb_value flo_and(mrb_state *mrb, mrb_value x);
+static mrb_value flo_or(mrb_state *mrb, mrb_value x);
+static mrb_value flo_xor(mrb_state *mrb, mrb_value x);
+#define bit_op(x,y,op1,op2) do {\
+ if (mrb_fixnum_p(y)) return mrb_fixnum_value(mrb_fixnum(x) op2 mrb_fixnum(y));\
+ return flo_ ## op1(mrb, mrb_float_value(mrb, mrb_fixnum(x)));\
+} while(0)
/* 15.2.8.3.9 */
/*
@@ -764,9 +884,7 @@ fix_and(mrb_state *mrb, mrb_value x)
mrb_value y;
mrb_get_args(mrb, "o", &y);
-
- y = bit_coerce(mrb, y);
- return mrb_fixnum_value(mrb_fixnum(x) & mrb_fixnum(y));
+ bit_op(x, y, and, &);
}
/* 15.2.8.3.10 */
@@ -783,9 +901,7 @@ fix_or(mrb_state *mrb, mrb_value x)
mrb_value y;
mrb_get_args(mrb, "o", &y);
-
- y = bit_coerce(mrb, y);
- return mrb_fixnum_value(mrb_fixnum(x) | mrb_fixnum(y));
+ bit_op(x, y, or, |);
}
/* 15.2.8.3.11 */
@@ -802,9 +918,7 @@ fix_xor(mrb_state *mrb, mrb_value x)
mrb_value y;
mrb_get_args(mrb, "o", &y);
-
- y = bit_coerce(mrb, y);
- return mrb_fixnum_value(mrb_fixnum(x) ^ mrb_fixnum(y));
+ bit_op(x, y, or, ^);
}
#define NUMERIC_SHIFT_WIDTH_MAX (MRB_INT_BIT-1)
@@ -850,15 +964,6 @@ rshift(mrb_int val, mrb_int width)
return mrb_fixnum_value(val >> width);
}
-static inline void
-fix_shift_get_width(mrb_state *mrb, mrb_int *width)
-{
- mrb_value y;
-
- mrb_get_args(mrb, "o", &y);
- *width = mrb_fixnum(bit_coerce(mrb, y));
-}
-
/* 15.2.8.3.12 */
/*
* call-seq:
@@ -872,8 +977,7 @@ fix_lshift(mrb_state *mrb, mrb_value x)
{
mrb_int width, val;
- fix_shift_get_width(mrb, &width);
-
+ mrb_get_args(mrb, "i", &width);
if (width == 0) {
return x;
}
@@ -897,8 +1001,7 @@ fix_rshift(mrb_state *mrb, mrb_value x)
{
mrb_int width, val;
- fix_shift_get_width(mrb, &width);
-
+ mrb_get_args(mrb, "i", &width);
if (width == 0) {
return x;
}
@@ -941,7 +1044,7 @@ fix_to_f(mrb_state *mrb, mrb_value num)
MRB_API mrb_value
mrb_flo_to_fixnum(mrb_state *mrb, mrb_value x)
{
- mrb_int z;
+ mrb_int z = 0;
if (!mrb_float_p(x)) {
mrb_raise(mrb, E_TYPE_ERROR, "non float value");
@@ -1167,6 +1270,7 @@ mrb_init_numeric(mrb_state *mrb)
/* Integer Class */
integer = mrb_define_class(mrb, "Integer", numeric); /* 15.2.8 */
+ MRB_SET_INSTANCE_TT(integer, MRB_TT_FIXNUM);
mrb_undef_class_method(mrb, integer, "new");
mrb_define_method(mrb, integer, "to_i", int_to_i, MRB_ARGS_NONE()); /* 15.2.8.3.24 */
mrb_define_method(mrb, integer, "to_int", int_to_i, MRB_ARGS_NONE());
@@ -1193,12 +1297,19 @@ mrb_init_numeric(mrb_state *mrb)
/* Float Class */
mrb->float_class = fl = mrb_define_class(mrb, "Float", numeric); /* 15.2.9 */
+ MRB_SET_INSTANCE_TT(fl, MRB_TT_FLOAT);
mrb_undef_class_method(mrb, fl, "new");
mrb_define_method(mrb, fl, "+", flo_plus, MRB_ARGS_REQ(1)); /* 15.2.9.3.1 */
mrb_define_method(mrb, fl, "-", flo_minus, MRB_ARGS_REQ(1)); /* 15.2.9.3.2 */
mrb_define_method(mrb, fl, "*", flo_mul, MRB_ARGS_REQ(1)); /* 15.2.9.3.3 */
mrb_define_method(mrb, fl, "%", flo_mod, MRB_ARGS_REQ(1)); /* 15.2.9.3.5 */
mrb_define_method(mrb, fl, "==", flo_eq, MRB_ARGS_REQ(1)); /* 15.2.9.3.7 */
+ mrb_define_method(mrb, fl, "~", flo_rev, MRB_ARGS_NONE());
+ mrb_define_method(mrb, fl, "&", flo_and, MRB_ARGS_REQ(1));
+ mrb_define_method(mrb, fl, "|", flo_or, MRB_ARGS_REQ(1));
+ mrb_define_method(mrb, fl, "^", flo_xor, MRB_ARGS_REQ(1));
+ mrb_define_method(mrb, fl, ">>", flo_lshift, MRB_ARGS_REQ(1));
+ mrb_define_method(mrb, fl, "<<", flo_rshift, MRB_ARGS_REQ(1));
mrb_define_method(mrb, fl, "ceil", flo_ceil, MRB_ARGS_NONE()); /* 15.2.9.3.8 */
mrb_define_method(mrb, fl, "finite?", flo_finite_p, MRB_ARGS_NONE()); /* 15.2.9.3.9 */
mrb_define_method(mrb, fl, "floor", flo_floor, MRB_ARGS_NONE()); /* 15.2.9.3.10 */
diff --git a/src/object.c b/src/object.c
index bb1a4ebc4..eb2c23e63 100644
--- a/src/object.c
+++ b/src/object.c
@@ -8,6 +8,7 @@
#include <mruby/class.h>
#include <mruby/numeric.h>
#include <mruby/string.h>
+#include <mruby/class.h>
MRB_API mrb_bool
mrb_obj_eq(mrb_state *mrb, mrb_value v1, mrb_value v2)
@@ -265,6 +266,7 @@ mrb_init_object(mrb_state *mrb)
struct RClass *f;
mrb->nil_class = n = mrb_define_class(mrb, "NilClass", mrb->object_class);
+ MRB_SET_INSTANCE_TT(n, MRB_TT_TRUE);
mrb_undef_class_method(mrb, n, "new");
mrb_define_method(mrb, n, "&", false_and, MRB_ARGS_REQ(1)); /* 15.2.4.3.1 */
mrb_define_method(mrb, n, "^", false_xor, MRB_ARGS_REQ(1)); /* 15.2.4.3.2 */
@@ -274,6 +276,7 @@ mrb_init_object(mrb_state *mrb)
mrb_define_method(mrb, n, "inspect", nil_inspect, MRB_ARGS_NONE());
mrb->true_class = t = mrb_define_class(mrb, "TrueClass", mrb->object_class);
+ MRB_SET_INSTANCE_TT(t, MRB_TT_TRUE);
mrb_undef_class_method(mrb, t, "new");
mrb_define_method(mrb, t, "&", true_and, MRB_ARGS_REQ(1)); /* 15.2.5.3.1 */
mrb_define_method(mrb, t, "^", true_xor, MRB_ARGS_REQ(1)); /* 15.2.5.3.2 */
@@ -282,6 +285,7 @@ mrb_init_object(mrb_state *mrb)
mrb_define_method(mrb, t, "inspect", true_to_s, MRB_ARGS_NONE());
mrb->false_class = f = mrb_define_class(mrb, "FalseClass", mrb->object_class);
+ MRB_SET_INSTANCE_TT(f, MRB_TT_TRUE);
mrb_undef_class_method(mrb, f, "new");
mrb_define_method(mrb, f, "&", false_and, MRB_ARGS_REQ(1)); /* 15.2.6.3.1 */
mrb_define_method(mrb, f, "^", false_xor, MRB_ARGS_REQ(1)); /* 15.2.6.3.2 */
@@ -348,7 +352,7 @@ mrb_check_convert_type(mrb_state *mrb, mrb_value val, enum mrb_vtype type, const
{
mrb_value v;
- if (mrb_type(val) == type && type != MRB_TT_DATA) return val;
+ if (mrb_type(val) == type && type != MRB_TT_DATA && type != MRB_TT_ISTRUCT) return val;
v = convert_type(mrb, val, tname, method, FALSE);
if (mrb_nil_p(v) || mrb_type(v) != type) return mrb_nil_value();
return v;
@@ -380,7 +384,7 @@ static const struct types {
/* {MRB_TT_VARMAP, "Varmap"}, */ /* internal use: dynamic variables */
/* {MRB_TT_NODE, "Node"}, */ /* internal use: syntax tree node */
/* {MRB_TT_UNDEF, "undef"}, */ /* internal use: #undef; should not happen */
- {-1, 0}
+ {MRB_TT_MAXDEFINE, 0}
};
MRB_API void
@@ -390,7 +394,7 @@ mrb_check_type(mrb_state *mrb, mrb_value x, enum mrb_vtype t)
enum mrb_vtype xt;
xt = mrb_type(x);
- if ((xt != t) || (xt == MRB_TT_DATA)) {
+ if ((xt != t) || (xt == MRB_TT_DATA) || (xt == MRB_TT_ISTRUCT)) {
while (type->type < MRB_TT_MAXDEFINE) {
if (type->type == t) {
const char *etype;
@@ -440,7 +444,7 @@ mrb_any_to_s(mrb_state *mrb, mrb_value obj)
mrb_str_cat_lit(mrb, str, "#<");
mrb_str_cat_cstr(mrb, str, cname);
mrb_str_cat_lit(mrb, str, ":");
- mrb_str_concat(mrb, str, mrb_ptr_to_str(mrb, mrb_cptr(obj)));
+ mrb_str_concat(mrb, str, mrb_ptr_to_str(mrb, mrb_ptr(obj)));
mrb_str_cat_lit(mrb, str, ">");
return str;
diff --git a/src/proc.c b/src/proc.c
index 1620bddf8..a75774667 100644
--- a/src/proc.c
+++ b/src/proc.c
@@ -140,7 +140,7 @@ mrb_proc_copy(struct RProc *a, struct RProc *b)
{
a->flags = b->flags;
a->body = b->body;
- if (!MRB_PROC_CFUNC_P(a)) {
+ if (!MRB_PROC_CFUNC_P(a) && a->body.irep) {
a->body.irep->refcnt++;
}
a->target_class = b->target_class;
@@ -148,19 +148,22 @@ mrb_proc_copy(struct RProc *a, struct RProc *b)
}
static mrb_value
-mrb_proc_initialize(mrb_state *mrb, mrb_value self)
+mrb_proc_s_new(mrb_state *mrb, mrb_value proc_class)
{
mrb_value blk;
+ mrb_value proc;
+ struct RProc *p;
mrb_get_args(mrb, "&", &blk);
if (mrb_nil_p(blk)) {
/* Calling Proc.new without a block is not implemented yet */
mrb_raise(mrb, E_ARGUMENT_ERROR, "tried to create Proc object without a block");
}
- else {
- mrb_proc_copy(mrb_proc_ptr(self), mrb_proc_ptr(blk));
- }
- return self;
+ p = (struct RProc *)mrb_obj_alloc(mrb, MRB_TT_PROC, mrb_class_ptr(proc_class));
+ mrb_proc_copy(p, mrb_proc_ptr(blk));
+ proc = mrb_obj_value(p);
+ mrb_funcall_with_block(mrb, proc, mrb_intern_lit(mrb, "initialize"), 0, NULL, blk);
+ return proc;
}
static mrb_value
@@ -188,18 +191,13 @@ mrb_proc_call_cfunc(mrb_state *mrb, struct RProc *p, mrb_value self)
return (p->body.func)(mrb, self);
}
-mrb_code*
-mrb_proc_iseq(mrb_state *mrb, struct RProc *p)
-{
- return p->body.irep->iseq;
-}
-
/* 15.2.17.4.2 */
static mrb_value
mrb_proc_arity(mrb_state *mrb, mrb_value self)
{
struct RProc *p = mrb_proc_ptr(self);
- mrb_code *iseq = mrb_proc_iseq(mrb, p);
+ struct mrb_irep *irep;
+ mrb_code *iseq;
mrb_aspec aspec;
int ma, op, ra, pa, arity;
@@ -208,6 +206,12 @@ mrb_proc_arity(mrb_state *mrb, mrb_value self)
return mrb_fixnum_value(-1);
}
+ irep = p->body.irep;
+ if (!irep) {
+ return mrb_fixnum_value(0);
+ }
+
+ iseq = irep->iseq;
/* arity is depend on OP_ENTER */
if (GET_OPCODE(*iseq) != OP_ENTER) {
return mrb_fixnum_value(0);
@@ -267,7 +271,7 @@ mrb_init_proc(mrb_state *mrb)
call_irep->iseq = call_iseq;
call_irep->ilen = 1;
- mrb_define_method(mrb, mrb->proc_class, "initialize", mrb_proc_initialize, MRB_ARGS_NONE());
+ mrb_define_class_method(mrb, mrb->proc_class, "new", mrb_proc_s_new, MRB_ARGS_ANY());
mrb_define_method(mrb, mrb->proc_class, "initialize_copy", mrb_proc_init_copy, MRB_ARGS_REQ(1));
mrb_define_method(mrb, mrb->proc_class, "arity", mrb_proc_arity, MRB_ARGS_NONE());
diff --git a/src/range.c b/src/range.c
index 079a1035e..2cb6f2361 100644
--- a/src/range.c
+++ b/src/range.c
@@ -12,6 +12,17 @@
#define RANGE_CLASS (mrb_class_get(mrb, "Range"))
+MRB_API struct RRange*
+mrb_range_ptr(mrb_state *mrb, mrb_value v)
+{
+ struct RRange *r = (struct RRange*)mrb_ptr(v);
+
+ if (r->edges == NULL) {
+ mrb_raise(mrb, E_ARGUMENT_ERROR, "uninitialized range");
+ }
+ return r;
+}
+
static void
range_check(mrb_state *mrb, mrb_value a, mrb_value b)
{
@@ -57,7 +68,7 @@ mrb_range_new(mrb_state *mrb, mrb_value beg, mrb_value end, mrb_bool excl)
mrb_value
mrb_range_beg(mrb_state *mrb, mrb_value range)
{
- struct RRange *r = mrb_range_ptr(range);
+ struct RRange *r = mrb_range_ptr(mrb, range);
return r->edges->beg;
}
@@ -76,7 +87,7 @@ mrb_range_beg(mrb_state *mrb, mrb_value range)
mrb_value
mrb_range_end(mrb_state *mrb, mrb_value range)
{
- struct RRange *r = mrb_range_ptr(range);
+ struct RRange *r = mrb_range_ptr(mrb, range);
return r->edges->end;
}
@@ -90,7 +101,7 @@ mrb_range_end(mrb_state *mrb, mrb_value range)
mrb_value
mrb_range_excl(mrb_state *mrb, mrb_value range)
{
- struct RRange *r = mrb_range_ptr(range);
+ struct RRange *r = mrb_range_ptr(mrb, range);
return mrb_bool_value(r->excl);
}
@@ -98,7 +109,7 @@ mrb_range_excl(mrb_state *mrb, mrb_value range)
static void
range_init(mrb_state *mrb, mrb_value range, mrb_value beg, mrb_value end, mrb_bool exclude_end)
{
- struct RRange *r = mrb_range_ptr(range);
+ struct RRange *r = mrb_range_raw_ptr(range);
range_check(mrb, beg, end);
r->excl = exclude_end;
@@ -129,6 +140,9 @@ mrb_range_initialize(mrb_state *mrb, mrb_value range)
exclusive = FALSE;
}
/* Ranges are immutable, so that they should be initialized only once. */
+ if (mrb_range_raw_ptr(range)->edges) {
+ mrb_name_error(mrb, mrb_intern_lit(mrb, "initialize"), "`initialize' called twice");
+ }
range_init(mrb, range, beg, end, exclusive);
return range;
}
@@ -161,8 +175,8 @@ mrb_range_eq(mrb_state *mrb, mrb_value range)
return mrb_false_value();
}
- rr = mrb_range_ptr(range);
- ro = mrb_range_ptr(obj);
+ rr = mrb_range_ptr(mrb, range);
+ ro = mrb_range_ptr(mrb, obj);
v1 = mrb_funcall(mrb, rr->edges->beg, "==", 1, ro->edges->beg);
v2 = mrb_funcall(mrb, rr->edges->end, "==", 1, ro->edges->end);
if (!mrb_bool(v1) || !mrb_bool(v2) || rr->excl != ro->excl) {
@@ -219,7 +233,7 @@ mrb_value
mrb_range_include(mrb_state *mrb, mrb_value range)
{
mrb_value val;
- struct RRange *r = mrb_range_ptr(range);
+ struct RRange *r = mrb_range_ptr(mrb, range);
mrb_value beg, end;
mrb_bool include_p;
@@ -227,31 +241,32 @@ mrb_range_include(mrb_state *mrb, mrb_value range)
beg = r->edges->beg;
end = r->edges->end;
- include_p = r_le(mrb, beg, val) && /* beg <= val */
- ((r->excl && r_gt(mrb, end, val)) || /* end > val */
- (r_ge(mrb, end, val))); /* end >= val */
+ include_p = r_le(mrb, beg, val) && /* beg <= val */
+ (r->excl ? r_gt(mrb, end, val) /* end > val */
+ : r_ge(mrb, end, val)); /* end >= val */
return mrb_bool_value(include_p);
}
-static mrb_bool
-range_beg_len(mrb_state *mrb, mrb_value range, mrb_int *begp, mrb_int *lenp, mrb_int len, mrb_bool trunc)
+MRB_API mrb_int
+mrb_range_beg_len(mrb_state *mrb, mrb_value range, mrb_int *begp, mrb_int *lenp, mrb_int len, mrb_bool trunc)
{
mrb_int beg, end;
- struct RRange *r = mrb_range_ptr(range);
+ struct RRange *r;
- if (mrb_type(range) != MRB_TT_RANGE) return FALSE;
+ if (mrb_type(range) != MRB_TT_RANGE) return 0;
+ r = mrb_range_ptr(mrb, range);
beg = mrb_int(mrb, r->edges->beg);
end = mrb_int(mrb, r->edges->end);
if (beg < 0) {
beg += len;
- if (beg < 0) return FALSE;
+ if (beg < 0) return 2;
}
if (trunc) {
- if (beg > len) return FALSE;
+ if (beg > len) return 2;
if (end > len) end = len;
}
@@ -263,13 +278,7 @@ range_beg_len(mrb_state *mrb, mrb_value range, mrb_int *begp, mrb_int *lenp, mrb
*begp = beg;
*lenp = len;
- return TRUE;
-}
-
-MRB_API mrb_bool
-mrb_range_beg_len(mrb_state *mrb, mrb_value range, mrb_int *begp, mrb_int *lenp, mrb_int len)
-{
- return range_beg_len(mrb, range, begp, lenp, len, TRUE);
+ return 1;
}
/* 15.2.14.4.12(x) */
@@ -284,7 +293,7 @@ static mrb_value
range_to_s(mrb_state *mrb, mrb_value range)
{
mrb_value str, str2;
- struct RRange *r = mrb_range_ptr(range);
+ struct RRange *r = mrb_range_ptr(mrb, range);
str = mrb_obj_as_string(mrb, r->edges->beg);
str2 = mrb_obj_as_string(mrb, r->edges->end);
@@ -309,7 +318,7 @@ static mrb_value
range_inspect(mrb_state *mrb, mrb_value range)
{
mrb_value str, str2;
- struct RRange *r = mrb_range_ptr(range);
+ struct RRange *r = mrb_range_ptr(mrb, range);
str = mrb_inspect(mrb, r->edges->beg);
str2 = mrb_inspect(mrb, r->edges->end);
@@ -349,8 +358,8 @@ range_eql(mrb_state *mrb, mrb_value range)
}
if (mrb_type(obj) != MRB_TT_RANGE) return mrb_false_value();
- r = mrb_range_ptr(range);
- o = mrb_range_ptr(obj);
+ r = mrb_range_ptr(mrb, range);
+ o = mrb_range_ptr(mrb, obj);
if (!mrb_eql(mrb, r->edges->beg, o->edges->beg) ||
!mrb_eql(mrb, r->edges->end, o->edges->end) ||
(r->excl != o->excl)) {
@@ -373,7 +382,7 @@ range_initialize_copy(mrb_state *mrb, mrb_value copy)
mrb_raise(mrb, E_TYPE_ERROR, "wrong argument class");
}
- r = mrb_range_ptr(src);
+ r = mrb_range_ptr(mrb, src);
range_init(mrb, copy, r->edges->beg, r->edges->end, r->excl);
return copy;
@@ -390,7 +399,7 @@ mrb_get_values_at(mrb_state *mrb, mrb_value obj, mrb_int olen, mrb_int argc, con
if (mrb_fixnum_p(argv[i])) {
mrb_ary_push(mrb, result, func(mrb, obj, mrb_fixnum(argv[i])));
}
- else if (range_beg_len(mrb, argv[i], &beg, &len, olen, FALSE)) {
+ else if (mrb_range_beg_len(mrb, argv[i], &beg, &len, olen, FALSE) == 1) {
mrb_int const end = olen < beg + len ? olen : beg + len;
for (j = beg; j < end; ++j) {
mrb_ary_push(mrb, result, func(mrb, obj, j));
diff --git a/src/state.c b/src/state.c
index 1259ac3a0..11b71dd63 100644
--- a/src/state.c
+++ b/src/state.c
@@ -159,7 +159,9 @@ mrb_irep_free(mrb_state *mrb, mrb_irep *irep)
}
mrb_free(mrb, irep->reps);
mrb_free(mrb, irep->lv);
- mrb_free(mrb, (void *)irep->filename);
+ if (irep->own_filename) {
+ mrb_free(mrb, (void *)irep->filename);
+ }
mrb_free(mrb, irep->lines);
mrb_debug_info_free(mrb, irep->debug_info);
mrb_free(mrb, irep);
@@ -261,6 +263,7 @@ mrb_add_irep(mrb_state *mrb)
irep = (mrb_irep *)mrb_malloc(mrb, sizeof(mrb_irep));
*irep = mrb_irep_zero;
irep->refcnt = 1;
+ irep->own_filename = FALSE;
return irep;
}
diff --git a/src/string.c b/src/string.c
index 15fcc502a..acf32167d 100644
--- a/src/string.c
+++ b/src/string.c
@@ -118,9 +118,12 @@ mrb_str_buf_new(mrb_state *mrb, size_t capa)
return mrb_obj_value(s);
}
-static inline void
-resize_capa(mrb_state *mrb, struct RString *s, mrb_int capacity)
+static void
+resize_capa(mrb_state *mrb, struct RString *s, size_t capacity)
{
+#if SIZE_MAX > MRB_INT_MAX
+ mrb_assert(capacity < MRB_INT_MAX);
+#endif
if (RSTR_EMBED_P(s)) {
if (RSTRING_EMBED_LEN_MAX < capacity) {
char *const tmp = (char *)mrb_malloc(mrb, capacity+1);
@@ -129,12 +132,12 @@ resize_capa(mrb_state *mrb, struct RString *s, mrb_int capacity)
RSTR_UNSET_EMBED_FLAG(s);
s->as.heap.ptr = tmp;
s->as.heap.len = len;
- s->as.heap.aux.capa = capacity;
+ s->as.heap.aux.capa = (mrb_int)capacity;
}
}
else {
- s->as.heap.ptr = (char *)mrb_realloc(mrb, RSTR_PTR(s), capacity+1);
- s->as.heap.aux.capa = capacity;
+ s->as.heap.ptr = (char*)mrb_realloc(mrb, RSTR_PTR(s), capacity+1);
+ s->as.heap.aux.capa = (mrb_int)capacity;
}
}
@@ -151,22 +154,26 @@ str_buf_cat(mrb_state *mrb, struct RString *s, const char *ptr, size_t len)
off = ptr - RSTR_PTR(s);
}
- if (RSTR_EMBED_P(s))
- capa = RSTRING_EMBED_LEN_MAX;
- else
- capa = s->as.heap.aux.capa;
+ capa = RSTR_CAPA(s);
+ if (capa <= RSTRING_EMBED_LEN_MAX)
+ capa = RSTRING_EMBED_LEN_MAX+1;
- if (RSTR_LEN(s) >= MRB_INT_MAX - (mrb_int)len) {
+ total = RSTR_LEN(s)+len;
+ if (total >= MRB_INT_MAX) {
+ size_error:
mrb_raise(mrb, E_ARGUMENT_ERROR, "string size too big");
}
- total = RSTR_LEN(s)+len;
if (capa <= total) {
while (total > capa) {
- if (capa + 1 >= MRB_INT_MAX / 2) {
- capa = (total + 4095) / 4096;
- break;
+ if (capa <= MRB_INT_MAX / 2) {
+ capa *= 2;
}
- capa = (capa + 1) * 2;
+ else {
+ capa = total;
+ }
+ }
+ if (capa < total || capa > MRB_INT_MAX) {
+ goto size_error;
}
resize_capa(mrb, s, capa);
}
@@ -361,7 +368,7 @@ mrb_memsearch(const void *x0, mrb_int m, const void *y0, mrb_int n)
return 0;
}
else if (m == 1) {
- const unsigned char *ys = memchr(y, *x, n);
+ const unsigned char *ys = (const unsigned char *)memchr(y, *x, n);
if (ys)
return ys - y;
@@ -416,7 +423,7 @@ byte_subseq(mrb_state *mrb, mrb_value str, mrb_int beg, mrb_int len)
mrb_shared_string *shared;
orig = mrb_str_ptr(str);
- if (RSTR_EMBED_P(orig)) {
+ if (RSTR_EMBED_P(orig) || RSTR_LEN(orig) == 0) {
s = str_new(mrb, orig->as.ary+beg, len);
}
else {
@@ -501,7 +508,7 @@ str_index(mrb_state *mrb, mrb_value str, mrb_value sub, mrb_int offset)
static void
check_frozen(mrb_state *mrb, struct RString *s)
{
- if (RSTR_FROZEN_P(s)) {
+ if (MRB_FROZEN_P(s)) {
mrb_raise(mrb, E_RUNTIME_ERROR, "can't modify frozen string");
}
}
@@ -512,6 +519,9 @@ str_replace(mrb_state *mrb, struct RString *s1, struct RString *s2)
long len;
check_frozen(mrb, s1);
+ s1->flags &= ~MRB_STR_NO_UTF;
+ s1->flags |= s2->flags&MRB_STR_NO_UTF;
+ if (s1 == s2) return mrb_obj_value(s1);
len = RSTR_LEN(s2);
if (RSTR_SHARED_P(s1)) {
str_decref(mrb, s1->as.heap.aux.shared);
@@ -662,7 +672,7 @@ mrb_str_modify(mrb_state *mrb, struct RString *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) {
+ if (shared->nofree == 0 && shared->refcnt == 1 && s->as.heap.ptr == shared->ptr) {
s->as.heap.ptr = shared->ptr;
s->as.heap.aux.capa = shared->len;
RSTR_PTR(s)[s->as.heap.len] = '\0';
@@ -688,27 +698,25 @@ mrb_str_modify(mrb_state *mrb, struct RString *s)
}
if (RSTR_NOFREE_P(s)) {
char *p = s->as.heap.ptr;
+ mrb_int len = s->as.heap.len;
- s->as.heap.ptr = (char *)mrb_malloc(mrb, (size_t)s->as.heap.len+1);
+ RSTR_UNSET_NOFREE_FLAG(s);
+ if (len < RSTRING_EMBED_LEN_MAX) {
+ RSTR_SET_EMBED_FLAG(s);
+ RSTR_SET_EMBED_LEN(s, len);
+ }
+ else {
+ s->as.heap.ptr = (char *)mrb_malloc(mrb, (size_t)len+1);
+ s->as.heap.aux.capa = len;
+ }
if (p) {
- memcpy(RSTR_PTR(s), p, s->as.heap.len);
+ memcpy(RSTR_PTR(s), p, len);
}
- RSTR_PTR(s)[s->as.heap.len] = '\0';
- s->as.heap.aux.capa = s->as.heap.len;
- RSTR_UNSET_NOFREE_FLAG(s);
+ RSTR_PTR(s)[len] = '\0';
return;
}
}
-static mrb_value
-mrb_str_freeze(mrb_state *mrb, mrb_value str)
-{
- struct RString *s = mrb_str_ptr(str);
-
- RSTR_SET_FROZEN_FLAG(s);
- return str;
-}
-
MRB_API mrb_value
mrb_str_resize(mrb_state *mrb, mrb_value str, mrb_int len)
{
@@ -760,8 +768,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);
+ if (RSTR_LEN(s2) == 0) {
+ return;
+ }
len = RSTR_LEN(s1) + RSTR_LEN(s2);
+ if (len < 0 || len >= MRB_INT_MAX) {
+ mrb_raise(mrb, E_ARGUMENT_ERROR, "string size too big");
+ }
if (RSTRING_CAPA(self) < len) {
resize_capa(mrb, s1, len);
}
@@ -1079,22 +1093,25 @@ num_index:
return mrb_nil_value();
case MRB_TT_RANGE:
- /* check if indx is Range */
- {
- mrb_int beg, len;
+ goto range_arg;
- len = RSTRING_CHAR_LEN(str);
- if (mrb_range_beg_len(mrb, indx, &beg, &len, len)) {
- return str_subseq(mrb, str, beg, len);
- }
- else {
- return mrb_nil_value();
- }
- }
- case MRB_TT_FLOAT:
default:
indx = mrb_Integer(mrb, indx);
if (mrb_nil_p(indx)) {
+ range_arg:
+ {
+ mrb_int beg, len;
+
+ len = RSTRING_CHAR_LEN(str);
+ switch (mrb_range_beg_len(mrb, indx, &beg, &len, len, TRUE)) {
+ case 1:
+ return str_subseq(mrb, str, beg, len);
+ case 2:
+ return mrb_nil_value();
+ default:
+ break;
+ }
+ }
mrb_raise(mrb, E_TYPE_ERROR, "can't convert to Fixnum");
}
idx = mrb_fixnum(indx);
@@ -1235,11 +1252,13 @@ mrb_str_chomp_bang(mrb_state *mrb, mrb_value str)
char *p, *pp;
mrb_int rslen;
mrb_int len;
+ mrb_int argc;
struct RString *s = mrb_str_ptr(str);
mrb_str_modify(mrb, s);
+ argc = mrb_get_args(mrb, "|S", &rs);
len = RSTR_LEN(s);
- if (mrb_get_args(mrb, "|S", &rs) == 0) {
+ if (argc == 0) {
if (len == 0) return mrb_nil_value();
smart_chomp:
if (RSTR_PTR(s)[len-1] == '\n') {
@@ -1530,22 +1549,12 @@ mrb_str_hash_m(mrb_state *mrb, mrb_value self)
static mrb_value
mrb_str_include(mrb_state *mrb, mrb_value self)
{
- mrb_int i;
mrb_value str2;
- mrb_bool include_p;
-
- mrb_get_args(mrb, "o", &str2);
- if (mrb_fixnum_p(str2)) {
- include_p = (memchr(RSTRING_PTR(self), mrb_fixnum(str2), RSTRING_LEN(self)) != NULL);
- }
- else {
- str2 = mrb_str_to_str(mrb, str2);
- i = str_index(mrb, self, str2, 0);
- include_p = (i != -1);
- }
-
- return mrb_bool_value(include_p);
+ mrb_get_args(mrb, "S", &str2);
+ if (str_index(mrb, self, str2, 0) < 0)
+ return mrb_bool_value(FALSE);
+ return mrb_bool_value(TRUE);
}
/* 15.2.10.5.22 */
@@ -1764,7 +1773,7 @@ mrb_str_reverse_bang(mrb_state *mrb, mrb_value str)
mrb_str_modify(mrb, mrb_str_ptr(str));
len = RSTRING_LEN(str);
- buf = mrb_malloc(mrb, (size_t)len);
+ buf = (char*)mrb_malloc(mrb, (size_t)len);
p = buf;
e = buf + len;
@@ -2225,7 +2234,7 @@ mrb_string_value_cstr(mrb_state *mrb, mrb_value *ptr)
char *p = RSTR_PTR(ps);
if (!p || p[len] != '\0') {
- if (RSTR_FROZEN_P(ps)) {
+ if (MRB_FROZEN_P(ps)) {
*ptr = str = mrb_str_dup(mrb, str);
ps = mrb_str_ptr(str);
}
@@ -2294,7 +2303,7 @@ mrb_cstr_to_dbl(mrb_state *mrb, const char * p, mrb_bool badcheck)
if (!badcheck && p[0] == '0' && (p[1] == 'x' || p[1] == 'X')) {
return 0.0;
}
- d = strtod(p, &end);
+ d = mrb_float_read(p, &end);
if (p == end) {
if (badcheck) {
bad:
@@ -2332,7 +2341,7 @@ bad:
return 0.0;
}
- d = strtod(p, &end);
+ d = mrb_float_read(p, &end);
if (badcheck) {
if (!end || p == end) goto bad;
while (*end && ISSPACE(*end)) end++;
@@ -2637,7 +2646,7 @@ mrb_str_inspect(mrb_state *mrb, mrb_value str)
}
#endif
c = *p;
- if (c == '"'|| c == '\\' || (c == '#' && IS_EVSTR(p, pend))) {
+ if (c == '"'|| c == '\\' || (c == '#' && IS_EVSTR(p+1, pend))) {
buf[0] = '\\'; buf[1] = c;
mrb_str_cat(mrb, result, buf, 2);
continue;
@@ -2723,8 +2732,8 @@ mrb_init_string(mrb_state *mrb)
mrb_define_method(mrb, s, "capitalize!", mrb_str_capitalize_bang, MRB_ARGS_NONE()); /* 15.2.10.5.8 */
mrb_define_method(mrb, s, "chomp", mrb_str_chomp, MRB_ARGS_ANY()); /* 15.2.10.5.9 */
mrb_define_method(mrb, s, "chomp!", mrb_str_chomp_bang, MRB_ARGS_ANY()); /* 15.2.10.5.10 */
- mrb_define_method(mrb, s, "chop", mrb_str_chop, MRB_ARGS_REQ(1)); /* 15.2.10.5.11 */
- mrb_define_method(mrb, s, "chop!", mrb_str_chop_bang, MRB_ARGS_REQ(1)); /* 15.2.10.5.12 */
+ mrb_define_method(mrb, s, "chop", mrb_str_chop, MRB_ARGS_NONE()); /* 15.2.10.5.11 */
+ mrb_define_method(mrb, s, "chop!", mrb_str_chop_bang, MRB_ARGS_NONE()); /* 15.2.10.5.12 */
mrb_define_method(mrb, s, "downcase", mrb_str_downcase, MRB_ARGS_NONE()); /* 15.2.10.5.13 */
mrb_define_method(mrb, s, "downcase!", mrb_str_downcase_bang, MRB_ARGS_NONE()); /* 15.2.10.5.14 */
mrb_define_method(mrb, s, "empty?", mrb_str_empty_p, MRB_ARGS_NONE()); /* 15.2.10.5.16 */
@@ -2754,6 +2763,240 @@ mrb_init_string(mrb_state *mrb)
mrb_define_method(mrb, s, "upcase!", mrb_str_upcase_bang, MRB_ARGS_NONE()); /* 15.2.10.5.43 */
mrb_define_method(mrb, s, "inspect", mrb_str_inspect, MRB_ARGS_NONE()); /* 15.2.10.5.46(x) */
mrb_define_method(mrb, s, "bytes", mrb_str_bytes, MRB_ARGS_NONE());
+}
+
+/*
+ * Source code for the "strtod" library procedure.
+ *
+ * Copyright (c) 1988-1993 The Regents of the University of California.
+ * Copyright (c) 1994 Sun Microsystems, Inc.
+ *
+ * Permission to use, copy, modify, and distribute this
+ * software and its documentation for any purpose and without
+ * fee is hereby granted, provided that the above copyright
+ * notice appear in all copies. The University of California
+ * makes no representations about the suitability of this
+ * software for any purpose. It is provided "as is" without
+ * express or implied warranty.
+ *
+ * RCS: @(#) $Id: strtod.c 11708 2007-02-12 23:01:19Z shyouhei $
+ */
- mrb_define_method(mrb, s, "freeze", mrb_str_freeze, MRB_ARGS_NONE());
+#include <ctype.h>
+#include <errno.h>
+
+static const int maxExponent = 511; /* Largest possible base 10 exponent. Any
+ * exponent larger than this will already
+ * produce underflow or overflow, so there's
+ * no need to worry about additional digits.
+ */
+static const double powersOf10[] = {/* Table giving binary powers of 10. Entry */
+ 10., /* is 10^2^i. Used to convert decimal */
+ 100., /* exponents into floating-point numbers. */
+ 1.0e4,
+ 1.0e8,
+ 1.0e16,
+ 1.0e32,
+ 1.0e64,
+ 1.0e128,
+ 1.0e256
+};
+
+MRB_API double
+mrb_float_read(const char *string, char **endPtr)
+/* const char *string; A decimal ASCII floating-point number,
+ * optionally preceded by white space.
+ * Must have form "-I.FE-X", where I is the
+ * integer part of the mantissa, F is the
+ * fractional part of the mantissa, and X
+ * is the exponent. Either of the signs
+ * may be "+", "-", or omitted. Either I
+ * or F may be omitted, or both. The decimal
+ * point isn't necessary unless F is present.
+ * The "E" may actually be an "e". E and X
+ * may both be omitted (but not just one).
+ */
+/* char **endPtr; If non-NULL, store terminating character's
+ * address here. */
+{
+ int sign, expSign = FALSE;
+ double fraction, dblExp;
+ const double *d;
+ register const char *p;
+ register int c;
+ int exp = 0; /* Exponent read from "EX" field. */
+ int fracExp = 0; /* Exponent that derives from the fractional
+ * part. Under normal circumstatnces, it is
+ * the negative of the number of digits in F.
+ * However, if I is very long, the last digits
+ * of I get dropped (otherwise a long I with a
+ * large negative exponent could cause an
+ * unnecessary overflow on I alone). In this
+ * case, fracExp is incremented one for each
+ * dropped digit. */
+ int mantSize; /* Number of digits in mantissa. */
+ int decPt; /* Number of mantissa digits BEFORE decimal
+ * point. */
+ const char *pExp; /* Temporarily holds location of exponent
+ * in string. */
+
+ /*
+ * Strip off leading blanks and check for a sign.
+ */
+
+ p = string;
+ while (isspace(*p)) {
+ p += 1;
+ }
+ if (*p == '-') {
+ sign = TRUE;
+ p += 1;
+ } else {
+ if (*p == '+') {
+ p += 1;
+ }
+ sign = FALSE;
+ }
+
+ /*
+ * Count the number of digits in the mantissa (including the decimal
+ * point), and also locate the decimal point.
+ */
+
+ decPt = -1;
+ for (mantSize = 0; ; mantSize += 1)
+ {
+ c = *p;
+ if (!isdigit(c)) {
+ if ((c != '.') || (decPt >= 0)) {
+ break;
+ }
+ decPt = mantSize;
+ }
+ p += 1;
+ }
+
+ /*
+ * Now suck up the digits in the mantissa. Use two integers to
+ * collect 9 digits each (this is faster than using floating-point).
+ * If the mantissa has more than 18 digits, ignore the extras, since
+ * they can't affect the value anyway.
+ */
+
+ pExp = p;
+ p -= mantSize;
+ if (decPt < 0) {
+ decPt = mantSize;
+ } else {
+ mantSize -= 1; /* One of the digits was the point. */
+ }
+ if (mantSize > 18) {
+ if (decPt - 18 > 29999) {
+ fracExp = 29999;
+ } else {
+ fracExp = decPt - 18;
+ }
+ mantSize = 18;
+ } else {
+ fracExp = decPt - mantSize;
+ }
+ if (mantSize == 0) {
+ fraction = 0.0;
+ p = string;
+ goto done;
+ } else {
+ int frac1, frac2;
+ frac1 = 0;
+ for ( ; mantSize > 9; mantSize -= 1)
+ {
+ c = *p;
+ p += 1;
+ if (c == '.') {
+ c = *p;
+ p += 1;
+ }
+ frac1 = 10*frac1 + (c - '0');
+ }
+ frac2 = 0;
+ for (; mantSize > 0; mantSize -= 1)
+ {
+ c = *p;
+ p += 1;
+ if (c == '.') {
+ c = *p;
+ p += 1;
+ }
+ frac2 = 10*frac2 + (c - '0');
+ }
+ fraction = (1.0e9 * frac1) + frac2;
+ }
+
+ /*
+ * Skim off the exponent.
+ */
+
+ p = pExp;
+ if ((*p == 'E') || (*p == 'e')) {
+ p += 1;
+ if (*p == '-') {
+ expSign = TRUE;
+ p += 1;
+ } else {
+ if (*p == '+') {
+ p += 1;
+ }
+ expSign = FALSE;
+ }
+ while (isdigit(*p)) {
+ exp = exp * 10 + (*p - '0');
+ if (exp > 19999) {
+ exp = 19999;
+ }
+ p += 1;
+ }
+ }
+ if (expSign) {
+ exp = fracExp - exp;
+ } else {
+ exp = fracExp + exp;
+ }
+
+ /*
+ * Generate a floating-point number that represents the exponent.
+ * Do this by processing the exponent one bit at a time to combine
+ * many powers of 2 of 10. Then combine the exponent with the
+ * fraction.
+ */
+
+ if (exp < 0) {
+ expSign = TRUE;
+ exp = -exp;
+ } else {
+ expSign = FALSE;
+ }
+ if (exp > maxExponent) {
+ exp = maxExponent;
+ errno = ERANGE;
+ }
+ dblExp = 1.0;
+ for (d = powersOf10; exp != 0; exp >>= 1, d += 1) {
+ if (exp & 01) {
+ dblExp *= *d;
+ }
+ }
+ if (expSign) {
+ fraction /= dblExp;
+ } else {
+ fraction *= dblExp;
+ }
+
+done:
+ if (endPtr != NULL) {
+ *endPtr = (char *) p;
+ }
+
+ if (sign) {
+ return -fraction;
+ }
+ return fraction;
}
diff --git a/src/symbol.c b/src/symbol.c
index 25ae132e1..a3ab05c85 100644
--- a/src/symbol.c
+++ b/src/symbol.c
@@ -10,6 +10,7 @@
#include <mruby/khash.h>
#include <mruby/string.h>
#include <mruby/dump.h>
+#include <mruby/class.h>
/* ------------------------------------------------------ */
typedef struct symbol_name {
@@ -481,6 +482,8 @@ mrb_init_symbol(mrb_state *mrb)
struct RClass *sym;
mrb->symbol_class = sym = mrb_define_class(mrb, "Symbol", mrb->object_class); /* 15.2.11 */
+ MRB_SET_INSTANCE_TT(sym, MRB_TT_SYMBOL);
+ mrb_undef_class_method(mrb, sym, "new");
mrb_define_method(mrb, sym, "===", sym_equal, MRB_ARGS_REQ(1)); /* 15.2.11.3.1 */
mrb_define_method(mrb, sym, "id2name", mrb_sym_to_s, MRB_ARGS_NONE()); /* 15.2.11.3.2 */
diff --git a/src/variable.c b/src/variable.c
index 29f43e69d..390da5455 100644
--- a/src/variable.c
+++ b/src/variable.c
@@ -44,7 +44,7 @@ iv_new(mrb_state *mrb)
{
iv_tbl *t;
- t = mrb_malloc(mrb, sizeof(iv_tbl));
+ t = (iv_tbl*)mrb_malloc(mrb, sizeof(iv_tbl));
t->size = 0;
t->rootseg = NULL;
t->last_len = 0;
@@ -102,7 +102,7 @@ iv_put(mrb_state *mrb, iv_tbl *t, mrb_sym sym, mrb_value val)
return;
}
- seg = mrb_malloc(mrb, sizeof(segment));
+ seg = (segment*)mrb_malloc(mrb, sizeof(segment));
if (!seg) return;
seg->next = NULL;
seg->key[0] = sym;
@@ -489,6 +489,9 @@ mrb_obj_iv_set(mrb_state *mrb, struct RObject *obj, mrb_sym sym, mrb_value v)
{
iv_tbl *t = obj->iv;
+ if (MRB_FROZEN_P(obj)) {
+ mrb_raisef(mrb, E_RUNTIME_ERROR, "can't modify frozen %S", mrb_obj_value(obj));
+ }
if (!t) {
t = obj->iv = iv_new(mrb);
}
diff --git a/src/vm.c b/src/vm.c
index a7418e6e7..96a9966a2 100644
--- a/src/vm.c
+++ b/src/vm.c
@@ -40,6 +40,11 @@ void abort(void);
#define MRB_STACK_GROWTH 128
#endif
+/* Maximum mrb_funcall() depth. Should be set lower on memory constrained systems. */
+#ifndef MRB_FUNCALL_DEPTH_MAX
+#define MRB_FUNCALL_DEPTH_MAX 512
+#endif
+
/* Maximum stack depth. Should be set lower on memory constrained systems.
The value below allows about 60000 recursive calls in the simplest case. */
#ifndef MRB_STACK_MAX
@@ -54,6 +59,10 @@ The value below allows about 60000 recursive calls in the simplest case. */
#define ARENA_RESTORE(mrb,ai) (mrb)->gc.arena_idx = (ai)
+#define CALL_MAXARGS 127
+
+void mrb_method_missing(mrb_state *mrb, mrb_sym name, mrb_value self, mrb_value args);
+
static inline void
stack_clear(mrb_value *from, size_t count)
{
@@ -130,9 +139,10 @@ static void
stack_extend_alloc(mrb_state *mrb, int room, int keep)
{
mrb_value *oldbase = mrb->c->stbase;
- int size = mrb->c->stend - mrb->c->stbase;
- int off = mrb->c->stack - mrb->c->stbase;
+ size_t size = mrb->c->stend - mrb->c->stbase;
+ size_t off = mrb->c->stack - mrb->c->stbase;
+ if (off > size) size = off;
#ifdef MRB_STACK_EXTEND_DOUBLING
if (room <= size)
size *= 2;
@@ -157,13 +167,16 @@ stack_extend_alloc(mrb_state *mrb, int room, int keep)
to prevent infinite recursion. However, do this only after resizing the stack, so mrb_raise has stack space to work with. */
if (size > MRB_STACK_MAX) {
init_new_stack_space(mrb, room, keep);
- mrb_raise(mrb, E_SYSSTACK_ERROR, "stack level too deep. (limit=" MRB_STRINGIZE(MRB_STACK_MAX) ")");
+ mrb_exc_raise(mrb, mrb_obj_value(mrb->stack_err));
}
}
static inline void
stack_extend(mrb_state *mrb, int room, int keep)
{
+ if (room < keep) {
+ room = keep;
+ }
if (mrb->c->stack + room >= mrb->c->stend) {
stack_extend_alloc(mrb, room, keep);
}
@@ -234,6 +247,7 @@ cipush(mrb_state *mrb)
ci->pc = 0;
ci->err = 0;
ci->proc = 0;
+ ci->acc = 0;
return ci;
}
@@ -274,13 +288,17 @@ ecall(mrb_state *mrb, int i)
mrb_callinfo *ci;
mrb_value *self = mrb->c->stack;
struct RObject *exc;
+ int cioff;
+ mrb_value *nstk;
if (i<0) return;
p = mrb->c->ensure[i];
if (!p) return;
if (mrb->c->ci->eidx > i)
mrb->c->ci->eidx = i;
+ cioff = mrb->c->ci - mrb->c->cibase;
ci = cipush(mrb);
+ nstk = ci->stackent;
ci->stackent = mrb->c->stack;
ci->mid = ci[-1].mid;
ci->acc = CI_ACC_SKIP;
@@ -292,6 +310,8 @@ ecall(mrb_state *mrb, int i)
exc = mrb->exc; mrb->exc = 0;
mrb_run(mrb, p, *self);
mrb->c->ensure[i] = NULL;
+ ci->stackent = nstk;
+ mrb->c->ci = mrb->c->cibase + cioff;
if (!mrb->exc) mrb->exc = exc;
}
@@ -362,10 +382,17 @@ mrb_funcall_with_block(mrb_state *mrb, mrb_value self, mrb_sym mid, mrb_int argc
c = mrb_class(mrb, self);
p = mrb_method_search_vm(mrb, &c, mid);
if (!p) {
+ mrb_sym missing = mrb_intern_lit(mrb, "method_missing");
+ p = mrb_method_search_vm(mrb, &c, missing);
+ if (!p) {
+ mrb_value args = mrb_ary_new_from_values(mrb, argc, argv);
+ mrb_method_missing(mrb, mid, self, args);
+ }
undef = mid;
- mid = mrb_intern_lit(mrb, "method_missing");
- p = mrb_method_search_vm(mrb, &c, mid);
- n++; argc++;
+ argc++;
+ }
+ if (mrb->c->ci - mrb->c->cibase > MRB_FUNCALL_DEPTH_MAX) {
+ mrb_exc_raise(mrb, mrb_obj_value(mrb->stack_err));
}
ci = cipush(mrb);
ci->mid = mid;
@@ -381,8 +408,18 @@ mrb_funcall_with_block(mrb_state *mrb, mrb_value self, mrb_sym mid, mrb_int argc
ci->nregs = argc + 2;
stack_extend(mrb, ci->nregs, 0);
}
+ else if (argc >= CALL_MAXARGS) {
+ mrb_value args = mrb_ary_new_from_values(mrb, argc, argv);
+ stack_extend(mrb, ci->nregs, 0);
+ mrb->c->stack[1] = args;
+ if (undef) {
+ mrb_ary_unshift(mrb, mrb->c->stack[1], mrb_symbol_value(undef));
+ }
+ ci->argc = -1;
+ argc = 1;
+ }
else {
- ci->nregs = p->body.irep->nregs + n;
+ ci->nregs = p->body.irep->nregs + argc;
stack_extend(mrb, ci->nregs, argc+2);
}
if (voff >= 0) {
@@ -395,7 +432,7 @@ mrb_funcall_with_block(mrb_state *mrb, mrb_value self, mrb_sym mid, mrb_int argc
stack_copy(mrb->c->stack+2, argv, argc-1);
}
}
- else if (argc > 0) {
+ else if (ci->argc > 0) {
stack_copy(mrb->c->stack+1, argv, argc);
}
mrb->c->stack[argc+1] = blk;
@@ -424,6 +461,33 @@ mrb_funcall_argv(mrb_state *mrb, mrb_value self, mrb_sym mid, mrb_int argc, cons
return mrb_funcall_with_block(mrb, self, mid, argc, argv, mrb_nil_value());
}
+mrb_value
+mrb_exec_irep(mrb_state *mrb, mrb_value self, struct RProc *p)
+{
+ mrb_callinfo *ci = mrb->c->ci;
+
+ ci->proc = p;
+ if (MRB_PROC_CFUNC_P(p)) {
+ return p->body.func(mrb, self);
+ }
+ if (ci->argc < 0) {
+ stack_extend(mrb, (p->body.irep->nregs < 3) ? 3 : p->body.irep->nregs, 3);
+ }
+ else {
+ stack_extend(mrb, p->body.irep->nregs, ci->argc+2);
+ }
+
+ ci->nregs = p->body.irep->nregs;
+ ci = cipush(mrb);
+ ci->nregs = 0;
+ ci->target_class = 0;
+ ci->pc = p->body.irep->iseq;
+ ci->stackent = mrb->c->stack;
+ ci->acc = 0;
+
+ return self;
+}
+
/* 15.3.1.3.4 */
/* 15.3.1.3.44 */
/*
@@ -465,7 +529,6 @@ mrb_f_send(mrb_state *mrb, mrb_value self)
ci = mrb->c->ci;
ci->mid = name;
ci->target_class = c;
- ci->proc = p;
regs = mrb->c->stack+1;
/* remove first symbol from arguments */
if (ci->argc >= 0) {
@@ -478,26 +541,7 @@ mrb_f_send(mrb_state *mrb, mrb_value self)
mrb_ary_shift(mrb, regs[0]);
}
- if (MRB_PROC_CFUNC_P(p)) {
- return p->body.func(mrb, self);
- }
-
- if (ci->argc < 0) {
- stack_extend(mrb, (p->body.irep->nregs < 3) ? 3 : p->body.irep->nregs, 3);
- }
- else {
- stack_extend(mrb, p->body.irep->nregs, ci->argc+2);
- }
-
- ci->nregs = p->body.irep->nregs;
- ci = cipush(mrb);
- ci->nregs = 0;
- ci->target_class = 0;
- ci->pc = p->body.irep->iseq;
- ci->stackent = mrb->c->stack;
- ci->acc = 0;
-
- return self;
+ return mrb_exec_irep(mrb, self, p);
}
static mrb_value
@@ -646,11 +690,13 @@ mrb_yield_with_class(mrb_state *mrb, mrb_value b, mrb_int argc, const mrb_value
if (MRB_PROC_CFUNC_P(p)) {
val = p->body.func(mrb, self);
mrb->c->stack = mrb->c->ci->stackent;
- cipop(mrb);
}
else {
+ int cioff = mrb->c->ci - mrb->c->cibase;
val = mrb_run(mrb, p, self);
+ mrb->c->ci = mrb->c->cibase + cioff;
}
+ cipop(mrb);
return val;
}
@@ -697,15 +743,22 @@ argnum_error(mrb_state *mrb, mrb_int num)
{
mrb_value exc;
mrb_value str;
+ mrb_int argc = mrb->c->ci->argc;
+ if (argc < 0) {
+ mrb_value args = mrb->c->stack[1];
+ if (mrb_array_p(args)) {
+ argc = RARRAY_LEN(args);
+ }
+ }
if (mrb->c->ci->mid) {
str = mrb_format(mrb, "'%S': wrong number of arguments (%S for %S)",
mrb_sym2str(mrb, mrb->c->ci->mid),
- mrb_fixnum_value(mrb->c->ci->argc), mrb_fixnum_value(num));
+ mrb_fixnum_value(argc), mrb_fixnum_value(num));
}
else {
str = mrb_format(mrb, "wrong number of arguments (%S for %S)",
- mrb_fixnum_value(mrb->c->ci->argc), mrb_fixnum_value(num));
+ mrb_fixnum_value(argc), mrb_fixnum_value(num));
}
exc = mrb_exc_new_str(mrb, E_ARGUMENT_ERROR, str);
mrb_exc_set(mrb, exc);
@@ -749,10 +802,6 @@ argnum_error(mrb_state *mrb, mrb_int num)
#endif
-#define CALL_MAXARGS 127
-
-void mrb_method_missing(mrb_state *mrb, mrb_sym name, mrb_value self, mrb_value args);
-
MRB_API mrb_value
mrb_vm_run(mrb_state *mrb, struct RProc *proc, mrb_value self, unsigned int stack_keep)
{
@@ -831,7 +880,15 @@ RETRY_TRY_BLOCK:
CASE(OP_LOADL) {
/* A Bx R(A) := Pool(Bx) */
+#ifdef MRB_WORD_BOXING
+ mrb_value val = pool[GETARG_Bx(i)];
+ if (mrb_float_p(val)) {
+ val = mrb_float_value(mrb, mrb_float(val));
+ }
+ regs[GETARG_A(i)] = val;
+#else
regs[GETARG_A(i)] = pool[GETARG_Bx(i)];
+#endif
NEXT;
}
@@ -1105,12 +1162,14 @@ RETRY_TRY_BLOCK:
}
if (GET_OPCODE(i) != OP_SENDB) {
SET_NIL_VALUE(regs[bidx]);
+ bidx = 0;
}
else {
mrb_value blk = regs[bidx];
if (!mrb_nil_p(blk) && mrb_type(blk) != MRB_TT_PROC) {
regs[bidx] = mrb_convert_type(mrb, blk, MRB_TT_PROC, "Proc", "to_proc");
}
+ bidx = 1;
}
c = mrb_class(mrb, recv);
m = mrb_method_search_vm(mrb, &c, mid);
@@ -1131,12 +1190,17 @@ RETRY_TRY_BLOCK:
mrb_method_missing(mrb, mid, recv, args);
}
mid = missing;
+ if (n == CALL_MAXARGS-1) {
+ regs[a+1] = mrb_ary_new_from_values(mrb, n, regs+a+1);
+ n++;
+ }
if (n == CALL_MAXARGS) {
mrb_ary_unshift(mrb, regs[a+1], sym);
}
else {
- value_move(regs+a+2, regs+a+1, ++n);
+ value_move(regs+a+2, regs+a+1, n+bidx);
regs[a+1] = sym;
+ n++;
}
}
@@ -1263,7 +1327,9 @@ RETRY_TRY_BLOCK:
else {
stack_extend(mrb, irep->nregs, ci->argc+2);
}
- regs[0] = m->env->stack[0];
+ if(m->env) {
+ regs[0] = m->env->stack[0];
+ }
pc = irep->iseq;
JUMP;
}
@@ -1279,7 +1345,7 @@ RETRY_TRY_BLOCK:
int a = GETARG_A(i);
int n = GETARG_C(i);
- if (mid == 0) {
+ if (mid == 0 || !mrb->c->ci->target_class) {
mrb_value exc;
exc = mrb_exc_new_str_lit(mrb, E_NOMETHOD_ERROR, "super called outside of method");
@@ -1290,8 +1356,24 @@ RETRY_TRY_BLOCK:
c = mrb->c->ci->target_class->super;
m = mrb_method_search_vm(mrb, &c, mid);
if (!m) {
- mid = mrb_intern_lit(mrb, "method_missing");
- m = mrb_method_search_vm(mrb, &c, mid);
+ mrb_sym missing = mrb_intern_lit(mrb, "method_missing");
+ m = mrb_method_search_vm(mrb, &c, missing);
+ if (!m) {
+ mrb_value args;
+
+ if (n == CALL_MAXARGS) {
+ args = regs[a+1];
+ }
+ else {
+ args = mrb_ary_new_from_values(mrb, n, regs+a+1);
+ }
+ mrb_method_missing(mrb, mid, recv, args);
+ }
+ mid = missing;
+ if (n == CALL_MAXARGS-1) {
+ regs[a+1] = mrb_ary_new_from_values(mrb, n, regs+a+1);
+ n++;
+ }
if (n == CALL_MAXARGS) {
mrb_ary_unshift(mrb, regs[a+1], mrb_symbol_value(ci->mid));
}
@@ -1306,11 +1388,22 @@ RETRY_TRY_BLOCK:
ci->mid = mid;
ci->proc = m;
ci->stackent = mrb->c->stack;
- if (n == CALL_MAXARGS) {
- ci->argc = -1;
- }
- else {
- ci->argc = n;
+ {
+ int bidx;
+ mrb_value blk;
+
+ if (n == CALL_MAXARGS) {
+ ci->argc = -1;
+ bidx = a+2;
+ }
+ else {
+ ci->argc = n;
+ bidx = a+n+1;
+ }
+ blk = regs[bidx];
+ if (!mrb_nil_p(blk) && mrb_type(blk) != MRB_TT_PROC) {
+ regs[bidx] = mrb_convert_type(mrb, blk, MRB_TT_PROC, "Proc", "to_proc");
+ }
}
ci->target_class = c;
ci->pc = pc + 1;
@@ -1578,7 +1671,7 @@ RETRY_TRY_BLOCK:
switch (GETARG_B(i)) {
case OP_R_RETURN:
/* Fall through to OP_R_NORMAL otherwise */
- if (proc->env && !MRB_PROC_STRICT_P(proc)) {
+ if (ci->acc >=0 && proc->env && !MRB_PROC_STRICT_P(proc)) {
struct REnv *e = top_env(mrb, proc);
if (!MRB_ENV_STACK_SHARED_P(e)) {
@@ -1647,10 +1740,11 @@ RETRY_TRY_BLOCK:
mrb->jmp = prev_jmp;
return v;
}
- cipop(mrb);
+ ci = mrb->c->ci;
acc = ci->acc;
mrb->c->stack = ci->stackent;
- if (acc == CI_ACC_SKIP) {
+ cipop(mrb);
+ if (acc == CI_ACC_SKIP || acc == CI_ACC_DIRECT) {
mrb->jmp = prev_jmp;
return v;
}
@@ -1681,9 +1775,20 @@ RETRY_TRY_BLOCK:
m = mrb_method_search_vm(mrb, &c, mid);
if (!m) {
mrb_value sym = mrb_symbol_value(mid);
+ mrb_sym missing = mrb_intern_lit(mrb, "method_missing");
+ m = mrb_method_search_vm(mrb, &c, missing);
+ if (!m) {
+ mrb_value args;
- mid = mrb_intern_lit(mrb, "method_missing");
- m = mrb_method_search_vm(mrb, &c, mid);
+ if (n == CALL_MAXARGS) {
+ args = regs[a+1];
+ }
+ else {
+ args = mrb_ary_new_from_values(mrb, n, regs+a+1);
+ }
+ mrb_method_missing(mrb, mid, recv, args);
+ }
+ mid = missing;
if (n == CALL_MAXARGS) {
mrb_ary_unshift(mrb, regs[a+1], sym);
}
@@ -2134,8 +2239,8 @@ RETRY_TRY_BLOCK:
CASE(OP_ARYCAT) {
/* A B mrb_ary_concat(R(A),R(B)) */
- mrb_ary_concat(mrb, regs[GETARG_A(i)],
- mrb_ary_splat(mrb, regs[GETARG_B(i)]));
+ mrb_value splat = mrb_ary_splat(mrb, regs[GETARG_B(i)]);
+ mrb_ary_concat(mrb, regs[GETARG_A(i)], splat);
ARENA_RESTORE(mrb, ai);
NEXT;
}
@@ -2261,7 +2366,7 @@ RETRY_TRY_BLOCK:
CASE(OP_CLASS) {
/* A B R(A) := newclass(R(A),Syms(B),R(A+1)) */
- struct RClass *c = 0;
+ struct RClass *c = 0, *baseclass;
int a = GETARG_A(i);
mrb_value base, super;
mrb_sym id = syms[GETARG_B(i)];
@@ -2269,7 +2374,10 @@ RETRY_TRY_BLOCK:
base = regs[a];
super = regs[a+1];
if (mrb_nil_p(base)) {
- base = mrb_obj_value(mrb->c->ci->target_class);
+ baseclass = mrb->c->ci->proc->target_class;
+ if (!baseclass) baseclass = mrb->c->ci->target_class;
+
+ base = mrb_obj_value(baseclass);
}
c = mrb_vm_define_class(mrb, base, super, id);
regs[a] = mrb_obj_value(c);
@@ -2279,14 +2387,17 @@ RETRY_TRY_BLOCK:
CASE(OP_MODULE) {
/* A B R(A) := newmodule(R(A),Syms(B)) */
- struct RClass *c = 0;
+ struct RClass *c = 0, *baseclass;
int a = GETARG_A(i);
mrb_value base;
mrb_sym id = syms[GETARG_B(i)];
base = regs[a];
if (mrb_nil_p(base)) {
- base = mrb_obj_value(mrb->c->ci->target_class);
+ baseclass = mrb->c->ci->proc->target_class;
+ if (!baseclass) baseclass = mrb->c->ci->target_class;
+
+ base = mrb_obj_value(baseclass);
}
c = mrb_vm_define_module(mrb, base, id);
regs[a] = mrb_obj_value(c);
@@ -2373,7 +2484,8 @@ RETRY_TRY_BLOCK:
CASE(OP_RANGE) {
/* A B C R(A) := range_new(R(B),R(B+1),C) */
int b = GETARG_B(i);
- regs[GETARG_A(i)] = mrb_range_new(mrb, regs[b], regs[b+1], GETARG_C(i));
+ mrb_value val = mrb_range_new(mrb, regs[b], regs[b+1], GETARG_C(i));
+ regs[GETARG_A(i)] = val;
ARENA_RESTORE(mrb, ai);
NEXT;
}
@@ -2439,7 +2551,12 @@ RETRY_TRY_BLOCK:
MRB_API mrb_value
mrb_run(mrb_state *mrb, struct RProc *proc, mrb_value self)
{
- return mrb_vm_run(mrb, proc, self, mrb->c->ci->argc + 2); /* argc + 2 (receiver and block) */
+ if (mrb->c->ci->argc < 0) {
+ return mrb_vm_run(mrb, proc, self, 3); /* receiver, args and block) */
+ }
+ else {
+ return mrb_vm_run(mrb, proc, self, mrb->c->ci->argc + 2); /* argc + 2 (receiver and block) */
+ }
}
MRB_API mrb_value
diff --git a/tasks/libmruby.rake b/tasks/libmruby.rake
index 095bedd52..23663d0a5 100644
--- a/tasks/libmruby.rake
+++ b/tasks/libmruby.rake
@@ -5,17 +5,17 @@ MRuby.each_target do
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('"', '\\"')}"
+ f.puts "MRUBY_CFLAGS = #{cc.all_flags}"
gem_flags = gems.map { |g| g.linker.flags }
gem_library_paths = gems.map { |g| g.linker.library_paths }
- f.puts "MRUBY_LDFLAGS = #{linker.all_flags(gem_library_paths, gem_flags).gsub('"', '\\"')} #{linker.option_library_path % "#{build_dir}/lib"}"
+ f.puts "MRUBY_LDFLAGS = #{linker.all_flags(gem_library_paths, gem_flags)} #{linker.option_library_path % "#{build_dir}/lib"}"
gem_flags_before_libraries = gems.map { |g| g.linker.flags_before_libraries }
- f.puts "MRUBY_LDFLAGS_BEFORE_LIBS = #{[linker.flags_before_libraries, gem_flags_before_libraries].flatten.join(' ').gsub('"', '\\"')}"
+ f.puts "MRUBY_LDFLAGS_BEFORE_LIBS = #{[linker.flags_before_libraries, gem_flags_before_libraries].flatten.join(' ')}"
gem_libraries = gems.map { |g| g.linker.libraries }
- f.puts "MRUBY_LIBS = #{linker.option_library % 'mruby'} #{linker.library_flags(gem_libraries).gsub('"', '\\"')}"
+ f.puts "MRUBY_LIBS = #{linker.option_library % 'mruby'} #{linker.library_flags(gem_libraries)}"
end
end
task :all => "#{build_dir}/lib/libmruby.flags.mak"
diff --git a/tasks/mrbgem_spec.rake b/tasks/mrbgem_spec.rake
index 78d912980..1f7cde529 100644
--- a/tasks/mrbgem_spec.rake
+++ b/tasks/mrbgem_spec.rake
@@ -1,6 +1,7 @@
require 'pathname'
require 'forwardable'
require 'tsort'
+require 'shellwords'
module MRuby
module Gem
@@ -89,7 +90,9 @@ module MRuby
build.libmruby << @objs
instance_eval(&@build_config_initializer) if @build_config_initializer
+ end
+ def setup_compilers
compilers.each do |compiler|
compiler.define_rules build_dir, "#{dir}"
compiler.defines << %Q[MRBGEM_#{funcname.upcase}_VERSION=#{version}]
@@ -126,6 +129,21 @@ module MRuby
"#{build_dir}/gem_test.c"
end
+ def search_package(name, version_query=nil)
+ package_query = name
+ package_query += " #{version_query}" if version_query
+ _pp "PKG-CONFIG", package_query
+ escaped_package_query = Shellwords.escape(package_query)
+ if system("pkg-config --exists #{escaped_package_query}")
+ cc.flags += [`pkg-config --cflags #{escaped_package_query}`.strip]
+ cxx.flags += [`pkg-config --cflags #{escaped_package_query}`.strip]
+ linker.flags_before_libraries += [`pkg-config --libs #{escaped_package_query}`.strip]
+ true
+ else
+ false
+ end
+ end
+
def funcname
@funcname ||= @name.gsub('-', '_')
end
@@ -403,6 +421,8 @@ module MRuby
@ary = tsort_dependencies gem_table.keys, gem_table, true
+ each(&:setup_compilers)
+
each do |g|
import_include_paths(g)
end
diff --git a/tasks/mruby_build.rake b/tasks/mruby_build.rake
index cff45ddf8..bd911493d 100644
--- a/tasks/mruby_build.rake
+++ b/tasks/mruby_build.rake
@@ -120,6 +120,7 @@ module MRuby
def enable_cxx_abi
return if @cxx_exception_disabled or @cxx_abi_enabled
compilers.each { |c| c.defines += %w(MRB_ENABLE_CXX_EXCEPTION) }
+ compilers.each { |c| c.flags << c.cxx_compile_flag }
linker.command = cxx.command if toolchains.find { |v| v == 'gcc' }
@cxx_abi_enabled = true
end
@@ -135,9 +136,13 @@ module MRuby
#define __STDC_CONSTANT_MACROS
#define __STDC_LIMIT_MACROS
+#ifndef MRB_ENABLE_CXX_EXCEPTION
extern "C" {
+#endif
#include "#{src}"
+#ifndef MRB_ENABLE_CXX_EXCEPTION
}
+#endif
#{src == "#{MRUBY_ROOT}/src/error.c"? 'mrb_int mrb_jmpbuf::jmpbuf_id = 0;' : ''}
EOS
diff --git a/tasks/mruby_build_commands.rake b/tasks/mruby_build_commands.rake
index f4805f46d..d688077ff 100644
--- a/tasks/mruby_build_commands.rake
+++ b/tasks/mruby_build_commands.rake
@@ -41,6 +41,7 @@ module MRuby
class Command::Compiler < Command
attr_accessor :flags, :include_paths, :defines, :source_exts
attr_accessor :compile_options, :option_define, :option_include_path, :out_ext
+ attr_accessor :cxx_compile_flag
def initialize(build, source_exts=[])
super(build)
diff --git a/tasks/toolchains/android.rake b/tasks/toolchains/android.rake
index 0cc60a7a3..7759a3f9c 100644
--- a/tasks/toolchains/android.rake
+++ b/tasks/toolchains/android.rake
@@ -203,23 +203,53 @@ Set ANDROID_PLATFORM environment variable or set :platform parameter
def cflags
flags = []
- flags += %W(-D__android__ --sysroot="#{sysroot}")
+ flags += %W(-MMD -MP)
+ flags += %W(-D__android__ -DANDROID --sysroot="#{sysroot}")
case toolchain
when :gcc
- flags += %W(-mandroid)
case arch
- when /armeabi-v7a/ then flags += %W(-march=armv7-a)
- when /armeabi/ then flags += %W(-march=armv5te)
- when /arm64-v8a/ then flags += %W(-march=armv8-a)
+ when /armeabi-v7a/ then flags += %W(-march=armv7-a -mfpu=vfpv3-d16 -mfloat-abi=softfp -fpic)
+ when /armeabi/ then flags += %W(-march=armv5te -mtune=xscale -msoft-float -fpic)
+ when /arm64-v8a/ then flags += %W(-march=armv8-a -fpic)
when /x86_64/ then flags += %W(-march=x86-64)
when /x86/ then flags += %W(-march=i686)
- when /mips64/ then flags += %W(-march=mips64)
- when /mips/ then flags += %W(-march=mips32)
+ when /mips64/ then flags += %W(-march=mips64r6 -fmessage-length=0 -fpic)
+ when /mips/ then flags += %W(-march=mips32 -fmessage-length=0 -fpic)
end
when :clang
flags += %W(-gcc-toolchain "#{gcc_toolchain_path.to_s}")
case arch
- when /armeabi-v7a/ then flags += %W(-target armv7-none-linux-androideabi)
+ when /armeabi-v7a/ then flags += %W(-target armv7-none-linux-androideabi -fpic)
+ when /armeabi/ then flags += %W(-target armv5te-none-linux-androideabi -fpic)
+ when /arm64-v8a/ then flags += %W(-target aarch64-none-linux-android -fpic)
+ when /x86_64/ then flags += %W(-target x86_64-none-linux-android -fPIC)
+ when /x86/ then flags += %W(-target i686-none-linux-android -fPIC)
+ when /mips64/ then flags += %W(-target mips64el-none-linux-android -fmessage-length=0 -fpic)
+ when /mips/ then flags += %W(-target mipsel-none-linux-android -fmessage-length=0 -fpic)
+ end
+ flags += %W(-Wno-invalid-command-line-argument -Wno-unused-command-line-argument)
+ end
+ flags += %W(-ffunction-sections -funwind-tables -fstack-protector-strong -no-canonical-prefixes)
+
+ flags
+ end
+
+ def ldflags
+ flags = []
+
+ flags += %W(--sysroot="#{sysroot}")
+
+ flags
+ end
+
+ def ldflags_before_libraries
+ flags = []
+
+ case toolchain
+ when :clang
+ flags += %W(-gcc-toolchain "#{gcc_toolchain_path.to_s}")
+ case arch
+ when /armeabi-v7a/ then flags += %W(-target armv7-none-linux-androideabi -Wl,--fix-cortex-a8)
when /armeabi/ then flags += %W(-target armv5te-none-linux-androideabi)
when /arm64-v8a/ then flags += %W(-target aarch64-none-linux-android)
when /x86_64/ then flags += %W(-target x86_64-none-linux-android)
@@ -228,10 +258,10 @@ Set ANDROID_PLATFORM environment variable or set :platform parameter
when /mips/ then flags += %W(-target mipsel-none-linux-android)
end
end
+ flags += %W(-no-canonical-prefixes)
flags
end
-
end
MRuby::Toolchain.new(:android) do |conf, params|
@@ -246,5 +276,6 @@ MRuby::Toolchain.new(:android) do |conf, params|
conf.archiver.command = android.ar
conf.linker.command = android.cc
- conf.linker.flags = []
+ conf.linker.flags = android.ldflags
+ conf.linker.flags_before_libraries = android.ldflags_before_libraries
end
diff --git a/tasks/toolchains/gcc.rake b/tasks/toolchains/gcc.rake
index 4897e8c52..59ae015ac 100644
--- a/tasks/toolchains/gcc.rake
+++ b/tasks/toolchains/gcc.rake
@@ -6,6 +6,7 @@ MRuby::Toolchain.new(:gcc) do |conf, _params|
cc.option_include_path = '-I%s'
cc.option_define = '-D%s'
cc.compile_options = '%{flags} -MMD -o %{outfile} -c %{infile}'
+ cc.cxx_compile_flag = '-x c++ -std=c++03'
end
[conf.cxx].each do |cxx|
@@ -15,6 +16,7 @@ MRuby::Toolchain.new(:gcc) do |conf, _params|
cxx.option_include_path = '-I%s'
cxx.option_define = '-D%s'
cxx.compile_options = '%{flags} -MMD -o %{outfile} -c %{infile}'
+ cxx.cxx_compile_flag = '-x c++ -std=c++03'
end
conf.linker do |linker|
diff --git a/tasks/toolchains/visualcpp.rake b/tasks/toolchains/visualcpp.rake
index 52820656b..5f5bab9c2 100644
--- a/tasks/toolchains/visualcpp.rake
+++ b/tasks/toolchains/visualcpp.rake
@@ -7,6 +7,7 @@ MRuby::Toolchain.new(:visualcpp) do |conf, _params|
cc.option_include_path = '/I%s'
cc.option_define = '/D%s'
cc.compile_options = "%{flags} /Fo%{outfile} %{infile}"
+ cc.cxx_compile_flag = '/TP'
end
conf.cxx do |cxx|
@@ -16,6 +17,7 @@ MRuby::Toolchain.new(:visualcpp) do |conf, _params|
cxx.option_include_path = '/I%s'
cxx.option_define = '/D%s'
cxx.compile_options = "%{flags} /Fo%{outfile} %{infile}"
+ cxx.cxx_compile_flag = '/TP'
end
conf.linker do |linker|
diff --git a/test/assert.rb b/test/assert.rb
index e8368c64c..7efc24e19 100644
--- a/test/assert.rb
+++ b/test/assert.rb
@@ -44,7 +44,8 @@ def assert(str = 'Assertion failed', iso = '')
begin
$mrbtest_assert = []
$mrbtest_assert_idx = 0
- if(!yield || $mrbtest_assert.size > 0)
+ yield
+ if($mrbtest_assert.size > 0)
$asserts.push(assertion_string('Fail: ', str, iso, nil))
$ko_test += 1
t_print('F')
@@ -213,7 +214,7 @@ def report()
t_print("\n")
$asserts.each do |msg|
- puts msg
+ t_print "#{msg}\n"
end
$total_test = $ok_test+$ko_test+$kill_test
@@ -232,7 +233,7 @@ end
##
# Performs fuzzy check for equality on methods returning floats
def check_float(a, b)
- tolerance = 1e-12
+ tolerance = Mrbtest::FLOAT_TOLERANCE
a = a.to_f
b = b.to_f
if a.finite? and b.finite?
diff --git a/test/t/array.rb b/test/t/array.rb
index 538ea0c3f..cf7ed4704 100644
--- a/test/t/array.rb
+++ b/test/t/array.rb
@@ -82,6 +82,14 @@ assert('Array#[]=', '15.2.12.5.5') do
a = [1,2,3,4,5]
a[2...4] = 6
assert_equal([1,2,6,5], a)
+
+ # passing self (#3274)
+ a = [1,2,3]
+ a[1,0] = a
+ assert_equal([1,1,2,3,2,3], a)
+ a = [1,2,3]
+ a[-1,0] = a
+ assert_equal([1,2,1,2,3,3], a)
end
assert('Array#clear', '15.2.12.5.6') do
@@ -98,6 +106,11 @@ end
assert('Array#concat', '15.2.12.5.8') do
assert_equal([1,2,3,4], [1, 2].concat([3, 4]))
+
+ # passing self (#3302)
+ a = [1,2,3]
+ a.concat(a)
+ assert_equal([1,2,3,1,2,3], a)
end
assert('Array#delete_at', '15.2.12.5.9') do
@@ -318,7 +331,8 @@ end
assert('Array#hash', '15.2.12.5.35') do
a = [ 1, 2, 3 ]
- assert_true(a.hash.is_a? Integer)
+ #assert_true(a.hash.is_a? Integer)
+ assert_true(a.hash.is_a? Integral) # mruby special
assert_equal([1,2].hash, [1,2].hash)
end
@@ -347,3 +361,22 @@ assert("Array (Longish inline array)") do
ary.each {|p| h[p.class] += 1}
assert_equal({Array=>200}, h)
end
+
+assert("Array#rindex") do
+ class Sneaky
+ def ==(*)
+ $a.clear
+ $a.replace([1])
+ false
+ end
+ end
+ $a = [2, 3, 4, 5, 6, 7, 8, 9, 10, Sneaky.new]
+ assert_equal 0, $a.rindex(1)
+end
+
+assert('Array#freeze') do
+ a = [].freeze
+ assert_raise(RuntimeError) do
+ a[0] = 1
+ end
+end
diff --git a/test/t/class.rb b/test/t/class.rb
index 7bcaaf90d..605b7ec40 100644
--- a/test/t/class.rb
+++ b/test/t/class.rb
@@ -397,7 +397,28 @@ assert('class variable in module and class << self style class method') do
assert_equal("value", ClassVariableInModuleTest.class_variable)
end
+assert('overriding class variable with a module (#3235)') do
+ module ModuleWithCVar
+ @@class_variable = 1
+ end
+ class CVarOverrideTest
+ @@class_variable = 2
+ include ModuleWithCVar
+
+ assert_equal(1, @@class_variable)
+ end
+end
+
assert('class with non-class/module outer raises TypeError') do
assert_raise(TypeError) { class 0::C1; end }
assert_raise(TypeError) { class []::C2; end }
end
+
+assert("remove_method doesn't segfault if the passed in argument isn't a symbol") do
+ klass = Class.new
+ assert_raise(TypeError) { klass.remove_method nil }
+ assert_raise(TypeError) { klass.remove_method 123 }
+ assert_raise(TypeError) { klass.remove_method 1.23 }
+ assert_raise(NameError) { klass.remove_method "hello" }
+ assert_raise(TypeError) { klass.remove_method Class.new }
+end
diff --git a/test/t/codegen.rb b/test/t/codegen.rb
new file mode 100644
index 000000000..709902741
--- /dev/null
+++ b/test/t/codegen.rb
@@ -0,0 +1,93 @@
+##
+# Codegen tests
+
+assert('peephole optimization does not eliminate move whose result is reused') do
+ assert_raise LocalJumpError do
+ def method
+ yield
+ end
+ method(&a &&= 0)
+ end
+end
+
+assert('empty condition in ternary expression parses correctly') do
+ assert_equal(() ? 1 : 2, 2)
+end
+
+assert('codegen absorbs arguments to redo and retry if they are the argument of a call') do
+ assert_nothing_raised do
+ a=*"1", case nil
+ when 1
+ redo |
+ 1
+ end
+ end
+
+ assert_nothing_raised do
+ a=*"1", case nil
+ when 1
+ retry |
+ 1
+ end
+ end
+end
+
+assert('method call with exactly 127 arguments') do
+ def args_to_ary(*args)
+ args
+ end
+
+ assert_equal [0]*127, args_to_ary(
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, \
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, \
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, \
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, \
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, \
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
+ )
+end
+
+assert('nested empty heredoc') do
+ _, a = nil, <<B
+#{<<A}
+A
+B
+ assert_equal "\n", a
+end
+
+assert('splat in case splat') do
+ a = *case
+ when 0
+ * = 1
+ end
+
+ assert_equal [1], a
+end
+
+assert('undef with 127 or more arguments') do
+ assert_raise NameError do
+ undef
+ a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a,
+ a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a,
+ a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a,
+ a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a
+ end
+end
+
+assert('next in normal loop with 127 arguments') do
+ assert_raise NameError do
+ while true
+ next A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A
+ end
+ end
+end
+
+assert('negate literal register alignment') do
+ a = *case
+ when 0
+ -0.0
+ 2
+ end
+
+ assert_equal [2], a
+end
diff --git a/test/t/hash.rb b/test/t/hash.rb
index c8d7a70ef..c63b8c009 100644
--- a/test/t/hash.rb
+++ b/test/t/hash.rb
@@ -16,6 +16,13 @@ assert('Hash#[]', '15.2.13.4.2') do
a = { 'abc' => 'abc' }
assert_equal 'abc', a['abc']
+
+ # Hash#[] should call #default (#3272)
+ hash = {}
+ def hash.default(k); self[k] = 1; end
+ hash[:foo] += 1
+
+ assert_equal 2, hash[:foo]
end
assert('Hash#[]=', '15.2.13.4.3') do
@@ -37,6 +44,10 @@ assert('Hash#dup') do
b = a.dup
a['a'] = 2
assert_equal({'a' => 1}, b)
+
+ c = Hash.new { |h, k| h[k] = k.upcase }
+ d = c.dup
+ assert_equal("FOO", d["foo"])
end
assert('Hash#default', '15.2.13.4.5') do
@@ -355,3 +366,10 @@ assert('Hash#rehash') do
h.rehash
assert_equal("b", h[[:b]])
end
+
+assert('Hash#freeze') do
+ h = {}.freeze
+ assert_raise(RuntimeError) do
+ h[:a] = 'b'
+ end
+end
diff --git a/test/t/kernel.rb b/test/t/kernel.rb
index 927166283..aff2dd461 100644
--- a/test/t/kernel.rb
+++ b/test/t/kernel.rb
@@ -221,6 +221,14 @@ assert('Kernel#dup', '15.3.1.3.9') do
assert_false c.respond_to?(:test)
end
+assert('Kernel#dup class') do
+ assert_nothing_raised do
+ Array.dup.new(200)
+ Range.dup.new(2, 3)
+ String.dup.new("a"*50)
+ end
+end
+
# Kernel#eval is provided by mruby-eval mrbgem '15.3.1.3.12'
assert('Kernel#extend', '15.3.1.3.13') do
@@ -249,6 +257,13 @@ assert('Kernel#extend works on toplevel', '15.3.1.3.13') do
assert_true respond_to?(:test_method)
end
+assert('Kernel#freeze') do
+ obj = Object.new
+ assert_equal obj, obj.freeze
+ assert_equal 0, 0.freeze
+ assert_equal :a, :a.freeze
+end
+
assert('Kernel#global_variables', '15.3.1.3.14') do
assert_equal Array, global_variables.class
end
@@ -512,6 +527,21 @@ assert('Kernel#to_s', '15.3.1.3.46') do
assert_equal to_s.class, String
end
+assert('Kernel#to_s on primitives') do
+ begin
+ Fixnum.alias_method :to_s_, :to_s
+ Fixnum.remove_method :to_s
+
+ assert_nothing_raised do
+ # segfaults if mrb_cptr is used
+ 1.to_s
+ end
+ ensure
+ Fixnum.alias_method :to_s, :to_s_
+ Fixnum.remove_method :to_s_
+ end
+end
+
assert('Kernel.local_variables', '15.3.1.2.7') do
a, b = 0, 1
a += b
@@ -519,11 +549,10 @@ assert('Kernel.local_variables', '15.3.1.2.7') do
vars = Kernel.local_variables.sort
assert_equal [:a, :b, :vars], vars
- Proc.new {
+ assert_equal [:a, :b, :c, :vars], Proc.new { |a, b|
c = 2
- vars = Kernel.local_variables.sort
- assert_equal [:a, :b, :c, :vars], vars
- }.call
+ Kernel.local_variables.sort
+ }.call(-1, -2)
end
assert('Kernel#!=') do
@@ -592,4 +621,3 @@ assert('stack extend') do
assert_equal 6, recurse(0, 5)
end
-
diff --git a/test/t/literals.rb b/test/t/literals.rb
index 5bf6d6e06..51a37c32d 100644
--- a/test/t/literals.rb
+++ b/test/t/literals.rb
@@ -26,7 +26,7 @@ assert('Literals Numerical', '8.7.6.2') do
assert_equal 10000000, 10_000_000
assert_equal 10, 1_0
# integer with exponent
- assert_equal 10.0, 1e1,
+ assert_equal 10.0, 1e1
assert_equal(0.1, 1e-1)
assert_equal 10.0, 1e+1
# float with exponent
diff --git a/test/t/module.rb b/test/t/module.rb
index 4bde20fbe..6b0632414 100644
--- a/test/t/module.rb
+++ b/test/t/module.rb
@@ -494,6 +494,18 @@ end
# Not ISO specified
+assert('Module#define_method') do
+ c = Class.new {
+ define_method(:m1) { :ok }
+ define_method(:m2, Proc.new { :ok })
+ }
+ assert_equal c.new.m1, :ok
+ assert_equal c.new.m2, :ok
+ assert_raise(TypeError) do
+ Class.new { define_method(:n1, nil) }
+ end
+end
+
# @!group prepend
assert('Module#prepend') do
module M0
diff --git a/test/t/nomethoderror.rb b/test/t/nomethoderror.rb
index 5fed79689..35cbdaee9 100644
--- a/test/t/nomethoderror.rb
+++ b/test/t/nomethoderror.rb
@@ -20,3 +20,52 @@ assert('NoMethodError#args', '15.2.32.2.1') do
end
end
end
+
+assert('Can still raise when Kernel#method_missing is removed') do
+ assert_raise(NoMethodError) do
+ begin
+ Kernel.alias_method(:old_method_missing, :method_missing)
+ Kernel.remove_method(:method_missing)
+ 1.__send__(:foo)
+ ensure
+ Kernel.alias_method(:method_missing, :old_method_missing)
+ Kernel.remove_method(:old_method_missing)
+ end
+ end
+end
+
+assert('Can still call super when Kernel#method_missing is removed') do
+ assert_raise(NoMethodError) do
+ class A
+ def foo
+ super
+ end
+ end
+ begin
+ Kernel.alias_method(:old_method_missing, :method_missing)
+ Kernel.remove_method(:method_missing)
+ A.new.foo
+ ensure
+ Kernel.alias_method(:method_missing, :old_method_missing)
+ Kernel.remove_method(:old_method_missing)
+ end
+ end
+end
+
+assert("NoMethodError#new does not return an exception") do
+ begin
+ class << NoMethodError
+ def new(*)
+ nil
+ end
+ end
+
+ assert_raise(TypeError) do
+ Object.q
+ end
+ ensure
+ class << NoMethodError
+ remove_method :new
+ end
+ end
+end
diff --git a/test/t/proc.rb b/test/t/proc.rb
index 888b7d56a..ef4566e66 100644
--- a/test/t/proc.rb
+++ b/test/t/proc.rb
@@ -136,6 +136,18 @@ assert('Proc#return_does_not_break_self') do
assert_equal c, c.block.call
end
+assert('call Proc#initialize if defined') do
+ a = []
+ c = Class.new(Proc) do
+ define_method(:initialize) do
+ a << :ok
+ end
+ end
+
+ assert_kind_of c, c.new{}
+ assert_equal [:ok], a
+end
+
assert('&obj call to_proc if defined') do
pr = Proc.new{}
def mock(&b)
diff --git a/test/t/range.rb b/test/t/range.rb
index 278b26902..5391369de 100644
--- a/test/t/range.rb
+++ b/test/t/range.rb
@@ -43,10 +43,11 @@ assert('Range#first', '15.2.14.4.7') do
end
assert('Range#include?', '15.2.14.4.8') do
- a = (1..10)
+ assert_true (1..10).include?(10)
+ assert_false (1..10).include?(11)
- assert_true a.include?(5)
- assert_false a.include?(20)
+ assert_true (1...10).include?(9)
+ assert_false (1...10).include?(10)
end
assert('Range#initialize', '15.2.14.4.9') do
@@ -57,6 +58,8 @@ assert('Range#initialize', '15.2.14.4.9') do
assert_true a.exclude_end?
assert_equal (1..10), b
assert_false b.exclude_end?
+
+ assert_raise(NameError) { (0..1).send(:initialize, 1, 3) }
end
assert('Range#last', '15.2.14.4.10') do
diff --git a/test/t/string.rb b/test/t/string.rb
index fbaada451..20dc49d92 100644
--- a/test/t/string.rb
+++ b/test/t/string.rb
@@ -251,6 +251,19 @@ assert('String#chomp!', '15.2.10.5.10') do
assert_equal 'abc', e
end
+assert('String#chomp! uses the correct length') do
+ class A
+ def to_str
+ $s.replace("AA")
+ "A"
+ end
+ end
+
+ $s = "AAA"
+ $s.chomp!(A.new)
+ assert_equal $s, "A"
+end
+
assert('String#chop', '15.2.10.5.11') do
a = ''.chop
b = 'abc'.chop
@@ -381,8 +394,6 @@ assert('String#hash', '15.2.10.5.20') do
end
assert('String#include?', '15.2.10.5.21') do
- assert_true 'abc'.include?(97)
- assert_false 'abc'.include?(100)
assert_true 'abc'.include?('a')
assert_false 'abc'.include?('d')
end
@@ -585,10 +596,14 @@ assert('String#to_f', '15.2.10.5.38') do
a = ''.to_f
b = '123456789'.to_f
c = '12345.6789'.to_f
+ d = '1e-2147483648'.to_f
+ e = '1e2147483648'.to_f
assert_float(0.0, a)
assert_float(123456789.0, b)
assert_float(12345.6789, c)
+ assert_float(0, d)
+ assert_float(Float::INFINITY, e)
end
assert('String#to_i', '15.2.10.5.39') do
@@ -685,4 +700,3 @@ assert('String#freeze') do
assert_raise(RuntimeError) { str.upcase! }
end
-
diff --git a/test/t/syntax.rb b/test/t/syntax.rb
index 3bc68484b..461f5430b 100644
--- a/test/t/syntax.rb
+++ b/test/t/syntax.rb
@@ -47,6 +47,19 @@ assert('yield', '11.3.5') do
end
end
+assert('redo in a for loop (#3275)') do
+ sum = 0
+ for i in 1..10
+ sum += i
+ i -= 1
+ if i > 0
+ redo
+ end
+ end
+
+ assert_equal 220, sum
+end
+
assert('Abbreviated variable assignment', '11.4.2.3.2') do
a ||= 1
b &&= 1
@@ -255,6 +268,13 @@ assert('multiple assignment (nosplat array rhs)') do
assert_equal 2, g
end
+assert('multiple assignment (empty array rhs #3236, #3239)') do
+ a,b,*c = []; assert_equal [nil, nil, []], [a, b, c]
+ a,b,*c = [1]; assert_equal [1, nil, []], [a, b, c]
+ a,b,*c = [nil]; assert_equal [nil,nil, []], [a, b, c]
+ a,b,*c = [[]]; assert_equal [[], nil, []], [a, b, c]
+end
+
assert('Return values of case statements') do
a = [] << case 1
when 3 then 2
diff --git a/test/t/unicode.rb b/test/t/unicode.rb
index 7edd65ef2..8622ae08a 100644
--- a/test/t/unicode.rb
+++ b/test/t/unicode.rb
@@ -2,34 +2,38 @@
assert('bare \u notation test') do
# Mininum and maximum one byte characters
- assert_equal("\u0000", "\x00")
- assert_equal("\u007F", "\x7F")
+ assert_equal("\x00", "\u0000")
+ assert_equal("\x7F", "\u007F")
# Mininum and maximum two byte characters
- assert_equal("\u0080", "\xC2\x80")
- assert_equal("\u07FF", "\xDF\xBF")
+ assert_equal("\xC2\x80", "\u0080")
+ assert_equal("\xDF\xBF", "\u07FF")
# Mininum and maximum three byte characters
- assert_equal("\u0800", "\xE0\xA0\x80")
- assert_equal("\uFFFF", "\xEF\xBF\xBF")
+ assert_equal("\xE0\xA0\x80", "\u0800")
+ assert_equal("\xEF\xBF\xBF", "\uFFFF")
# Four byte characters require the \U notation
end
assert('braced \u notation test') do
# Mininum and maximum one byte characters
- assert_equal("\u{0000}", "\x00")
- assert_equal("\u{007F}", "\x7F")
+ assert_equal("\x00", "\u{0000}")
+ assert_equal("\x7F", "\u{007F}")
# Mininum and maximum two byte characters
- assert_equal("\u{0080}", "\xC2\x80")
- assert_equal("\u{07FF}", "\xDF\xBF")
+ assert_equal("\xC2\x80", "\u{0080}")
+ assert_equal("\xDF\xBF", "\u{07FF}")
# Mininum and maximum three byte characters
- assert_equal("\u{0800}", "\xE0\xA0\x80")
- assert_equal("\u{FFFF}", "\xEF\xBF\xBF")
+ assert_equal("\xE0\xA0\x80", "\u{0800}")
+ assert_equal("\xEF\xBF\xBF", "\u{FFFF}")
# Mininum and maximum four byte characters
- assert_equal("\u{10000}", "\xF0\x90\x80\x80")
- assert_equal("\u{10FFFF}", "\xF4\x8F\xBF\xBF")
+ assert_equal("\xF0\x90\x80\x80", "\u{10000}")
+ assert_equal("\xF4\x8F\xBF\xBF", "\u{10FFFF}")
+end
+
+assert('braced multiple \u notation test') do
+ assert_equal("ABC", "\u{41 42 43}")
end