diff options
93 files changed, 1645 insertions, 788 deletions
@@ -123,3 +123,8 @@ task :deep_clean => ["clean"] do end puts "Cleaned up mrbgems build folder" end + +desc 'generate document' +task :doc do + load "#{MRUBY_ROOT}/doc/language/generator.rb" +end diff --git a/bintest/mrbc.rb b/bintest/mrbc.rb new file mode 100644 index 000000000..b016378a1 --- /dev/null +++ b/bintest/mrbc.rb @@ -0,0 +1,12 @@ +require 'tempfile' + +assert('Compiling multiple files without new line in last line. #2361') do + a, b, out = Tempfile.new('a.rb'), Tempfile.new('b.rb'), Tempfile.new('out.mrb') + a.write('module A; end') + a.flush + b.write('module B; end') + b.flush + result = `bin/mrbc -c -o #{out.path} #{a.path} #{b.path} 2>&1` + assert_equal "bin/mrbc:#{a.path}:Syntax OK", result.chomp + assert_equal 0, $?.exitstatus +end diff --git a/doc/api/README.md b/doc/api/README.md new file mode 100644 index 000000000..131e50515 --- /dev/null +++ b/doc/api/README.md @@ -0,0 +1,30 @@ +# C API Reference + +This is a C API Reference. +The structure of this document will follow the directory structure of `include/` directory. + +## Headers list +Header name|Features +-----------|-------- +[mrbconf.h](../mrbconf/README.md)|Defines macros for mruby configurations. +[mruby.h](./mruby.h.md)|Main header of mruby C API. Include this first. +[mruby/array.h](./mruby.array.h.md)|`Array` class. +[mruby/class.h](./mruby.class.h.md)|`Class` class. +[mruby/compile.h](./mruby.compile.h.md)|mruby compiler. +[mruby/data.h](./mruby.data.h.md)|User defined object. +[mruby/debug.h](./mruby.debug.h.md)|Debugging. +[mruby/dump.h](./mruby.dump.h.md)|Dumping compiled mruby script. +[mruby/error.h](./mruby.error.h.md)|Error handling. +[mruby/gc.h](./mruby.gc.h.md)|Uncommon memory management stuffs. +[mruby/hash.h](./mruby.hash.h.md)|`Hash` class. +[mruby/irep.h](./mruby.irep.h.md)|Compiled mruby script. +[mruby/khash.h](./mruby.khash.h.md)|Defines of khash which is used in hash table of mruby. +[mruby/numeric.h](./mruby.numeric.h.md)|`Numeric` class and sub-classes of it. +[mruby/opode.h](./mruby.opcode.h.md)|Operation codes used in mruby VM. +[mruby/proc.h](./mruby.proc.h.md)|`Proc` class. +[mruby/range.h](./mruby.range.h.md)|`Range` class. +[mruby/re.h](./mruby.re.h.md)|`Regexp` class. +[mruby/string.h](./mruby.string.h.md)|`String` class. +[mruby/value.h](./mruby.value.h.md)|`mrb_value` functions and macros. +[mruby/variable.h](./mruby.variable.h.md)|Functions to access to mruby variables. +[mruby/version.h](./mruby.version.h.md)|Macros of mruby version. diff --git a/doc/api/mruby.h.md b/doc/api/mruby.h.md new file mode 100644 index 000000000..bd844a749 --- /dev/null +++ b/doc/api/mruby.h.md @@ -0,0 +1,69 @@ +# mruby.h + +Basic header of mruby. +It includes **mrbconf.h**, **mruby/value.h**, **mruby/version.h** internally. + +## `mrb_state` management + +### mrb_open +```C +mrb_state* mrb_open(); +``` +Creates new `mrb_state`. + +### mrb_allocf +```C +typedef void* (*mrb_allocf) (struct mrb_state *mrb, void *ptr, size_t s, void *ud); +``` +Function pointer type of custom allocator used in `mrb_open_allocf`. + +The function pointing it must behave similarly as `realloc` except: +* If `ptr` is `NULL` it must allocate new space. +* If `s` is `NULL`, `ptr` must be freed. + +### mrb_open_allocf +```C +mrb_state* mrb_open_allocf(mrb_allocf f, void *ud); +``` +Create new `mrb_state` with custom allocator. +`ud` will be passed to custom allocator `f`. +If user data isn't required just pass `NULL`. +Function pointer `f` must satisfy requirements of its type. + +### mrb_close +```C +void mrb_close(mrb_state *mrb); +``` +Deletes `mrb_state`. + +## Method + +### mrb_get_args +```C +int mrb_get_args(mrb_state *mrb, const char *format, ...); +``` +Retrieve arguments from `mrb_state`. +Use it inside a function pointed by `mrb_func_t`. +It returns number of function retrieved. +`format` is a list of following format specifier: + +char|mruby type|retrieve types|note +:---:|----------|--------------|--- +`o`|`Object`|`mrb_value`|Could be used to retreive any type of argument +`C`|`Class`/`Module`|`mrb_value`| +`S`|`String`|`mrb_value`| +`A`|`Array`|`mrb_value`| +`H`|`Hash`|`mrb_value`| +`s`|`String`|`char*`, `mrb_int`| +`z`|`String`|`char*`| +`a`|`Array`|`mrb_value*`, `mrb_int`| +`f`|`Float`|`mrb_float`| +`i`|`Integer`|`mrb_int`| +`b`|boolean|`mrb_bool`| +`n`|`Symbol`|`mrb_sym`| +`&`|block|`mrb_value`| +`*`|rest arguments|`mrb_value*`, `mrb_int`|Receive the rest of arguments as an array. +<code>|</code>|optional||After this spec following specs would be optional. +`?`|optional given|`mrb_bool`|True if preceding argument is given. Used to check optional argument is given. + +The passing variadic arguments must be a pointer of retreiving type. diff --git a/doc/compile/README.md b/doc/compile/README.md index 4309dcdc4..2d3bec367 100644 --- a/doc/compile/README.md +++ b/doc/compile/README.md @@ -1,6 +1,6 @@ # Compile -mruby is using Rake to compile and cross-compile all libraries and +mruby uses Rake to compile and cross-compile all libraries and binaries. ## Prerequisites @@ -22,10 +22,11 @@ Optional: Inside of the root directory of the mruby source a file exists called *build_config.rb*. This file contains the build configuration of mruby and looks like this for example: - - MRuby::Build.new do |conf| - toolchain :gcc - end +```ruby +MRuby::Build.new do |conf| + toolchain :gcc +end +``` All tools necessary to compile mruby can be set or modified here. In case you want to maintain an additional *build_config.rb* you can define a @@ -49,29 +50,33 @@ configure the build environment for specific compiler infrastructures. #### GCC Toolchain configuration for the GNU C Compiler. - - toolchain :gcc +```ruby +toolchain :gcc +``` #### clang Toolchain configuration for the LLVM C Compiler clang. Mainly equal to the GCC toolchain. - - toolchain :clang +```ruby +toolchain :clang +``` #### Visual Studio 2010, 2012 and 2013 Toolchain configuration for Visual Studio on Windows. If you use the [Visual Studio Command Prompt](http://msdn.microsoft.com/en-us/library/ms229859\(v=vs.110\).aspx), you normally do not have to specify this manually, since it gets automatically detected by our build process. - - toolchain :visualcpp +``` +toolchain :visualcpp +``` #### Android Toolchain configuration for Android. - - toolchain :androideabi +```ruby +toolchain :androideabi +``` Requires the custom standalone Android NDK and the toolchain path in ```ANDROID_STANDALONE_TOOLCHAIN```. @@ -84,123 +89,132 @@ process. The following tools can be selected: * mirb (mruby interactive shell) To select them declare conf.gem as follows: - - conf.gem "#{root}/mrbgems/mruby-bin-mruby" - - conf.gem "#{root}/mrbgems/mruby-bin-mirb" +```ruby +conf.gem "#{root}/mrbgems/mruby-bin-mruby" +conf.gem "#{root}/mrbgems/mruby-bin-mirb" +``` ### File Separator Some environments require a different file separator character. It is possible to set the character via ```conf.file_separator```. - - conf.file_separator = '/' +```ruby +conf.file_separator = '/' +``` ### C Compiler Configuration of the C compiler binary, flags and include paths. - - conf.cc do |cc| - cc.command = ... - cc.flags = ... - cc.include_paths = ... - cc.defines = ... - cc.option_include_path = ... - cc.option_define = ... - cc.compile_options = ... - end +```ruby +conf.cc do |cc| + cc.command = ... + cc.flags = ... + cc.include_paths = ... + cc.defines = ... + cc.option_include_path = ... + cc.option_define = ... + cc.compile_options = ... +end +``` C Compiler has header searcher to detect installed library. If you need a include path of header file use ```search_header_path```: - - # Searches ```iconv.h```. - # If found it will return include path of the header file. - # Otherwise it will return nil . - fail 'iconv.h not found' unless conf.cc.search_header_path 'iconv.h' +```ruby +# Searches ```iconv.h```. +# If found it will return include path of the header file. +# Otherwise it will return nil . +fail 'iconv.h not found' unless conf.cc.search_header_path 'iconv.h' +``` If you need a full file name of header file use ```search_header```: - - # Searches ```iconv.h```. - # If found it will return full path of the header file. - # Otherwise it will return nil . - iconv_h = conf.cc.search_header 'iconv.h' - print "iconv.h found: #{iconv_h}\n" +```ruby +# Searches ```iconv.h```. +# If found it will return full path of the header file. +# Otherwise it will return nil . +iconv_h = conf.cc.search_header 'iconv.h' +print "iconv.h found: #{iconv_h}\n" +``` Header searcher uses compiler's ```include_paths``` by default. When you are using GCC toolchain (including clang toolchain since its base is gcc toolchain) it will use compiler specific include paths too. (For example ```/usr/local/include```, ```/usr/include```) If you need a special header search paths define a singleton method ```header_search_paths``` to C compiler: - - def conf.cc.header_search_paths - ['/opt/local/include'] + include_paths - end - +```ruby +def conf.cc.header_search_paths + ['/opt/local/include'] + include_paths +end +``` ### Linker Configuration of the Linker binary, flags and library paths. - - conf.linker do |linker| - linker.command = ... - linker.flags = ... - linker.flags_before_libraries = ... - linker.libraries = ... - linker.flags_after_libraries = ... - linker.library_paths = .... - linker.option_library = ... - linker.option_library_path = ... - linker.link_options = ... - end +```ruby +conf.linker do |linker| + linker.command = ... + linker.flags = ... + linker.flags_before_libraries = ... + linker.libraries = ... + linker.flags_after_libraries = ... + linker.library_paths = .... + linker.option_library = ... + linker.option_library_path = ... + linker.link_options = ... +end +``` ### Archiver Configuration of the Archiver binary and flags. - - conf.archiver do |archiver| - archiver.command = ... - archiver.archive_options = ... - end +```ruby +conf.archiver do |archiver| + archiver.command = ... + archiver.archive_options = ... +end +``` ### Parser Generator Configuration of the Parser Generator binary and flags. - - conf.yacc do |yacc| - yacc.command = ... - yacc.compile_options = ... - end +```ruby +conf.yacc do |yacc| + yacc.command = ... + yacc.compile_options = ... +end +``` ### GPerf Configuration of the GPerf binary and flags. - - conf.gperf do |gperf| - gperf.command = ... - gperf.compile_options = ... - end +```ruby +conf.gperf do |gperf| + gperf.command = ... + gperf.compile_options = ... +end +``` ### File Extensions - - conf.exts do |exts| - exts.object = ... - exts.executable = ... - exts.library = ... - end +```ruby +conf.exts do |exts| + exts.object = ... + exts.executable = ... + exts.library = ... +end +``` ### Mrbgems Integrate GEMs in the build process. - - # Integrate GEM with additional configuration - conf.gem 'path/to/gem' do |g| - g.cc.flags << ... - end - - # Integrate GEM without additional configuration - conf.gem 'path/to/another/gem' - +```ruby +# Integrate GEM with additional configuration +conf.gem 'path/to/gem' do |g| + g.cc.flags << ... +end + +# Integrate GEM without additional configuration +conf.gem 'path/to/another/gem' +``` See doc/mrbgems/README.md for more option about mrbgems. @@ -209,8 +223,9 @@ See doc/mrbgems/README.md for more option about mrbgems. Configuration Mrbtest build process. If you want mrbtest.a only, You should set ```conf.build_mrbtest_lib_only``` - - conf.build_mrbtest_lib_only +```ruby +conf.build_mrbtest_lib_only +``` ### Bintest @@ -220,8 +235,9 @@ See ```mruby-bin-*/bintest/*.rb``` if you need examples. If you want a temporary files use `tempfile` module of CRuby instead of ```/tmp/```. You can enable it with following: - - conf.enable_bintest +```ruby +conf.enable_bintest +``` ### C++ ABI @@ -230,24 +246,27 @@ It is called C++ ABI mode. By using C++ exception it can release C++ stack object correctly. Whenever you mix C++ code C++ ABI mode would be enabled automatically. If you need to enable C++ ABI mode explicity add the following: - - conf.enable_cxx_abi +```ruby +conf.enable_cxx_abi +``` #### C++ exception disabling. If you need to force C++ exception disable (For example using a compiler option to disable C++ exception) add following: - - conf.disable_cxx_exception +```ruby +conf.disable_cxx_exception +``` Note that it must be called before ```enable_cxx_abi``` or ```gem``` method. ### Debugging mode To enable debugging mode add the following: - - conf.enable_debug +```ruby +conf.enable_debug +``` When debugging mode is enabled * Macro ```MRB_DEBUG``` would be defined. @@ -263,13 +282,14 @@ achieve this the *build_config.rb* needs to contain an instance of ```MRuby::CrossBuild```. This instance defines the compilation tools and flags for the target platform. An example could look like this: +```ruby +MRuby::CrossBuild.new('32bit') do |conf| + toolchain :gcc - MRuby::CrossBuild.new('32bit') do |conf| - toolchain :gcc - - conf.cc.flags << "-m32" - conf.linker.flags << "-m32" - end + conf.cc.flags << "-m32" + conf.linker.flags << "-m32" +end +``` All configuration options of ```MRuby::Build``` can also be used in ```MRuby::CrossBuild```. @@ -278,15 +298,16 @@ in ```MRuby::CrossBuild```. In cross compilation, you can run ```mrbtest``` on emulator if you have it by changing configuration of test runner. - - conf.test_runner do |t| - t.command = ... # set emulator. this value must be non nil or false - t.flags = ... # set flags of emulator - - def t.run(bin) # override `run` if you need to change the behavior of it - ... # `bin` is the full path of mrbtest - end - end +```ruby +conf.test_runner do |t| + t.command = ... # set emulator. this value must be non nil or false + t.flags = ... # set flags of emulator + + def t.run(bin) # override `run` if you need to change the behavior of it + ... # `bin` is the full path of mrbtest + end +end +``` ## Build process @@ -438,12 +459,14 @@ To build a minimal mruby library you need to use the Cross Compiling feature due to the reason that there are functions (i.e. stdio) which can't be disabled for the main build. - MRuby::CrossBuild.new('Minimal') do |conf| - toolchain :gcc +```ruby +MRuby::CrossBuild.new('Minimal') do |conf| + toolchain :gcc - conf.cc.defines = %w(DISABLE_STDIO) - conf.bins = [] - end + conf.cc.defines = %w(DISABLE_STDIO) + conf.bins = [] +end +``` This configuration defines a cross compile build called 'Minimal' which is using the GCC and compiles for the host machine. It also disables diff --git a/doc/language/Core.md b/doc/language/Core.md index 033939865..d7331e7f3 100644 --- a/doc/language/Core.md +++ b/doc/language/Core.md @@ -1328,6 +1328,12 @@ ISO Code | Source File | C Function --- | --- | --- 15.3.1.2.5 | src/kernel.c | mrb_f_block_given_p_m +#### local_variables + +ISO Code | Source File | C Function +--- | --- | --- +15.3.1.2.7 | src/kernel.c | mrb_local_variables + #### raise ISO Code | Source File | C Function @@ -1498,6 +1504,12 @@ ISO Code | Source File | C Function --- | --- | --- 15.3.1.3.26 | src/kernel.c | mrb_obj_is_kind_of_m +#### local_variables + +ISO Code | Source File | C Function +--- | --- | --- +15.3.1.3.28 | src/kernel.c | mrb_local_variables + #### methods ISO Code | Source File | C Function diff --git a/doc/mrbconf/README.md b/doc/mrbconf/README.md new file mode 100644 index 000000000..f0dd50da5 --- /dev/null +++ b/doc/mrbconf/README.md @@ -0,0 +1,106 @@ +# mruby configuration macros. + +## How to use these macros. +You can use mrbconfs with following ways: +* Write them in `mrbconf.h`. + * Using compiler flags is prefered when building a cross binaries or multiple mruby binaries + since it's easier to use different mrbconf per each `MRuby::Build`. + * Most flags can be enabled by just commenting in. +* Pass them as compiler flags. + * Make sure you pass the same flags to all compilers since some mrbconf(e.g., `MRB_GC_FIXED_ARENA`) + changes `struct` layout and cause memory access error when C and other language(e.g., C++) is mixed. + +## stdio setting. +`ENABLE_STDIO` +* Will be defined automatically if `DISABLE_STDIO` isn't defined. +* Uses `<stdio.h>` functions. + +`DISABLE_STDIO` +* When defined `<stdio.h>` functions won't be used. + +## Debug macros. +`ENABLE_DEBUG` +* When defined code fetch hook and debug OP hook will be enabled. +* When using any of the hook set function pointer `code_fetch_hook` and/or `debug_op_hook` of `mrb_state`. +* Fetch hook will be called before any OP. +* Debug OP hook will be called when dispatching `OP_DEBUG`. + +`DISABLE_DEBUG` +* Will be define automatically if `ENABLE_DEBUG` isn't defined. + +`MRB_DEBUG` +* When defined `mrb_assert*` macro will be defined with macros from `<assert.h>`. +* Could be enabled via `enable_debug` method of `MRuby::Build`. + +## Stack configuration + +`MRB_STACK_EXTEND_DOUBLING` +* If defined doubles the stack size when extending it. +* Else extends stack with `MRB_STACK_GROWTH`. + +`MRB_STACK_GROWTH` +* Default value is `128`. +* Used in stack extending. +* Ignored when `MRB_STACK_EXTEND_DOUBLING` is defined. + +`MRB_STACK_MAX` +* Default value is `0x40000 - MRB_STACK_GROWTH`. +* Raises `RuntimeError` when stack size exceeds this value. + +## Primitive type configuration. + +`MRB_USE_FLOAT` +* When defined single precision floating point type(C type `float`) is used as `mrb_float`. +* Else double precision floating point type(C type `double`) is used as `mrb_float`. + +`MRB_INT16` +* When defined `int16_t` will be defined as `mrb_int`. +* Conflicts with `MRB_INT64`. + +`MRB_INT64` +* When defined `int64_t` will be defined as `mrb_int`. +* Conflicts with `MRB_INT16`. +* When `MRB_INT16` or `MRB_INT64` isn't defined `int`(most of the times 32-bit integer) +will be defined as `mrb_int`. + +## Garbage collector configuration. + +`MRB_GC_STRESS` +* When defined full GC is emitted per each `RBasic` allocation. +* Mainly used in memory manager debugging. + +`MRB_GC_TURN_OFF_GENERATIONAL` +* When defined turns generational GC by default. + +`MRB_GC_FIXED_ARENA` +* When defined used fixed size GC arena. +* Raises `RuntimeError` when this is defined and GC arena size exceeds `MRB_GC_ARENA_SIZE`. +* Useful tracking unnecessary mruby object allocation. + +`MRB_GC_ARENA_SIZE` +* Default value is 100. +* Ignored when `MRB_GC_FIXED_ARENA` isn't defined. +* Defines fixed GC arena size. + +## Memory pool configuration. + +`POOL_ALIGNMENT` +* Default value is `4`. +* If you're allocating data types that requires alignment more than default value define the +largest value of required alignment. + +`POOL_PAGE_SIZE` +* Default value is `16000`. +* Specifies page size of pool page. +* Smaller the value is increases memory overhead. + +## State atexit configuration. + +`MRB_FIXED_STATE_ATEXIT_STACK` +* If defined enables fixed size `mrb_state` atexit stack. +* Raises `RuntimeError` when `mrb_state_atexit` call count to same `mrb_state` exceeds +`MRB_FIXED_STATE_ATEXIT_STACK_SIZE`'s value. + +`MRB_FIXED_STATE_ATEXIT_STACK_SIZE` +* Default value is `5`. +* If `MRB_FIXED_STATE_ATEXIT_STACK` isn't defined this macro is ignored. diff --git a/doc/mrbgems/README.md b/doc/mrbgems/README.md index 7ac225730..040687333 100644 --- a/doc/mrbgems/README.md +++ b/doc/mrbgems/README.md @@ -10,20 +10,21 @@ build configuration (i.e. *build_config.rb*), mrbgems will be activated and the extension integrated. To add a GEM into the *build_config.rb* add the following line for example: - - conf.gem '/path/to/your/gem/dir' +```ruby +conf.gem '/path/to/your/gem/dir' +``` You can also use a relative path which would be relative from the mruby root: - - conf.gem 'examples/mrbgems/ruby_extension_example' +```ruby +conf.gem 'examples/mrbgems/ruby_extension_example' +``` A remote GIT repository location for a GEM is also supported: - - conf.gem :git => 'https://github.com/masuidrive/mrbgems-example.git', :branch => 'master' - - conf.gem :github => 'masuidrive/mrbgems-example', :branch => 'master' - - conf.gem :bitbucket => 'mruby/mrbgems-example', :branch => 'master' +```ruby +conf.gem :git => 'https://github.com/masuidrive/mrbgems-example.git', :branch => 'master' +conf.gem :github => 'masuidrive/mrbgems-example', :branch => 'master' +conf.gem :bitbucket => 'mruby/mrbgems-example', :branch => 'master' +``` To pull all gems from remote GIT repository on build, call ```./minirake -p```, or ```./minirake --pull-gems```. @@ -41,11 +42,12 @@ via `config.gem`, but wrapped in an `MRuby::GemBox` object. GemBoxes are loaded into mruby via `config.gembox 'boxname'`. Below we have created a GemBox containing *mruby-time* and *mrbgems-example*: - - MRuby::GemBox.new do |conf| - conf.gem "#{root}/mrbgems/mruby-time" - conf.gem :github => 'masuidrive/mrbgems-example' - end +```ruby +MRuby::GemBox.new do |conf| + conf.gem "#{root}/mrbgems/mruby-time" + conf.gem :github => 'masuidrive/mrbgems-example' +end +``` As mentioned, the GemBox uses the same conventions as `MRuby::Build`. The GemBox must be saved with a *.gembox* extension inside the *mrbgems* directory to to be @@ -54,16 +56,17 @@ picked up by mruby. To use this example GemBox, we save it as `custom.gembox` inside the *mrbgems* directory in mruby, and add the following to our *build_config.rb* file inside the build block: - - conf.gembox 'custom' - +```ruby +conf.gembox 'custom' +``` This will cause the *custom* GemBox to be read in during the build process, adding *mruby-time* and *mrbgems-example* to the build. If you want, you can put GemBox outside of mruby directory. In that case you must -specify absolute path like below. - - conf.gembox "#{ENV["HOME"]}/mygemboxes/custom" +specify an absolute path like below. +```ruby +conf.gembox "#{ENV["HOME"]}/mygemboxes/custom" +``` There are two GemBoxes that ship with mruby: [default](../../mrbgems/default.gembox) and [full-core](../../mrbgems/full-core.gembox). The [default](../../mrbgems/default.gembox) GemBox @@ -76,6 +79,8 @@ The maximal GEM structure looks like this: +- GEM_NAME <- Name of GEM | + +- include/ <- Header for Ruby extension (will exported) + | +- mrblib/ <- Source for Ruby extension | +- src/ <- Source for C extension @@ -87,20 +92,22 @@ The maximal GEM structure looks like this: +- README.md <- Readme for GEM The folder *mrblib* contains pure Ruby files to extend mruby. The folder *src* -contains C files to extend mruby. The folder *test* contains C and pure Ruby files -for testing purposes which will be used by `mrbtest`. *mrbgem.rake* contains -the specification to compile C and Ruby files. *README.md* is a short description -of your GEM. +contains C/C++ files to extend mruby. The folder *include* contains C/C++ header +files. The folder *test* contains C/C++ and pure Ruby files for testing purposes +which will be used by `mrbtest`. *mrbgem.rake* contains the specification +to compile C and Ruby files. *README.md* is a short description of your GEM. ## Build process mrbgems expects a specification file called *mrbgem.rake* inside of your GEM directory. A typical GEM specification could look like this for example: - - MRuby::Gem::Specification.new('c_and_ruby_extension_example') do |spec| - spec.license = 'MIT' - spec.author = 'mruby developers' - end +```ruby +MRuby::Gem::Specification.new('c_and_ruby_extension_example') do |spec| + spec.license = 'MIT' + spec.author = 'mruby developers' + spec.summary = 'Example mrbgem using C and ruby' +end +``` The mrbgems build process will use this specification to compile Object and Ruby files. The compilation results will be added to *lib/libmruby.a*. This file exposes @@ -113,29 +120,32 @@ information purpose: * `spec.author` or `spec.authors` (Developer name or a list of them) * `spec.version` (Current version) * `spec.description` (Detailed description) -* `spec.summary` (Short summary) +* `spec.summary` + * One line short description of mrbgem. + * Printed in build summary of rake when set. * `spec.homepage` (Homepage) * `spec.requirements` (External requirements as information for user) -The license and author properties are required in every GEM! +The `license` and `author` properties are required in every GEM! In case your GEM is depending on other GEMs please use `spec.add_dependency(gem, *requirements[, default_get_info])` like: +```ruby +MRuby::Gem::Specification.new('c_and_ruby_extension_example') do |spec| + spec.license = 'MIT' + spec.author = 'mruby developers' - MRuby::Gem::Specification.new('c_and_ruby_extension_example') do |spec| - spec.license = 'MIT' - spec.author = 'mruby developers' + # Add GEM dependency mruby-parser. + # The version must be between 1.0.0 and 1.5.2 . + spec.add_dependency('mruby-parser', '>= 1.0.0', '<= 1.5.2') - # Add GEM dependency mruby-parser. - # The version must be between 1.0.0 and 1.5.2 . - spec.add_dependency('mruby-parser', '>= 1.0.0', '<= 1.5.2') + # Use any version of mruby-uv from github. + spec.add_dependency('mruby-uv', '>= 0.0.0', :github => 'mattn/mruby-uv') - # Use any version of mruby-uv from github. - spec.add_dependency('mruby-uv', '>= 0.0.0', :github => 'mattn/mruby-uv') - - # Use latest mruby-onig-regexp from github. (version requirements can be ignored) - spec.add_dependency('mruby-onig-regexp', :github => 'mattn/mruby-onig-regexp') - end + # Use latest mruby-onig-regexp from github. (version requirements can be ignored) + spec.add_dependency('mruby-onig-regexp', :github => 'mattn/mruby-onig-regexp') +end +``` The version requirements and default gem information are optional. @@ -159,6 +169,23 @@ Its format is same as argument of method `MRuby::Build#gem`, expect that it can' When a special version of depedency is required, use `MRuby::Build#gem` in *build_config.rb* to override default gem. +If you have conflicting GEMs use the following method: +* `spec.add_conflict(gem, *requirements)` + * The `requirements` argument is same as in `add_dependency` method. + +like following code: +```ruby +MRuby::Gem::Specification.new 'some-regexp-binding' do |spec| + spec.license = 'BSD' + spec.author = 'John Doe' + + spec.add_conflict 'mruby-onig-regexp', '> 0.0.0' + spec.add_conflict 'mruby-hs-regexp' + spec.add_conflict 'mruby-pcre-regexp' + spec.add_conflict 'mruby-regexp-pcre' +end +``` + In case your GEM has more complex build requirements you can use the following options additionally inside of your GEM specification: @@ -173,6 +200,19 @@ the following options additionally inside of your GEM specification: * `spec.test_objs` (Object test files for integration into mrbtest) * `spec.test_preload` (Initialization files for mrbtest) +### include_paths and depencency + +Your GEM can export include paths to another GEMs that depends on your GEM. +By default, `/...absolute path.../{GEM_NAME}/include` will be exported. +So it is recommended not to put GEM's local header files on include/. + +These exports are retroactive. +For example: when B depends to C and A depends to B, A will get include paths exported by C. + +Exported include_paths are automatically appended to GEM local include_paths by Minirake. +You can use `spec.export_include_paths` accessor if you want more complex build. + + ## C Extension mruby can be extended with C. This is possible by using the C API to @@ -184,12 +224,13 @@ mrbgems expects that you have implemented a C method called `mrb_YOURGEMNAME_gem_init(mrb_state)`. `YOURGEMNAME` will be replaced by the name of your GEM. If you call your GEM *c_extension_example*, your initialisation method could look like this: - - void - mrb_c_extension_example_gem_init(mrb_state* mrb) { - struct RClass *class_cextension = mrb_define_module(mrb, "CExtension"); - mrb_define_class_method(mrb, class_cextension, "c_method", mrb_c_method, MRB_ARGS_NONE()); - } +```C +void +mrb_c_extension_example_gem_init(mrb_state* mrb) { + struct RClass *class_cextension = mrb_define_module(mrb, "CExtension"); + mrb_define_class_method(mrb, class_cextension, "c_method", mrb_c_method, MRB_ARGS_NONE()); +} +``` ### Finalize @@ -198,10 +239,12 @@ mrbgems expects that you have implemented a C method called by the name of your GEM. If you call your GEM *c_extension_example*, your finalizer method could look like this: - void - mrb_c_extension_example_gem_final(mrb_state* mrb) { - free(someone); - } +```C +void +mrb_c_extension_example_gem_final(mrb_state* mrb) { + free(someone); +} +``` ### Example diff --git a/include/mrbconf.h b/include/mrbconf.h index c84b32cd8..ac33ff0bf 100644 --- a/include/mrbconf.h +++ b/include/mrbconf.h @@ -59,6 +59,12 @@ /* fixed size GC arena */ //#define MRB_GC_FIXED_ARENA +/* state atexit stack size */ +//#define MRB_FIXED_STATE_ATEXIT_STACK_SIZE 5 + +/* fixed size state atexit stack */ +//#define MRB_FIXED_STATE_ATEXIT_STACK + /* -DDISABLE_XXXX to drop following features */ //#define DISABLE_STDIO /* use of stdio */ diff --git a/include/mruby.h b/include/mruby.h index c1f45bf0b..80bbe2d68 100644 --- a/include/mruby.h +++ b/include/mruby.h @@ -52,6 +52,10 @@ typedef void* (*mrb_allocf) (struct mrb_state *mrb, void*, size_t, void *ud); #define MRB_GC_ARENA_SIZE 100 #endif +#ifndef MRB_FIXED_STATE_ATEXIT_STACK_SIZE +#define MRB_FIXED_STATE_ATEXIT_STACK_SIZE 5 +#endif + typedef struct { mrb_sym mid; struct RProc *proc; @@ -102,6 +106,8 @@ enum gc_state { struct mrb_jmpbuf; +typedef void (*mrb_atexit_func)(struct mrb_state*); + typedef struct mrb_state { struct mrb_jmpbuf *jmp; @@ -167,8 +173,16 @@ typedef struct mrb_state { struct RClass *eException_class; struct RClass *eStandardError_class; + struct RObject *nomem_err; /* pre-allocated NoMemoryError */ void *ud; /* auxiliary data */ + +#ifdef MRB_FIXED_STATE_ATEXIT_STACK + mrb_atexit_func atexit_stack[MRB_FIXED_STATE_ATEXIT_STACK_SIZE]; +#else + mrb_atexit_func *atexit_stack; +#endif + mrb_int atexit_stack_len; } mrb_state; #if __STDC_VERSION__ >= 201112L @@ -282,8 +296,11 @@ mrb_value mrb_str_new_static(mrb_state *mrb, const char *p, size_t len); mrb_state* mrb_open(void); mrb_state* mrb_open_allocf(mrb_allocf, void *ud); +mrb_state* mrb_open_core(mrb_allocf, void *ud); void mrb_close(mrb_state*); +void* mrb_default_allocf(mrb_state*, void*, size_t, void*); + mrb_value mrb_top_self(mrb_state *); mrb_value mrb_run(mrb_state*, struct RProc*, mrb_value); mrb_value mrb_toplevel_run(mrb_state*, struct RProc*); @@ -328,8 +345,7 @@ mrb_value mrb_obj_clone(mrb_state *mrb, mrb_value self); /* need to include <ctype.h> to use these macros */ #ifndef ISPRINT -/* #define ISASCII(c) isascii((int)(unsigned char)(c)) */ -#define ISASCII(c) 1 +#define ISASCII(c) (!(((int)(unsigned char)(c)) & ~0x7f)) #define ISPRINT(c) (ISASCII(c) && isprint((int)(unsigned char)(c))) #define ISSPACE(c) (ISASCII(c) && isspace((int)(unsigned char)(c))) #define ISUPPER(c) (ISASCII(c) && isupper((int)(unsigned char)(c))) @@ -413,6 +429,8 @@ void* mrb_pool_realloc(struct mrb_pool*, void*, size_t oldlen, size_t newlen); mrb_bool mrb_pool_can_realloc(struct mrb_pool*, void*, size_t); void* mrb_alloca(mrb_state *mrb, size_t); +void mrb_state_atexit(mrb_state *mrb, mrb_atexit_func func); + #ifdef MRB_DEBUG #include <assert.h> #define mrb_assert(p) assert(p) @@ -422,6 +440,12 @@ void* mrb_alloca(mrb_state *mrb, size_t); #define mrb_assert_int_fit(t1,n,t2,max) ((void)0) #endif +#if __STDC_VERSION__ >= 201112L +#define mrb_static_assert(exp, str) _Static_assert(exp, str) +#else +#define mrb_static_assert(exp, str) mrb_assert(exp) +#endif + mrb_value mrb_format(mrb_state *mrb, const char *format, ...); #if defined(__cplusplus) diff --git a/include/mruby/compile.h b/include/mruby/compile.h index 7f896e1fd..188df315d 100644 --- a/include/mruby/compile.h +++ b/include/mruby/compile.h @@ -28,6 +28,7 @@ typedef struct mrbc_context { mrb_bool capture_errors:1; mrb_bool dump_result:1; mrb_bool no_exec:1; + mrb_bool keep_lv:1; } mrbc_context; mrbc_context* mrbc_context_new(mrb_state *mrb); diff --git a/include/mruby/error.h b/include/mruby/error.h index 7ae2d4348..4d37f1701 100644 --- a/include/mruby/error.h +++ b/include/mruby/error.h @@ -19,6 +19,7 @@ void mrb_exc_print(mrb_state *mrb, struct RObject *exc); void mrb_print_backtrace(mrb_state *mrb); mrb_value mrb_exc_backtrace(mrb_state *mrb, mrb_value exc); mrb_value mrb_get_backtrace(mrb_state *mrb); +mrb_noreturn void mrb_no_method_error(mrb_state *mrb, mrb_sym id, mrb_int argc, const mrb_value *argv, const char *fmt, ...); /* declaration for fail method */ mrb_value mrb_f_raise(mrb_state*, mrb_value); diff --git a/include/mruby/string.h b/include/mruby/string.h index dd73f5f1e..f8a1fa7bd 100644 --- a/include/mruby/string.h +++ b/include/mruby/string.h @@ -11,8 +11,6 @@ extern "C" { #endif -#define IS_EVSTR(p,e) ((p) < (e) && (*(p) == '$' || *(p) == '@' || *(p) == '{')) - extern const char mrb_digitmap[]; #define RSTRING_EMBED_LEN_MAX ((mrb_int)(sizeof(void*) * 3 - 1)) @@ -32,21 +30,42 @@ struct RString { } as; }; -#define mrb_str_ptr(s) ((struct RString*)(mrb_ptr(s))) -#define RSTRING(s) ((struct RString*)(mrb_ptr(s))) -#define RSTRING_PTR(s)\ - ((RSTRING(s)->flags & MRB_STR_EMBED) ?\ - RSTRING(s)->as.ary :\ - RSTRING(s)->as.heap.ptr) -#define RSTRING_LEN(s)\ - ((RSTRING(s)->flags & MRB_STR_EMBED) ?\ - (mrb_int)((RSTRING(s)->flags & MRB_STR_EMBED_LEN_MASK) >> MRB_STR_EMBED_LEN_SHIFT) :\ - RSTRING(s)->as.heap.len) -#define RSTRING_CAPA(s)\ - ((RSTRING(s)->flags & MRB_STR_EMBED) ?\ - RSTRING_EMBED_LEN_MAX :\ - RSTRING(s)->as.heap.aux.capa) -#define RSTRING_END(s) (RSTRING_PTR(s) + RSTRING_LEN(s)) +#define RSTR_EMBED_P(s) ((s)->flags & MRB_STR_EMBED) +#define RSTR_SET_EMBED_FLAG(s) ((s)->flags |= MRB_STR_EMBED) +#define RSTR_UNSET_EMBED_FLAG(s) ((s)->flags &= ~(MRB_STR_EMBED|MRB_STR_EMBED_LEN_MASK)) +#define RSTR_SET_EMBED_LEN(s, n) do {\ + size_t tmp_n = (n);\ + s->flags &= ~MRB_STR_EMBED_LEN_MASK;\ + s->flags |= (tmp_n) << MRB_STR_EMBED_LEN_SHIFT;\ +} while (0) +#define RSTR_SET_LEN(s, n) do {\ + if (RSTR_EMBED_P(s)) {\ + RSTR_SET_EMBED_LEN((s),(n));\ + } else {\ + s->as.heap.len = (mrb_int)(n);\ + }\ +} while (0) +#define RSTR_EMBED_LEN(s)\ + (mrb_int)(((s)->flags & MRB_STR_EMBED_LEN_MASK) >> MRB_STR_EMBED_LEN_SHIFT) +#define RSTR_PTR(s) ((RSTR_EMBED_P(s)) ? (s)->as.ary : (s)->as.heap.ptr) +#define RSTR_LEN(s) ((RSTR_EMBED_P(s)) ? RSTR_EMBED_LEN(s) : (s)->as.heap.len) +#define RSTR_CAPA(s) (RSTR_EMBED_P(s) ? RSTRING_EMBED_LEN_MAX : (s)->as.heap.aux.capa) + +#define RSTR_SHARED_P(s) ((s)->flags & MRB_STR_SHARED) +#define RSTR_SET_SHARED_FLAG(s) ((s)->flags |= MRB_STR_SHARED) +#define RSTR_UNSET_SHARED_FLAG(s) ((s)->flags &= ~MRB_STR_SHARED) + +#define RSTR_NOFREE_P(s) ((s)->flags & MRB_STR_NOFREE) +#define RSTR_SET_NOFREE_FLAG(s) ((s)->flags |= MRB_STR_NOFREE) +#define RSTR_UNSET_NOFREE_FLAG(s) ((s)->flags &= ~MRB_STR_NOFREE) + +#define mrb_str_ptr(s) ((struct RString*)(mrb_ptr(s))) +#define RSTRING(s) mrb_str_ptr(s) +#define RSTRING_PTR(s) RSTR_PTR(RSTRING(s)) +#define RSTRING_EMBED_LEN(s) RSTR_ENBED_LEN(RSTRING(s)) +#define RSTRING_LEN(s) RSTR_LEN(RSTRING(s)) +#define RSTRING_CAPA(s) RSTR_CAPA(RSTRING(s)) +#define RSTRING_END(s) (RSTRING_PTR(s) + RSTRING_LEN(s)) mrb_int mrb_str_strlen(mrb_state*, struct RString*); #define MRB_STR_SHARED 1 diff --git a/include/mruby/value.h b/include/mruby/value.h index 83696715d..98af9626d 100644 --- a/include/mruby/value.h +++ b/include/mruby/value.h @@ -70,7 +70,9 @@ typedef short mrb_sym; # ifndef __cplusplus # define inline __inline # endif -# define snprintf _snprintf +# if _MSC_VER < 1900 +# define snprintf _snprintf +# endif # if _MSC_VER < 1800 # include <float.h> # define isfinite(n) _finite(n) diff --git a/mrbgems/mruby-array-ext/src/array.c b/mrbgems/mruby-array-ext/src/array.c index 014137e99..d69f0ac44 100644 --- a/mrbgems/mruby-array-ext/src/array.c +++ b/mrbgems/mruby-array-ext/src/array.c @@ -2,6 +2,7 @@ #include "mruby/value.h" #include "mruby/array.h" #include "mruby/range.h" +#include "mruby/hash.h" /* * call-seq: @@ -105,6 +106,48 @@ mrb_ary_values_at(mrb_state *mrb, mrb_value self) return mrb_get_values_at(mrb, self, RARRAY_LEN(self), argc, argv, mrb_ary_ref); } +/* + * call-seq: + * ary.to_h -> Hash + * + * Returns the result of interpreting <i>aray</i> as an array of + * <tt>[key, value]</tt> paris. + * + * [[:foo, :bar], [1, 2]].to_h + * # => {:foo => :bar, 1 => 2} + */ + +static mrb_value +mrb_ary_to_h(mrb_state *mrb, mrb_value ary) +{ + mrb_int i; + mrb_value v, hash; + + hash = mrb_hash_new_capa(mrb, 0); + + for (i = 0; i < RARRAY_LEN(ary); ++i) { + v = mrb_check_array_type(mrb, RARRAY_PTR(ary)[i]); + + if (mrb_nil_p(v)) { + mrb_raisef(mrb, E_TYPE_ERROR, "wrong element type %S at %S (expected array)", + mrb_str_new_cstr(mrb, mrb_obj_classname(mrb, RARRAY_PTR(ary)[i])), + mrb_fixnum_value(i) + ); + } + + if (RARRAY_LEN(v) != 2) { + mrb_raisef(mrb, E_ARGUMENT_ERROR, "wrong array length at %S (expected 2, was %S)", + mrb_fixnum_value(i), + mrb_fixnum_value(RARRAY_LEN(v)) + ); + } + + mrb_hash_set(mrb, hash, RARRAY_PTR(v)[0], RARRAY_PTR(v)[1]); + } + + return hash; +} + void mrb_mruby_array_ext_gem_init(mrb_state* mrb) { @@ -114,6 +157,7 @@ mrb_mruby_array_ext_gem_init(mrb_state* mrb) mrb_define_method(mrb, a, "at", mrb_ary_at, MRB_ARGS_REQ(1)); mrb_define_method(mrb, a, "rassoc", mrb_ary_rassoc, MRB_ARGS_REQ(1)); mrb_define_method(mrb, a, "values_at", mrb_ary_values_at, MRB_ARGS_ANY()); + mrb_define_method(mrb, a, "to_h", mrb_ary_to_h, MRB_ARGS_REQ(0)); } void diff --git a/mrbgems/mruby-array-ext/test/array.rb b/mrbgems/mruby-array-ext/test/array.rb index cb5652dde..8c919f7e0 100644 --- a/mrbgems/mruby-array-ext/test/array.rb +++ b/mrbgems/mruby-array-ext/test/array.rb @@ -285,3 +285,11 @@ assert('Array#values_at') do assert_equal ['none', nil, nil, 'red', 'green', 'purple'], a.values_at(4..6, 0...3) assert_raise(TypeError) { a.values_at 'tt' } end + +assert('Array#to_h') do + assert_equal({}, [].to_h) + assert_equal({a: 1, b:2}, [[:a, 1], [:b, 2]].to_h) + + assert_raise(TypeError) { [1].to_h } + assert_raise(ArgumentError) { [[1]].to_h } +end diff --git a/mrbgems/mruby-bin-mirb/tools/mirb/mirb.c b/mrbgems/mruby-bin-mirb/tools/mirb/mirb.c index 5c9524161..dece361a5 100644 --- a/mrbgems/mruby-bin-mirb/tools/mirb/mirb.c +++ b/mrbgems/mruby-bin-mirb/tools/mirb/mirb.c @@ -258,7 +258,7 @@ check_keyword(const char *buf, const char *word) size_t len = strlen(word); /* skip preceding spaces */ - while (*p && isspace(*p)) { + while (*p && isspace((unsigned char)*p)) { p++; } /* check keyword */ @@ -268,7 +268,7 @@ check_keyword(const char *buf, const char *word) p += len; /* skip trailing spaces */ while (*p) { - if (!isspace(*p)) return 0; + if (!isspace((unsigned char)*p)) return 0; p++; } return 1; @@ -424,7 +424,7 @@ main(int argc, char **argv) else { /* no */ if (!mrb_respond_to(mrb, result, mrb_intern_lit(mrb, "inspect"))){ - result = mrb_any_to_s(mrb,result); + result = mrb_any_to_s(mrb, result); } p(mrb, result, 1); } diff --git a/mrbgems/mruby-bin-mruby/bintest/mruby.rb b/mrbgems/mruby-bin-mruby/bintest/mruby.rb index a453400fc..de211c1ba 100644 --- a/mrbgems/mruby-bin-mruby/bintest/mruby.rb +++ b/mrbgems/mruby-bin-mruby/bintest/mruby.rb @@ -30,3 +30,17 @@ assert '$0 value' do # one liner assert_equal '"-e"', `./bin/mruby -e 'p $0'`.chomp end + +assert '__END__', '8.6' do + script = Tempfile.new('test.rb') + + script.write <<EOS +p 'test' + __END__ = 'fin' +p __END__ +__END__ +p 'legend' +EOS + script.flush + assert_equal "\"test\"\n\"fin\"\n", `./bin/mruby #{script.path}` +end diff --git a/mrbgems/mruby-enum-ext/mrblib/enum.rb b/mrbgems/mruby-enum-ext/mrblib/enum.rb index 5abb75d54..b13d00a84 100644 --- a/mrbgems/mruby-enum-ext/mrblib/enum.rb +++ b/mrbgems/mruby-enum-ext/mrblib/enum.rb @@ -669,4 +669,30 @@ module Enumerable end ary end + + ## + # call-seq: + # enum.to_h -> hash + # + # Returns the result of interpreting <i>enum</i> as a list of + # <tt>[key, value]</tt> pairs. + # + # %i[hello world].each_with_index.to_h + # # => {:hello => 0, :world => 1} + # + + def to_h + h = {} + self.each do |*v| + v = v.__svalue + raise TypeError, "wrong element type #{v.class} (expected Array)" unless v.is_a? Array + raise ArgumentError, "element has wrong array length (expected 2, was #{v.size})" if v.size != 2 + h[v[0]] = v[1] + end + h + end + + def nil.to_h + {} + end end diff --git a/mrbgems/mruby-enum-ext/test/enum.rb b/mrbgems/mruby-enum-ext/test/enum.rb index bce9cb15d..08b553fe5 100644 --- a/mrbgems/mruby-enum-ext/test/enum.rb +++ b/mrbgems/mruby-enum-ext/test/enum.rb @@ -144,3 +144,19 @@ assert("Enumerable#zip") do assert_equal [[1, 4, 7], [2, 5, 8]], [1, 2].zip(a, b) assert_equal [[4, 1, 8], [5, 2, nil], [6, nil, nil]], a.zip([1, 2], [8]) end + +assert("Enumerable#to_h") do + c = Class.new { + include Enumerable + def each + yield [1,2] + yield [3,4] + end + } + h0 = {1=>2, 3=>4} + h = c.new.to_h + assert_equal Hash, h.class + assert_equal h0, h + # mruby-enum-ext also provides nil.to_h + assert_equal Hash.new, nil.to_h +end diff --git a/mrbgems/mruby-hash-ext/mrblib/hash.rb b/mrbgems/mruby-hash-ext/mrblib/hash.rb index 504848a74..9da08dc3a 100644 --- a/mrbgems/mruby-hash-ext/mrblib/hash.rb +++ b/mrbgems/mruby-hash-ext/mrblib/hash.rb @@ -182,4 +182,15 @@ class Hash end nil end + + ## + # call-seq: + # hsh.to_h -> hsh or new_hash + # + # Returns +self+. If called on a subclass of Hash, converts + # the receiver to a Hash object. + # + def to_h + self + end end diff --git a/mrbgems/mruby-hash-ext/test/hash.rb b/mrbgems/mruby-hash-ext/test/hash.rb index 62cfc8856..2bc5b911a 100644 --- a/mrbgems/mruby-hash-ext/test/hash.rb +++ b/mrbgems/mruby-hash-ext/test/hash.rb @@ -115,3 +115,9 @@ assert("Hash#key") do assert_nil h.key('nil') assert_equal 'nil', h.key(nil) end + +assert("Hash#to_h") do + h = { "a" => 100, "b" => 200 } + assert_equal Hash, h.to_h.class + assert_equal h, h.to_h +end diff --git a/mrbgems/mruby-kernel-ext/src/kernel.c b/mrbgems/mruby-kernel-ext/src/kernel.c index 1ce63ac94..a6ecd72bd 100644 --- a/mrbgems/mruby-kernel-ext/src/kernel.c +++ b/mrbgems/mruby-kernel-ext/src/kernel.c @@ -26,7 +26,7 @@ mrb_mruby_kernel_ext_gem_init(mrb_state *mrb) { struct RClass *krn = mrb->kernel_module; - mrb_define_module_function(mrb, krn, "fail", mrb_f_raise, MRB_ARGS_NONE()); + mrb_define_module_function(mrb, krn, "fail", mrb_f_raise, MRB_ARGS_OPT(2)); mrb_define_method(mrb, krn, "__method__", mrb_f_method, MRB_ARGS_NONE()); } diff --git a/mrbgems/mruby-sprintf/mrblib/string.rb b/mrbgems/mruby-sprintf/mrblib/string.rb new file mode 100644 index 000000000..d7e55536a --- /dev/null +++ b/mrbgems/mruby-sprintf/mrblib/string.rb @@ -0,0 +1,9 @@ +class String + def %(args) + if args.is_a? Array + sprintf(self, *args) + else + sprintf(self, args) + end + end +end diff --git a/mrbgems/mruby-sprintf/test/sprintf.rb b/mrbgems/mruby-sprintf/test/sprintf.rb index 52e94fb83..7007df1fa 100644 --- a/mrbgems/mruby-sprintf/test/sprintf.rb +++ b/mrbgems/mruby-sprintf/test/sprintf.rb @@ -1,3 +1,8 @@ -## -# Kernel#sprintf Kernel#format Test +#assert('Kernel.sprintf') do +#end +assert('String#%') do + assert_equal "one=1", "one=%d" % 1 + assert_equal "1 one 1.0", "%d %s %3.1f" % [ 1, "one", 1.01 ] + assert_equal "123 < 456", "%{num} < %<str>s" % { num: 123, str: "456" } +end diff --git a/mrbgems/mruby-string-ext/mrblib/string.rb b/mrbgems/mruby-string-ext/mrblib/string.rb index 45c631b94..b8cb93199 100644 --- a/mrbgems/mruby-string-ext/mrblib/string.rb +++ b/mrbgems/mruby-string-ext/mrblib/string.rb @@ -1,4 +1,28 @@ class String + + ## + # call-seq: + # string.clear -> string + # + # Makes string empty. + # + # a = "abcde" + # a.clear #=> "" + # + def clear + self.replace("") + end + + ## + # call-seq: + # str.lstrip -> new_str + # + # Returns a copy of <i>str</i> with leading whitespace removed. See also + # <code>String#rstrip</code> and <code>String#strip</code>. + # + # " hello ".lstrip #=> "hello " + # "hello".lstrip #=> "hello" + # def lstrip a = 0 z = self.size - 1 @@ -6,6 +30,16 @@ class String (z >= 0) ? self[a..z] : "" end + ## + # call-seq: + # str.rstrip -> new_str + # + # Returns a copy of <i>str</i> with trailing whitespace removed. See also + # <code>String#lstrip</code> and <code>String#strip</code>. + # + # " hello ".rstrip #=> " hello" + # "hello".rstrip #=> "hello" + # def rstrip a = 0 z = self.size - 1 @@ -13,6 +47,15 @@ class String (z >= 0) ? self[a..z] : "" end + ## + # call-seq: + # str.strip -> new_str + # + # Returns a copy of <i>str</i> with leading and trailing whitespace removed. + # + # " hello ".strip #=> "hello" + # "\tgoodbye\r\n".strip #=> "goodbye" + # def strip a = 0 z = self.size - 1 @@ -21,31 +64,61 @@ class String (z >= 0) ? self[a..z] : "" end + ## + # call-seq: + # str.lstrip! -> self or nil + # + # Removes leading whitespace from <i>str</i>, returning <code>nil</code> if no + # change was made. See also <code>String#rstrip!</code> and + # <code>String#strip!</code>. + # + # " hello ".lstrip #=> "hello " + # "hello".lstrip! #=> nil + # def lstrip! s = self.lstrip (s == self) ? nil : self.replace(s) end + ## + # call-seq: + # str.rstrip! -> self or nil + # + # Removes trailing whitespace from <i>str</i>, returning <code>nil</code> if + # no change was made. See also <code>String#lstrip!</code> and + # <code>String#strip!</code>. + # + # " hello ".rstrip #=> " hello" + # "hello".rstrip! #=> nil + # def rstrip! s = self.rstrip (s == self) ? nil : self.replace(s) end + ## + # call-seq: + # str.strip! -> str or nil + # + # Removes leading and trailing whitespace from <i>str</i>. Returns + # <code>nil</code> if <i>str</i> was not altered. + # def strip! s = self.strip (s == self) ? nil : self.replace(s) end -# call-seq: -# str.casecmp(other_str) -> -1, 0, +1 or nil -# -# Case-insensitive version of <code>String#<=></code>. -# -# "abcdef".casecmp("abcde") #=> 1 -# "aBcDeF".casecmp("abcdef") #=> 0 -# "abcdef".casecmp("abcdefg") #=> -1 -# "abcdef".casecmp("ABCDEF") #=> 0 -# + ## + # call-seq: + # str.casecmp(other_str) -> -1, 0, +1 or nil + # + # Case-insensitive version of <code>String#<=></code>. + # + # "abcdef".casecmp("abcde") #=> 1 + # "aBcDeF".casecmp("abcdef") #=> 0 + # "abcdef".casecmp("abcdefg") #=> -1 + # "abcdef".casecmp("ABCDEF") #=> 0 + # def casecmp(str) self.downcase <=> str.to_str.downcase rescue NoMethodError @@ -73,4 +146,74 @@ class String [ "", "", self ] end end + + ## + # call-seq: + # str.slice!(fixnum) -> new_str or nil + # str.slice!(fixnum, fixnum) -> new_str or nil + # str.slice!(range) -> new_str or nil + # str.slice!(other_str) -> new_str or nil + # + # Deletes the specified portion from <i>str</i>, and returns the portion + # deleted. + # + # string = "this is a string" + # string.slice!(2) #=> "i" + # string.slice!(3..6) #=> " is " + # string.slice!("r") #=> "r" + # string #=> "thsa sting" + # + def slice!(arg1, arg2=nil) + raise "wrong number of arguments (for 1..2)" if arg1 == nil && arg2 == nil + + if arg1 != nil && arg2 != nil + idx = arg1 + idx += self.size if arg1 < 0 + if idx >= 0 && idx <= self.size && arg2 > 0 + str = self[idx, arg2] + else + return nil + end + else + validated = false + if arg1.kind_of?(Range) + beg = arg1.begin + ed = arg1.end + beg += self.size if beg < 0 + ed += self.size if ed < 0 + validated = true + elsif arg1.kind_of?(String) + validated = true + else + idx = arg1 + idx += self.size if arg1 < 0 + validated = true if idx >=0 && arg1 < self.size + end + if validated + str = self[arg1] + else + return nil + end + end + unless str == nil || str == "" + if arg1 != nil && arg2 !=nil + idx = arg1 >= 0 ? arg1 : self.size+arg1 + str2 = self[0...idx] + self[idx+arg2..-1] + else + if arg1.kind_of?(Range) + idx = beg >= 0 ? beg : self.size+beg + idx2 = ed>= 0 ? ed : self.size+ed + str2 = self[0...idx] + self[idx2+1..-1] + elsif arg1.kind_of?(String) + idx = self.index(arg1) + str2 = self[0...idx] + self[idx+arg1.size..-1] unless idx == nil + else + idx = arg1 >= 0 ? arg1 : self.size+arg1 + str2 = self[0...idx] + self[idx+1..-1] + end + end + self.replace(str2) unless str2 == nil + end + str + end end diff --git a/mrbgems/mruby-string-ext/src/string.c b/mrbgems/mruby-string-ext/src/string.c index 2c0a406ad..f04f12c4b 100644 --- a/mrbgems/mruby-string-ext/src/string.c +++ b/mrbgems/mruby-string-ext/src/string.c @@ -1,6 +1,8 @@ #include <ctype.h> #include <string.h> #include "mruby.h" +#include "mruby/array.h" +#include "mruby/class.h" #include "mruby/string.h" static mrb_value @@ -175,6 +177,68 @@ mrb_str_oct(mrb_state *mrb, mrb_value self) return mrb_str_to_inum(mrb, self, 8, FALSE); } +/* + * call-seq: + * string.chr -> string + * + * Returns a one-character string at the beginning of the string. + * + * a = "abcde" + * a.chr #=> "a" + */ +static mrb_value +mrb_str_chr(mrb_state *mrb, mrb_value self) +{ + return mrb_str_substr(mrb, self, 0, 1); +} + +/* + * call-seq: + * string.lines -> array of string + * + * Returns strings per line; + * + * a = "abc\ndef" + * a.lines #=> ["abc\n", "def"] + */ +static mrb_value +mrb_str_lines(mrb_state *mrb, mrb_value self) +{ + mrb_value result; + mrb_value blk; + int ai; + mrb_int len; + mrb_value arg; + char *p = RSTRING_PTR(self), *t; + char *e = p + RSTRING_LEN(self); + + mrb_get_args(mrb, "&", &blk); + + result = mrb_ary_new(mrb); + + if (!mrb_nil_p(blk)) { + while (p < e) { + t = p; + while (p < e && *p != '\n') p++; + if (*p == '\n') p++; + len = (mrb_int) (p - t); + arg = mrb_str_new(mrb, t, len); + mrb_yield_argv(mrb, blk, 1, &arg); + } + return self; + } + while (p < e) { + ai = mrb_gc_arena_save(mrb); + t = p; + while (p < e && *p != '\n') p++; + if (*p == '\n') p++; + len = (mrb_int) (p - t); + mrb_ary_push(mrb, result, mrb_str_new(mrb, t, len)); + mrb_gc_arena_restore(mrb, ai); + } + return result; +} + void mrb_mruby_string_ext_gem_init(mrb_state* mrb) { @@ -190,6 +254,8 @@ mrb_mruby_string_ext_gem_init(mrb_state* mrb) mrb_define_method(mrb, s, "end_with?", mrb_str_end_with, MRB_ARGS_REST()); mrb_define_method(mrb, s, "hex", mrb_str_hex, MRB_ARGS_NONE()); mrb_define_method(mrb, s, "oct", mrb_str_oct, MRB_ARGS_NONE()); + mrb_define_method(mrb, s, "chr", mrb_str_chr, MRB_ARGS_NONE()); + mrb_define_method(mrb, s, "lines", mrb_str_lines, MRB_ARGS_NONE()); } void diff --git a/mrbgems/mruby-string-ext/test/string.rb b/mrbgems/mruby-string-ext/test/string.rb index 01712a607..2a8dd0cca 100644 --- a/mrbgems/mruby-string-ext/test/string.rb +++ b/mrbgems/mruby-string-ext/test/string.rb @@ -161,3 +161,93 @@ assert('String#oct') do assert_equal 8, "010".oct assert_equal (-8), "-10".oct end + +assert('String#chr') do + assert_equal "a", "abcde".chr +end + +assert('String#lines') do + assert_equal ["Hel\n", "lo\n", "World!"], "Hel\nlo\nWorld!".lines + assert_equal ["Hel\n", "lo\n", "World!\n"], "Hel\nlo\nWorld!\n".lines + assert_equal ["\n", "\n", "\n"], "\n\n\n".lines + assert_equal [], "".lines +end + +assert('String#clear') do + # embed string + s = "foo" + assert_equal("", s.clear) + assert_equal("", s) + + # not embed string and not shared string + s = "foo" * 100 + a = s + assert_equal("", s.clear) + assert_equal("", s) + assert_equal("", a) + + # shared string + s = "foo" * 100 + a = s[10, 90] # create shared string + assert_equal("", s.clear) # clear + assert_equal("", s) # s is cleared + assert_not_equal("", a) # a should not be affected +end + +assert('String#slice!') do + a = "AooBar" + b = a.dup + assert_equal "A", a.slice!(0) + assert_equal "AooBar", b + + a = "FooBar" + assert_equal "r", a.slice!(-1) + assert_equal "FooBa", a + + a = "FooBar" + assert_nil a.slice!(6) + assert_nil a.slice!(-7) + assert_equal "FooBar", a + + a = "FooBar" + assert_equal "Foo", a.slice!(0, 3) + assert_equal "Bar", a + + a = "FooBar" + assert_equal "Bar", a.slice!(-3, 3) + assert_equal "Foo", a + + a = "FooBar" + assert_equal "", a.slice!(6, 2) + assert_equal "FooBar", a + + a = "FooBar" + assert_nil a.slice!(-7,10) + assert_equal "FooBar", a + + a = "FooBar" + assert_equal "Foo", a.slice!(0..2) + assert_equal "Bar", a + + a = "FooBar" + assert_equal "Bar", a.slice!(-3..-1) + assert_equal "Foo", a + + a = "FooBar" + assert_equal "", a.slice!(6..2) + assert_equal "FooBar", a + + a = "FooBar" + assert_nil a.slice!(-10..-7) + assert_equal "FooBar", a + + a = "FooBar" + assert_equal "Foo", a.slice!("Foo") + assert_equal "Bar", a + + a = "FooBar" + assert_nil a.slice!("xyzzy") + assert_equal "FooBar", a + + assert_raise(ArgumentError) { "foo".slice! } +end diff --git a/mrbgems/mruby-string-utf8/mrbgem.rake b/mrbgems/mruby-string-utf8/mrbgem.rake index 86d0a6da3..7642d4e07 100644 --- a/mrbgems/mruby-string-utf8/mrbgem.rake +++ b/mrbgems/mruby-string-utf8/mrbgem.rake @@ -2,4 +2,5 @@ MRuby::Gem::Specification.new('mruby-string-utf8') do |spec| spec.license = 'MIT' spec.author = 'mruby developers' spec.summary = 'UTF-8 support in String class' + spec.add_dependency('mruby-string-ext', :core => 'mruby-string-ext') end diff --git a/mrbgems/mruby-string-utf8/src/string.c b/mrbgems/mruby-string-utf8/src/string.c index edda491fc..874fa8dbb 100644 --- a/mrbgems/mruby-string-utf8/src/string.c +++ b/mrbgems/mruby-string-utf8/src/string.c @@ -1,17 +1,12 @@ #include "mruby.h" #include "mruby/array.h" +#include "mruby/class.h" #include "mruby/string.h" #include "mruby/range.h" #include "mruby/re.h" #include <ctype.h> #include <string.h> -#define STR_EMBED_P(s) ((s)->flags & MRB_STR_EMBED) -#define STR_EMBED_LEN(s)\ - (mrb_int)(((s)->flags & MRB_STR_EMBED_LEN_MASK) >> MRB_STR_EMBED_LEN_SHIFT) -#define STR_PTR(s) ((STR_EMBED_P(s)) ? (s)->as.ary : (s)->as.heap.ptr) -#define STR_LEN(s) ((STR_EMBED_P(s)) ? STR_EMBED_LEN(s) : (mrb_int)(s)->as.heap.len) - static const char utf8len_codepage[256] = { 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, @@ -36,27 +31,6 @@ static char utf8len_codepage_zero[256] = 3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,4,4,4,4,4,0,0,0,0,0,0,0,0,0,0,0, }; -static const char isspacetable[256] = { - 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 -}; - -#define ascii_isspace(c) isspacetable[(unsigned char)(c)] - static mrb_int utf8code(unsigned char* p) { @@ -127,9 +101,7 @@ mrb_utf8_strlen(mrb_value str, mrb_int len) static mrb_value mrb_str_size(mrb_state *mrb, mrb_value str) { - mrb_int size = mrb_utf8_strlen(str, -1); - - return mrb_fixnum_value(size); + return mrb_fixnum_value(mrb_utf8_strlen(str, -1)); } #define RSTRING_LEN_UTF8(s) mrb_utf8_strlen(s, -1) @@ -161,10 +133,10 @@ mrb_memsearch_qs(const unsigned char *xs, mrb_int m, const unsigned char *ys, mr qstable[i] = m + 1; for (; x < xe; ++x) qstable[*x] = xe - x; - /* Searching */ + /* Searching */ for (; y + m <= ys + n; y += *(qstable + y[m])) { if (*xs == *y && memcmp(xs, y, m) == 0) - return y - ys; + return y - ys; } return -1; } @@ -272,17 +244,17 @@ str_rindex(mrb_state *mrb, mrb_value str, mrb_value sub, mrb_int pos) mrb_int len = RSTRING_LEN(sub); /* substring longer than string */ - if (STR_LEN(ps) < len) return -1; - if (STR_LEN(ps) - pos < len) { - pos = STR_LEN(ps) - len; + if (RSTR_LEN(ps) < len) return -1; + if (RSTR_LEN(ps) - pos < len) { + pos = RSTR_LEN(ps) - len; } - sbeg = STR_PTR(ps); - s = STR_PTR(ps) + pos; + sbeg = RSTR_PTR(ps); + s = RSTR_PTR(ps) + pos; t = RSTRING_PTR(sub); if (len) { while (sbeg <= s) { if (memcmp(s, t, len) == 0) { - return s - STR_PTR(ps); + return s - RSTR_PTR(ps); } s--; } @@ -572,7 +544,7 @@ mrb_str_split_m(mrb_state *mrb, mrb_value str) if (mrb_string_p(spat)) { split_type = string; if (RSTRING_LEN(spat) == 1 && RSTRING_PTR(spat)[0] == ' '){ - split_type = awk; + split_type = awk; } } else { @@ -594,7 +566,7 @@ mrb_str_split_m(mrb_state *mrb, mrb_value str) int ai = mrb_gc_arena_save(mrb); c = (unsigned char)*ptr++; if (skip) { - if (ascii_isspace(c)) { + if (ISSPACE(c)) { beg = ptr - bptr; } else { @@ -603,7 +575,7 @@ mrb_str_split_m(mrb_state *mrb, mrb_value str) if (lim_p && lim <= i) break; } } - else if (ascii_isspace(c)) { + else if (ISSPACE(c)) { mrb_ary_push(mrb, result, str_subseq(mrb, str, beg, end-beg)); mrb_gc_arena_restore(mrb, ai); skip = 1; @@ -667,6 +639,80 @@ mrb_str_split_m(mrb_state *mrb, mrb_value str) return result; } +static mrb_value +mrb_str_chr(mrb_state *mrb, mrb_value self) +{ + return str_substr(mrb, self, 0, 1); +} + +static mrb_value +mrb_str_chars(mrb_state *mrb, mrb_value self) +{ + mrb_value result; + mrb_value blk; + int ai; + mrb_int len; + mrb_value arg; + char *p = RSTRING_PTR(self); + char *e = p + RSTRING_LEN(self); + + mrb_get_args(mrb, "&", &blk); + + result = mrb_ary_new(mrb); + + if (!mrb_nil_p(blk)) { + while (p < e) { + len = utf8len((unsigned char*) p); + arg = mrb_str_new(mrb, p, len); + mrb_yield_argv(mrb, blk, 1, &arg); + p += len; + } + return self; + } + while (p < e) { + ai = mrb_gc_arena_save(mrb); + len = utf8len((unsigned char*) p); + mrb_ary_push(mrb, result, mrb_str_new(mrb, p, len)); + mrb_gc_arena_restore(mrb, ai); + p += len; + } + return result; +} + +static mrb_value +mrb_str_codepoints(mrb_state *mrb, mrb_value self) +{ + mrb_value result; + mrb_value blk; + int ai; + mrb_int len; + mrb_value arg; + char *p = RSTRING_PTR(self); + char *e = p + RSTRING_LEN(self); + + mrb_get_args(mrb, "&", &blk); + + result = mrb_ary_new(mrb); + + if (!mrb_nil_p(blk)) { + while (p < e) { + len = utf8len((unsigned char*) p); + arg = mrb_fixnum_value(utf8code((unsigned char*) p)); + mrb_yield_argv(mrb, blk, 1, &arg); + p += len; + } + return self; + } + while (p < e) { + ai = mrb_gc_arena_save(mrb); + len = utf8len((unsigned char*) p); + mrb_ary_push(mrb, result, mrb_fixnum_value(utf8code((unsigned char*) p))); + mrb_gc_arena_restore(mrb, ai); + p += len; + } + return result; +} + void mrb_mruby_string_utf8_gem_init(mrb_state* mrb) { @@ -682,6 +728,11 @@ mrb_mruby_string_utf8_gem_init(mrb_state* mrb) mrb_define_method(mrb, s, "reverse", mrb_str_reverse, MRB_ARGS_NONE()); mrb_define_method(mrb, s, "reverse!", mrb_str_reverse_bang, MRB_ARGS_NONE()); mrb_define_method(mrb, s, "rindex", mrb_str_rindex_m, MRB_ARGS_ANY()); + mrb_define_method(mrb, s, "chr", mrb_str_chr, MRB_ARGS_NONE()); + mrb_define_method(mrb, s, "chars", mrb_str_chars, MRB_ARGS_NONE()); + mrb_alias_method(mrb, s, mrb_intern_lit(mrb, "each_char"), mrb_intern_lit(mrb, "chars")); + mrb_define_method(mrb, s, "codepoints", mrb_str_codepoints, MRB_ARGS_NONE()); + mrb_alias_method(mrb, s, mrb_intern_lit(mrb, "each_codepoint"), mrb_intern_lit(mrb, "codepoints")); mrb_define_method(mrb, mrb->fixnum_class, "chr", mrb_fixnum_chr, MRB_ARGS_NONE()); } diff --git a/mrbgems/mruby-string-utf8/test/string.rb b/mrbgems/mruby-string-utf8/test/string.rb index 1bfa8512c..5b4180037 100644 --- a/mrbgems/mruby-string-utf8/test/string.rb +++ b/mrbgems/mruby-string-utf8/test/string.rb @@ -66,3 +66,44 @@ assert('String#rindex') do assert_equal 12, str.rindex('ち') assert_equal 3, str.rindex('ち', 10) end + +assert('String#chr(utf-8)') do + assert_equal "こ", "こんにちは世界!".chr +end + +assert('String#chars') do + expect = ['こ', 'ん', 'に', 'ち', 'は', '世', '界', '!'] + assert_equal expect, "こんにちは世界!".chars + s = "" + "こんにちは世界!".chars do |x| + s += x + end + assert_equal "こんにちは世界!", s +end + +assert('String#each_char') do + expect = ['こ', 'ん', 'に', 'ち', 'は', '世', '界', '!'] + s = "" + "こんにちは世界!".each_char do |x| + s += x + end + assert_equal "こんにちは世界!", s +end +assert('String#codepoints') do + expect = [12371, 12435, 12395, 12385, 12399, 19990, 30028, 33] + assert_equal expect, "こんにちは世界!".codepoints + cp = [] + "こんにちは世界!".codepoints do |x| + cp << x + end + assert_equal expect, cp +end + +assert('String#each_codepoint') do + expect = [12371, 12435, 12395, 12385, 12399, 19990, 30028, 33] + cp = [] + "こんにちは世界!".each_codepoint do |x| + cp << x + end + assert_equal expect, cp +end diff --git a/mrbgems/mruby-struct/src/struct.c b/mrbgems/mruby-struct/src/struct.c index 6894ffec9..930384806 100644 --- a/mrbgems/mruby-struct/src/struct.c +++ b/mrbgems/mruby-struct/src/struct.c @@ -4,8 +4,8 @@ ** See Copyright Notice in mruby.h */ +#include <ctype.h> #include <string.h> -#include <stdarg.h> #include "mruby.h" #include "mruby/array.h" #include "mruby/string.h" @@ -40,13 +40,7 @@ struct_ivar_get(mrb_state *mrb, mrb_value c, mrb_sym id) } } -mrb_value -mrb_struct_iv_get(mrb_state *mrb, mrb_value c, const char *name) -{ - return struct_ivar_get(mrb, c, mrb_intern_cstr(mrb, name)); -} - -mrb_value +static mrb_value mrb_struct_s_members(mrb_state *mrb, mrb_value klass) { mrb_value members = struct_ivar_get(mrb, klass, mrb_intern_lit(mrb, "__members__")); @@ -60,7 +54,7 @@ mrb_struct_s_members(mrb_state *mrb, mrb_value klass) return members; } -mrb_value +static mrb_value mrb_struct_members(mrb_state *mrb, mrb_value s) { mrb_value members = mrb_struct_s_members(mrb, mrb_obj_value(mrb_obj_class(mrb, s))); @@ -109,7 +103,7 @@ mrb_struct_members_m(mrb_state *mrb, mrb_value obj) return mrb_struct_s_members_m(mrb, mrb_obj_value(mrb_obj_class(mrb, obj))); } -mrb_value +static mrb_value mrb_struct_getmember(mrb_state *mrb, mrb_value obj, mrb_sym id) { mrb_value members, slot, *ptr, *ptr_members; @@ -149,7 +143,7 @@ static mrb_value mrb_struct_ref9(mrb_state* mrb, mrb_value obj) {return RSTRUCT_ #define numberof(array) (int)(sizeof(array) / sizeof((array)[0])) #define N_REF_FUNC numberof(ref_func) -static mrb_value (*const ref_func[])(mrb_state*, mrb_value) = { +static const mrb_func_t ref_func[] = { mrb_struct_ref0, mrb_struct_ref1, mrb_struct_ref2, @@ -162,7 +156,7 @@ static mrb_value (*const ref_func[])(mrb_state*, mrb_value) = { mrb_struct_ref9, }; -mrb_sym +static mrb_sym mrb_id_attrset(mrb_state *mrb, mrb_sym id) { const char *name; @@ -185,8 +179,7 @@ static mrb_value mrb_struct_set(mrb_state *mrb, mrb_value obj, mrb_value val) { const char *name; - size_t i, len; - mrb_int slen; + mrb_int i, len, slen; mrb_sym mid; mrb_value members, slot, *ptr, *ptr_members; @@ -217,19 +210,18 @@ mrb_struct_set_m(mrb_state *mrb, mrb_value obj) return mrb_struct_set(mrb, obj, val); } -#define is_notop_id(id) (id) /* ((id)>tLAST_TOKEN) */ -#define is_local_id(id) (is_notop_id(id)) /* &&((id)&ID_SCOPE_MASK)==ID_LOCAL) */ -int -mrb_is_local_id(mrb_sym id) +static mrb_bool +is_local_id(mrb_state *mrb, const char *name) { - return is_local_id(id); + if (!name) return FALSE; + return !ISUPPER(name[0]); } -#define is_const_id(id) (is_notop_id(id)) /* &&((id)&ID_SCOPE_MASK)==ID_CONST) */ -int -mrb_is_const_id(mrb_sym id) +static mrb_bool +is_const_id(mrb_state *mrb, const char *name) { - return is_const_id(id); + if (!name) return FALSE; + return ISUPPER(name[0]); } static mrb_value @@ -248,7 +240,7 @@ make_struct(mrb_state *mrb, mrb_value name, mrb_value members, struct RClass * k /* old style: should we warn? */ name = mrb_str_to_str(mrb, name); id = mrb_obj_to_sym(mrb, name); - if (!mrb_is_const_id(id)) { + if (!is_const_id(mrb, mrb_sym2name_len(mrb, id, NULL))) { mrb_name_error(mrb, id, "identifier %S needs to be constant", name); } if (mrb_const_defined_at(mrb, klass, id)) { @@ -270,7 +262,9 @@ make_struct(mrb_state *mrb, mrb_value name, mrb_value members, struct RClass * k ai = mrb_gc_arena_save(mrb); for (i=0; i< len; i++) { mrb_sym id = mrb_symbol(ptr_members[i]); - if (mrb_is_local_id(id) || mrb_is_const_id(id)) { + const char *name = mrb_sym2name_len(mrb, id, NULL); + + if (is_local_id(mrb, name) || is_const_id(mrb, name)) { if (i < N_REF_FUNC) { mrb_define_method_id(mrb, c, id, ref_func[i], MRB_ARGS_NONE()); } @@ -284,27 +278,6 @@ make_struct(mrb_state *mrb, mrb_value name, mrb_value members, struct RClass * k return nstr; } -mrb_value -mrb_struct_define(mrb_state *mrb, const char *name, ...) -{ - va_list ar; - mrb_value nm, ary; - char *mem; - - if (!name) nm = mrb_nil_value(); - else nm = mrb_str_new_cstr(mrb, name); - ary = mrb_ary_new(mrb); - - va_start(ar, name); - while ((mem = va_arg(ar, char*)) != 0) { - mrb_sym slot = mrb_intern_cstr(mrb, mem); - mrb_ary_push(mrb, ary, mrb_symbol_value(slot)); - } - va_end(ar); - - return make_struct(mrb, nm, ary, struct_class(mrb)); -} - /* 15.2.18.3.1 */ /* * call-seq: @@ -344,7 +317,7 @@ mrb_struct_s_def(mrb_state *mrb, mrb_value klass) { mrb_value name, rest; mrb_value *pargv; - int argcnt; + mrb_int argcnt; mrb_int i; mrb_value b, st; mrb_sym id; @@ -391,7 +364,7 @@ mrb_struct_s_def(mrb_state *mrb, mrb_value klass) return st; } -static int +static mrb_int num_members(mrb_state *mrb, struct RClass *klass) { mrb_value members; @@ -407,10 +380,10 @@ num_members(mrb_state *mrb, struct RClass *klass) /* */ static mrb_value -mrb_struct_initialize_withArg(mrb_state *mrb, int argc, mrb_value *argv, mrb_value self) +mrb_struct_initialize_withArg(mrb_state *mrb, mrb_int argc, mrb_value *argv, mrb_value self) { struct RClass *klass = mrb_obj_class(mrb, self); - int i, n; + mrb_int i, n; n = num_members(mrb, klass); if (n < argc) { @@ -436,14 +409,8 @@ mrb_struct_initialize_m(mrb_state *mrb, /*int argc, mrb_value *argv,*/ mrb_value return mrb_struct_initialize_withArg(mrb, argc, argv, self); } -mrb_value -mrb_struct_initialize(mrb_state *mrb, mrb_value self, mrb_value values) -{ - return mrb_struct_initialize_withArg(mrb, RARRAY_LEN(values), RARRAY_PTR(values), self); -} - static mrb_value -inspect_struct(mrb_state *mrb, mrb_value s, int recur) +inspect_struct(mrb_state *mrb, mrb_value s, mrb_bool recur) { const char *cn = mrb_class_name(mrb, mrb_obj_class(mrb, s)); mrb_value members, str = mrb_str_new_lit(mrb, "#<struct "); @@ -464,6 +431,8 @@ inspect_struct(mrb_state *mrb, mrb_value s, int recur) for (i=0; i<len; i++) { mrb_value slot; mrb_sym id; + const char *name; + mrb_int len; if (i > 0) { mrb_str_cat_lit(mrb, str, ", "); @@ -473,11 +442,8 @@ inspect_struct(mrb_state *mrb, mrb_value s, int recur) } slot = ptr_members[i]; id = mrb_symbol(slot); - if (mrb_is_local_id(id) || mrb_is_const_id(id)) { - const char *name; - mrb_int len; - - name = mrb_sym2name_len(mrb, id, &len); + name = mrb_sym2name_len(mrb, id, &len); + if (is_local_id(mrb, name) || is_const_id(mrb, name)) { mrb_str_append(mrb, str, mrb_str_new(mrb, name, len)); } else { @@ -501,12 +467,12 @@ inspect_struct(mrb_state *mrb, mrb_value s, int recur) static mrb_value mrb_struct_inspect(mrb_state *mrb, mrb_value s) { - return inspect_struct(mrb, s, 0); + return inspect_struct(mrb, s, FALSE); } /* 15.2.18.4.9 */ /* :nodoc: */ -mrb_value +static mrb_value mrb_struct_init_copy(mrb_state *mrb, mrb_value copy) { mrb_value s; @@ -584,7 +550,7 @@ struct_aref_int(mrb_state *mrb, mrb_value s, mrb_int i) * joe[:name] #=> "Joe Smith" * joe[0] #=> "Joe Smith" */ -mrb_value +static mrb_value mrb_struct_aref(mrb_state *mrb, mrb_value s) { mrb_value idx; @@ -651,7 +617,7 @@ mrb_struct_aset_sym(mrb_state *mrb, mrb_value s, mrb_sym id, mrb_value val) * joe.zip #=> "90210" */ -mrb_value +static mrb_value mrb_struct_aset(mrb_state *mrb, mrb_value s) { mrb_int i; diff --git a/mrbgems/mruby-struct/test/struct.rb b/mrbgems/mruby-struct/test/struct.rb index f4151c493..911e657bd 100644 --- a/mrbgems/mruby-struct/test/struct.rb +++ b/mrbgems/mruby-struct/test/struct.rb @@ -5,10 +5,6 @@ assert('Struct', '15.2.18') do Struct.class == Class end -assert('Struct superclass', '15.2.18.2') do - Struct.superclass == Object -end - assert('Struct.new', '15.2.18.3.1') do c = Struct.new(:m1, :m2) c.superclass == Struct and diff --git a/mrbgems/mruby-symbol-ext/mrblib/symbol.rb b/mrbgems/mruby-symbol-ext/mrblib/symbol.rb index 4cf18f647..1e3d24b80 100644 --- a/mrbgems/mruby-symbol-ext/mrblib/symbol.rb +++ b/mrbgems/mruby-symbol-ext/mrblib/symbol.rb @@ -1,6 +1,8 @@ class Symbol include Comparable + alias intern to_sym + def to_proc ->(obj,*args,&block) do obj.__send__(self, *args, &block) @@ -9,23 +11,12 @@ class Symbol ## # call-seq: - # sym.length -> integer - # - # Same as <code>sym.to_s.length</code>. - - def length - self.to_s.length - end - alias :size :length - - ## - # call-seq: # sym.capitalize -> symbol # # Same as <code>sym.to_s.capitalize.intern</code>. def capitalize - self.to_s.capitalize.intern + (self.to_s.capitalize! || self).to_sym end ## @@ -35,7 +26,7 @@ class Symbol # Same as <code>sym.to_s.downcase.intern</code>. def downcase - self.to_s.downcase.intern + (self.to_s.downcase! || self).to_sym end ## @@ -45,7 +36,7 @@ class Symbol # Same as <code>sym.to_s.upcase.intern</code>. def upcase - self.to_s.upcase.intern + (self.to_s.upcase! || self).to_sym end ## @@ -56,7 +47,9 @@ class Symbol def casecmp(other) return nil unless other.kind_of?(Symbol) - self.to_s.upcase <=> other.to_s.upcase + lhs = self.to_s; lhs.upcase! + rhs = other.to_s; rhs.upcase! + lhs <=> rhs end # @@ -66,7 +59,7 @@ class Symbol # Returns that _sym_ is :"" or not. def empty? - self.to_s.empty? + self.length == 0 end end diff --git a/mrbgems/mruby-symbol-ext/src/symbol.c b/mrbgems/mruby-symbol-ext/src/symbol.c index 4ed5d83c6..7ca749999 100644 --- a/mrbgems/mruby-symbol-ext/src/symbol.c +++ b/mrbgems/mruby-symbol-ext/src/symbol.c @@ -42,11 +42,27 @@ mrb_sym_all_symbols(mrb_state *mrb, mrb_value self) return ary; } +/* + * call-seq: + * sym.length -> integer + * + * Same as <code>sym.to_s.length</code>. + */ +static mrb_value +mrb_sym_length(mrb_state *mrb, mrb_value self) +{ + mrb_int len; + mrb_sym2name_len(mrb, mrb_symbol(self), &len); + return mrb_fixnum_value(len); +} + void mrb_mruby_symbol_ext_gem_init(mrb_state* mrb) { struct RClass *s = mrb->symbol_class; mrb_define_class_method(mrb, s, "all_symbols", mrb_sym_all_symbols, MRB_ARGS_NONE()); + mrb_define_method(mrb, s, "length", mrb_sym_length, MRB_ARGS_NONE()); + mrb_define_method(mrb, s, "size", mrb_sym_length, MRB_ARGS_NONE()); } void diff --git a/mrbgems/mruby-symbol-ext/test/symbol.rb b/mrbgems/mruby-symbol-ext/test/symbol.rb index 59df7ea9d..6070d1418 100644 --- a/mrbgems/mruby-symbol-ext/test/symbol.rb +++ b/mrbgems/mruby-symbol-ext/test/symbol.rb @@ -19,14 +19,17 @@ end assert("Symbol#capitalize") do assert_equal :Hello, :hello.capitalize assert_equal :Hello, :HELLO.capitalize + assert_equal :Hello, :Hello.capitalize end assert("Symbol#downcase") do assert_equal :hello, :hEllO.downcase + assert_equal :hello, :hello.downcase end assert("Symbol#upcase") do assert_equal :HELLO, :hEllO.upcase + assert_equal :HELLO, :HELLO.upcase end assert("Symbol#casecmp") do @@ -39,3 +42,7 @@ end assert("Symbol#empty?") do assert_true :''.empty? end + +assert('Symbol#intern') do + assert_equal :test, :test.intern +end diff --git a/mrbgems/mruby-time/src/time.c b/mrbgems/mruby-time/src/time.c index 3f8ffabef..410b36173 100644 --- a/mrbgems/mruby-time/src/time.c +++ b/mrbgems/mruby-time/src/time.c @@ -713,7 +713,7 @@ mrb_mruby_time_gem_init(mrb_state* mrb) tc = mrb_define_class(mrb, "Time", mrb->object_class); MRB_SET_INSTANCE_TT(tc, MRB_TT_DATA); mrb_include_module(mrb, tc, mrb_module_get(mrb, "Comparable")); - mrb_define_class_method(mrb, tc, "at", mrb_time_at, MRB_ARGS_ANY()); /* 15.2.19.6.1 */ + mrb_define_class_method(mrb, tc, "at", mrb_time_at, MRB_ARGS_REQ(1) | MRB_ARGS_OPT(1)); /* 15.2.19.6.1 */ mrb_define_class_method(mrb, tc, "gm", mrb_time_gm, MRB_ARGS_ARG(1,6)); /* 15.2.19.6.2 */ mrb_define_class_method(mrb, tc, "local", mrb_time_local, MRB_ARGS_ARG(1,6)); /* 15.2.19.6.3 */ mrb_define_class_method(mrb, tc, "mktime", mrb_time_local, MRB_ARGS_ARG(1,6));/* 15.2.19.6.4 */ diff --git a/mrbgems/mruby-time/test/time.rb b/mrbgems/mruby-time/test/time.rb index 450d87b43..ba9b48fab 100644 --- a/mrbgems/mruby-time/test/time.rb +++ b/mrbgems/mruby-time/test/time.rb @@ -9,10 +9,6 @@ assert('Time', '15.2.19') do Time.class == Class end -assert('Time superclass', '15.2.19.2') do - Time.superclass == Object -end - assert('Time.at', '15.2.19.6.1') do Time.at(1300000000.0) end diff --git a/mrblib/error.rb b/mrblib/error.rb index 6e8181e9d..a5b6b3223 100644 --- a/mrblib/error.rb +++ b/mrblib/error.rb @@ -48,6 +48,12 @@ end # ISO 15.2.32 class NoMethodError < NameError + attr_reader :args + + def initialize(message=nil, name=nil, args=nil) + @args = args + super message, name + end end # ISO 15.2.33 diff --git a/mrblib/kernel.rb b/mrblib/kernel.rb index 81d7acf5d..d0fe47300 100644 --- a/mrblib/kernel.rb +++ b/mrblib/kernel.rb @@ -4,39 +4,24 @@ # ISO 15.3.1 module Kernel - # 15.3.1.2.1 - def self.`(s) - raise NotImplementedError.new("` not implemented") - end - + # 15.3.1.2.1 Kernel.` + # provided by Kernel#` # 15.3.1.3.5 def `(s) - Kernel.`(s) + raise NotImplementedError.new("backquotes not implemented") end ## - # Calls the given block repetitively. - # - # ISO 15.3.1.2.8 - # provided by Kernel#loop - # def self.loop #(&block) - # while(true) - # yield - # end - # end + # 15.3.1.2.3 Kernel.eval + # 15.3.1.3.12 Kernel#eval + # NotImplemented by mruby core; use mruby-eval gem - # 15.3.1.2.3 - def self.eval(s) - raise NotImplementedError.new("eval not implemented") - end - - # 15.3.1.3.12 - def eval(s) - Kernel.eval(s) - end + ## + # ISO 15.3.1.2.8 Kernel.loop + # provided by Kernel#loop ## - # Alias for +Kernel.loop+. + # Calls the given block repetitively. # # ISO 15.3.1.3.29 def loop diff --git a/mrblib/mrblib.rake b/mrblib/mrblib.rake index 0c549f5b8..d156a2683 100644 --- a/mrblib/mrblib.rake +++ b/mrblib/mrblib.rake @@ -6,8 +6,8 @@ MRuby.each_target do self.libmruby << objfile("#{current_build_dir}/mrblib") file objfile("#{current_build_dir}/mrblib") => "#{current_build_dir}/mrblib.c" - file "#{current_build_dir}/mrblib.c" => [mrbcfile] + Dir.glob("#{current_dir}/*.rb").sort do |t| - mrbc_, *rbfiles = t.prerequisites + file "#{current_build_dir}/mrblib.c" => [mrbcfile, __FILE__] + Dir.glob("#{current_dir}/*.rb").sort do |t| + _, _, *rbfiles = t.prerequisites FileUtils.mkdir_p File.dirname(t.name) open(t.name, 'w') do |f| _pp "GEN", "*.rb", "#{t.name.relative_path}" diff --git a/mrblib/print.rb b/mrblib/print.rb deleted file mode 100644 index 1ae3ae84b..000000000 --- a/mrblib/print.rb +++ /dev/null @@ -1,18 +0,0 @@ -## -# Kernel -# -# ISO 15.3.1 -module Kernel - def print(*a) - raise NotImplementedError.new('print not available') - end - def puts(*a) - raise NotImplementedError.new('puts not available') - end - def p(*a) - raise NotImplementedError.new('p not available') - end - def printf(*args) - raise NotImplementedError.new('printf not available') - end -end diff --git a/src/backtrace.c b/src/backtrace.c index 7768a3206..1e1f9fa1a 100644 --- a/src/backtrace.c +++ b/src/backtrace.c @@ -71,7 +71,6 @@ output_backtrace(mrb_state *mrb, mrb_int ciidx, mrb_code *pc0, output_stream_fun for (i = ciidx; i >= 0; i--) { ci = &mrb->c->cibase[i]; filename = NULL; - lineno = -1; if (!ci->proc) continue; if (MRB_PROC_CFUNC_P(ci->proc)) { diff --git a/src/class.c b/src/class.c index db9a8ac26..42e2d5d33 100644 --- a/src/class.c +++ b/src/class.c @@ -1216,8 +1216,7 @@ mrb_bob_missing(mrb_state *mrb, mrb_value mod) repr = mrb_any_to_s(mrb, mod); } - mrb_raisef(mrb, E_NOMETHOD_ERROR, "undefined method '%S' for %S", - mrb_sym2str(mrb, name), repr); + mrb_no_method_error(mrb, name, alen, a, "undefined method '%S' for %S", mrb_sym2str(mrb, name), repr); /* not reached */ return mrb_nil_value(); } @@ -1980,6 +1979,9 @@ mrb_init_class(mrb_state *mrb) name_class(mrb, mod, mrb_intern_lit(mrb, "Module")); /* 15.2.2 */ name_class(mrb, cls, mrb_intern_lit(mrb, "Class")); /* 15.2.3 */ + mrb->proc_class = mrb_define_class(mrb, "Proc", mrb->object_class); /* 15.2.17 */ + MRB_SET_INSTANCE_TT(mrb->proc_class, MRB_TT_PROC); + 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()); diff --git a/src/codegen.c b/src/codegen.c index cec0d226f..03c752826 100644 --- a/src/codegen.c +++ b/src/codegen.c @@ -475,6 +475,8 @@ new_msym(codegen_scope *s, mrb_sym sym) { size_t i, len; + mrb_assert(s->irep); + len = s->irep->slen; if (len > 256) len = 256; for (i=0; i<len; i++) { @@ -549,7 +551,8 @@ for_body(codegen_scope *s, node *tree) /* generate receiver */ codegen(s, tree->cdr->car, VAL); /* generate loop-block */ - s = scope_new(s->mrb, s, tree->car); + s = scope_new(s->mrb, s, NULL); + push(); /* push for a block parameter */ lp = loop_push(s, LOOP_FOR); lp->pc1 = new_label(s); @@ -1105,21 +1108,23 @@ readint_mrb_int(codegen_scope *s, const char *p, int base, mrb_bool neg, mrb_boo codegen_error(s, "malformed readint input"); } - if (neg) { - if ((MRB_INT_MIN + n)/base > result) { - *overflow = TRUE; - return 0; + if(base > 0) { + if (neg) { + if ((MRB_INT_MIN + n)/base > result) { + *overflow = TRUE; + return 0; + } + result *= base; + result -= n; } - result *= base; - result -= n; - } - else { - if ((MRB_INT_MAX - n)/base < result) { - *overflow = TRUE; - return 0; + else { + if ((MRB_INT_MAX - n)/base < result) { + *overflow = TRUE; + return 0; + } + result *= base; + result += n; } - result *= base; - result += n; } p++; } @@ -2651,7 +2656,7 @@ print_r(mrb_state *mrb, mrb_irep *irep, size_t n, int pre) if (n == 0) return 0; - for (i=0; i<irep->nlocals; i++) { + for (i=0; i+1<irep->nlocals; i++) { if (irep->lv[i].r == n) { mrb_sym sym = irep->lv[i].name; if (pre) printf(" "); @@ -2994,7 +2999,7 @@ codedump(mrb_state *mrb, mrb_irep *irep) { mrb_value v = irep->pool[GETARG_Bx(c)]; mrb_value s = mrb_str_dump(mrb, mrb_str_new(mrb, RSTRING_PTR(v), RSTRING_LEN(v))); - printf("OP_STRING\tR%d\tL(%d)\t; %s", GETARG_A(c), GETARG_B(c), RSTRING_PTR(s)); + printf("OP_STRING\tR%d\tL(%d)\t; %s", GETARG_A(c), GETARG_Bx(c), RSTRING_PTR(s)); } print_lv(mrb, irep, c, RA); break; diff --git a/src/dump.c b/src/dump.c index 1acb466a0..b820f1a68 100644 --- a/src/dump.c +++ b/src/dump.c @@ -343,7 +343,6 @@ write_section_irep(mrb_state *mrb, mrb_irep *irep, uint8_t *bin) if (result != MRB_DUMP_OK) { return result; } - cur += rsize; section_size += rsize; write_section_irep_header(mrb, section_size, bin); diff --git a/src/error.c b/src/error.c index 360df8f2e..5ca013527 100644 --- a/src/error.c +++ b/src/error.c @@ -227,7 +227,9 @@ mrb_noreturn void mrb_exc_raise(mrb_state *mrb, mrb_value exc) { mrb->exc = mrb_obj_ptr(exc); - exc_debug_info(mrb, mrb->exc); + if (!mrb->out_of_memory) { + exc_debug_info(mrb, mrb->exc); + } if (!mrb->jmp) { mrb_p(mrb, exc); abort(); @@ -442,12 +444,26 @@ mrb_sys_fail(mrb_state *mrb, const char *mesg) } } +mrb_noreturn void +mrb_no_method_error(mrb_state *mrb, mrb_sym id, mrb_int argc, const mrb_value *argv, char const* fmt, ...) +{ + mrb_value exc; + va_list ap; + + va_start(ap, fmt); + exc = mrb_funcall(mrb, mrb_obj_value(E_NOMETHOD_ERROR), "new", 3, + mrb_vformat(mrb, fmt, ap), mrb_symbol_value(id), + mrb_ary_new_from_values(mrb, argc, argv)); + va_end(ap); + mrb_exc_raise(mrb, exc); +} + void mrb_init_exception(mrb_state *mrb) { - struct RClass *exception, *script_error; + struct RClass *exception, *runtime_error, *script_error; - mrb->eException_class = exception = mrb_define_class(mrb, "Exception", mrb->object_class); /* 15.2.22 */ + mrb->eException_class = exception = mrb_define_class(mrb, "Exception", mrb->object_class); /* 15.2.22 */ mrb_define_class_method(mrb, exception, "exception", mrb_instance_new, MRB_ARGS_ANY()); mrb_define_method(mrb, exception, "exception", exc_exception, MRB_ARGS_ANY()); mrb_define_method(mrb, exception, "initialize", exc_initialize, MRB_ARGS_ANY()); @@ -457,8 +473,9 @@ mrb_init_exception(mrb_state *mrb) mrb_define_method(mrb, exception, "inspect", exc_inspect, MRB_ARGS_NONE()); mrb_define_method(mrb, exception, "backtrace", mrb_exc_backtrace, MRB_ARGS_NONE()); - mrb->eStandardError_class = mrb_define_class(mrb, "StandardError", mrb->eException_class); /* 15.2.23 */ - mrb_define_class(mrb, "RuntimeError", mrb->eStandardError_class); /* 15.2.28 */ - script_error = mrb_define_class(mrb, "ScriptError", mrb->eException_class); /* 15.2.37 */ - mrb_define_class(mrb, "SyntaxError", script_error); /* 15.2.38 */ + mrb->eStandardError_class = mrb_define_class(mrb, "StandardError", mrb->eException_class); /* 15.2.23 */ + runtime_error = mrb_define_class(mrb, "RuntimeError", mrb->eStandardError_class); /* 15.2.28 */ + mrb->nomem_err = mrb_obj_ptr(mrb_exc_new_str(mrb, runtime_error, mrb_str_new_lit(mrb, "Out of memory"))); + script_error = mrb_define_class(mrb, "ScriptError", mrb->eException_class); /* 15.2.37 */ + mrb_define_class(mrb, "SyntaxError", script_error); /* 15.2.38 */ } @@ -34,7 +34,7 @@ == Two White Types - There're two white color types in a flip-flop fassion: White-A and White-B, + There're two white color types in a flip-flop fashion: White-A and White-B, which respectively represent the Current White color (the newly allocated objects in the current GC cycle) and the Sweep Target White color (the dead objects to be swept). @@ -43,8 +43,8 @@ that time, all the dead objects have been swept, while the newly created objects in the current GC cycle which finally remains White are now regarded as dead objects. Instead of traversing all the White-A objects and - paint them as White-B, just switch the meaning of White-A and White-B would - be much cheaper. + painting them as White-B, just switch the meaning of White-A and White-B as + this will be much cheaper. As a result, the objects we sweep in the current GC cycle are always left from the previous GC cycle. This allows us to sweep objects @@ -73,8 +73,8 @@ mruby's GC offers an Generational Mode while re-using the tri-color GC infrastructure. It will treat the Black objects as Old objects after each - sweep phase, instead of paint them to White. The key idea are still same as - the traditional generational GC: + sweep phase, instead of painting them White. The key ideas are still the same + as traditional generational GC: * Minor GC - just traverse the Young objects (Gray objects) in the mark phase, then only sweep the newly created objects, and leave @@ -82,7 +82,7 @@ * Major GC - same as a full regular GC cycle. - The difference to a "traditional" generational GC is, that the major GC + The difference from "traditional" generational GC is, that the major GC in mruby is triggered incrementally in a tri-color manner. @@ -189,7 +189,7 @@ mrb_realloc(mrb_state *mrb, void *p, size_t len) } else { mrb->out_of_memory = TRUE; - mrb_raise(mrb, E_RUNTIME_ERROR, "Out of memory"); + mrb_exc_raise(mrb, mrb_obj_value(mrb->nomem_err)); } } else { @@ -705,6 +705,8 @@ root_scan_phase(mrb_state *mrb) mrb_gc_mark(mrb, (struct RBasic*)mrb->top_self); /* mark exception */ mrb_gc_mark(mrb, (struct RBasic*)mrb->exc); + /* mark pre-allocated exception */ + mrb_gc_mark(mrb, (struct RBasic*)mrb->nomem_err); mark_context(mrb, mrb->root_c); if (mrb->root_c->fib) { diff --git a/src/hash.c b/src/hash.c index 997610953..1d449db3f 100644 --- a/src/hash.c +++ b/src/hash.c @@ -216,11 +216,12 @@ mrb_hash_set(mrb_state *mrb, mrb_value hash, mrb_value key, mrb_value val) if (r != 0) { /* expand */ int ai = mrb_gc_arena_save(mrb); - kh_key(h, k) = KEY(key); + key = kh_key(h, k) = KEY(key); mrb_gc_arena_restore(mrb, ai); kh_value(h, k).n = kh_size(h)-1; } + mrb_field_write_barrier_value(mrb, (struct RBasic*)RHASH(hash), key); mrb_field_write_barrier_value(mrb, (struct RBasic*)RHASH(hash), val); return; } diff --git a/src/init.c b/src/init.c index c08c4b046..955d6e3a1 100644 --- a/src/init.c +++ b/src/init.c @@ -24,8 +24,6 @@ void mrb_init_gc(mrb_state*); void mrb_init_math(mrb_state*); void mrb_init_version(mrb_state*); void mrb_init_mrblib(mrb_state*); -void mrb_init_mrbgems(mrb_state*); -void mrb_final_mrbgems(mrb_state*); #define DONE mrb_gc_arena_restore(mrb, 0); void @@ -50,15 +48,4 @@ mrb_init_core(mrb_state *mrb) mrb_init_gc(mrb); DONE; mrb_init_version(mrb); DONE; mrb_init_mrblib(mrb); DONE; -#ifndef DISABLE_GEMS - mrb_init_mrbgems(mrb); DONE; -#endif -} - -void -mrb_final_core(mrb_state *mrb) -{ -#ifndef DISABLE_GEMS - mrb_final_mrbgems(mrb); DONE; -#endif } diff --git a/src/kernel.c b/src/kernel.c index f6f2872ea..0258e5c15 100644 --- a/src/kernel.c +++ b/src/kernel.c @@ -1118,7 +1118,7 @@ mrb_init_kernel(mrb_state *mrb) mrb_define_class_method(mrb, krn, "iterator?", mrb_f_block_given_p_m, MRB_ARGS_NONE()); /* 15.3.1.2.5 */ mrb_define_class_method(mrb, krn, "local_variables", mrb_local_variables, MRB_ARGS_NONE()); /* 15.3.1.2.7 */ ; /* 15.3.1.2.11 */ - mrb_define_class_method(mrb, krn, "raise", mrb_f_raise, MRB_ARGS_ANY()); /* 15.3.1.2.12 */ + mrb_define_class_method(mrb, krn, "raise", mrb_f_raise, MRB_ARGS_OPT(2)); /* 15.3.1.2.12 */ mrb_define_method(mrb, krn, "singleton_class", mrb_singleton_class, MRB_ARGS_NONE()); diff --git a/src/mruby_core.rake b/src/mruby_core.rake index 04be0736c..88fca83fc 100644 --- a/src/mruby_core.rake +++ b/src/mruby_core.rake @@ -13,7 +13,7 @@ MRuby.each_target do cxx_abi_dependency = %w(codegen error vm) cxx_abi_objs = cxx_abi_dependency.map { |v| src = "#{current_build_dir}/#{v}.cxx" - file src => "#{current_dir}/#{v}.c" do |t| + file src => ["#{current_dir}/#{v}.c", __FILE__] do |t| File.open(t.name, 'w') do |f| f.write <<EOS #define __STDC_CONSTANT_MACROS @@ -37,7 +37,7 @@ EOS } cxx_abi_objs << objfile("#{current_build_dir}/y.tab") - file "#{current_build_dir}/y.tab.cxx" => "#{current_build_dir}/y.tab.c" do |t| + file "#{current_build_dir}/y.tab.cxx" => ["#{current_build_dir}/y.tab.c", __FILE__] do |t| File.open(t.name, 'w') do |f| f.write <<EOS #define __STDC_CONSTANT_MACROS diff --git a/src/parse.y b/src/parse.y index 9f185ee73..0a215f393 100644 --- a/src/parse.y +++ b/src/parse.y @@ -42,11 +42,7 @@ static void yywarning(parser_state *p, const char *s); static void backref_error(parser_state *p, node *n); static void tokadd(parser_state *p, int32_t c); -#ifndef isascii -#define isascii(c) (((c) & ~0x7f) == 0) -#endif - -#define identchar(c) (isalnum(c) || (c) == '_' || !isascii(c)) +#define identchar(c) (ISALNUM(c) || (c) == '_' || !ISASCII(c)) typedef unsigned int stack_type; @@ -257,7 +253,11 @@ local_var_p(parser_state *p, mrb_sym sym) static void local_add_f(parser_state *p, mrb_sym sym) { - p->locals->car = push(p->locals->car, nsym(sym)); + if (p->locals->car && !p->locals->car->car) { + p->locals->car->car = nsym(sym); + } else { + p->locals->car = push(p->locals->car, nsym(sym)); + } } static void @@ -2875,15 +2875,15 @@ var_ref : variable | keyword_self { $$ = new_self(p); - } + } | keyword_true { $$ = new_true(p); - } + } | keyword_false { $$ = new_false(p); - } + } | keyword__FILE__ { if (!p->filename) { @@ -3377,7 +3377,9 @@ nextc(parser_state *p) c = (unsigned char)*p->s++; } } - p->column++; + if (c >= 0) { + p->column++; + } if (c == '\r') { c = nextc(p); if (c != '\n') { @@ -3392,16 +3394,17 @@ nextc(parser_state *p) if (!p->cxt) return -1; else { if (p->cxt->partial_hook(p) < 0) - return -1; - return -2; + return -1; /* end of program(s) */ + return -2; /* end of a file in the program files */ } } static void pushback(parser_state *p, int c) { - if (c < 0) return; - p->column--; + if (c >= 0) { + p->column--; + } p->pb = cons((node*)(intptr_t)c, p->pb); } @@ -3425,7 +3428,7 @@ peekc_n(parser_state *p, int n) do { c0 = nextc(p); - if (c0 < 0) return c0; + if (c0 == -1) return c0; /* do not skip partial EOF */ list = push(list, (node*)(intptr_t)c0); } while(n--); if (p->pb) { @@ -3781,7 +3784,7 @@ read_escape(parser_state *p) eof: case -1: - case -2: + case -2: /* end of a file */ yyerror(p, "Invalid escape character syntax"); return '\0'; @@ -3911,7 +3914,8 @@ parse_string(parser_state *p) return tHD_LITERAL_DELIM; } } - } while (ISSPACE(c = nextc(p))); + c = nextc(p); + } while (ISSPACE(c)); pushback(p, c); return tLITERAL_DELIM; } @@ -4096,7 +4100,7 @@ parser_yylex(parser_state *p) case '#': /* it's a comment */ skip(p, '\n'); /* fall through */ - case -2: /* end of partial script. */ + case -2: /* end of a file */ case '\n': maybe_heredoc: heredoc_treat_nextline(p); @@ -4131,7 +4135,7 @@ parser_yylex(parser_state *p) goto retry; } case -1: /* EOF */ - case -2: /* end of partial script */ + case -2: /* end of a file */ goto normal_newline; default: pushback(p, c); @@ -4205,14 +4209,14 @@ parser_yylex(parser_state *p) static const char end[] = "\n=end"; if (peeks(p, begin)) { c = peekc_n(p, sizeof(begin)-1); - if (c < 0 || isspace(c)) { + if (c < 0 || ISSPACE(c)) { do { if (!skips(p, end)) { yyerror(p, "embedded document meets end of file"); return 0; } c = nextc(p); - } while (!(c < 0 || isspace(c))); + } while (!(c < 0 || ISSPACE(c))); if (c != '\n') skip(p, '\n'); p->lineno++; p->column = 0; @@ -4243,7 +4247,6 @@ parser_yylex(parser_state *p) return '='; case '<': - last_state = p->lstate; c = nextc(p); if (c == '<' && p->lstate != EXPR_DOT && @@ -4337,7 +4340,7 @@ parser_yylex(parser_state *p) yyerror(p, "incomplete character syntax"); return 0; } - if (isspace(c)) { + if (ISSPACE(c)) { if (!IS_ARG()) { int c2; switch (c) { @@ -4374,7 +4377,7 @@ parser_yylex(parser_state *p) p->lstate = EXPR_VALUE; return '?'; } - token_column = newtok(p); + newtok(p); /* need support UTF-8 if configured */ if ((isalnum(c) || c == '_')) { int c2 = nextc(p); @@ -4538,7 +4541,7 @@ parser_yylex(parser_state *p) is_float = seen_point = seen_e = nondigit = 0; p->lstate = EXPR_END; - token_column = newtok(p); + newtok(p); if (c == '-' || c == '+') { tokadd(p, c); c = nextc(p); @@ -5160,7 +5163,6 @@ parser_yylex(parser_state *p) { int result = 0; - last_state = p->lstate; switch (tok(p)[0]) { case '$': p->lstate = EXPR_END; @@ -5190,7 +5192,7 @@ parser_yylex(parser_state *p) pushback(p, c); } } - if (result == 0 && isupper((int)(unsigned char)tok(p)[0])) { + if (result == 0 && ISUPPER(tok(p)[0])) { result = tCONSTANT; } else { @@ -5556,7 +5558,12 @@ load_exec(mrb_state *mrb, parser_state *p, mrbc_context *c) if (c->target_class) { target = c->target_class; } - keep = c->slen + 1; + if (c->keep_lv) { + keep = c->slen + 1; + } + else { + c->keep_lv = TRUE; + } } proc->target_class = target; if (mrb->c->ci) { @@ -5857,7 +5864,7 @@ mrb_parser_dump(mrb_state *mrb, node *tree, int offset) node *n2 = tree->car; mrb_bool first_lval = TRUE; - if (n2 && (n2->car || n2->cdr)) { + if (n2 && (n2->car || n2->cdr)) { dump_prefix(offset+1); printf("local variables:\n"); dump_prefix(offset+2); diff --git a/src/proc.c b/src/proc.c index d4fe86680..a61296ed4 100644 --- a/src/proc.c +++ b/src/proc.c @@ -149,9 +149,14 @@ mrb_proc_arity(mrb_state *mrb, mrb_value self) { struct RProc *p = mrb_proc_ptr(self); mrb_code *iseq = mrb_proc_iseq(mrb, p); - mrb_aspec aspec = GETARG_Ax(*iseq); + mrb_aspec aspec; int ma, ra, pa, arity; + if (MRB_PROC_CFUNC_P(p)) { + // TODO cfunc aspec not implemented yet + return mrb_fixnum_value(-1); + } + aspec = GETARG_Ax(*iseq); ma = MRB_ASPEC_REQ(aspec); ra = MRB_ASPEC_REST(aspec); pa = MRB_ASPEC_POST(aspec); @@ -201,9 +206,6 @@ mrb_init_proc(mrb_state *mrb) call_irep->iseq = call_iseq; call_irep->ilen = 1; - mrb->proc_class = mrb_define_class(mrb, "Proc", mrb->object_class); /* 15.2.17 */ - MRB_SET_INSTANCE_TT(mrb->proc_class, MRB_TT_PROC); - mrb_define_method(mrb, mrb->proc_class, "initialize", mrb_proc_initialize, MRB_ARGS_NONE()); 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/state.c b/src/state.c index 9dd798f92..8857d3294 100644 --- a/src/state.c +++ b/src/state.c @@ -14,7 +14,7 @@ void mrb_init_heap(mrb_state*); void mrb_init_core(mrb_state*); -void mrb_final_core(mrb_state*); +void mrb_init_mrbgems(mrb_state*); static mrb_value inspect_main(mrb_state *mrb, mrb_value mod) @@ -23,14 +23,14 @@ inspect_main(mrb_state *mrb, mrb_value mod) } mrb_state* -mrb_open_allocf(mrb_allocf f, void *ud) +mrb_open_core(mrb_allocf f, void *ud) { static const mrb_state mrb_state_zero = { 0 }; static const struct mrb_context mrb_context_zero = { 0 }; mrb_state *mrb; #ifdef MRB_NAN_BOXING - mrb_assert(sizeof(void*) == 4); + mrb_static_assert(sizeof(void*) == 4, "when using NaN boxing sizeof pointer must be 4 byte"); #endif mrb = (mrb_state *)(f)(NULL, NULL, sizeof(mrb_state), ud); @@ -40,6 +40,7 @@ mrb_open_allocf(mrb_allocf f, void *ud) mrb->ud = ud; mrb->allocf = f; mrb->current_white_part = MRB_GC_WHITE_A; + mrb->atexit_stack_len = 0; #ifndef MRB_GC_FIXED_ARENA mrb->arena = (struct RBasic**)mrb_malloc(mrb, sizeof(struct RBasic*)*MRB_GC_ARENA_SIZE); @@ -50,13 +51,14 @@ mrb_open_allocf(mrb_allocf f, void *ud) mrb->c = (struct mrb_context*)mrb_malloc(mrb, sizeof(struct mrb_context)); *mrb->c = mrb_context_zero; mrb->root_c = mrb->c; + mrb_init_core(mrb); return mrb; } -static void* -allocf(mrb_state *mrb, void *p, size_t size, void *ud) +void* +mrb_default_allocf(mrb_state *mrb, void *p, size_t size, void *ud) { if (size == 0) { free(p); @@ -102,8 +104,20 @@ mrb_alloca_free(mrb_state *mrb) mrb_state* mrb_open(void) { - mrb_state *mrb = mrb_open_allocf(allocf, NULL); + mrb_state *mrb = mrb_open_allocf(mrb_default_allocf, NULL); + + return mrb; +} +mrb_state* +mrb_open_allocf(mrb_allocf f, void *ud) +{ + mrb_state *mrb = mrb_open_core(f, ud); + +#ifndef DISABLE_GEMS + mrb_init_mrbgems(mrb); + mrb_gc_arena_restore(mrb, 0); +#endif return mrb; } @@ -221,7 +235,15 @@ mrb_free_context(mrb_state *mrb, struct mrb_context *c) void mrb_close(mrb_state *mrb) { - mrb_final_core(mrb); + if (mrb->atexit_stack_len > 0) { + mrb_int i; + for (i = mrb->atexit_stack_len; i > 0; --i) { + mrb->atexit_stack[i - 1](mrb); + } +#ifndef MRB_FIXED_STATE_ATEXIT_STACK + mrb_free(mrb, mrb->atexit_stack); +#endif + } /* free */ mrb_gc_free_gv(mrb); @@ -258,3 +280,24 @@ mrb_top_self(mrb_state *mrb) } return mrb_obj_value(mrb->top_self); } + +void +mrb_state_atexit(mrb_state *mrb, mrb_atexit_func f) +{ +#ifdef MRB_FIXED_STATE_ATEXIT_STACK + if (mrb->atexit_stack_len + 1 > MRB_FIXED_STATE_ATEXIT_STACK_SIZE) { + mrb_raise(mrb, E_RUNTIME_ERROR, "exceeded fixed state atexit stack limit"); + } +#else + size_t stack_size; + + stack_size = sizeof(mrb_atexit_func) * (mrb->atexit_stack_len + 1); + if (mrb->atexit_stack_len == 0) { + mrb->atexit_stack = (mrb_atexit_func*)mrb_malloc(mrb, stack_size); + } else { + mrb->atexit_stack = (mrb_atexit_func*)mrb_realloc(mrb, mrb->atexit_stack, stack_size); + } +#endif + + mrb->atexit_stack[mrb->atexit_stack_len++] = f; +} diff --git a/src/string.c b/src/string.c index 1572cab14..6570c89fb 100644 --- a/src/string.c +++ b/src/string.c @@ -17,28 +17,6 @@ #include "mruby/string.h" #include "mruby/re.h" -#define STR_EMBED_P(s) ((s)->flags & MRB_STR_EMBED) -#define STR_SET_EMBED_FLAG(s) ((s)->flags |= MRB_STR_EMBED) -#define STR_UNSET_EMBED_FLAG(s) ((s)->flags &= ~(MRB_STR_EMBED|MRB_STR_EMBED_LEN_MASK)) -#define STR_SET_EMBED_LEN(s, n) do {\ - size_t tmp_n = (n);\ - s->flags &= ~MRB_STR_EMBED_LEN_MASK;\ - s->flags |= (tmp_n) << MRB_STR_EMBED_LEN_SHIFT;\ -} while (0) -#define STR_SET_LEN(s, n) do {\ - if (STR_EMBED_P(s)) {\ - STR_SET_EMBED_LEN((s),(n));\ - } else {\ - s->as.heap.len = (mrb_int)(n);\ - }\ -} while (0) -#define RSTRING_EMBED_LEN(s) \ - (mrb_int)((RSTRING(s)->flags & MRB_STR_EMBED_LEN_MASK) >> MRB_STR_EMBED_LEN_SHIFT) -#define STR_EMBED_LEN(s)\ - (mrb_int)(((s)->flags & MRB_STR_EMBED_LEN_MASK) >> MRB_STR_EMBED_LEN_SHIFT) -#define STR_PTR(s) ((STR_EMBED_P(s)) ? (s)->as.ary : (s)->as.heap.ptr) -#define STR_LEN(s) ((STR_EMBED_P(s)) ? STR_EMBED_LEN(s) : (s)->as.heap.len) - const char mrb_digitmap[] = "0123456789abcdefghijklmnopqrstuvwxyz"; typedef struct mrb_shared_string { @@ -48,18 +26,14 @@ typedef struct mrb_shared_string { mrb_int len; } mrb_shared_string; -#define STR_SHARED_P(s) ((s)->flags & MRB_STR_SHARED) -#define STR_SET_SHARED_FLAG(s) ((s)->flags |= MRB_STR_SHARED) -#define STR_UNSET_SHARED_FLAG(s) ((s)->flags &= ~MRB_STR_SHARED) - static mrb_value str_replace(mrb_state *mrb, struct RString *s1, struct RString *s2); static mrb_value mrb_str_subseq(mrb_state *mrb, mrb_value str, mrb_int beg, mrb_int len); mrb_int mrb_str_strlen(mrb_state *mrb, struct RString *s) { - mrb_int i, max = STR_LEN(s); - char *p = STR_PTR(s); + mrb_int i, max = RSTR_LEN(s); + char *p = RSTR_PTR(s); if (!p) return 0; for (i=0; i<max; i++) { @@ -73,19 +47,19 @@ mrb_str_strlen(mrb_state *mrb, struct RString *s) static inline void resize_capa(mrb_state *mrb, struct RString *s, mrb_int capacity) { - if (STR_EMBED_P(s)) { + if (RSTR_EMBED_P(s)) { if (RSTRING_EMBED_LEN_MAX < capacity) { char *const tmp = (char *)mrb_malloc(mrb, capacity+1); - const mrb_int len = STR_EMBED_LEN(s); + const mrb_int len = RSTR_EMBED_LEN(s); memcpy(tmp, s->as.ary, len); - STR_UNSET_EMBED_FLAG(s); + RSTR_UNSET_EMBED_FLAG(s); s->as.heap.ptr = tmp; s->as.heap.len = len; s->as.heap.aux.capa = capacity; } } else { - s->as.heap.ptr = (char *)mrb_realloc(mrb, STR_PTR(s), capacity+1); + s->as.heap.ptr = (char *)mrb_realloc(mrb, RSTR_PTR(s), capacity+1); s->as.heap.aux.capa = capacity; } } @@ -105,20 +79,20 @@ str_decref(mrb_state *mrb, mrb_shared_string *shared) void mrb_str_modify(mrb_state *mrb, struct RString *s) { - if (STR_SHARED_P(s)) { + if (RSTR_SHARED_P(s)) { mrb_shared_string *shared = s->as.heap.aux.shared; if (shared->refcnt == 1 && s->as.heap.ptr == shared->ptr) { s->as.heap.ptr = shared->ptr; s->as.heap.aux.capa = shared->len; - STR_PTR(s)[s->as.heap.len] = '\0'; + RSTR_PTR(s)[s->as.heap.len] = '\0'; mrb_free(mrb, shared); } else { char *ptr, *p; mrb_int len; - p = STR_PTR(s); + p = RSTR_PTR(s); len = s->as.heap.len; ptr = (char *)mrb_malloc(mrb, (size_t)len + 1); if (p) { @@ -129,19 +103,19 @@ mrb_str_modify(mrb_state *mrb, struct RString *s) s->as.heap.aux.capa = len; str_decref(mrb, shared); } - STR_UNSET_SHARED_FLAG(s); + RSTR_UNSET_SHARED_FLAG(s); return; } - if (s->flags & MRB_STR_NOFREE) { + if (RSTR_NOFREE_P(s)) { char *p = s->as.heap.ptr; s->as.heap.ptr = (char *)mrb_malloc(mrb, (size_t)s->as.heap.len+1); if (p) { - memcpy(STR_PTR(s), p, s->as.heap.len); + memcpy(RSTR_PTR(s), p, s->as.heap.len); } - STR_PTR(s)[s->as.heap.len] = '\0'; + RSTR_PTR(s)[s->as.heap.len] = '\0'; s->as.heap.aux.capa = s->as.heap.len; - s->flags &= ~MRB_STR_NOFREE; + RSTR_UNSET_NOFREE_FLAG(s); return; } } @@ -153,13 +127,13 @@ mrb_str_resize(mrb_state *mrb, mrb_value str, mrb_int len) struct RString *s = mrb_str_ptr(str); mrb_str_modify(mrb, s); - slen = STR_LEN(s); + slen = RSTR_LEN(s); if (len != slen) { if (slen < len || slen - len > 256) { resize_capa(mrb, s, len); } - STR_SET_LEN(s, len); - STR_PTR(s)[len] = '\0'; /* sentinel */ + RSTR_SET_LEN(s, len); + RSTR_PTR(s)[len] = '\0'; /* sentinel */ } return str; } @@ -173,8 +147,8 @@ str_new(mrb_state *mrb, const char *p, size_t len) s = mrb_obj_alloc_string(mrb); if (len < RSTRING_EMBED_LEN_MAX) { - STR_SET_EMBED_FLAG(s); - STR_SET_EMBED_LEN(s,len); + RSTR_SET_EMBED_FLAG(s); + RSTR_SET_EMBED_LEN(s,len); if (p) { memcpy(s->as.ary, p, len); } @@ -189,7 +163,7 @@ str_new(mrb_state *mrb, const char *p, size_t len) memcpy(s->as.heap.ptr, p, len); } } - STR_PTR(s)[len] = '\0'; + RSTR_PTR(s)[len] = '\0'; return s; } @@ -228,7 +202,7 @@ mrb_str_buf_new(mrb_state *mrb, size_t capa) s->as.heap.len = 0; s->as.heap.aux.capa = capa; s->as.heap.ptr = (char *)mrb_malloc(mrb, capa+1); - STR_PTR(s)[0] = '\0'; + RSTR_PTR(s)[0] = '\0'; return mrb_obj_value(s); } @@ -242,19 +216,19 @@ str_buf_cat(mrb_state *mrb, struct RString *s, const char *ptr, size_t len) if (len == 0) return; mrb_str_modify(mrb, s); - if (ptr >= STR_PTR(s) && ptr <= STR_PTR(s) + (size_t)STR_LEN(s)) { - off = ptr - STR_PTR(s); + if (ptr >= RSTR_PTR(s) && ptr <= RSTR_PTR(s) + (size_t)RSTR_LEN(s)) { + off = ptr - RSTR_PTR(s); } - if (STR_EMBED_P(s)) + if (RSTR_EMBED_P(s)) capa = RSTRING_EMBED_LEN_MAX; else capa = s->as.heap.aux.capa; - if (STR_LEN(s) >= MRB_INT_MAX - (mrb_int)len) { + if (RSTR_LEN(s) >= MRB_INT_MAX - (mrb_int)len) { mrb_raise(mrb, E_ARGUMENT_ERROR, "string size too big"); } - total = STR_LEN(s)+len; + total = RSTR_LEN(s)+len; if (capa <= total) { while (total > capa) { if (capa + 1 >= MRB_INT_MAX / 2) { @@ -266,12 +240,12 @@ str_buf_cat(mrb_state *mrb, struct RString *s, const char *ptr, size_t len) resize_capa(mrb, s, capa); } if (off != -1) { - ptr = STR_PTR(s) + off; + ptr = RSTR_PTR(s) + off; } - memcpy(STR_PTR(s) + STR_LEN(s), ptr, len); + memcpy(RSTR_PTR(s) + RSTR_LEN(s), ptr, len); mrb_assert_int_fit(size_t, total, mrb_int, MRB_INT_MAX); - STR_SET_LEN(s, total); - STR_PTR(s)[total] = '\0'; /* sentinel */ + RSTR_SET_LEN(s, total); + RSTR_PTR(s)[total] = '\0'; /* sentinel */ } mrb_value @@ -324,11 +298,11 @@ mrb_str_new_static(mrb_state *mrb, const char *p, size_t len) void mrb_gc_free_str(mrb_state *mrb, struct RString *str) { - if (STR_EMBED_P(str)) + if (RSTR_EMBED_P(str)) /* no code */; - else if (STR_SHARED_P(str)) + else if (RSTR_SHARED_P(str)) str_decref(mrb, str->as.heap.aux.shared); - else if ((str->flags & MRB_STR_NOFREE) == 0) + else if (!RSTR_NOFREE_P(str)) mrb_free(mrb, str->as.heap.ptr); } @@ -342,34 +316,34 @@ mrb_str_to_cstr(mrb_state *mrb, mrb_value str0) } s = str_new(mrb, RSTRING_PTR(str0), RSTRING_LEN(str0)); - if ((strlen(STR_PTR(s)) ^ STR_LEN(s)) != 0) { + if ((strlen(RSTR_PTR(s)) ^ RSTR_LEN(s)) != 0) { mrb_raise(mrb, E_ARGUMENT_ERROR, "string contains null byte"); } - return STR_PTR(s); + return RSTR_PTR(s); } static void str_make_shared(mrb_state *mrb, struct RString *s) { - if (!STR_SHARED_P(s)) { + if (!RSTR_SHARED_P(s)) { mrb_shared_string *shared = (mrb_shared_string *)mrb_malloc(mrb, sizeof(mrb_shared_string)); shared->refcnt = 1; - if (STR_EMBED_P(s)) { - const mrb_int len = STR_EMBED_LEN(s); + if (RSTR_EMBED_P(s)) { + const mrb_int len = RSTR_EMBED_LEN(s); char *const tmp = (char *)mrb_malloc(mrb, len+1); memcpy(tmp, s->as.ary, len); tmp[len] = '\0'; - STR_UNSET_EMBED_FLAG(s); + RSTR_UNSET_EMBED_FLAG(s); s->as.heap.ptr = tmp; s->as.heap.len = len; shared->nofree = FALSE; shared->ptr = s->as.heap.ptr; } - else if (s->flags & MRB_STR_NOFREE) { + else if (RSTR_NOFREE_P(s)) { shared->nofree = TRUE; shared->ptr = s->as.heap.ptr; - s->flags &= ~MRB_STR_NOFREE; + RSTR_UNSET_NOFREE_FLAG(s); } else { shared->nofree = FALSE; @@ -382,7 +356,7 @@ str_make_shared(mrb_state *mrb, struct RString *s) } shared->len = s->as.heap.len; s->as.heap.aux.shared = shared; - STR_SET_SHARED_FLAG(s); + RSTR_SET_SHARED_FLAG(s); } } @@ -397,8 +371,8 @@ mrb_str_body(mrb_value str, int *len_p) { struct RString *s = mrb_str_ptr(str); - *len_p = STR_LEN(s); - return STR_PTR(s); + *len_p = RSTR_LEN(s); + return RSTR_PTR(s); } /* @@ -418,14 +392,14 @@ mrb_str_concat(mrb_state *mrb, mrb_value self, mrb_value other) other = mrb_str_to_str(mrb, other); } s2 = mrb_str_ptr(other); - len = STR_LEN(s1) + STR_LEN(s2); + len = RSTR_LEN(s1) + RSTR_LEN(s2); if (RSTRING_CAPA(self) < len) { resize_capa(mrb, s1, len); } - memcpy(STR_PTR(s1)+STR_LEN(s1), STR_PTR(s2), STR_LEN(s2)); - STR_SET_LEN(s1, len); - STR_PTR(s1)[len] = '\0'; + memcpy(RSTR_PTR(s1)+RSTR_LEN(s1), RSTR_PTR(s2), RSTR_LEN(s2)); + RSTR_SET_LEN(s1, len); + RSTR_PTR(s1)[len] = '\0'; } /* @@ -441,9 +415,9 @@ mrb_str_plus(mrb_state *mrb, mrb_value a, mrb_value b) struct RString *s2 = mrb_str_ptr(b); struct RString *t; - t = str_new(mrb, 0, STR_LEN(s) + STR_LEN(s2)); - memcpy(STR_PTR(t), STR_PTR(s), STR_LEN(s)); - memcpy(STR_PTR(t) + STR_LEN(s), STR_PTR(s2), STR_LEN(s2)); + t = str_new(mrb, 0, RSTR_LEN(s) + RSTR_LEN(s2)); + memcpy(RSTR_PTR(t), RSTR_PTR(s), RSTR_LEN(s)); + memcpy(RSTR_PTR(t) + RSTR_LEN(s), RSTR_PTR(s2), RSTR_LEN(s2)); return mrb_obj_value(t); } @@ -475,7 +449,7 @@ static mrb_value mrb_str_bytesize(mrb_state *mrb, mrb_value self) { struct RString *s = mrb_str_ptr(self); - return mrb_fixnum_value(STR_LEN(s)); + return mrb_fixnum_value(RSTR_LEN(s)); } /* 15.2.10.5.26 */ @@ -490,7 +464,7 @@ mrb_value mrb_str_size(mrb_state *mrb, mrb_value self) { struct RString *s = mrb_str_ptr(self); - return mrb_fixnum_value(STR_LEN(s)); + return mrb_fixnum_value(RSTR_LEN(s)); } /* 15.2.10.5.1 */ @@ -521,7 +495,7 @@ mrb_str_times(mrb_state *mrb, mrb_value self) len = RSTRING_LEN(self)*times; str2 = str_new(mrb, 0, len); str_with_class(mrb, str2, self); - p = STR_PTR(str2); + p = RSTR_PTR(str2); if (len > 0) { n = RSTRING_LEN(self); memcpy(p, RSTRING_PTR(self), n); @@ -531,7 +505,7 @@ mrb_str_times(mrb_state *mrb, mrb_value self) } memcpy(p + n, p, len-n); } - p[STR_LEN(str2)] = '\0'; + p[RSTR_LEN(str2)] = '\0'; return mrb_obj_value(str2); } @@ -555,11 +529,11 @@ mrb_str_cmp(mrb_state *mrb, mrb_value str1, mrb_value str2) struct RString *s1 = mrb_str_ptr(str1); struct RString *s2 = mrb_str_ptr(str2); - len = lesser(STR_LEN(s1), STR_LEN(s2)); - retval = memcmp(STR_PTR(s1), STR_PTR(s2), len); + len = lesser(RSTR_LEN(s1), RSTR_LEN(s2)); + retval = memcmp(RSTR_PTR(s1), RSTR_PTR(s2), len); if (retval == 0) { - if (STR_LEN(s1) == STR_LEN(s2)) return 0; - if (STR_LEN(s1) > STR_LEN(s2)) return 1; + if (RSTR_LEN(s1) == RSTR_LEN(s2)) return 0; + if (RSTR_LEN(s1) > RSTR_LEN(s2)) return 1; return -1; } if (retval > 0) return 1; @@ -840,8 +814,6 @@ num_index: * str.slice(fixnum) => fixnum or nil * str.slice(fixnum, fixnum) => new_str or nil * str.slice(range) => new_str or nil - * str.slice(regexp) => new_str or nil - * str.slice(regexp, fixnum) => new_str or nil * str.slice(other_str) => new_str or nil * * Element Reference---If passed a single <code>Fixnum</code>, returns the code @@ -853,10 +825,7 @@ num_index: * <code>nil</code> if the initial offset falls outside the string, the length * is negative, or the beginning of the range is greater than the end. * - * If a <code>Regexp</code> is supplied, the matching portion of <i>str</i> is - * returned. If a numeric parameter follows the regular expression, that - * component of the <code>MatchData</code> is returned instead. If a - * <code>String</code> is given, that string is returned if it occurs in + * If a <code>String</code> is given, that string is returned if it occurs in * <i>str</i>. In both cases, <code>nil</code> is returned if there is no * match. * @@ -868,10 +837,6 @@ num_index: * a[-4..-2] #=> "her" * a[12..-1] #=> nil * a[-2..-4] #=> "" - * a[/[aeiou](.)\1/] #=> "ell" - * a[/[aeiou](.)\1/, 0] #=> "ell" - * a[/[aeiou](.)\1/, 1] #=> "l" - * a[/[aeiou](.)\1/, 2] #=> nil * a["lo"] #=> "lo" * a["bye"] #=> nil */ @@ -913,8 +878,8 @@ mrb_str_capitalize_bang(mrb_state *mrb, mrb_value str) struct RString *s = mrb_str_ptr(str); mrb_str_modify(mrb, s); - if (STR_LEN(s) == 0 || !STR_PTR(s)) return mrb_nil_value(); - p = STR_PTR(s); pend = STR_PTR(s) + STR_LEN(s); + if (RSTR_LEN(s) == 0 || !RSTR_PTR(s)) return mrb_nil_value(); + p = RSTR_PTR(s); pend = RSTR_PTR(s) + RSTR_LEN(s); if (ISLOWER(*p)) { *p = TOUPPER(*p); modify = TRUE; @@ -970,29 +935,29 @@ mrb_str_chomp_bang(mrb_state *mrb, mrb_value str) struct RString *s = mrb_str_ptr(str); mrb_str_modify(mrb, s); - len = STR_LEN(s); + len = RSTR_LEN(s); if (mrb_get_args(mrb, "|S", &rs) == 0) { if (len == 0) return mrb_nil_value(); smart_chomp: - if (STR_PTR(s)[len-1] == '\n') { - STR_SET_LEN(s, STR_LEN(s) - 1); - if (STR_LEN(s) > 0 && - STR_PTR(s)[STR_LEN(s)-1] == '\r') { - STR_SET_LEN(s, STR_LEN(s) - 1); + if (RSTR_PTR(s)[len-1] == '\n') { + RSTR_SET_LEN(s, RSTR_LEN(s) - 1); + if (RSTR_LEN(s) > 0 && + RSTR_PTR(s)[RSTR_LEN(s)-1] == '\r') { + RSTR_SET_LEN(s, RSTR_LEN(s) - 1); } } - else if (STR_PTR(s)[len-1] == '\r') { - STR_SET_LEN(s, STR_LEN(s) - 1); + else if (RSTR_PTR(s)[len-1] == '\r') { + RSTR_SET_LEN(s, RSTR_LEN(s) - 1); } else { return mrb_nil_value(); } - STR_PTR(s)[STR_LEN(s)] = '\0'; + RSTR_PTR(s)[RSTR_LEN(s)] = '\0'; return str; } if (len == 0 || mrb_nil_p(rs)) return mrb_nil_value(); - p = STR_PTR(s); + p = RSTR_PTR(s); rslen = RSTRING_LEN(rs); if (rslen == 0) { while (len>0 && p[len-1] == '\n') { @@ -1000,8 +965,8 @@ mrb_str_chomp_bang(mrb_state *mrb, mrb_value str) if (len>0 && p[len-1] == '\r') len--; } - if (len < STR_LEN(s)) { - STR_SET_LEN(s, len); + if (len < RSTR_LEN(s)) { + RSTR_SET_LEN(s, len); p[len] = '\0'; return str; } @@ -1018,8 +983,8 @@ mrb_str_chomp_bang(mrb_state *mrb, mrb_value str) if (p[len-1] == newline && (rslen <= 1 || memcmp(RSTRING_PTR(rs), pp, rslen) == 0)) { - STR_SET_LEN(s, len - rslen); - p[STR_LEN(s)] = '\0'; + RSTR_SET_LEN(s, len - rslen); + p[RSTR_LEN(s)] = '\0'; return str; } return mrb_nil_value(); @@ -1069,17 +1034,17 @@ mrb_str_chop_bang(mrb_state *mrb, mrb_value str) struct RString *s = mrb_str_ptr(str); mrb_str_modify(mrb, s); - if (STR_LEN(s) > 0) { + if (RSTR_LEN(s) > 0) { mrb_int len; - len = STR_LEN(s) - 1; - if (STR_PTR(s)[len] == '\n') { + len = RSTR_LEN(s) - 1; + if (RSTR_PTR(s)[len] == '\n') { if (len > 0 && - STR_PTR(s)[len-1] == '\r') { + RSTR_PTR(s)[len-1] == '\r') { len--; } } - STR_SET_LEN(s, len); - STR_PTR(s)[len] = '\0'; + RSTR_SET_LEN(s, len); + RSTR_PTR(s)[len] = '\0'; return str; } return mrb_nil_value(); @@ -1127,8 +1092,8 @@ mrb_str_downcase_bang(mrb_state *mrb, mrb_value str) struct RString *s = mrb_str_ptr(str); mrb_str_modify(mrb, s); - p = STR_PTR(s); - pend = STR_PTR(s) + STR_LEN(s); + p = RSTR_PTR(s); + pend = RSTR_PTR(s) + RSTR_LEN(s); while (p < pend) { if (ISUPPER(*p)) { *p = TOLOWER(*p); @@ -1177,7 +1142,7 @@ mrb_str_empty_p(mrb_state *mrb, mrb_value self) { struct RString *s = mrb_str_ptr(self); - return mrb_bool_value(STR_LEN(s) == 0); + return mrb_bool_value(RSTR_LEN(s) == 0); } /* 15.2.10.5.17 */ @@ -1206,7 +1171,7 @@ mrb_str_subseq(mrb_state *mrb, mrb_value str, mrb_int beg, mrb_int len) mrb_shared_string *shared; orig = mrb_str_ptr(str); - if (STR_EMBED_P(orig)) { + if (RSTR_EMBED_P(orig)) { s = str_new(mrb, orig->as.ary+beg, len); } else { str_make_shared(mrb, orig); @@ -1215,7 +1180,7 @@ mrb_str_subseq(mrb_state *mrb, mrb_value str, mrb_int beg, mrb_int len) s->as.heap.ptr = orig->as.heap.ptr + beg; s->as.heap.len = len; s->as.heap.aux.shared = shared; - STR_SET_SHARED_FLAG(s); + RSTR_SET_SHARED_FLAG(s); shared->refcnt++; } @@ -1247,8 +1212,8 @@ mrb_str_hash(mrb_state *mrb, mrb_value str) { /* 1-8-7 */ struct RString *s = mrb_str_ptr(str); - mrb_int len = STR_LEN(s); - char *p = STR_PTR(s); + mrb_int len = RSTR_LEN(s); + char *p = RSTR_PTR(s); mrb_int key = 0; while (len--) { @@ -1393,27 +1358,31 @@ str_replace(mrb_state *mrb, struct RString *s1, struct RString *s2) { long len; - len = STR_LEN(s2); - if (STR_SHARED_P(s2)) { - L_SHARE: - if (STR_SHARED_P(s1)) { - str_decref(mrb, s1->as.heap.aux.shared); - } - else if (!STR_EMBED_P(s1) && !(s1->flags & MRB_STR_NOFREE)) { - mrb_free(mrb, s1->as.heap.ptr); - } - STR_UNSET_EMBED_FLAG(s1); + len = RSTR_LEN(s2); + if (RSTR_SHARED_P(s1)) { + str_decref(mrb, s1->as.heap.aux.shared); + } + else if (!RSTR_EMBED_P(s1) && !RSTR_NOFREE_P(s1)) { + mrb_free(mrb, s1->as.heap.ptr); + } + + RSTR_UNSET_NOFREE_FLAG(s1); + + if (RSTR_SHARED_P(s2)) { +L_SHARE: + RSTR_UNSET_EMBED_FLAG(s1); s1->as.heap.ptr = s2->as.heap.ptr; s1->as.heap.len = len; s1->as.heap.aux.shared = s2->as.heap.aux.shared; - STR_SET_SHARED_FLAG(s1); + RSTR_SET_SHARED_FLAG(s1); s1->as.heap.aux.shared->refcnt++; } else { if (len <= RSTRING_EMBED_LEN_MAX) { - STR_SET_EMBED_FLAG(s1); - memcpy(s1->as.ary, STR_PTR(s2), len); - STR_SET_EMBED_LEN(s1, len); + RSTR_UNSET_SHARED_FLAG(s1); + RSTR_SET_EMBED_FLAG(s1); + memcpy(s1->as.ary, RSTR_PTR(s2), len); + RSTR_SET_EMBED_LEN(s1, len); } else { str_make_shared(mrb, s2); @@ -1510,7 +1479,7 @@ mrb_ptr_to_str(mrb_state *mrb, void *p) uintptr_t n = (uintptr_t)p; p_str = str_new(mrb, NULL, 2 + sizeof(uintptr_t) * CHAR_BIT / 4); - p1 = STR_PTR(p_str); + p1 = RSTR_PTR(p_str); *p1++ = '0'; *p1++ = 'x'; p2 = p1; @@ -1520,7 +1489,7 @@ mrb_ptr_to_str(mrb_state *mrb, void *p) n /= 16; } while (n > 0); *p2 = '\0'; - STR_SET_LEN(p_str, (mrb_int)(p2 - STR_PTR(p_str))); + RSTR_SET_LEN(p_str, (mrb_int)(p2 - RSTR_PTR(p_str))); while (p1 < p2) { const char c = *p1; @@ -1564,7 +1533,7 @@ mrb_str_reverse(mrb_state *mrb, mrb_value str) s2 = str_new(mrb, 0, RSTRING_LEN(str)); str_with_class(mrb, s2, str); s = RSTRING_PTR(str); e = RSTRING_END(str) - 1; - p = STR_PTR(s2); + p = RSTR_PTR(s2); while (e >= s) { *p++ = *e--; @@ -1587,9 +1556,9 @@ mrb_str_reverse_bang(mrb_state *mrb, mrb_value str) char c; mrb_str_modify(mrb, s); - if (STR_LEN(s) > 1) { - p = STR_PTR(s); - e = p + STR_LEN(s) - 1; + if (RSTR_LEN(s) > 1) { + p = RSTR_PTR(s); + e = p + RSTR_LEN(s) - 1; while (p < e) { c = *p; *p++ = *e; @@ -1625,17 +1594,17 @@ mrb_str_rindex(mrb_state *mrb, mrb_value str, mrb_value sub, mrb_int pos) mrb_int len = RSTRING_LEN(sub); /* substring longer than string */ - if (STR_LEN(ps) < len) return -1; - if (STR_LEN(ps) - pos < len) { - pos = STR_LEN(ps) - len; + if (RSTR_LEN(ps) < len) return -1; + if (RSTR_LEN(ps) - pos < len) { + pos = RSTR_LEN(ps) - len; } - sbeg = STR_PTR(ps); - s = STR_PTR(ps) + pos; + sbeg = RSTR_PTR(ps); + s = RSTR_PTR(ps) + pos; t = RSTRING_PTR(sub); if (len) { while (sbeg <= s) { if (memcmp(s, t, len) == 0) { - return s - STR_PTR(ps); + return s - RSTR_PTR(ps); } s--; } @@ -1728,27 +1697,6 @@ mrb_str_rindex_m(mrb_state *mrb, mrb_value str) return mrb_nil_value(); } -static const char isspacetable[256] = { - 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 -}; - -#define ascii_isspace(c) isspacetable[(unsigned char)(c)] - /* 15.2.10.5.35 */ /* @@ -1844,7 +1792,7 @@ mrb_str_split_m(mrb_state *mrb, mrb_value str) int ai = mrb_gc_arena_save(mrb); c = (unsigned char)*ptr++; if (skip) { - if (ascii_isspace(c)) { + if (ISSPACE(c)) { beg = ptr - bptr; } else { @@ -1853,7 +1801,7 @@ mrb_str_split_m(mrb_state *mrb, mrb_value str) if (lim_p && lim <= i) break; } } - else if (ascii_isspace(c)) { + else if (ISSPACE(c)) { mrb_ary_push(mrb, result, mrb_str_subseq(mrb, str, beg, end-beg)); mrb_gc_arena_restore(mrb, ai); skip = TRUE; @@ -2068,11 +2016,11 @@ mrb_string_value_cstr(mrb_state *mrb, mrb_value *ptr) { struct RString *ps = mrb_str_ptr(*ptr); mrb_int len = mrb_str_strlen(mrb, ps); - char *p = STR_PTR(ps); + char *p = RSTR_PTR(ps); if (!p || p[len] != '\0') { mrb_str_modify(mrb, ps); - return STR_PTR(ps); + return RSTR_PTR(ps); } return p; } @@ -2094,7 +2042,7 @@ mrb_str_to_inum(mrb_state *mrb, mrb_value str, mrb_int base, mrb_bool badcheck) len = RSTRING_LEN(str); if (s[len]) { /* no sentinel somehow */ struct RString *temp_str = str_new(mrb, s, len); - s = STR_PTR(temp_str); + s = RSTR_PTR(temp_str); } } return mrb_cstr_to_inum(mrb, s, base, badcheck); @@ -2214,7 +2162,7 @@ mrb_str_to_dbl(mrb_state *mrb, mrb_value str, mrb_bool badcheck) } if (s[len]) { /* no sentinel somehow */ struct RString *temp_str = str_new(mrb, s, len); - s = STR_PTR(temp_str); + s = RSTR_PTR(temp_str); } } return mrb_cstr_to_dbl(mrb, s, badcheck); @@ -2308,6 +2256,8 @@ mrb_str_upcase(mrb_state *mrb, mrb_value self) return str; } +#define IS_EVSTR(p,e) ((p) < (e) && (*(p) == '$' || *(p) == '@' || *(p) == '{')) + /* * call-seq: * str.dump -> new_str @@ -2353,7 +2303,7 @@ mrb_str_dump(mrb_state *mrb, mrb_value str) result = str_new(mrb, 0, len); str_with_class(mrb, result, str); p = RSTRING_PTR(str); pend = p + RSTRING_LEN(str); - q = STR_PTR(result); + q = RSTR_PTR(result); *q++ = '"'; while (p < pend) { unsigned char c = *p++; @@ -2532,8 +2482,8 @@ static mrb_value mrb_str_bytes(mrb_state *mrb, mrb_value str) { struct RString *s = mrb_str_ptr(str); - mrb_value a = mrb_ary_new_capa(mrb, STR_LEN(s)); - unsigned char *p = (unsigned char *)(STR_PTR(s)), *pend = p + STR_LEN(s); + mrb_value a = mrb_ary_new_capa(mrb, RSTR_LEN(s)); + unsigned char *p = (unsigned char *)(RSTR_PTR(s)), *pend = p + RSTR_LEN(s); while (p < pend) { mrb_ary_push(mrb, a, mrb_fixnum_value(p[0])); @@ -2548,6 +2498,8 @@ mrb_init_string(mrb_state *mrb) { struct RClass *s; + mrb_static_assert(RSTRING_EMBED_LEN_MAX < (1 << 5), "pointer size too big for embedded string"); + s = mrb->string_class = mrb_define_class(mrb, "String", mrb->object_class); /* 15.2.10 */ MRB_SET_INSTANCE_TT(s, MRB_TT_STRING); diff --git a/src/variable.c b/src/variable.c index 5f762dd0b..74bb591cf 100644 --- a/src/variable.c +++ b/src/variable.c @@ -124,10 +124,10 @@ iv_put(mrb_state *mrb, iv_tbl *t, mrb_sym sym, mrb_value val) * mrb * t the variable table to be searched. * sym the symbol to be used as the key. - * vp the value pointer. Recieves the value if the specified symbol contains - * in the instance variable table. + * vp the value pointer. Receives the value if the specified symbol is + * contained in the instance variable table. * Returns - * true if the specfiyed symbol contains in the instance variable table. + * true if the specified symbol is contained in the instance variable table. */ static mrb_bool iv_get(mrb_state *mrb, iv_tbl *t, mrb_sym sym, mrb_value *vp) @@ -159,10 +159,10 @@ iv_get(mrb_state *mrb, iv_tbl *t, mrb_sym sym, mrb_value *vp) * Parameters * t the variable table to be searched. * sym the symbol to be used as the key. - * vp the value pointer. Recieve the deleted value if the symbol contans - * in the instance varible table. + * vp the value pointer. Receive the deleted value if the symbol is + * contained in the instance variable table. * Returns - * true if the specfied symbol contains in the instance variable table. + * true if the specified symbol is contained in the instance variable table. */ static mrb_bool iv_del(mrb_state *mrb, iv_tbl *t, mrb_sym sym, mrb_value *vp) @@ -625,7 +625,7 @@ mrb_yield_with_class(mrb_state *mrb, mrb_value b, mrb_int argc, const mrb_value stack_extend(mrb, ci->nregs, 0); } else { - ci->nregs = p->body.irep->nregs + 1; + ci->nregs = p->body.irep->nregs; stack_extend(mrb, ci->nregs, argc+2); } @@ -793,7 +793,7 @@ RETRY_TRY_BLOCK: } stack_extend(mrb, irep->nregs, stack_keep); mrb->c->ci->proc = proc; - mrb->c->ci->nregs = irep->nregs + 1; + mrb->c->ci->nregs = irep->nregs; regs = mrb->c->stack; regs[0] = self; @@ -811,7 +811,7 @@ RETRY_TRY_BLOCK: CASE(OP_LOADL) { /* A Bx R(A) := Pool(Bx) */ - regs[GETARG_A(i)] = pool[GETARG_Bx(i)]; + regs[GETARG_A(i)] = pool[GETARG_Bx(i)]; NEXT; } diff --git a/tasks/libmruby.rake b/tasks/libmruby.rake index 887cc69aa..095bedd52 100644 --- a/tasks/libmruby.rake +++ b/tasks/libmruby.rake @@ -1,7 +1,10 @@ MRuby.each_target do file libfile("#{build_dir}/lib/libmruby") => libmruby.flatten do |t| archiver.run t.name, t.prerequisites - open("#{build_dir}/lib/libmruby.flags.mak", 'w') do |f| + end + + file "#{build_dir}/lib/libmruby.flags.mak" => [__FILE__, libfile("#{build_dir}/lib/libmruby")] do |t| + open(t.name, 'w') do |f| f.puts "MRUBY_CFLAGS = #{cc.all_flags.gsub('"', '\\"')}" gem_flags = gems.map { |g| g.linker.flags } @@ -15,4 +18,5 @@ MRuby.each_target do f.puts "MRUBY_LIBS = #{linker.option_library % 'mruby'} #{linker.library_flags(gem_libraries).gsub('"', '\\"')}" end end + task :all => "#{build_dir}/lib/libmruby.flags.mak" end diff --git a/tasks/mrbgem_spec.rake b/tasks/mrbgem_spec.rake index 67edffbc5..2a15a1f46 100644 --- a/tasks/mrbgem_spec.rake +++ b/tasks/mrbgem_spec.rake @@ -32,7 +32,9 @@ module MRuby attr_accessor :bins attr_accessor :requirements - attr_reader :dependencies + attr_reader :dependencies, :conflicts + + attr_accessor :export_include_paths attr_block MRuby::Build::COMMANDS @@ -44,27 +46,24 @@ module MRuby end def run_test_in_other_mrb_state? - not test_preload.nil? or not test_objs.empty? + not test_preload.nil? or not test_objs.empty? or not test_args.empty? end def setup MRuby::Gem.current = self - @build.compilers.each do |compiler| - compiler.include_paths << "#{dir}/include" - end if File.directory? "#{dir}/include" MRuby::Build::COMMANDS.each do |command| instance_variable_set("@#{command}", @build.send(command).clone) end @linker = LinkerConfig.new([], [], [], []) @rbfiles = Dir.glob("#{dir}/mrblib/*.rb").sort - @objs = Dir.glob("#{dir}/src/*.{c,cpp,cxx,cc,m,asm,S}").map do |f| + @objs = Dir.glob("#{dir}/src/*.{c,cpp,cxx,cc,m,asm,s,S}").map do |f| objfile(f.relative_path_from(@dir).to_s.pathmap("#{build_dir}/%X")) end @objs << objfile("#{build_dir}/gem_init") @test_rbfiles = Dir.glob("#{dir}/test/*.rb") - @test_objs = Dir.glob("#{dir}/test/*.{c,cpp,cxx,cc,m,asm,S}").map do |f| + @test_objs = Dir.glob("#{dir}/test/*.{c,cpp,cxx,cc,m,asm,s,S}").map do |f| objfile(f.relative_path_from(dir).to_s.pathmap("#{build_dir}/%X")) end @test_preload = nil # 'test/assert.rb' @@ -73,7 +72,9 @@ module MRuby @bins = [] @requirements = [] - @dependencies = [] + @dependencies, @conflicts = [], [] + @export_include_paths = [] + @export_include_paths << "#{dir}/include" if File.directory? "#{dir}/include" instance_eval(&@initializer) @@ -88,6 +89,7 @@ module MRuby compilers.each do |compiler| compiler.define_rules build_dir, "#{dir}" compiler.defines << %Q[MRBGEM_#{funcname.upcase}_VERSION=#{version}] + compiler.include_paths << "#{dir}/include" if File.directory? "#{dir}/include" end define_gem_init_builder @@ -100,6 +102,10 @@ module MRuby @dependencies << {:gem => name, :requirements => requirements, :default => default_gem} end + def add_conflict(name, *req) + @conflicts << {:gem => name, :requirements => req.empty? ? nil : req} + end + def self.bin=(bin) @bins = [bin].flatten end @@ -124,7 +130,7 @@ module MRuby def define_gem_init_builder file objfile("#{build_dir}/gem_init") => "#{build_dir}/gem_init.c" - file "#{build_dir}/gem_init.c" => [build.mrbcfile] + [rbfiles].flatten do |t| + file "#{build_dir}/gem_init.c" => [build.mrbcfile, __FILE__] + [rbfiles].flatten do |t| FileUtils.mkdir_p build_dir generate_gem_init("#{build_dir}/gem_init.c") end @@ -182,6 +188,7 @@ module MRuby f.puts %Q[#include "mruby/irep.h"] f.puts %Q[#include "mruby/string.h"] f.puts %Q[#include "mruby/variable.h"] + f.puts %Q[#include "mruby/hash.h"] unless test_args.empty? end def version_ok?(req_versions) @@ -320,6 +327,12 @@ module MRuby fail "#{name} version should be #{req_versions.join(' and ')} but was '#{dep_g.version}'" end end + + cfls = g.conflicts.select { |c| + cfl_g = gem_table[c[:gem]] + cfl_g and cfl_g.version_ok?(c[:requirements] || ['>= 0.0.0']) + }.map { |c| "#{c[:gem]}(#{gem_table[c[:gem]].version})" } + fail "Conflicts of gem `#{g.name}` found: #{cfls.join ', '}" unless cfls.empty? end class << gem_table @@ -337,6 +350,25 @@ module MRuby rescue TSort::Cyclic => e fail "Circular mrbgem dependency found: #{e.message}" end + + each do |g| + import_include_paths(g) + end + end + + def import_include_paths(g) + gem_table = @ary.reduce({}) { |res,v| res[v.name] = v; res } + g.dependencies.each do |dep| + dep_g = gem_table[dep[:gem]] + # We can do recursive call safely + # as circular dependency has already detected in the caller. + import_include_paths(dep_g) + + g.compilers.each do |compiler| + compiler.include_paths += dep_g.export_include_paths + g.export_include_paths += dep_g.export_include_paths + end + end end end # List end # Gem diff --git a/tasks/mrbgems.rake b/tasks/mrbgems.rake index b57f318e0..2d17be931 100644 --- a/tasks/mrbgems.rake +++ b/tasks/mrbgems.rake @@ -7,7 +7,7 @@ MRuby.each_target do # loader all gems self.libmruby << objfile("#{build_dir}/mrbgems/gem_init") file objfile("#{build_dir}/mrbgems/gem_init") => ["#{build_dir}/mrbgems/gem_init.c", "#{build_dir}/LEGAL"] - file "#{build_dir}/mrbgems/gem_init.c" => [MRUBY_CONFIG] do |t| + file "#{build_dir}/mrbgems/gem_init.c" => [MRUBY_CONFIG, __FILE__] do |t| FileUtils.mkdir_p "#{build_dir}/mrbgems" open(t.name, 'w') do |f| f.puts %Q[/*] @@ -26,21 +26,22 @@ MRuby.each_target do f.puts %Q[] f.puts %Q[#{gems.map{|g| "void GENERATED_TMP_mrb_%s_gem_final(mrb_state* mrb);" % g.funcname}.join("\n")}] f.puts %Q[] - f.puts %Q[void] - f.puts %Q[mrb_init_mrbgems(mrb_state *mrb) {] - f.puts %Q[#{gems.map{|g| "GENERATED_TMP_mrb_%s_gem_init(mrb);" % g.funcname}.join("\n")}] + f.puts %Q[static void] + f.puts %Q[mrb_final_mrbgems(mrb_state *mrb) {] + f.puts %Q[#{gems.map{|g| "GENERATED_TMP_mrb_%s_gem_final(mrb);" % g.funcname}.join("\n")}] f.puts %Q[}] f.puts %Q[] f.puts %Q[void] - f.puts %Q[mrb_final_mrbgems(mrb_state *mrb) {] - f.puts %Q[#{gems.map{|g| "GENERATED_TMP_mrb_%s_gem_final(mrb);" % g.funcname}.join("\n")}] + f.puts %Q[mrb_init_mrbgems(mrb_state *mrb) {] + f.puts %Q[#{gems.map{|g| "GENERATED_TMP_mrb_%s_gem_init(mrb);" % g.funcname}.join("\n")}] + f.puts %Q[mrb_state_atexit(mrb, mrb_final_mrbgems);] f.puts %Q[}] end end end # legal documents - file "#{build_dir}/LEGAL" => [MRUBY_CONFIG] do |t| + file "#{build_dir}/LEGAL" => [MRUBY_CONFIG, __FILE__] do |t| open(t.name, 'w+') do |f| f.puts <<LEGAL Copyright (c) #{Time.now.year} mruby developers diff --git a/tasks/mrbgems_test.rake b/tasks/mrbgems_test.rake index d06b7a24c..a6e9eb99e 100644 --- a/tasks/mrbgems_test.rake +++ b/tasks/mrbgems_test.rake @@ -10,7 +10,7 @@ MRuby.each_target do test_rbobj = g.test_rbireps.ext(exts.object) file test_rbobj => g.test_rbireps - file g.test_rbireps => [g.test_rbfiles].flatten + [g.build.mrbcfile] do |t| + file g.test_rbireps => [g.test_rbfiles].flatten + [g.build.mrbcfile, __FILE__] do |t| open(t.name, 'w') do |f| g.print_gem_test_header(f) test_preload = g.test_preload and [g.dir, MRUBY_ROOT].map {|dir| @@ -117,7 +117,7 @@ MRuby.each_target do no_mrb_open_test_lib = no_mrb_open_test.ext(exts.object) file no_mrb_open_test_lib => "#{no_mrb_open_test}.c" - file "#{no_mrb_open_test}.c" => no_mrb_open_test_rbfiles + [MRUBY_CONFIG] do |t| + file "#{no_mrb_open_test}.c" => no_mrb_open_test_rbfiles + [MRUBY_CONFIG, __FILE__] do |t| open(t.name, 'w') do |f| f.puts %Q[/*] f.puts %Q[ * This file contains a test code for following gems:] diff --git a/tasks/mruby_build.rake b/tasks/mruby_build.rake index 966c602a4..09175d533 100644 --- a/tasks/mruby_build.rake +++ b/tasks/mruby_build.rake @@ -209,6 +209,7 @@ module MRuby def run_bintest targets = @gems.select { |v| File.directory? "#{v.dir}/bintest" }.map { |v| filename v.dir } + targets << filename(".") if File.directory? "./bintest" sh "ruby test/bintest.rb #{targets.join ' '}" end diff --git a/tasks/mruby_build_gem.rake b/tasks/mruby_build_gem.rake index e58dc5c71..5d2dc030c 100644 --- a/tasks/mruby_build_gem.rake +++ b/tasks/mruby_build_gem.rake @@ -35,8 +35,9 @@ module MRuby Gem.current.build_config_initializer = block gems << Gem.current - cxx_srcs = Dir.glob("#{Gem.current.dir}/src/*.{cpp,cxx,cc}") - cxx_srcs += Dir.glob("#{Gem.current.dir}/test/*.{cpp,cxx,cc}") + cxx_srcs = ['src', 'test', 'tools'].map do |subdir| + Dir.glob("#{Gem.current.dir}/#{subdir}/*.{cpp,cxx,cc}") + end.flatten enable_cxx_abi unless cxx_srcs.empty? Gem.current diff --git a/tasks/toolchains/gcc.rake b/tasks/toolchains/gcc.rake index 821100748..7edf93642 100644 --- a/tasks/toolchains/gcc.rake +++ b/tasks/toolchains/gcc.rake @@ -2,7 +2,6 @@ MRuby::Toolchain.new(:gcc) do |conf| [conf.cc, conf.objc, conf.asm].each do |cc| cc.command = ENV['CC'] || 'gcc' cc.flags = [ENV['CFLAGS'] || %w(-g -std=gnu99 -O3 -Wall -Werror-implicit-function-declaration -Wdeclaration-after-statement)] - cc.include_paths = ["#{MRUBY_ROOT}/include"] cc.defines = %w(DISABLE_GEMS) cc.option_include_path = '-I%s' cc.option_define = '-D%s' @@ -12,7 +11,6 @@ MRuby::Toolchain.new(:gcc) do |conf| [conf.cxx].each do |cxx| cxx.command = ENV['CXX'] || 'g++' cxx.flags = [ENV['CXXFLAGS'] || ENV['CFLAGS'] || %w(-g -O3 -Wall -Werror-implicit-function-declaration)] - cxx.include_paths = ["#{MRUBY_ROOT}/include"] cxx.defines = %w(DISABLE_GEMS) cxx.option_include_path = '-I%s' cxx.option_define = '-D%s' diff --git a/tasks/toolchains/visualcpp.rake b/tasks/toolchains/visualcpp.rake index a5726dce7..8838f8a41 100644 --- a/tasks/toolchains/visualcpp.rake +++ b/tasks/toolchains/visualcpp.rake @@ -3,7 +3,6 @@ MRuby::Toolchain.new(:visualcpp) do |conf| cc.command = ENV['CC'] || 'cl.exe' # C4013: implicit function declaration cc.flags = [ENV['CFLAGS'] || %w(/c /nologo /W3 /we4013 /Zi /MD /O2 /D_CRT_SECURE_NO_WARNINGS)] - cc.include_paths = ["#{MRUBY_ROOT}/include"] cc.defines = %w(DISABLE_GEMS MRB_STACK_EXTEND_DOUBLING) cc.option_include_path = '/I%s' cc.option_define = '/D%s' @@ -13,7 +12,6 @@ MRuby::Toolchain.new(:visualcpp) do |conf| [conf.cxx].each do |cxx| cxx.command = ENV['CXX'] || 'cl.exe' cxx.flags = [ENV['CXXFLAGS'] || ENV['CFLAGS'] || %w(/c /nologo /W3 /Zi /MD /O2 /EHsc /D_CRT_SECURE_NO_WARNINGS)] - cxx.include_paths = ["#{MRUBY_ROOT}/include"] cxx.defines = %w(DISABLE_GEMS MRB_STACK_EXTEND_DOUBLING) cxx.option_include_path = '/I%s' cxx.option_define = '/D%s' diff --git a/test/mrbtest.rake b/test/mrbtest.rake index e8eb6addd..c28cf3577 100644 --- a/test/mrbtest.rake +++ b/test/mrbtest.rake @@ -31,15 +31,15 @@ MRuby.each_target do end file ass_lib => ass_c - file ass_c => "#{current_dir}/assert.rb" do |t| + file ass_c => ["#{current_dir}/assert.rb", __FILE__] do |t| FileUtils.mkdir_p File.dirname t.name open(t.name, 'w') do |f| - mrbc.run f, [t.prerequisites], 'mrbtest_assert_irep' + mrbc.run f, [t.prerequisites.first], 'mrbtest_assert_irep' end end file mlib => clib - file clib => [mrbcfile, init] + mrbs do |t| + file clib => [mrbcfile, init, __FILE__] + mrbs do |t| _pp "GEN", "*.rb", "#{clib.relative_path}" FileUtils.mkdir_p File.dirname(clib) open(clib, 'w') do |f| diff --git a/test/t/argumenterror.rb b/test/t/argumenterror.rb index c8d277cc5..abb53429b 100644 --- a/test/t/argumenterror.rb +++ b/test/t/argumenterror.rb @@ -14,8 +14,3 @@ assert('ArgumentError', '15.2.24') do assert_equal(Class, ArgumentError.class) assert_equal(ArgumentError, e2.class) end - -assert('ArgumentError superclass', '15.2.24.2') do - assert_equal(StandardError, ArgumentError.superclass) -end - diff --git a/test/t/array.rb b/test/t/array.rb index 56daf0b01..538ea0c3f 100644 --- a/test/t/array.rb +++ b/test/t/array.rb @@ -5,10 +5,6 @@ assert('Array', '15.2.12') do assert_equal(Class, Array.class) end -assert('Array superclass', '15.2.12.2') do - assert_equal(Object, Array.superclass) -end - assert('Array inclueded modules', '15.2.12.3') do assert_true(Array.include?(Enumerable)) end diff --git a/test/t/class.rb b/test/t/class.rb index 821259c5e..f49ccf494 100644 --- a/test/t/class.rb +++ b/test/t/class.rb @@ -5,10 +5,6 @@ assert('Class', '15.2.3') do assert_equal(Class, Class.class) end -assert('Class superclass', '15.2.3.2') do - assert_equal(Module, Class.superclass) -end - assert('Class#initialize', '15.2.3.3.1') do c = Class.new do def test diff --git a/test/t/enumerable.rb b/test/t/enumerable.rb index 5c9b14f00..359c3451b 100644 --- a/test/t/enumerable.rb +++ b/test/t/enumerable.rb @@ -107,15 +107,17 @@ assert('Enumerable#member?', '15.3.2.2.15') do end assert('Enumerable#partition', '15.3.2.2.16') do - [0,1,2,3,4,5,6,7,8,9].partition do |i| + partition = [0,1,2,3,4,5,6,7,8,9].partition do |i| i % 2 == 0 - end == [[0,2,4,6,8], [1,3,5,7,9]] + end + assert_equal [[0,2,4,6,8], [1,3,5,7,9]], partition end assert('Enumerable#reject', '15.3.2.2.17') do - [0,1,2,3,4,5,6,7,8,9].reject do |i| + reject = [0,1,2,3,4,5,6,7,8,9].reject do |i| i % 2 == 0 - end == [1,3,5,7,9] + end + assert_equal [1,3,5,7,9], reject end assert('Enumerable#select', '15.3.2.2.18') do diff --git a/test/t/exception.rb b/test/t/exception.rb index be487162f..d27813028 100644 --- a/test/t/exception.rb +++ b/test/t/exception.rb @@ -5,10 +5,6 @@ assert('Exception', '15.2.22') do assert_equal Class, Exception.class end -assert('Exception superclass', '15.2.22.2') do - assert_equal Object, Exception.superclass -end - assert('Exception.exception', '15.2.22.4.1') do e = Exception.exception('a') diff --git a/test/t/false.rb b/test/t/false.rb index bc684f2e6..3582f697a 100644 --- a/test/t/false.rb +++ b/test/t/false.rb @@ -11,10 +11,6 @@ assert('FalseClass false', '15.2.6.1') do assert_false FalseClass.method_defined? :new end -assert('FalseClass superclass', '15.2.6.2') do - assert_equal Object, FalseClass.superclass -end - assert('FalseClass#&', '15.2.6.3.1') do assert_false false.&(true) assert_false false.&(false) diff --git a/test/t/float.rb b/test/t/float.rb index ded434320..d45709173 100644 --- a/test/t/float.rb +++ b/test/t/float.rb @@ -5,10 +5,6 @@ assert('Float', '15.2.9') do assert_equal Class, Float.class end -assert('Float superclass', '15.2.9.2') do - assert_equal Numeric, Float.superclass -end - assert('Float#+', '15.2.9.3.1') do a = 3.123456788 + 0.000000001 b = 3.123456789 + 1 diff --git a/test/t/hash.rb b/test/t/hash.rb index 0d8d137c4..eee7c7b6a 100644 --- a/test/t/hash.rb +++ b/test/t/hash.rb @@ -5,10 +5,6 @@ assert('Hash', '15.2.13') do assert_equal Class, Hash.class end -assert('Hash superclass', '15.2.13.2') do - assert_equal Object, Hash.superclass -end - assert('Hash#==', '15.2.13.4.1') do assert_true({ 'abc' => 'abc' } == { 'abc' => 'abc' }) assert_false({ 'abc' => 'abc' } == { 'cba' => 'cba' }) diff --git a/test/t/indexerror.rb b/test/t/indexerror.rb index ea008a227..a8dce23a0 100644 --- a/test/t/indexerror.rb +++ b/test/t/indexerror.rb @@ -4,7 +4,3 @@ assert('IndexError', '15.2.33') do assert_equal Class, IndexError.class end - -assert('IndexError superclass', '15.2.33.2') do - assert_equal StandardError, IndexError.superclass -end diff --git a/test/t/integer.rb b/test/t/integer.rb index 6560dddfe..c50ef112c 100644 --- a/test/t/integer.rb +++ b/test/t/integer.rb @@ -5,10 +5,6 @@ assert('Integer', '15.2.8') do assert_equal Class, Integer.class end -assert('Integer superclass', '15.2.8.2') do - assert_equal Numeric, Integer.superclass -end - assert('Integer#+', '15.2.8.3.1') do a = 1+1 b = 1+1.0 diff --git a/test/t/kernel.rb b/test/t/kernel.rb index 427d71e36..6df2294d5 100644 --- a/test/t/kernel.rb +++ b/test/t/kernel.rb @@ -586,8 +586,5 @@ assert('stack extend') do end assert_equal 6, recurse(0, 5) - assert_raise RuntimeError do - recurse(0, 100000) - end end diff --git a/test/t/module.rb b/test/t/module.rb index fcf46fe3a..5ac794330 100644 --- a/test/t/module.rb +++ b/test/t/module.rb @@ -5,10 +5,6 @@ assert('Module', '15.2.2') do assert_equal Class, Module.class end -assert('Module superclass', '15.2.2.2') do - assert_equal Object, Module.superclass -end - # TODO not implemented ATM assert('Module.constants', '15.2.2.3.1') do # TODO not implemented ATM assert('Module.nesting', '15.2.2.3.2') do diff --git a/test/t/nameerror.rb b/test/t/nameerror.rb index 3e3c59264..28682bedc 100644 --- a/test/t/nameerror.rb +++ b/test/t/nameerror.rb @@ -5,10 +5,6 @@ assert('NameError', '15.2.31') do assert_equal Class, NameError.class end -assert('NameError superclass', '15.2.31.2') do - assert_equal StandardError, NameError.superclass -end - assert('NameError#name', '15.2.31.2.1') do # This check is not duplicate with 15.2.31.2.2 check. diff --git a/test/t/nil.rb b/test/t/nil.rb index 971ce2e8e..53b922f9a 100644 --- a/test/t/nil.rb +++ b/test/t/nil.rb @@ -10,10 +10,6 @@ assert('NilClass', '15.2.4.1') do assert_false NilClass.method_defined? :new end -assert('NilClass superclass', '15.2.4.2') do - assert_equal Object, NilClass.superclass -end - assert('NilClass#&', '15.2.4.3.1') do assert_false nil.&(true) assert_false nil.&(nil) diff --git a/test/t/nomethoderror.rb b/test/t/nomethoderror.rb index 561e545f9..5fed79689 100644 --- a/test/t/nomethoderror.rb +++ b/test/t/nomethoderror.rb @@ -8,6 +8,15 @@ assert('NoMethodError', '15.2.32') do end end -assert('NoMethodError superclass', '15.2.32.2') do - assert_equal NameError, NoMethodError.superclass +assert('NoMethodError#args', '15.2.32.2.1') do + a = NoMethodError.new 'test', :test, [1, 2] + assert_equal [1, 2], a.args + + assert_nothing_raised do + begin + doesNotExistAsAMethodNameForVerySure 3, 1, 4 + rescue NoMethodError => e + assert_equal [3, 1, 4], e.args + end + end end diff --git a/test/t/numeric.rb b/test/t/numeric.rb index ef977da29..2b01611a7 100644 --- a/test/t/numeric.rb +++ b/test/t/numeric.rb @@ -5,10 +5,6 @@ assert('Numeric', '15.2.7') do assert_equal Class, Numeric.class end -assert('Numeric superclass', '15.2.7.2') do - assert_equal Object, Numeric.superclass -end - assert('Numeric#+@', '15.2.7.4.1') do assert_equal(+1, +1) end diff --git a/test/t/proc.rb b/test/t/proc.rb index 9c1b7d4c7..22ccceb68 100644 --- a/test/t/proc.rb +++ b/test/t/proc.rb @@ -5,10 +5,6 @@ assert('Proc', '15.2.17') do assert_equal Class, Proc.class end -assert('Proc superclass', '15.2.17.2') do - assert_equal Object, Proc.superclass -end - assert('Proc.new', '15.2.17.3.1') do assert_raise ArgumentError do Proc.new diff --git a/test/t/range.rb b/test/t/range.rb index b35da40ab..278b26902 100644 --- a/test/t/range.rb +++ b/test/t/range.rb @@ -5,10 +5,6 @@ assert('Range', '15.2.14') do assert_equal Class, Range.class end -assert('Range superclass', '15.2.14.2') do - assert_equal Object, Range.superclass -end - assert('Range#==', '15.2.14.4.1') do assert_true (1..10) == (1..10) assert_false (1..10) == (1..100) diff --git a/test/t/rangeerror.rb b/test/t/rangeerror.rb index 8dc683745..97878096e 100644 --- a/test/t/rangeerror.rb +++ b/test/t/rangeerror.rb @@ -4,7 +4,3 @@ assert('RangeError', '15.2.26') do assert_equal Class, RangeError.class end - -assert('RangeError superclass', '15.2.26.2') do - assert_equal StandardError, RangeError.superclass -end diff --git a/test/t/standarderror.rb b/test/t/standarderror.rb index cab99834e..c349b08cf 100644 --- a/test/t/standarderror.rb +++ b/test/t/standarderror.rb @@ -4,7 +4,3 @@ assert('StandardError', '15.2.23') do assert_equal Class, StandardError.class end - -assert('StandardError superclass', '15.2.23.2') do - assert_equal Exception, StandardError.superclass -end diff --git a/test/t/string.rb b/test/t/string.rb index 5ecb51530..c0e545e87 100644 --- a/test/t/string.rb +++ b/test/t/string.rb @@ -5,10 +5,6 @@ assert('String', '15.2.10') do assert_equal Class, String.class end -assert('String superclass', '15.2.10.2') do - assert_equal Object, String.superclass -end - assert('String#<=>', '15.2.10.5.1') do a = '' <=> '' b = '' <=> 'not empty' @@ -320,6 +316,13 @@ assert('String#replace', '15.2.10.5.28') do b.replace(c); c.replace(b); assert_equal c, b + + # shared string + s = "foo" * 100 + a = s[10, 90] # create shared string + assert_equal("", s.replace("")) # clear + assert_equal("", s) # s is cleared + assert_not_equal("", a) # a should not be affected end assert('String#reverse', '15.2.10.5.29') do diff --git a/test/t/superclass.rb b/test/t/superclass.rb new file mode 100644 index 000000000..9fd8830b3 --- /dev/null +++ b/test/t/superclass.rb @@ -0,0 +1,46 @@ +[ + # [:Object, :implementation_defined_value, '15.2.2.1'], + [:Module, :Object, '15.2.2.2'], + [:Class, :Module, '15.2.3.2'], + [:NilClass, :Object, '15.2.4.2'], + [:TrueClass, :Object, '15.2.5.2'], + [:FalseClass, :Object, '15.2.6.2'], + [:Numeric, :Object, '15.2.7.2'], + [:Integer, :Numeric, '15.2.8.2'], + [:Float, :Numeric, '15.2.9.2'], + [:String, :Object, '15.2.10.2'], + [:Symbol, :Object, '15.2.11.2'], + [:Array, :Object, '15.2.12.2'], + [:Hash, :Object, '15.2.13.2'], + [:Range, :Object, '15.2.14.2'], +# [:Regexp, :Object, '15.2.15.2'], #No Regexp in mruby core +# [:MatchData, :Object, '15.2.16.2'], + [:Proc, :Object, '15.2.17.2'], +# [:Struct, :Object, '15.2.18.2'], +# [:Time, :Object, '15.2.19.2'], +# [:IO, :Object, '15.2.20.2'], +# [:File, :IO, '15.2.21.2'], + [:Exception, :Object, '15.2.22.2'], + [:StandardError, :Exception, '15.2.23.2'], + [:ArgumentError, :StandardError, '15.2.24.2'], + [:LocalJumpError, :StandardError, '15.2.25.2'], + [:RangeError, :StandardError, '12.2.26.2'], + [:RegexpError, :StandardError, '12.2.27.2'], + [:RuntimeError, :StandardError, '12.2.28.2'], + [:TypeError, :StandardError, '12.2.29.2'], +# [:ZeroDivisionError, :StandardError, '12.2.30.2'], # No ZeroDivisionError in mruby + [:NameError, :StandardError, '15.2.31.2'], + [:NoMethodError, :NameError, '15.2.32.2'], + [:IndexError, :StandardError, '15.2.33.2'], +# [:IOError, :StandardError, '12.2.34.2'], +# [:EOFError, :IOError, '12.2.35.2'], +# [:SystemCallError, :StandardError, '15.2.36.2'], + [:ScriptError, :Exception, '12.2.37.2'], + [:SyntaxError, :ScriptError, '12.2.38.2'], +# [:LoadError, :ScriptError, '12.2.39,2'], +].each do |cls, super_cls, iso| + assert "Direct superclass of #{cls}", iso do + skip "#{cls} isn't defined" unless Object.const_defined? cls + assert_equal Object.const_get(super_cls), Object.const_get(cls).superclass + end +end diff --git a/test/t/symbol.rb b/test/t/symbol.rb index f852dcd00..b0252849d 100644 --- a/test/t/symbol.rb +++ b/test/t/symbol.rb @@ -5,10 +5,6 @@ assert('Symbol', '15.2.11') do assert_equal Class, Symbol.class end -assert('Symbol superclass', '15.2.11.2') do - assert_equal Object, Symbol.superclass -end - assert('Symbol#===', '15.2.11.3.1') do assert_true :abc == :abc assert_false :abc == :cba diff --git a/test/t/true.rb b/test/t/true.rb index e5da2112c..74f605ef0 100644 --- a/test/t/true.rb +++ b/test/t/true.rb @@ -11,10 +11,6 @@ assert('TrueClass true', '15.2.5.1') do assert_false TrueClass.method_defined? :new end -assert('TrueClass superclass', '15.2.5.2') do - assert_equal Object, TrueClass.superclass -end - assert('TrueClass#&', '15.2.5.3.1') do assert_true true.&(true) assert_false true.&(false) diff --git a/test/t/typeerror.rb b/test/t/typeerror.rb index a91fb1be2..32536a74f 100644 --- a/test/t/typeerror.rb +++ b/test/t/typeerror.rb @@ -4,8 +4,3 @@ assert('TypeError', '15.2.29') do assert_equal Class, TypeError.class end - -assert('TypeError superclass', '15.2.29.2') do - assert_equal StandardError, TypeError.superclass -end - diff --git a/tools/mrbc/mrbc.c b/tools/mrbc/mrbc.c index e5858e54a..52e762a50 100644 --- a/tools/mrbc/mrbc.c +++ b/tools/mrbc/mrbc.c @@ -270,7 +270,7 @@ main(int argc, char **argv) fprintf(stderr, "%s: no program file given\n", args.prog); return EXIT_FAILURE; } - if (args.outfile == NULL) { + if (args.outfile == NULL && !args.check_syntax) { if (n + 1 == argc) { args.outfile = get_outfilename(mrb, argv[n], args.initname ? C_EXT : RITEBIN_EXT); } |
