diff options
110 files changed, 3373 insertions, 4990 deletions
diff --git a/.gitignore b/.gitignore index 1a8c5990c..800680821 100644 --- a/.gitignore +++ b/.gitignore @@ -20,4 +20,6 @@ cscope.out /src/y.tab.c /bin /build -/lib +/mruby-source-*.gem +doc/api +.yardoc diff --git a/.travis.yml b/.travis.yml index bcfce3984..fa57b936d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,5 +1,7 @@ language: c +sudo: false + matrix: include: - os: linux @@ -7,8 +9,10 @@ matrix: - os: osx osx_image: xcod6.4 -install: - - if [ $TRAVIS_OS_NAME = 'linux' ]; then sudo apt-get update -qq; sudo apt-get -q install gperf; fi +addons: + apt: + packages: + - gperf env: MRUBY_CONFIG=travis_config.rb script: "./minirake all test" diff --git a/.yardopts b/.yardopts new file mode 100644 index 000000000..017b7ce70 --- /dev/null +++ b/.yardopts @@ -0,0 +1,8 @@ +--plugin mruby +--plugin coderay +--output-dir doc/api +- +AUTHORS +MITL +CONTRIBUTING.md +doc/guides/*.md @@ -1,9 +1,9 @@ [![Build Status][build-status-img]][travis-ci] -## What's mruby +## What is mruby mruby is the lightweight implementation of the Ruby language complying to (part -of) the [ISO standard][ISO-standard]. Its syntax is Ruby 1.9 compatible. +of) the [ISO standard][ISO-standard]. Its syntax is Ruby 1.9 compatible. mruby can be linked and embedded within your application. We provide the interpreter program "mruby" and the interactive mruby shell "mirb" as examples. @@ -15,39 +15,28 @@ program under the "test" directory for an example. This achievement was sponsored by the Regional Innovation Creation R&D Programs of the Ministry of Economy, Trade and Industry of Japan. - ## How to get mruby -The stable version 1.1.0 of mruby can be downloaded via the following URL: - - https://github.com/mruby/mruby/archive/1.1.0.zip +The stable version 1.1.0 of mruby can be downloaded via the following URL: [https://github.com/mruby/mruby/archive/1.1.0.zip](https://github.com/mruby/mruby/archive/1.1.0.zip) -The latest development version of mruby can be downloaded via the following URL: - - https://github.com/mruby/mruby/zipball/master +The latest development version of mruby can be downloaded via the following URL: [https://github.com/mruby/mruby/zipball/master](https://github.com/mruby/mruby/zipball/master) The trunk of the mruby source tree can be checked out with the following command: $ git clone https://github.com/mruby/mruby.git - ## mruby home-page -The URL of the mruby home-page is: - - http://www.mruby.org/ - +The URL of the mruby home-page is: [http://www.mruby.org](http://www.mruby.org). ## Mailing list -We don't have mailing list, use GitHub forum <http://github.com/mruby/mruby>. - +We don't have mailing list, but you can use [GitHub issues](https://github.com/mruby/mruby). ## How to compile and install (mruby and gems) -See the [doc/compile/README.md](doc/compile/README.md) file. - +See the [doc/guides/compile.md](doc/guides/compile.md) file. ## Running Tests @@ -59,7 +48,6 @@ Or $ ruby ./minirake test - ## How to customize mruby (mrbgems) mruby contains a package manager called *mrbgems*. To create extensions @@ -67,29 +55,9 @@ in C and/or Ruby you should create a *GEM*. For a documentation of how to use mrbgems consult the file [doc/mrbgems/README.md](doc/mrbgems/README.md). For example code of how to use mrbgems look into the folder *examples/mrbgems/*. - ## License -Copyright (c) 2015 mruby developers - -Permission is hereby granted, free of charge, to any person obtaining a -copy of this software and associated documentation files (the "Software"), -to deal in the Software without restriction, including without limitation -the rights to use, copy, modify, merge, publish, distribute, sublicense, -and/or sell copies of the Software, and to permit persons to whom the -Software is furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -DEALINGS IN THE SOFTWARE. - +mruby is released under the [MIT License](MITL). ## Note for License @@ -108,17 +76,15 @@ file.) Please ask us if you want to distribute your code under another license. - ## How to Contribute -See the [contribution guidelines][contribution-guidelines] then send a pull +See the [contribution guidelines][contribution-guidelines], and then send a pull request to <http://github.com/mruby/mruby>. We consider you have granted non-exclusive right to your contributed code under MIT license. If you want to be named as one of mruby developers, please include an update to the AUTHORS file in your pull request. [ISO-standard]: http://www.iso.org/iso/iso_catalogue/catalogue_tc/catalogue_detail.htm?csnumber=59579 -[build-status-img]: https://travis-ci.org/mruby/mruby.png?branch=master -[contribution-guidelines]: https://github.com/mruby/mruby/blob/master/CONTRIBUTING.md +[build-status-img]: https://travis-ci.org/mruby/mruby.svg?branch=master +[contribution-guidelines]: CONTRIBUTING.md [travis-ci]: https://travis-ci.org/mruby/mruby - @@ -26,16 +26,13 @@ load "#{MRUBY_ROOT}/mrblib/mrblib.rake" load "#{MRUBY_ROOT}/tasks/mrbgems.rake" load "#{MRUBY_ROOT}/tasks/libmruby.rake" -load "#{MRUBY_ROOT}/tasks/mrbgems_test.rake" -load "#{MRUBY_ROOT}/test/mrbtest.rake" - load "#{MRUBY_ROOT}/tasks/benchmark.rake" ############################## # generic build targets, rules task :default => :all -bin_path = "#{MRUBY_ROOT}/bin" +bin_path = ENV['INSTALL_DIR'] || "#{MRUBY_ROOT}/bin" FileUtils.mkdir_p bin_path, { :verbose => $verbose } depfiles = MRuby.targets['host'].bins.map do |bin| @@ -74,7 +71,7 @@ MRuby.each_target do |target| end if target == MRuby.targets['host'] - install_path = MRuby.targets['host'].exefile("#{MRUBY_ROOT}/bin/#{bin}") + install_path = MRuby.targets['host'].exefile("#{bin_path}/#{bin}") file install_path => exec do |t| FileUtils.rm_f t.name, { :verbose => $verbose } @@ -83,7 +80,7 @@ MRuby.each_target do |target| depfiles += [ install_path ] elsif target == MRuby.targets['host-debug'] unless MRuby.targets['host'].gems.map {|g| g.bins}.include?([bin]) - install_path = MRuby.targets['host-debug'].exefile("#{MRUBY_ROOT}/bin/#{bin}") + install_path = MRuby.targets['host-debug'].exefile("#{bin_path}/#{bin}") file install_path => exec do |t| FileUtils.rm_f t.name, { :verbose => $verbose } @@ -117,9 +114,9 @@ task :all => depfiles do end desc "run all mruby tests" -task :test => ["all"] + MRuby.targets.values.map { |t| t.build_mrbtest_lib_only? ? t.libfile("#{t.build_dir}/test/mrbtest") : t.exefile("#{t.build_dir}/test/mrbtest") } do +task :test => ["all"] do MRuby.each_target do - run_test unless build_mrbtest_lib_only? + run_test if test_enabled? end end @@ -142,5 +139,10 @@ end desc 'generate document' task :doc do - load "#{MRUBY_ROOT}/doc/language/generator.rb" + begin + sh "mrbdoc" + rescue + puts "ERROR: To generate documents, you should install yard-mruby gem." + puts " $ gem install yard-mruby" + end end diff --git a/build_config.rb b/build_config.rb index 3408f19a1..96b1d4684 100644 --- a/build_config.rb +++ b/build_config.rb @@ -108,6 +108,16 @@ MRuby::Build.new('host-debug') do |conf| # conf.enable_bintest end +MRuby::Build.new('test') do |conf| + toolchain :gcc + + enable_debug + conf.enable_bintest + conf.enable_test + + conf.gembox 'default' +end + # Define cross build settings # MRuby::CrossBuild.new('32bit') do |conf| # toolchain :gcc diff --git a/doc/api/README.md b/doc/api/README.md deleted file mode 100644 index a83330d82..000000000 --- a/doc/api/README.md +++ /dev/null @@ -1,30 +0,0 @@ -# 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 deleted file mode 100644 index 06bab2d56..000000000 --- a/doc/api/mruby.h.md +++ /dev/null @@ -1,217 +0,0 @@ -# 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`. -When applicable, implicit conversions (such as `to_str`, -`to_ary`, `to_hash`) are applied to received arguments. -Use it inside a function pointed by `mrb_func_t`. -It returns the number of arguments retrieved. -`format` is a list of following format specifiers: - -char|mruby type|retrieve types|note -:---:|----------|--------------|--- -`o`|`Object`|`mrb_value`|Could be used to retrieve any type of argument -`C`|`Class`/`Module`|`mrb_value`| -`S`|`String`|`mrb_value`|when ! follows, the value may be nil -`A`|`Array`|`mrb_value`|when ! follows, the value may be nil -`H`|`Hash`|`mrb_value`|when ! follows, the value may be nil -`s`|`String`|`char*`, `mrb_int`|Receive two arguments; s! gives (NULL,0) for nil -`z`|`String`|`char*`|NUL terminated string; z! gives NULL for nil -`a`|`Array`|`mrb_value*`, `mrb_int`|Receive two arguments; a! gives (NULL,0) for nil -`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 retrieving type. - -### mrb_define_class -```C -MRB_API struct RClass *mrb_define_class(mrb_state *, const char*, struct RClass*); -``` -Defines a new class. If you're creating a gem it may look something like this: - -```C -void mrb_example_gem_init(mrb_state* mrb) { - struct RClass *example_class; - example_class = mrb_define_class(mrb, "Example_Class", mrb->object_class); -} - -void mrb_example_gem_final(mrb_state* mrb) { - //free(TheAnimals); -} -``` -### mrb_define_method - -```C -MRB_API void mrb_define_method(mrb_state*, struct RClass*, const char*, mrb_func_t, mrb_aspec); -``` - -Defines a global function in ruby. If you're creating a gem it may look something like this: - -```C -mrb_value example_method(mrb_state* mrb, mrb_value self){ - puts("Executing example command!"); - return self; -} - -void mrb_example_gem_init(mrb_state* mrb) { - mrb_define_method(mrb, mrb->kernel_module, "example_method", example_method, MRB_ARGS_NONE()); -} - -void mrb_example_gem_final(mrb_state* mrb) { - //free(TheAnimals); -} -``` - -Or maybe you want to create a class method for a class? It might look something like this: - -```C -mrb_value example_method(mrb_state* mrb, mrb_value self){ - puts("Examples are like pizza..."); - return self; -} - -void mrb_example_gem_init(mrb_state* mrb) { - struct RClass *example_class; - example_class = mrb_define_class(mrb, "Example_Class", mrb->object_class); - mrb_define_method(mrb, example_class, "example_method", example_method, MRB_ARGS_NONE()); -} - -void mrb_example_gem_final(mrb_state* mrb) { - //free(TheAnimals); -} -``` -### mrb_define_module - -```C -MRB_API struct RClass *mrb_define_module(mrb_state *, const char*); -``` - -Defines a module. If you're creating a gem it may look something like this: - -```C -mrb_value example_method(mrb_state* mrb, mrb_value self){ - puts("Examples are like tacos..."); - return self; -} - -void mrb_example_gem_init(mrb_state* mrb) { - struct RClass *example_module; - example_module = mrb_define_module(mrb, "Example_Module"); -} - -void mrb_example_gem_final(mrb_state* mrb) { - //free(TheAnimals); -} -``` - -### mrb_define_module_function - -```C -MRB_API void mrb_define_module_function(mrb_state*, struct RClass*, const char*, mrb_func_t, mrb_aspec); -``` - -Defines a module function. If you're creating a gem it may look something like this: - - -```C -mrb_value example_method(mrb_state* mrb, mrb_value self){ - puts("Examples are like hot wings..."); - return self; -} - -void mrb_example_gem_init(mrb_state* mrb) { - struct RClass *example_module; - example_module = mrb_define_module(mrb, "Example_Module"); - mrb_define_module_function(mrb, example_module, "example_method", example_method, MRB_ARGS_NONE()); -} - -void mrb_example_gem_final(mrb_state* mrb) { - //free(TheAnimals); -} -``` - -### mrb_define_const - -```C -MRB_API void mrb_define_const(mrb_state*, struct RClass*, const char *name, mrb_value); -``` - -Defines a constant. If you're creating a gem it may look something like this: - -```C -mrb_value example_method(mrb_state* mrb, mrb_value self){ - puts("Examples are like hot wings..."); - return self; -} - -void mrb_example_gem_init(mrb_state* mrb) { - mrb_define_const(mrb, mrb->kernel_module, "EXAPMLE_CONSTANT", mrb_fixnum_value(0x00000001)); -} - -void mrb_example_gem_final(mrb_state* mrb) { - //free(TheAnimals); -} -``` - -### mrb_str_new_cstr - -```C -MRB_API mrb_value mrb_str_new_cstr(mrb_state*, const char*); -``` - -Turns a C string into a Ruby string value. - - -### mrb_value mrb_funcall - -```C -MRB_API mrb_value mrb_funcall(mrb_state*, mrb_value, const char*, mrb_int,...); -``` -Call existing ruby functions. diff --git a/doc/api/mruby/array.h.md b/doc/api/mruby/array.h.md deleted file mode 100644 index 36c253cec..000000000 --- a/doc/api/mruby/array.h.md +++ /dev/null @@ -1,250 +0,0 @@ -### mrb_ary_new
-
-```C
-mrb_value mrb_ary_new(mrb_state *mrb);
-```
-Initializes an array.
-#### Example
-In this example we read from a Ruby file inside C. The Ruby code will print what you pass as an argument and what class the passed in value is. In this case we are declaring a variable new_ary of data type mrb_value. Then we are initializing it with the mrb_ary_new function which only takes an mruby state as an argument.
-```C
-#include <stdio.h>
-#include <mruby.h>
-#include "mruby/array.h" // Needs the array header.
-#include "mruby/compile.h"
-
-int main(int argc, char *argv[])
-{
- mrb_value new_ary; // Declare variable.
- mrb_state *mrb = mrb_open();
- if (!mrb) { /* handle error */ }
- FILE *fp = fopen("test.rb","r");
- new_ary = mrb_ary_new(mrb);
- mrb_value obj = mrb_load_file(mrb,fp);
- mrb_funcall(mrb, obj, "method_name", 1, new_ary);
- fclose(fp);
- mrb_close(mrb);
- return 0;
-}
-```
-test.rb
-```Ruby
-class Example_Class
- def method_name(a)
- puts a
- puts a.class
- end
-end
-Example_Class.new
-```
-
-### mrb_ary_push
-```C
-void mrb_ary_push(mrb_state*, mrb_value, mrb_value);
-```
-Pushes given value into an array.
-#### Example
-In this example we read from a Ruby file inside C. The Ruby code will print what you pass as an argument and what class the passed in value is. In this case after initializing our array. We are declaring two variables with the mrb_int data type random_value1 & random_value2 and we initialize them 70 and 60 respectively. Then we use the mrb_ary_push function to push values those values into the array.
-```C
-#include <stdio.h>
-#include <mruby.h>
-#include "mruby/array.h" // Needs the array header.
-#include "mruby/compile.h"
-
-int main(int argc, char *argv[])
-{
- mrb_value new_ary; // Declare variable.
- mrb_int random_value1 = 70; // Initialize variable
- mrb_int random_value2 = 60; // Initialize variable
- mrb_state *mrb = mrb_open();
- if (!mrb) { /* handle error */ }
- FILE *fp = fopen("test.rb","r");
- new_ary = mrb_ary_new(mrb); // Initialize ruby array.
- /* Pushes the fixnum value from random_value1 to the new_ary instance. */
- mrb_ary_push(mrb, new_ary, mrb_fixnum_value(random_value1));
- /* Pushes the fixnum value from random_value2 to the new_ary instance. */
- mrb_ary_push(mrb, new_ary, mrb_fixnum_value(random_value2));
- mrb_value obj = mrb_load_file(mrb,fp);
- mrb_funcall(mrb, obj, "method_name", 1, new_ary);
- fclose(fp);
- mrb_close(mrb);
- return 0;
-}
-```
-test.rb
-```Ruby
-class Example_Class
- def method_name(a)
- puts a
- puts a.class
- end
-end
-Example_Class.new
-```
-#### Result
-After compiling you should get these results.
-```Ruby
-[70, 60]
-Array
-```
-
-## mrb_ary_pop
-```C
-mrb_value mrb_ary_pop(mrb_state *mrb, mrb_value ary);
-```
-Pops the last element from the array.
-#### Example
-In this example we read from a Ruby file inside C. The Ruby code will print what you pass as an argument and what class the passed in value is. In this case after initializing our array. We are declaring two variables with the mrb_int data type random_value1 & random_value2 and we initialize them 70 and 60 respectively. Then we use the mrb_ary_push function to push values those values into the array. Now here in the Ruby files we add another method
-called pop_ary that will return the array alone(just to be clean) and you should see the last element gone.
-```C
-#include <stdio.h>
-#include <mruby.h>
-#include "mruby/array.h" // Needs the array header.
-#include "mruby/compile.h"
-
-int main(int argc, char *argv[])
-{
- mrb_value new_ary; // Declare variable.
- mrb_int random_value1 = 70; // Initialize variable
- mrb_int random_value2 = 60; // Initialize variable
- mrb_state *mrb = mrb_open();
- if (!mrb) { /* handle error */ }
- FILE *fp = fopen("test.rb","r");
- new_ary = mrb_ary_new(mrb); // Initialize ruby array.
- /* Pushes the fixnum value from random_value1 to the new_ary instance. */
- mrb_ary_push(mrb, new_ary, mrb_fixnum_value(random_value1));
- /* Pushes the fixnum value from random_value2 to the new_ary instance. */
- mrb_ary_push(mrb, new_ary, mrb_fixnum_value(random_value2));
- mrb_value obj = mrb_load_file(mrb,fp);
- mrb_funcall(mrb, obj, "method_name", 1, new_ary);
- mrb_ary_pop(mrb, new_ary); // Pops the last element of the array. In this case 60.
- mrb_funcall(mrb, obj, "pop_ary", 1, new_ary); // Calls the method again to show the results.
- fclose(fp);
- mrb_close(mrb);
- return 0;
-}
-```
-test.rb
-```Ruby
-class Example_Class
- def method_name(a)
- puts a
- puts a.class
- end
- def pop_ary(a)
- puts a
- end
-end
-Example_Class.new
-```
-#### Result
-After compiling you should get these results.
-```Ruby
-[70, 60]
-Array
-[70]
-```
-## mrb_ary_ref
-```C
-mrb_value mrb_ary_ref(mrb_state *mrb, mrb_value ary, mrb_int n);
-```
-Returns a reference to an element of the array. Specified by the value given to mrb_int n.
-#### Example
-In this example we read from a Ruby file inside C. The Ruby code will print what you pass as an argument and what class the passed in value is. In this case we're declaring a variable ary_ref with the data type of mrb_value. Then we assign mrb_ary_ref to it getting new_ary's value at index 1.
-```C
-#include <stdio.h>
-#include <mruby.h>
-#include "mruby/array.h" // Needs the array header.
-#include "mruby/compile.h"
-
-int main(int argc, char *argv[])
-{
- mrb_value ary_ref; // Declare variable.
- mrb_value new_ary; // Declare variable.
- mrb_int random_value1 = 70; // Initialize variable
- mrb_int random_value2 = 60; // Initialize variable
- mrb_state *mrb = mrb_open();
- if (!mrb) { /* handle error */ }
- FILE *fp = fopen("test.rb","r");
- new_ary = mrb_ary_new(mrb); // Initialize ruby array.
- /* Pushes the fixnum value from random_value1 to the new_ary instance. */
- mrb_ary_push(mrb, new_ary, mrb_fixnum_value(random_value1));
- /* Pushes the fixnum value from random_value2 to the new_ary instance. */
- mrb_ary_push(mrb, new_ary, mrb_fixnum_value(random_value2));
- ary_ref = mrb_ary_ref(mrb, new_ary, 1); // Gets the value of new_ary's second element at index 1.
- mrb_value obj = mrb_load_file(mrb,fp);
- /* Passing the value from ary_ref to the method method_name.*/
- mrb_funcall(mrb, obj, "method_name", 1, ary_ref);
- fclose(fp);
- mrb_close(mrb);
- return 0;
-}
-```
-test.rb
-```Ruby
-class Example_Class
- def method_name(a)
- puts a
- puts a.class
- end
-end
-Example_Class.new
-```
-#### Result
-After compiling you should get these results.
-```Ruby
-60
-Fixnum
-```
-
-### mrb_ary_set
-```C
-void mrb_ary_set(mrb_state *mrb, mrb_value ary, mrb_int n, mrb_value val);
-```
-Sets a value to an index.
-#### Example
-In this example we read from a Ruby file inside C. The Ruby code will print what you pass as an argument and what class the passed in value is. In this case we're declaring a variable ary_ref with the data type of mrb_value. Then we assign mrb_ary_ref to it getting new_ary's value at index 1.
-```C
-#include <stdio.h>
-#include <mruby.h>
-#include "mruby/array.h" // Needs the array header.
-#include "mruby/compile.h"
-
-int main(int argc, char *argv[])
-{
- mrb_value new_ary;
- mrb_value ary_obj;
- mrb_int random_value1 = 70;
- mrb_int random_value2 = 60;
- mrb_state *mrb = mrb_open();
- if (!mrb) { /* handle error */ }
- FILE *fp = fopen("test.rb","r");
- new_ary = mrb_ary_new(mrb);
- mrb_ary_push(mrb, new_ary, mrb_fixnum_value(random_value1));
- mrb_ary_push(mrb, new_ary, mrb_fixnum_value(random_value2));
- /* Sets the fixnum value of 7 to the second index of the array.*/
- mrb_ary_set(mrb, new_ary, 2, mrb_fixnum_value(7));
- mrb_value obj = mrb_load_file(mrb,fp);
- mrb_funcall(mrb, obj, "before_after", 1, new_ary);
- fclose(fp);
- mrb_close(mrb);
- return 0;
-}
-```
-test.rb
-```Ruby
-class Example_Class
- def method_name(a)
- puts a
- puts a.class
- end
- def before_after(a)
- puts a
- end
-end
-Example_Class.new
-```
-#### Result
-After compiling you should get these results.
-```Ruby
-[70, 60, 7]
-```
diff --git a/doc/api/mruby/hash.h.md b/doc/api/mruby/hash.h.md deleted file mode 100644 index fa12ea670..000000000 --- a/doc/api/mruby/hash.h.md +++ /dev/null @@ -1,380 +0,0 @@ -### mrb_hash_new
-
-```C
-mrb_value mrb_hash_new(mrb_state *mrb);
-```
-
-Initializes a hash.
-#### Example
-
-In this example we read from a Ruby file inside C. The Ruby code will print what you pass as an argument
-and what class the passed in value is. This example initializes a hash. In pure Ruby doing this is equivalent
-to Hash.new.
-
-```C
-#include <stdio.h>
-#include <mruby.h>
-#include "mruby/hash.h" // Needs the hash header.
-#include "mruby/compile.h"
-
-int main(int argc, char *argv[])
-{
- mrb_state *mrb = mrb_open();
- if (!mrb) { /* handle error */ }
- mrb_value new_hash; // Declare variable.
- FILE *fp = fopen("test_ext.rb","r");
- new_hash = mrb_hash_new(mrb); // Initialize hash.
- mrb_value obj = mrb_load_file(mrb,fp);
- mrb_funcall(mrb, obj, "method_name", 1, new_hash);
- fclose(fp);
- mrb_close(mrb);
- return 0;
-}
-```
-
-#### test_ext.rb
-
-``` Ruby
-class Example_Class
- def method_name(a)
- puts a
- puts a.class
- end
-end
-Example_Class.new
-```
-
-### mrb_hash_set
-
-```C
-void mrb_hash_set(mrb_state *mrb, mrb_value hash, mrb_value key, mrb_value val);
-```
-
-Sets a keys and values to hashes.
-#### Example
-
-In this example we read from a Ruby file inside C. The Ruby code will print what you pass as an argument
-and what class the passed in value is. This example sets a key and value pair to a hash. In pure Ruby doing this is equivalent to:
-
-```Ruby
-a = {:da_key => 80}
-```
-
-```C
-#include <stdio.h>
-#include <mruby.h>
-#include "mruby/hash.h" // Needs the hash header.
-#include "mruby/compile.h"
-
-int main(int argc, char *argv[])
-{
- mrb_state *mrb = mrb_open();
- if (!mrb) { /* handle error */ }
- mrb_value new_hash; // Declare variable.
- mrb_sym hash_key = mrb_intern_cstr(mrb, "da_key"); // Declare a symbol.
- mrb_int hash_value = 80; // Declare a fixnum value.
- FILE *fp = fopen("test_ext.rb","r");
- new_hash = mrb_hash_new(mrb); // Initialize hash.
- mrb_value obj = mrb_load_file(mrb,fp);
- mrb_hash_set(mrb, new_hash, mrb_symbol_value(hash_key), mrb_fixnum_value(hash_value)); // Set values to hash.
- mrb_funcall(mrb, obj, "method_name", 1, new_hash);
- fclose(fp);
- mrb_close(mrb);
- return 0;
-}
-```
-
-#### test_ext.rb
-
-```Ruby
-class Example_Class
- def method_name(a)
- puts a
- puts a.class
- end
-end
-Example_Class.new
-```
-
-#### Result
-
-After compiling you should get these results.
-
-```Ruby
-{:da_key=>80}
-Hash
-```
-
-### mrb_hash_get
-
-```C
-mrb_value mrb_hash_get(mrb_state *mrb, mrb_value hash, mrb_value key);
-```
-
-Gets a value from a key.
-#### Example
-
-In this example we read from a Ruby file inside C. The Ruby code will print what you pass as an argument
-and what class the passed in value is. This example gets a value from a key. In pure Ruby doing this is equivalent to:
-
-```Ruby
-a = {:da_key => 80}
-a[:da_key]
-```
-
-```C
-#include <stdio.h>
-#include <mruby.h>
-#include "mruby/hash.h" // Needs the hash header.
-#include "mruby/compile.h"
-
-int main(int argc, char *argv[])
-{
- mrb_state *mrb = mrb_open();
- if (!mrb) { /* handle error */ }
- mrb_value new_hash; // Declare variable for new hash object.
- mrb_value get_hash_value; // Declare variable for getting a value from a hash.
- mrb_sym hash_key_a = mrb_intern_cstr(mrb, "da_key1"); // Declare a symbol.
- mrb_sym hash_key_b = mrb_intern_cstr(mrb, "da_key2"); // Declare a symbol.
- mrb_int hash_value_a = 80; // Declare a fixnum value.
- mrb_int hash_value_b = 90; // Declare a fixnum value.
- FILE *fp = fopen("test_ext.rb","r");
- new_hash = mrb_hash_new(mrb); // Initialize hash.
- mrb_value obj = mrb_load_file(mrb,fp);
- mrb_hash_set(mrb, new_hash, mrb_symbol_value(hash_key_a), mrb_fixnum_value(hash_value_a)); // Set values to hash.
- mrb_hash_set(mrb, new_hash, mrb_symbol_value(hash_key_b), mrb_fixnum_value(hash_value_b)); // Set values to hash.
- get_hash_value = mrb_hash_get(mrb, new_hash, mrb_symbol_value(hash_key_b)); // Get value from hash.
- mrb_funcall(mrb, obj, "method_name", 1, get_hash_value);
- fclose(fp);
- mrb_close(mrb);
- return 0;
-}
-```
-
-#### test_ext.rb
-
-```Ruby
-class Example_Class
- def method_name(a)
- puts a
- puts a.class
- end
-end
-Example_Class.new
-```
-
-#### Result
-
-After compiling you should get these results.
-
-```Ruby
-90
-Fixnum
-```
-
-### mrb_hash_delete_key
-
-```C
-mrb_value mrb_hash_delete_key(mrb_state *mrb, mrb_value hash, mrb_value key);
-```
-
-Deletes hash key and value pair.
-#### Example
-
-In this example we read from a Ruby file inside C. The Ruby code will print what you pass as an argument
-and what class the passed in value is. This example deletes hash key and value pair. In pure Ruby doing this is equivalent to:
-
-```Ruby
-a = {:da_key1 => 80,:da_key2 => 90}
-a.delete(:da_key2)
-```
-
-```C
-#include <stdio.h>
-#include <mruby.h>
-#include "mruby/hash.h" // Needs the hash header.
-#include "mruby/compile.h"
-
-int main(int argc, char *argv[])
-{
- mrb_state *mrb = mrb_open();
- if (!mrb) { /* handle error */ }
- mrb_value new_hash; // Declare variable for new hash object.
- mrb_value get_hash_value; // Declare variable for getting a value from a hash.
- mrb_sym hash_key_a = mrb_intern_cstr(mrb, "da_key1"); // Declare a symbol.
- mrb_sym hash_key_b = mrb_intern_cstr(mrb, "da_key2"); // Declare a symbol.
- mrb_sym hash_key_b = mrb_intern_cstr(mrb, "da_key2"); // Declare a symbol.
- mrb_int hash_value_a = 80; // Declare a fixnum value.
- mrb_int hash_value_b = 90; // Declare a fixnum value.
- FILE *fp = fopen("test_ext.rb","r");
- new_hash = mrb_hash_new(mrb); // Initialize hash.
- mrb_value obj = mrb_load_file(mrb,fp);
- mrb_hash_set(mrb, new_hash, mrb_symbol_value(hash_key_a), mrb_fixnum_value(hash_value_a)); // Set values to hash.
- mrb_hash_set(mrb, new_hash, mrb_symbol_value(hash_key_b), mrb_fixnum_value(hash_value_b)); // Set values to hash.
- mrb_funcall(mrb, obj, "method_name", 1, new_hash);
- mrb_hash_delete_key(mrb, new_hash, mrb_symbol_value(hash_key_b));
- mrb_funcall(mrb, obj, "another_method_name", 1, new_hash);
- fclose(fp);
- mrb_close(mrb);
- return 0;
-}
-```
-
-#### test_ext.rb
-
-```Ruby
-class Example_Class
- def method_name(a)
- puts "Hash pre deletion #{a}"
- #puts a.class
- end
- # Show deleted key and value pair.
- def another_method_name(a)
- puts "Hash post deletion #{a}"
- end
-end
-Example_Class.new
-```
-
-#### Result
-
-After compiling you should get these results.
-
-```Ruby
-Hash pre deletion {:da_key1 => 80, :da_key2 => 90}
-Hash post deletion {:da_key1 => 80}
-```
-
-### mrb_hash_keys
-
-```C
-mrb_value mrb_hash_keys(mrb_state *mrb, mrb_value hash);
-```
-
-Gets an array of keys.
-#### Example
-
-In this example we read from a Ruby file inside C. The Ruby code will print what you pass as an argument
-and what class the passed in value is. This example gets an array of keys from a hash.
-
-```C
-#include <stdio.h>
-#include <mruby.h>
-#include "mruby/hash.h" // Needs the hash header.
-#include "mruby/compile.h"
-
-int main(int argc, char *argv[])
-{
- mrb_state *mrb = mrb_open();
- if (!mrb) { /* handle error */ }
- mrb_value new_hash; // Declare variable for new hash object.
- mrb_value get_hash_keys; // Declare variable for getting an array of keys.
- mrb_sym hash_key_a = mrb_intern_cstr(mrb, "da_key1"); // Declare a symbol.
- mrb_sym hash_key_b = mrb_intern_cstr(mrb, "da_key2"); // Declare a symbol.
- mrb_int hash_value_a = 80; // Declare a fixnum value.
- mrb_int hash_value_b = 90; // Declare a fixnum value.
- FILE *fp = fopen("test_ext.rb","r");
- new_hash = mrb_hash_new(mrb); // Initialize hash.
- mrb_value obj = mrb_load_file(mrb,fp);
- mrb_hash_set(mrb, new_hash, mrb_symbol_value(hash_key_a), mrb_fixnum_value(hash_value_a)); // Set values to hash.
- mrb_hash_set(mrb, new_hash, mrb_symbol_value(hash_key_b), mrb_fixnum_value(hash_value_b)); // Set values to hash.
- get_hash_keys = mrb_hash_keys(mrb, new_hash); // get an array of keys.
- mrb_funcall(mrb, obj, "method_name", 1, get_hash_keys);
- fclose(fp);
- mrb_close(mrb);
- return 0;
-}
-```
-
-#### test_ext.rb
-
-```Ruby
-class Example_Class
- def method_name(a)
- puts a
- puts a.class
- end
-end
-Example_Class.new
-```
-
-#### Result
-
-After compiling you should get these results.
-
-```Ruby
-[:da_key1, :da_key2]
-Array
-```
-
-### mrb_hash_clear
-
-```C
-mrb_value mrb_hash_clear(mrb_state *mrb, mrb_value hash);
-```
-
-Clears the hash.
-#### Example
-
-In this example we read from a Ruby file inside C. The Ruby code will print what you pass as an argument
-and what class the passed in value is. This example clears the hash. In pure Ruby doing this is equivalent to:
-
-```Ruby
-a = {:da_key1 => 80,:da_key2 => 90}
-a.clear
-```
-
-```C
-#include <stdio.h>
-#include <mruby.h>
-#include "mruby/hash.h" // Needs the hash header.
-#include "mruby/compile.h"
-
-int main(int argc, char *argv[])
-{
- mrb_state *mrb = mrb_open();
- if (!mrb) { /* handle error */ }
- mrb_value new_hash; // Declare variable for new hash object.
- mrb_value get_hash; // Declare variable for getting a hash.
- mrb_sym hash_key_a = mrb_intern_cstr(mrb, "da_key1"); // Declare a symbol.
- mrb_sym hash_key_b = mrb_intern_cstr(mrb, "da_key2"); // Declare a symbol.
- mrb_int hash_value_a = 80; // Declare a fixnum value.
- mrb_int hash_value_b = 90; // Declare a fixnum value.
- FILE *fp = fopen("test_ext.rb","r");
- new_hash = mrb_hash_new(mrb); // Initialize hash.
- mrb_value obj = mrb_load_file(mrb,fp);
- mrb_hash_set(mrb, new_hash, mrb_symbol_value(hash_key_a), mrb_fixnum_value(hash_value_a)); // Set values to hash.
- mrb_hash_set(mrb, new_hash, mrb_symbol_value(hash_key_b), mrb_fixnum_value(hash_value_b)); // Set values to hash.
- mrb_funcall(mrb, obj, "method_name", 1, new_hash);
- get_hash = mrb_hash_clear(mrb, new_hash);
- mrb_funcall(mrb, obj, "another_method_name", 1, get_hash);
- fclose(fp);
- mrb_close(mrb);
- return 0;
-}
-```
-
-#### test_ext.rb
-
-```Ruby
-class Example_Class
- def method_name(a)
- puts "Hash pre clear #{a}"
- #puts a.class
- end
- # Show clear hash.
- def another_method_name(a)
- puts "Hash post clear #{a}"
- end
-end
-Example_Class.new
-```
-
-#### Result
-
-After compiling you should get these results.
-
-```Ruby
-Hash pre clear {:da_key1 => 80, :da_key2 => 90}
-Hash post clear {}
-```
diff --git a/doc/api/mruby/value.h.md b/doc/api/mruby/value.h.md deleted file mode 100644 index fbf2dadf1..000000000 --- a/doc/api/mruby/value.h.md +++ /dev/null @@ -1,238 +0,0 @@ -### mrb_float_value
-```C
-static inline mrb_value mrb_float_value(struct mrb_state *mrb, mrb_float f)
-```
-
-Returns a float in Ruby.
-
-##### Example
-
-In this example we read from a Ruby file inside C. The Ruby code will print what you pass as an argument
-and what class the passed in value is. In this case we are passing in mrb_float f = 0.09. Alternatively
-double i = 0.09 could also be used.
-
-
-example.c
-```C
-#include <stdio.h>
-#include <mruby.h>
-#include "mruby/compile.h"
-#include "mruby/string.h"
-
-int
-main(void)
-{
- mrb_float f = 0.09;// or double i = 0.09;
- mrb_state *mrb = mrb_open();
- if (!mrb) { /* handle error */ }
- FILE *fp = fopen("test.rb","r");
- mrb_value obj = mrb_load_file(mrb,fp);
- mrb_funcall(mrb, obj, "method_name", 1, mrb_float_value(mrb, f));
- fclose(fp);
- mrb_close(mrb);
- return 0;
-}
-
-```
-
-test.rb
-```Ruby
-class My_Class
- def method_name(s)
- puts s
- puts s.class
- end
-end
-a = My_Class.new
-```
-
-### mrb_fixnum_value
-
-```C
-static inline mrb_value mrb_fixnum_value(mrb_int i)
-```
-
-Returns a fixnum in Ruby.
-
-##### Example
-
-In this example we read from a Ruby file inside C. The Ruby code will print what you pass as an argument
-and what class the passed in value is. In this case we are passing in mrb_int i = 99. Alternativly int i = 99
-could also be used.
-
-
-example.c
-```C
-#include <stdio.h>
-#include <mruby.h>
-#include "mruby/compile.h"
-
-int
-main(void)
-{
- mrb_int i = 99; // or int i = 99;
- mrb_state *mrb = mrb_open();
- if (!mrb) { /* handle error */ }
- FILE *fp = fopen("test.rb","r");
- mrb_value obj = mrb_load_file(mrb,fp);
- mrb_funcall(mrb, obj, "method_name", 1, mrb_fixnum_value(i));
- fclose(fp);
- mrb_close(mrb);
- return 0;
-}
-
-```
-
-test.rb
-```Ruby
-class My_Class
- def method_name(s)
- puts s
- puts s.class
- end
-end
-a = My_Class.new
-```
-
-
-
-### mrb_nil_value
-
-```C
-static inline mrb_value mrb_nil_value(void)
-```
-
-Returns nil in Ruby.
-
-##### Example
-
-In this example we read from a Ruby file inside C. The Ruby code will print what you pass as an argument
-and what class the passed in value is. In this case we are passing in nothing and we will get NillClass.
-
-
-example.c
-```C
-#include <stdio.h>
-#include <mruby.h>
-#include "mruby/compile.h"
-
-int
-main(void)
-{
- mrb_state *mrb = mrb_open();
- if (!mrb) { /* handle error */ }
- FILE *fp = fopen("test.rb","r");
- mrb_value obj = mrb_load_file(mrb,fp);
- mrb_funcall(mrb, obj, "method_name", 1, mrb_nil_value());
- fclose(fp);
- mrb_close(mrb);
- return 0;
-}
-
-```
-
-test.rb
-```Ruby
-class My_Class
- def method_name(s)
- puts s
- puts s.class
- end
-end
-a = My_Class.new
-```
-
-
-### mrb_false_value
-
-```C
-static inline mrb_value mrb_false_value(void)
-```
-
-Returns false in Ruby.
-
-##### Example
-
-In this example we read from a Ruby file inside C. The Ruby code will print what you pass as an argument
-and what class the passed in value is. In this case we are passing in nothing and we will get FalseClass.
-
-
-example.c
-```C
-#include <stdio.h>
-#include <mruby.h>
-#include "mruby/compile.h"
-
-int
-main(void)
-{
- mrb_state *mrb = mrb_open();
- if (!mrb) { /* handle error */ }
- FILE *fp = fopen("test.rb","r");
- mrb_value obj = mrb_load_file(mrb,fp);
- mrb_funcall(mrb, obj, "method_name", 1, mrb_false_value());
- fclose(fp);
- mrb_close(mrb);
- return 0;
-}
-
-```
-
-test.rb
-```Ruby
-class My_Class
- def method_name(s)
- puts s
- puts s.class
- end
-end
-a = My_Class.new
-```
-
-
-
-### mrb_true_value
-
-```C
-static inline mrb_value mrb_true_value(void)
-```
-
-Returns true in Ruby.
-
-##### Example
-
-In this example we read from a Ruby file inside C. The Ruby code will print what you pass as an argument
-and what class the passed in value is. In this case we are passing in nothing and we will get TrueClass.
-
-
-example.c
-```C
-#include <stdio.h>
-#include <mruby.h>
-#include "mruby/compile.h"
-
-int
-main(void)
-{
- mrb_state *mrb = mrb_open();
- if (!mrb) { /* handle error */ }
- FILE *fp = fopen("test.rb","r");
- mrb_value obj = mrb_load_file(mrb,fp);
- mrb_funcall(mrb, obj, "method_name", 1, mrb_true_value());
- fclose(fp);
- mrb_close(mrb);
- return 0;
-}
-
-```
-
-test.rb
-```Ruby
-class My_Class
- def method_name(s)
- puts s
- puts s.class
- end
-end
-a = My_Class.new
-```
diff --git a/doc/compile/README.md b/doc/guides/compile.md index 8e91e7546..9081b1298 100644 --- a/doc/compile/README.md +++ b/doc/guides/compile.md @@ -75,7 +75,7 @@ toolchain :visualcpp Toolchain configuration for Android. ```ruby -toolchain :androideabi +toolchain :androidndk ``` Requires the custom standalone Android NDK and the toolchain path diff --git a/doc/debugger/README.md b/doc/guides/debugger.md index a13e91cec..a13e91cec 100755..100644 --- a/doc/debugger/README.md +++ b/doc/guides/debugger.md diff --git a/doc/guides/gc-arena-howto.md b/doc/guides/gc-arena-howto.md new file mode 100644 index 000000000..f2a0448bd --- /dev/null +++ b/doc/guides/gc-arena-howto.md @@ -0,0 +1,171 @@ +# How to use `mrb_gc_arena_save()`/`mrb_gc_arena_restore()` + +This is basically English translation of [Matz's blog post](http://www.rubyist.net/~matz/20130731.html) written in Japanese. +Some parts are updated to reflect recent changes. + +When you are extending mruby using C language, you may encounter +mysterious "arena overflow error" or memory leak or very slow +execution speed. This is an error indicating overflow of "GC arena" +implementing "conservative GC". + +GC (garbage collector) must ensure that object is "alive", in other +words, that it is referenced by somewhere from program. This can be +determined by checking that that object can be directly or indirectly +referenced by root. The local variables, global variables and +constants etc are root. + +If program execution is performed inside mruby VM, there is nothing to +worry about because GC can access all roots owned by VM. + +The problem arises when executing C functions. The object referenced +by C variable is also "alive", but mruby GC cannot aware of this, so +it might mistakenly recognize the objects referenced by only C +variables as dead. + +It is a fatal bug for GC to collect live objects. + +In CRuby, we scan C stack area, and use C variable as root to check +whether object is alive or not. Of course, because we are accessing C +stack just as memory region, we never know it is an integer or a +pointer. We workaround this by assuming that if it looks like a +pointer, then assume it as a pointer. We call it "conservative". + +By the way, CRuby's "conservative GC" has some problems. + +Its biggest problem is we have no way to access to the stack area in +portable way. Therefore, we cannot use this method if we'd like to +implement highly portable runtime, like mruby. + +So we came up an another plan to implement "conservative GC" in mruby. + +Again, the problem is that there is an object which was created in C +function, and is not referenced by Ruby world, and cannot be treated +as garbage. + +In mruby, we recognize all objects created in C function are alive. +Then we have no problem such as recognizing live object as dead. + +This means that because we cannot collect truly dead object, we may +get a little bit less efficiency, but GC itself is highly portable. +We can say goodbye to the problem that GC deletes live objects due to +optimization which sometimes occurs in CRuby. + +According to this idea, we have a table, called "GC arena", which +remembers objects created in C function. The arena is stack +structure, when C function execution is returned to mruby VM, all +objects registered in the arena are popped. + +This works very well, but GC arena causes another problem. "arena +overflow error" or memory leak. + +As of this writing, mruby automatically extend arena to remember +objects (See `MRB_GC_FIXED_ARENA` and `MRB_GC_ARENA_SIZE` in +doc/mrbconf/README.md). If you keep creating objects in C functions, +it increases memory usage, since GC never kick in. This memory usage +may look like memory leak, and also makes execution slower. + +With the build time configuration, you can limit the maximum size of +arena (e.g., 100). Then if you create many objects, arena overflows, +thus you will get "arena overflow error". + +To workaround these problems, we have `mrb_gc_arena_save()` and +`mrb_gc_arena_restore()` functions. + +`int mrb_gc_arena_save(mrb)` returns the current position of the stack +top of GC arena, and `void mrb_gc_arena_restore(mrb, idx)` sets the +stack top position to back to given idx. We uses them like so: + +```c +int arena_idx = mrb_gc_arena_save(mrb); + +...create objects... +mrb_gc_arena_restore(mrb, arena_idx); + +``` + +In mruby, C function call are surrounded by this save/restore, but we +can further optimize memory usage by surrounding save/restore, and can +avoid arena overflow. + +Let's take a real example. Here is the source code of `Array#inspect`: + +```c +static mrb_value +inspect_ary(mrb_state *mrb, mrb_value ary, mrb_value list) +{ + mrb_int i; + mrb_value s, arystr; + char head[] = { '[' }; + char sep[] = { ',', ' ' }; + char tail[] = { ']' }; + + /* check recursive */ + for(i=0; i<RARRAY_LEN(list); i++) { + if (mrb_obj_equal(mrb, ary, RARRAY_PTR(list)[i])) { + return mrb_str_new(mrb, "[...]", 5); + } + } + + mrb_ary_push(mrb, list, ary); + + arystr = mrb_str_buf_new(mrb, 64); + mrb_str_buf_cat(mrb, arystr, head, sizeof(head)); + + for(i=0; i<RARRAY_LEN(ary); i++) { + int ai = mrb_gc_arena_save(mrb); + + if (i > 0) { + mrb_str_buf_cat(mrb, arystr, sep, sizeof(sep)); + } + if (mrb_array_p(RARRAY_PTR(ary)[i])) { + s = inspect_ary(mrb, RARRAY_PTR(ary)[i], list); + } + else { + s = mrb_inspect(mrb, RARRAY_PTR(ary)[i]); + } + mrb_str_buf_cat(mrb, arystr, RSTRING_PTR(s), RSTRING_LEN(s)); + mrb_gc_arena_restore(mrb, ai); + } + + mrb_str_buf_cat(mrb, arystr, tail, sizeof(tail)); + mrb_ary_pop(mrb, list); + + return arystr; +} +``` + +This is a real example, so a little bit complicated, so bear with me. +The essence of `Array#inspect` is that after stringifying each element +of array using `inspect` method, we join them together so that we can +get `inspect` representation of entire array. + +After the `inspect` representation of entire array is created, we no +longer require the individual string representation. That means that +we don't have to register these temporal objects into GC arena. + +Therefore, in `ary_inspect()` function, we do: + +* save the position of the stack top using `mrb_gc_arena_save()`. +* get `inspect` representation of each element. +* append it to the constructing entire `inspect` representation of array. +* restore stack top position using `mrb_gc_arena_restore()`. + +to keep the arena size small. + +Please note that the final `inspect` representation of entire array +was created before the call of `mrb_gc_arena_restore()`. Otherwise, +required temporal object may be deleted by GC. + +We may have a usecase that after creating many temporal objects, we'd +like to keep some of them. In this case, we cannot use the same idea +in `ary_inspect()` like appending objects to existing one. Instead, +after `mrb_gc_arena_restore()`, we register back the objects we'd like +to keep to the arena using `mrb_gc_protect(mrb, obj)`. Use +`mrb_gc_protect()` with caution because its usage could lead to arena +overflow error. + +We also have to mention that when `mrb_funcall` is called in top +level, its return value is also registered to GC arena, so calling +them repeatedly eventually lead to arena overflow error. Use +`mrb_gc_arena_save()` and `mrb_gc_arena_restore()` or possible use of +`mrb_gc_protect()` to workaround this. diff --git a/doc/mrbconf/README.md b/doc/guides/mrbconf.md index dfff41898..dfff41898 100644 --- a/doc/mrbconf/README.md +++ b/doc/guides/mrbconf.md diff --git a/doc/mrbgems/README.md b/doc/guides/mrbgems.md index f75231f71..f75231f71 100644 --- a/doc/mrbgems/README.md +++ b/doc/guides/mrbgems.md diff --git a/doc/language/Core.md b/doc/language/Core.md deleted file mode 100644 index 390581f87..000000000 --- a/doc/language/Core.md +++ /dev/null @@ -1,1590 +0,0 @@ -# Core Classes - -## Array - -ISO Code | Mixins | Source File ---- | --- | --- -15.2.12 | n/a | src/array.c - -### Class Methods - -#### [] - -ISO Code | Source File | C Function ---- | --- | --- -15.2.12.4.1 | src/array.c | mrb_ary_s_create - -### Methods - -#### * - -ISO Code | Source File | C Function ---- | --- | --- -15.2.12.5.2 | src/array.c | mrb_ary_times - -#### + - -ISO Code | Source File | C Function ---- | --- | --- -15.2.12.5.1 | src/array.c | mrb_ary_plus - -#### << - -ISO Code | Source File | C Function ---- | --- | --- -15.2.12.5.3 | src/array.c | mrb_ary_push_m - -#### [] - -ISO Code | Source File | C Function ---- | --- | --- -15.2.12.5.4 | src/array.c | mrb_ary_aget - -#### []= - -ISO Code | Source File | C Function ---- | --- | --- -15.2.12.5.5 | src/array.c | mrb_ary_aset - -#### __ary_cmp - -ISO Code | Source File | C Function ---- | --- | --- -n/a | src/array.c | mrb_ary_cmp - -#### __ary_eq - -ISO Code | Source File | C Function ---- | --- | --- -n/a | src/array.c | mrb_ary_eq - -#### clear - -ISO Code | Source File | C Function ---- | --- | --- -15.2.12.5.6 | src/array.c | mrb_ary_clear - -#### concat - -ISO Code | Source File | C Function ---- | --- | --- -15.2.12.5.8 | src/array.c | mrb_ary_concat_m - -#### delete_at - -ISO Code | Source File | C Function ---- | --- | --- -15.2.12.5.9 | src/array.c | mrb_ary_delete_at - -#### empty? - -ISO Code | Source File | C Function ---- | --- | --- -15.2.12.5.12 | src/array.c | mrb_ary_empty_p - -#### first - -ISO Code | Source File | C Function ---- | --- | --- -15.2.12.5.13 | src/array.c | mrb_ary_first - -#### index - -ISO Code | Source File | C Function ---- | --- | --- -15.2.12.5.14 | src/array.c | mrb_ary_index_m - -#### initialize_copy - -ISO Code | Source File | C Function ---- | --- | --- -15.2.12.5.16 | src/array.c | mrb_ary_replace_m - -#### join - -ISO Code | Source File | C Function ---- | --- | --- -15.2.12.5.17 | src/array.c | mrb_ary_join_m - -#### last - -ISO Code | Source File | C Function ---- | --- | --- -15.2.12.5.18 | src/array.c | mrb_ary_last - -#### length - -ISO Code | Source File | C Function ---- | --- | --- -15.2.12.5.19 | src/array.c | mrb_ary_size - -#### pop - -ISO Code | Source File | C Function ---- | --- | --- -15.2.12.5.21 | src/array.c | mrb_ary_pop - -#### push - -ISO Code | Source File | C Function ---- | --- | --- -15.2.12.5.22 | src/array.c | mrb_ary_push_m - -#### replace - -ISO Code | Source File | C Function ---- | --- | --- -15.2.12.5.23 | src/array.c | mrb_ary_replace_m - -#### reverse - -ISO Code | Source File | C Function ---- | --- | --- -15.2.12.5.24 | src/array.c | mrb_ary_reverse - -#### reverse! - -ISO Code | Source File | C Function ---- | --- | --- -15.2.12.5.25 | src/array.c | mrb_ary_reverse_bang - -#### rindex - -ISO Code | Source File | C Function ---- | --- | --- -15.2.12.5.26 | src/array.c | mrb_ary_rindex_m - -#### shift - -ISO Code | Source File | C Function ---- | --- | --- -15.2.12.5.27 | src/array.c | mrb_ary_shift - -#### size - -ISO Code | Source File | C Function ---- | --- | --- -15.2.12.5.28 | src/array.c | mrb_ary_size - -#### slice - -ISO Code | Source File | C Function ---- | --- | --- -15.2.12.5.29 | src/array.c | mrb_ary_aget - -#### unshift - -ISO Code | Source File | C Function ---- | --- | --- -15.2.12.5.30 | src/array.c | mrb_ary_unshift_m - -## Exception - -ISO Code | Mixins | Source File ---- | --- | --- -15.2.22 | n/a | src/error.c - -### Class Methods - -#### exception - -ISO Code | Source File | C Function ---- | --- | --- -n/a | src/class.c | mrb_instance_new - -### Methods - -#### == - -ISO Code | Source File | C Function ---- | --- | --- -n/a | src/error.c | exc_equal - -#### backtrace - -ISO Code | Source File | C Function ---- | --- | --- -n/a | src/backtrace.c | mrb_exc_backtrace - -#### exception - -ISO Code | Source File | C Function ---- | --- | --- -n/a | src/error.c | exc_exception - -#### initialize - -ISO Code | Source File | C Function ---- | --- | --- -n/a | src/error.c | exc_initialize - -#### inspect - -ISO Code | Source File | C Function ---- | --- | --- -n/a | src/error.c | exc_inspect - -#### message - -ISO Code | Source File | C Function ---- | --- | --- -n/a | src/error.c | exc_message - -#### to_s - -ISO Code | Source File | C Function ---- | --- | --- -n/a | src/error.c | exc_to_s - -## FalseClass - -ISO Code | Mixins | Source File ---- | --- | --- -n/a | n/a | src/object.c - -### Methods - -#### & - -ISO Code | Source File | C Function ---- | --- | --- -15.2.6.3.1 | src/object.c | false_and - -#### ^ - -ISO Code | Source File | C Function ---- | --- | --- -15.2.6.3.2 | src/object.c | false_xor - -#### inspect - -ISO Code | Source File | C Function ---- | --- | --- -n/a | src/object.c | false_to_s - -#### to_s - -ISO Code | Source File | C Function ---- | --- | --- -15.2.6.3.3 | src/object.c | false_to_s - -#### | - -ISO Code | Source File | C Function ---- | --- | --- -15.2.6.3.4 | src/object.c | false_or - -## Fixnum - -ISO Code | Mixins | Source File ---- | --- | --- -n/a | n/a | src/numeric.c - -### Methods - -#### % - -ISO Code | Source File | C Function ---- | --- | --- -15.2.8.3.5 | src/numeric.c | fix_mod - -#### & - -ISO Code | Source File | C Function ---- | --- | --- -15.2.8.3.9 | src/numeric.c | fix_and - -#### * - -ISO Code | Source File | C Function ---- | --- | --- -15.2.8.3.3 | src/numeric.c | fix_mul - -#### + - -ISO Code | Source File | C Function ---- | --- | --- -15.2.8.3.1 | src/numeric.c | fix_plus - -#### - - -ISO Code | Source File | C Function ---- | --- | --- -15.2.8.3.2 | src/numeric.c | fix_minus - -#### << - -ISO Code | Source File | C Function ---- | --- | --- -15.2.8.3.12 | src/numeric.c | fix_lshift - -#### == - -ISO Code | Source File | C Function ---- | --- | --- -15.2.8.3.7 | src/numeric.c | fix_equal - -#### >> - -ISO Code | Source File | C Function ---- | --- | --- -15.2.8.3.13 | src/numeric.c | fix_rshift - -#### ^ - -ISO Code | Source File | C Function ---- | --- | --- -15.2.8.3.11 | src/numeric.c | fix_xor - -#### divmod - -ISO Code | Source File | C Function ---- | --- | --- -15.2.8.3.30 | src/numeric.c | fix_divmod - -#### eql? - -ISO Code | Source File | C Function ---- | --- | --- -15.2.8.3.16 | src/numeric.c | fix_eql - -#### hash - -ISO Code | Source File | C Function ---- | --- | --- -15.2.8.3.18 | src/numeric.c | flo_hash - -#### inspect - -ISO Code | Source File | C Function ---- | --- | --- -n/a | src/numeric.c | fix_to_s - -#### to_f - -ISO Code | Source File | C Function ---- | --- | --- -15.2.8.3.23 | src/numeric.c | fix_to_f - -#### to_s - -ISO Code | Source File | C Function ---- | --- | --- -15.2.8.3.25 | src/numeric.c | fix_to_s - -#### | - -ISO Code | Source File | C Function ---- | --- | --- -15.2.8.3.10 | src/numeric.c | fix_or - -#### ~ - -ISO Code | Source File | C Function ---- | --- | --- -15.2.8.3.8 | src/numeric.c | fix_rev - -## Float - -ISO Code | Mixins | Source File ---- | --- | --- -15.2.9 | n/a | src/numeric.c - -### Methods - -#### % - -ISO Code | Source File | C Function ---- | --- | --- -15.2.9.3.5 | src/numeric.c | flo_mod - -#### * - -ISO Code | Source File | C Function ---- | --- | --- -15.2.9.3.3 | src/numeric.c | flo_mul - -#### + - -ISO Code | Source File | C Function ---- | --- | --- -15.2.9.3.1 | src/numeric.c | flo_plus - -#### - - -ISO Code | Source File | C Function ---- | --- | --- -15.2.9.3.2 | src/numeric.c | flo_minus - -#### == - -ISO Code | Source File | C Function ---- | --- | --- -15.2.9.3.7 | src/numeric.c | flo_eq - -#### ceil - -ISO Code | Source File | C Function ---- | --- | --- -15.2.9.3.8 | src/numeric.c | flo_ceil - -#### divmod - -ISO Code | Source File | C Function ---- | --- | --- -n/a | src/numeric.c | flo_divmod - -#### eql? - -ISO Code | Source File | C Function ---- | --- | --- -15.2.8.3.16 | src/numeric.c | flo_eql - -#### finite? - -ISO Code | Source File | C Function ---- | --- | --- -15.2.9.3.9 | src/numeric.c | flo_finite_p - -#### floor - -ISO Code | Source File | C Function ---- | --- | --- -15.2.9.3.10 | src/numeric.c | flo_floor - -#### infinite? - -ISO Code | Source File | C Function ---- | --- | --- -15.2.9.3.11 | src/numeric.c | flo_infinite_p - -#### inspect - -ISO Code | Source File | C Function ---- | --- | --- -n/a | src/numeric.c | flo_to_s - -#### nan? - -ISO Code | Source File | C Function ---- | --- | --- -n/a | src/numeric.c | flo_nan_p - -#### round - -ISO Code | Source File | C Function ---- | --- | --- -15.2.9.3.12 | src/numeric.c | flo_round - -#### to_f - -ISO Code | Source File | C Function ---- | --- | --- -15.2.9.3.13 | src/numeric.c | flo_to_f - -#### to_i - -ISO Code | Source File | C Function ---- | --- | --- -15.2.9.3.14 | src/numeric.c | flo_truncate - -#### to_int - -ISO Code | Source File | C Function ---- | --- | --- -n/a | src/numeric.c | flo_truncate - -#### to_s - -ISO Code | Source File | C Function ---- | --- | --- -15.2.9.3.16 | src/numeric.c | flo_to_s - -#### truncate - -ISO Code | Source File | C Function ---- | --- | --- -15.2.9.3.15 | src/numeric.c | flo_truncate - -## Hash - -ISO Code | Mixins | Source File ---- | --- | --- -15.2.13 | n/a | src/hash.c - -### Methods - -#### [] - -ISO Code | Source File | C Function ---- | --- | --- -15.2.13.4.2 | src/hash.c | mrb_hash_aget - -#### []= - -ISO Code | Source File | C Function ---- | --- | --- -15.2.13.4.3 | src/hash.c | mrb_hash_aset - -#### __delete - -ISO Code | Source File | C Function ---- | --- | --- -15.2.13.4.8 | src/hash.c | mrb_hash_delete - -#### clear - -ISO Code | Source File | C Function ---- | --- | --- -15.2.13.4.4 | src/hash.c | mrb_hash_clear - -#### default - -ISO Code | Source File | C Function ---- | --- | --- -15.2.13.4.5 | src/hash.c | mrb_hash_default - -#### default= - -ISO Code | Source File | C Function ---- | --- | --- -15.2.13.4.6 | src/hash.c | mrb_hash_set_default - -#### default_proc - -ISO Code | Source File | C Function ---- | --- | --- -15.2.13.4.7 | src/hash.c | mrb_hash_default_proc - -#### default_proc= - -ISO Code | Source File | C Function ---- | --- | --- -15.2.13.4.7 | src/hash.c | mrb_hash_set_default_proc - -#### dup - -ISO Code | Source File | C Function ---- | --- | --- -n/a | src/hash.c | mrb_hash_dup - -#### empty? - -ISO Code | Source File | C Function ---- | --- | --- -15.2.13.4.12 | src/hash.c | mrb_hash_empty_p - -#### has_key? - -ISO Code | Source File | C Function ---- | --- | --- -15.2.13.4.13 | src/hash.c | mrb_hash_has_key - -#### has_value? - -ISO Code | Source File | C Function ---- | --- | --- -15.2.13.4.14 | src/hash.c | mrb_hash_has_value - -#### include? - -ISO Code | Source File | C Function ---- | --- | --- -15.2.13.4.15 | src/hash.c | mrb_hash_has_key - -#### initialize - -ISO Code | Source File | C Function ---- | --- | --- -15.2.13.4.16 | src/hash.c | mrb_hash_init - -#### key? - -ISO Code | Source File | C Function ---- | --- | --- -15.2.13.4.18 | src/hash.c | mrb_hash_has_key - -#### keys - -ISO Code | Source File | C Function ---- | --- | --- -15.2.13.4.19 | src/hash.c | mrb_hash_keys - -#### length - -ISO Code | Source File | C Function ---- | --- | --- -15.2.13.4.20 | src/hash.c | mrb_hash_size_m - -#### member? - -ISO Code | Source File | C Function ---- | --- | --- -15.2.13.4.21 | src/hash.c | mrb_hash_has_key - -#### shift - -ISO Code | Source File | C Function ---- | --- | --- -15.2.13.4.24 | src/hash.c | mrb_hash_shift - -#### size - -ISO Code | Source File | C Function ---- | --- | --- -15.2.13.4.25 | src/hash.c | mrb_hash_size_m - -#### store - -ISO Code | Source File | C Function ---- | --- | --- -15.2.13.4.26 | src/hash.c | mrb_hash_aset - -#### to_hash - -ISO Code | Source File | C Function ---- | --- | --- -15.2.13.4.29 | src/hash.c | mrb_hash_to_hash - -#### value? - -ISO Code | Source File | C Function ---- | --- | --- -15.2.13.4.27 | src/hash.c | mrb_hash_has_value - -#### values - -ISO Code | Source File | C Function ---- | --- | --- -15.2.13.4.28 | src/hash.c | mrb_hash_values - -## Integer - -ISO Code | Mixins | Source File ---- | --- | --- -15.2.8 | n/a | src/numeric.c - -### Methods - -#### to_i - -ISO Code | Source File | C Function ---- | --- | --- -15.2.8.3.24 | src/numeric.c | int_to_i - -#### to_int - -ISO Code | Source File | C Function ---- | --- | --- -n/a | src/numeric.c | int_to_i - -## NilClass - -ISO Code | Mixins | Source File ---- | --- | --- -n/a | n/a | src/object.c - -### Methods - -#### & - -ISO Code | Source File | C Function ---- | --- | --- -15.2.4.3.1 | src/object.c | false_and - -#### ^ - -ISO Code | Source File | C Function ---- | --- | --- -15.2.4.3.2 | src/object.c | false_xor - -#### inspect - -ISO Code | Source File | C Function ---- | --- | --- -n/a | src/object.c | nil_inspect - -#### nil? - -ISO Code | Source File | C Function ---- | --- | --- -15.2.4.3.4 | src/object.c | mrb_true - -#### to_s - -ISO Code | Source File | C Function ---- | --- | --- -15.2.4.3.5 | src/object.c | nil_to_s - -#### | - -ISO Code | Source File | C Function ---- | --- | --- -15.2.4.3.3 | src/object.c | false_or - -## Numeric - -ISO Code | Mixins | Source File ---- | --- | --- -15.2.7 | n/a | src/numeric.c - -### Methods - -#### ** - -ISO Code | Source File | C Function ---- | --- | --- -n/a | src/numeric.c | num_pow - -#### / - -ISO Code | Source File | C Function ---- | --- | --- -15.2.8.3.4 | src/numeric.c | num_div - -#### <=> - -ISO Code | Source File | C Function ---- | --- | --- -15.2.9.3.6 | src/numeric.c | num_cmp - -#### quo - -ISO Code | Source File | C Function ---- | --- | --- -15.2.7.4.5 | src/numeric.c | num_div - -## Proc - -ISO Code | Mixins | Source File ---- | --- | --- -15.2.17 | n/a | src/proc.c - -### Methods - -#### arity - -ISO Code | Source File | C Function ---- | --- | --- -n/a | src/proc.c | mrb_proc_arity - -#### initialize - -ISO Code | Source File | C Function ---- | --- | --- -n/a | src/proc.c | mrb_proc_initialize - -#### initialize_copy - -ISO Code | Source File | C Function ---- | --- | --- -n/a | src/proc.c | mrb_proc_init_copy - -## Range - -ISO Code | Mixins | Source File ---- | --- | --- -15.2.14 | n/a | src/range.c - -### Methods - -#### == - -ISO Code | Source File | C Function ---- | --- | --- -15.2.14.4.1 | src/range.c | mrb_range_eq - -#### === - -ISO Code | Source File | C Function ---- | --- | --- -15.2.14.4.2 | src/range.c | mrb_range_include - -#### begin - -ISO Code | Source File | C Function ---- | --- | --- -15.2.14.4.3 | src/range.c | mrb_range_beg - -#### end - -ISO Code | Source File | C Function ---- | --- | --- -15.2.14.4.5 | src/range.c | mrb_range_end - -#### eql? - -ISO Code | Source File | C Function ---- | --- | --- -15.2.14.4.14 | src/range.c | range_eql - -#### exclude_end? - -ISO Code | Source File | C Function ---- | --- | --- -15.2.14.4.6 | src/range.c | mrb_range_excl - -#### first - -ISO Code | Source File | C Function ---- | --- | --- -15.2.14.4.7 | src/range.c | mrb_range_beg - -#### include? - -ISO Code | Source File | C Function ---- | --- | --- -15.2.14.4.8 | src/range.c | mrb_range_include - -#### initialize - -ISO Code | Source File | C Function ---- | --- | --- -15.2.14.4.9 | src/range.c | mrb_range_initialize - -#### initialize_copy - -ISO Code | Source File | C Function ---- | --- | --- -15.2.14.4.15 | src/range.c | range_initialize_copy - -#### inspect - -ISO Code | Source File | C Function ---- | --- | --- -15.2.14.4.13 | src/range.c | range_inspect - -#### last - -ISO Code | Source File | C Function ---- | --- | --- -15.2.14.4.10 | src/range.c | mrb_range_end - -#### member? - -ISO Code | Source File | C Function ---- | --- | --- -15.2.14.4.11 | src/range.c | mrb_range_include - -#### to_s - -ISO Code | Source File | C Function ---- | --- | --- -15.2.14.4.12 | src/range.c | range_to_s - -## RuntimeError - -ISO Code | Mixins | Source File ---- | --- | --- -15.2.28 | n/a | src/error.c - -## ScriptError - -ISO Code | Mixins | Source File ---- | --- | --- -15.2.37 | n/a | src/error.c - -## StandardError - -ISO Code | Mixins | Source File ---- | --- | --- -15.2.23 | n/a | src/error.c - -## String - -ISO Code | Mixins | Source File ---- | --- | --- -15.2.10 | n/a | src/string.c - -### Methods - -#### * - -ISO Code | Source File | C Function ---- | --- | --- -15.2.10.5.5 | src/string.c | mrb_str_times - -#### + - -ISO Code | Source File | C Function ---- | --- | --- -15.2.10.5.4 | src/string.c | mrb_str_plus_m - -#### <=> - -ISO Code | Source File | C Function ---- | --- | --- -15.2.10.5.1 | src/string.c | mrb_str_cmp_m - -#### == - -ISO Code | Source File | C Function ---- | --- | --- -15.2.10.5.2 | src/string.c | mrb_str_equal_m - -#### [] - -ISO Code | Source File | C Function ---- | --- | --- -15.2.10.5.6 | src/string.c | mrb_str_aref_m - -#### bytes - -ISO Code | Source File | C Function ---- | --- | --- -n/a | src/string.c | mrb_str_bytes - -#### bytesize - -ISO Code | Source File | C Function ---- | --- | --- -n/a | src/string.c | mrb_str_size - -#### capitalize - -ISO Code | Source File | C Function ---- | --- | --- -15.2.10.5.7 | src/string.c | mrb_str_capitalize - -#### capitalize! - -ISO Code | Source File | C Function ---- | --- | --- -15.2.10.5.8 | src/string.c | mrb_str_capitalize_bang - -#### chomp - -ISO Code | Source File | C Function ---- | --- | --- -15.2.10.5.9 | src/string.c | mrb_str_chomp - -#### chomp! - -ISO Code | Source File | C Function ---- | --- | --- -15.2.10.5.10 | src/string.c | mrb_str_chomp_bang - -#### chop - -ISO Code | Source File | C Function ---- | --- | --- -15.2.10.5.11 | src/string.c | mrb_str_chop - -#### chop! - -ISO Code | Source File | C Function ---- | --- | --- -15.2.10.5.12 | src/string.c | mrb_str_chop_bang - -#### downcase - -ISO Code | Source File | C Function ---- | --- | --- -15.2.10.5.13 | src/string.c | mrb_str_downcase - -#### downcase! - -ISO Code | Source File | C Function ---- | --- | --- -15.2.10.5.14 | src/string.c | mrb_str_downcase_bang - -#### empty? - -ISO Code | Source File | C Function ---- | --- | --- -15.2.10.5.16 | src/string.c | mrb_str_empty_p - -#### eql? - -ISO Code | Source File | C Function ---- | --- | --- -15.2.10.5.17 | src/string.c | mrb_str_eql - -#### hash - -ISO Code | Source File | C Function ---- | --- | --- -15.2.10.5.20 | src/string.c | mrb_str_hash_m - -#### include? - -ISO Code | Source File | C Function ---- | --- | --- -15.2.10.5.21 | src/string.c | mrb_str_include - -#### index - -ISO Code | Source File | C Function ---- | --- | --- -15.2.10.5.22 | src/string.c | mrb_str_index_m - -#### initialize - -ISO Code | Source File | C Function ---- | --- | --- -15.2.10.5.23 | src/string.c | mrb_str_init - -#### initialize_copy - -ISO Code | Source File | C Function ---- | --- | --- -15.2.10.5.24 | src/string.c | mrb_str_replace - -#### inspect - -ISO Code | Source File | C Function ---- | --- | --- -15.2.10.5.46 | src/string.c | mrb_str_inspect - -#### intern - -ISO Code | Source File | C Function ---- | --- | --- -15.2.10.5.25 | src/string.c | mrb_str_intern - -#### length - -ISO Code | Source File | C Function ---- | --- | --- -15.2.10.5.26 | src/string.c | mrb_str_size - -#### replace - -ISO Code | Source File | C Function ---- | --- | --- -15.2.10.5.28 | src/string.c | mrb_str_replace - -#### reverse - -ISO Code | Source File | C Function ---- | --- | --- -15.2.10.5.29 | src/string.c | mrb_str_reverse - -#### reverse! - -ISO Code | Source File | C Function ---- | --- | --- -15.2.10.5.30 | src/string.c | mrb_str_reverse_bang - -#### rindex - -ISO Code | Source File | C Function ---- | --- | --- -15.2.10.5.31 | src/string.c | mrb_str_rindex_m - -#### size - -ISO Code | Source File | C Function ---- | --- | --- -15.2.10.5.33 | src/string.c | mrb_str_size - -#### slice - -ISO Code | Source File | C Function ---- | --- | --- -15.2.10.5.34 | src/string.c | mrb_str_aref_m - -#### split - -ISO Code | Source File | C Function ---- | --- | --- -15.2.10.5.35 | src/string.c | mrb_str_split_m - -#### to_f - -ISO Code | Source File | C Function ---- | --- | --- -15.2.10.5.38 | src/string.c | mrb_str_to_f - -#### to_i - -ISO Code | Source File | C Function ---- | --- | --- -15.2.10.5.39 | src/string.c | mrb_str_to_i - -#### to_s - -ISO Code | Source File | C Function ---- | --- | --- -15.2.10.5.40 | src/string.c | mrb_str_to_s - -#### to_str - -ISO Code | Source File | C Function ---- | --- | --- -n/a | src/string.c | mrb_str_to_s - -#### to_sym - -ISO Code | Source File | C Function ---- | --- | --- -15.2.10.5.41 | src/string.c | mrb_str_intern - -#### upcase - -ISO Code | Source File | C Function ---- | --- | --- -15.2.10.5.42 | src/string.c | mrb_str_upcase - -#### upcase! - -ISO Code | Source File | C Function ---- | --- | --- -15.2.10.5.43 | src/string.c | mrb_str_upcase_bang - -## Symbol - -ISO Code | Mixins | Source File ---- | --- | --- -15.2.11 | n/a | src/symbol.c - -### Methods - -#### <=> - -ISO Code | Source File | C Function ---- | --- | --- -n/a | src/symbol.c | sym_cmp - -#### === - -ISO Code | Source File | C Function ---- | --- | --- -15.2.11.3.1 | src/symbol.c | sym_equal - -#### id2name - -ISO Code | Source File | C Function ---- | --- | --- -15.2.11.3.2 | src/symbol.c | mrb_sym_to_s - -#### inspect - -ISO Code | Source File | C Function ---- | --- | --- -15.2.11.3.5 | src/symbol.c | sym_inspect - -#### to_s - -ISO Code | Source File | C Function ---- | --- | --- -15.2.11.3.3 | src/symbol.c | mrb_sym_to_s - -#### to_sym - -ISO Code | Source File | C Function ---- | --- | --- -15.2.11.3.4 | src/symbol.c | sym_to_sym - -## SyntaxError - -ISO Code | Mixins | Source File ---- | --- | --- -15.2.38 | n/a | src/error.c - -## TrueClass - -ISO Code | Mixins | Source File ---- | --- | --- -n/a | n/a | src/object.c - -### Methods - -#### & - -ISO Code | Source File | C Function ---- | --- | --- -15.2.5.3.1 | src/object.c | true_and - -#### ^ - -ISO Code | Source File | C Function ---- | --- | --- -15.2.5.3.2 | src/object.c | true_xor - -#### inspect - -ISO Code | Source File | C Function ---- | --- | --- -n/a | src/object.c | true_to_s - -#### to_s - -ISO Code | Source File | C Function ---- | --- | --- -15.2.5.3.3 | src/object.c | true_to_s - -#### | - -ISO Code | Source File | C Function ---- | --- | --- -15.2.5.3.4 | src/object.c | true_or - -# Core Modules - -## Comparable - -ISO Code | Source File ---- | --- -15.3.3 | src/compar.c - -## Enumerable - -ISO Code | Source File ---- | --- -15.3.2 | src/enum.c - -## GC - -ISO Code | Source File ---- | --- -n/a | src/gc.c - -### Class Methods - -#### disable - -ISO Code | Source File | C Function ---- | --- | --- -n/a | src/gc.c | gc_disable - -#### enable - -ISO Code | Source File | C Function ---- | --- | --- -n/a | src/gc.c | gc_enable - -#### generational_mode - -ISO Code | Source File | C Function ---- | --- | --- -n/a | src/gc.c | gc_generational_mode_get - -#### generational_mode= - -ISO Code | Source File | C Function ---- | --- | --- -n/a | src/gc.c | gc_generational_mode_set - -#### interval_ratio - -ISO Code | Source File | C Function ---- | --- | --- -n/a | src/gc.c | gc_interval_ratio_get - -#### interval_ratio= - -ISO Code | Source File | C Function ---- | --- | --- -n/a | src/gc.c | gc_interval_ratio_set - -#### start - -ISO Code | Source File | C Function ---- | --- | --- -n/a | src/gc.c | gc_start - -#### step_ratio - -ISO Code | Source File | C Function ---- | --- | --- -n/a | src/gc.c | gc_step_ratio_get - -#### step_ratio= - -ISO Code | Source File | C Function ---- | --- | --- -n/a | src/gc.c | gc_step_ratio_set - -#### test - -ISO Code | Source File | C Function ---- | --- | --- -n/a | src/gc.c | gc_test - -## Kernel - -ISO Code | Source File ---- | --- -15.3.1 | src/kernel.c - -### Class Methods - -#### block_given? - -ISO Code | Source File | C Function ---- | --- | --- -15.3.1.2.2 | src/kernel.c | mrb_f_block_given_p_m - -#### global_variables - -ISO Code | Source File | C Function ---- | --- | --- -15.3.1.2.4 | src/kernel.c | mrb_f_global_variables - -#### iterator? - -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 ---- | --- | --- -15.3.1.2.12 | src/kernel.c | mrb_f_raise - -### Methods - -#### != - -ISO Code | Source File | C Function ---- | --- | --- -n/a | src/kernel.c | mrb_obj_not_equal_m - -#### == - -ISO Code | Source File | C Function ---- | --- | --- -15.3.1.3.1 | src/kernel.c | mrb_obj_equal_m - -#### === - -ISO Code | Source File | C Function ---- | --- | --- -15.3.1.3.2 | src/kernel.c | mrb_equal_m - -#### __case_eqq - -ISO Code | Source File | C Function ---- | --- | --- -n/a | src/kernel.c | mrb_obj_ceqq - -#### __id__ - -ISO Code | Source File | C Function ---- | --- | --- -15.3.1.3.3 | src/kernel.c | mrb_obj_id_m - -#### __send__ - -ISO Code | Source File | C Function ---- | --- | --- -15.3.1.3.4 | src/kernel.c | mrb_f_send - -#### block_given? - -ISO Code | Source File | C Function ---- | --- | --- -15.3.1.3.6 | src/kernel.c | mrb_f_block_given_p_m - -#### class - -ISO Code | Source File | C Function ---- | --- | --- -15.3.1.3.7 | src/kernel.c | mrb_obj_class_m - -#### clone - -ISO Code | Source File | C Function ---- | --- | --- -15.3.1.3.8 | src/kernel.c | mrb_obj_clone - -#### define_singleton_method - -ISO Code | Source File | C Function ---- | --- | --- -n/a | src/kernel.c | mod_define_singleton_method - -#### dup - -ISO Code | Source File | C Function ---- | --- | --- -15.3.1.3.9 | src/kernel.c | mrb_obj_dup - -#### eql? - -ISO Code | Source File | C Function ---- | --- | --- -15.3.1.3.10 | src/kernel.c | mrb_obj_equal_m - -#### equal? - -ISO Code | Source File | C Function ---- | --- | --- -15.3.1.3.11 | src/kernel.c | mrb_obj_equal_m - -#### extend - -ISO Code | Source File | C Function ---- | --- | --- -15.3.1.3.13 | src/kernel.c | mrb_obj_extend_m - -#### global_variables - -ISO Code | Source File | C Function ---- | --- | --- -15.3.1.3.14 | src/kernel.c | mrb_f_global_variables - -#### hash - -ISO Code | Source File | C Function ---- | --- | --- -15.3.1.3.15 | src/kernel.c | mrb_obj_hash - -#### initialize_copy - -ISO Code | Source File | C Function ---- | --- | --- -15.3.1.3.16 | src/kernel.c | mrb_obj_init_copy - -#### inspect - -ISO Code | Source File | C Function ---- | --- | --- -15.3.1.3.17 | src/kernel.c | mrb_obj_inspect - -#### instance_eval - -ISO Code | Source File | C Function ---- | --- | --- -15.3.1.3.18 | src/kernel.c | mrb_obj_instance_eval - -#### instance_of? - -ISO Code | Source File | C Function ---- | --- | --- -15.3.1.3.19 | src/kernel.c | obj_is_instance_of - -#### instance_variable_defined? - -ISO Code | Source File | C Function ---- | --- | --- -15.3.1.3.20 | src/kernel.c | mrb_obj_ivar_defined - -#### instance_variable_get - -ISO Code | Source File | C Function ---- | --- | --- -15.3.1.3.21 | src/kernel.c | mrb_obj_ivar_get - -#### instance_variable_set - -ISO Code | Source File | C Function ---- | --- | --- -15.3.1.3.22 | src/kernel.c | mrb_obj_ivar_set - -#### instance_variables - -ISO Code | Source File | C Function ---- | --- | --- -15.3.1.3.23 | src/kernel.c | mrb_obj_instance_variables - -#### is_a? - -ISO Code | Source File | C Function ---- | --- | --- -15.3.1.3.24 | src/kernel.c | mrb_obj_is_kind_of_m - -#### iterator? - -ISO Code | Source File | C Function ---- | --- | --- -15.3.1.3.25 | src/kernel.c | mrb_f_block_given_p_m - -#### kind_of? - -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 ---- | --- | --- -15.3.1.3.31 | src/kernel.c | mrb_obj_methods_m - -#### nil? - -ISO Code | Source File | C Function ---- | --- | --- -15.3.1.3.32 | src/kernel.c | mrb_false - -#### object_id - -ISO Code | Source File | C Function ---- | --- | --- -15.3.1.3.33 | src/kernel.c | mrb_obj_id_m - -#### private_methods - -ISO Code | Source File | C Function ---- | --- | --- -15.3.1.3.36 | src/kernel.c | mrb_obj_private_methods - -#### protected_methods - -ISO Code | Source File | C Function ---- | --- | --- -15.3.1.3.37 | src/kernel.c | mrb_obj_protected_methods - -#### public_methods - -ISO Code | Source File | C Function ---- | --- | --- -15.3.1.3.38 | src/kernel.c | mrb_obj_public_methods - -#### raise - -ISO Code | Source File | C Function ---- | --- | --- -15.3.1.3.40 | src/kernel.c | mrb_f_raise - -#### remove_instance_variable - -ISO Code | Source File | C Function ---- | --- | --- -15.3.1.3.41 | src/kernel.c | mrb_obj_remove_instance_variable - -#### respond_to? - -ISO Code | Source File | C Function ---- | --- | --- -15.3.1.3.43 | src/kernel.c | obj_respond_to - -#### send - -ISO Code | Source File | C Function ---- | --- | --- -15.3.1.3.44 | src/kernel.c | mrb_f_send - -#### singleton_class - -ISO Code | Source File | C Function ---- | --- | --- -n/a | src/kernel.c | mrb_singleton_class - -#### singleton_methods - -ISO Code | Source File | C Function ---- | --- | --- -15.3.1.3.45 | src/kernel.c | mrb_obj_singleton_methods_m - -#### to_s - -ISO Code | Source File | C Function ---- | --- | --- -15.3.1.3.46 | src/kernel.c | mrb_any_to_s - diff --git a/doc/language/README.md b/doc/language/README.md deleted file mode 100644 index 24dd598ac..000000000 --- a/doc/language/README.md +++ /dev/null @@ -1,9 +0,0 @@ -# Language - -mruby is an implementation of the Ruby programming language. -These documents are describing the language features and libraries -which are provided together with mruby. - -## Built-In Class and Modules - -see *doc/language/Core.md* diff --git a/doc/language/generator.rb b/doc/language/generator.rb deleted file mode 100755 index c5bab1f84..000000000 --- a/doc/language/generator.rb +++ /dev/null @@ -1,15 +0,0 @@ -#!/usr/bin/env ruby - -require 'pty' - -c_dir = File.dirname(__FILE__) -MRUBY_ROOT = File.expand_path("#{c_dir}/../..") -DOC_DIR = File.expand_path(c_dir) - -cmd = "ruby #{DOC_DIR}/mrbdoc/mrbdoc.rb #{MRUBY_ROOT} #{DOC_DIR} false" -IO.popen(cmd, "r+") do |io| - io.close_write - while line = io.gets - puts line - end -end diff --git a/doc/language/mrbdoc/lib/mrbdoc_analyze.rb b/doc/language/mrbdoc/lib/mrbdoc_analyze.rb deleted file mode 100644 index 94f368c08..000000000 --- a/doc/language/mrbdoc/lib/mrbdoc_analyze.rb +++ /dev/null @@ -1,231 +0,0 @@ -class MRBDoc - SRC_DIR = 'src' - MRBLIB_DIR = 'mrblib' - - def analyze_code dir, &block - @mrb_files = {} - @dir = File.expand_path(dir) - - block.call "MRBDOC\tanalyze #{@dir}" - - analyze(dir) do |progress| - block.call progress - end - end - - def each_file(&block); @mrb_files.each {|k,v| block.call k,v}; end - - def find_c_func(c_func_name) - each_file do |file_name, file| - c_func = file.c_funcs(c_func_name) - return c_func unless c_func.nil? - end - {} - end - - def find_c_file(rb_obj_name, c_func_name) - last_file_name_match = '' - each_file do |file_name, file| - c_func = file.c_funcs(c_func_name) - if c_func and file.rb_class(rb_obj_name) or file.rb_module(rb_obj_name) - return file_name - elsif c_func - last_file_name_match = file_name - end - end - last_file_name_match - end - - def find_c_file_by_class(name) - each_file do |file_name, file| - rb_class = file.rb_class(name) - return file_name unless rb_class.nil? - end - 'nil' - end - - def find_c_file_by_module(name) - each_file do |file_name, file| - rb_module = file.rb_module(name) - return file_name unless rb_module.nil? - end - 'nil' - end - - private - - def analyze dir, &block - collect_all_files dir, &block - end - - def collect_all_files dir, &block - l = lambda {|f| block.call " - #{f.name}"} - collect_files(src_code_dir(dir), /\.c$/, &l) - collect_files(mrb_code_dir(dir), /\.rb$/, &l) - end - - def collect_files dir, rxp, &block - Dir.foreach(dir) do |file| - next unless file =~ rxp - - file_path = "#{dir}/#{file}" - mrb_file = MRBFile.new "#{file_path}" - @mrb_files["#{file_path}"] = mrb_file - - block.call mrb_file - end - end - - def src_code_dir dir; File.expand_path SRC_DIR, dir; end - def mrb_code_dir dir; File.expand_path MRBLIB_DIR, dir; end -end - -class MRBFile - attr_reader :name - attr_reader :file - - def initialize mrb_file - @file = mrb_file - @name = File.basename file - @c_funcs = {} - @rb_class_c_def = {} - @rb_method_c_def = {} - @rb_class_method_c_def = {} - @rb_module_c_def = {} - @last_line = nil - @assignments = {} - - @assignments['mrb->object_class'] = 'Object' - @assignments['mrb->kernel_module'] = 'Kernel' - @assignments['mrb->module_class'] = 'Module' - @assignments['mrb->nil_class'] = 'NilClass' - @assignments['mrb->true_class'] = 'TrueClass' - @assignments['mrb->class_class'] = 'Class' - - analyze - end - - def each_class &block - @rb_class_c_def.each do |class_name, class_hsh| - block.call class_name, class_hsh - end - end - - def each_method name, &block - @rb_method_c_def.each do |met_name, met_hsh| - met_name_tmp = met_name.sub /^#{name}_/, '' - block.call met_name_tmp, met_hsh if met_hsh[:rb_class] == name - end - end - - def each_class_method name, &block - @rb_class_method_c_def.each do |met_name, met_hsh| - met_name_tmp = met_name.sub /^#{name}_/, '' - block.call met_name_tmp, met_hsh if met_hsh[:rb_class] == name - end - end - - def each_module &block - @rb_module_c_def.each do |module_name, module_hsh| - block.call module_name, module_hsh - end - end - - def each_core_object &block - each_class {|n| block.call n} - each_module {|n| block.call n} - end - - def c_funcs c_func_name; @c_funcs[c_func_name]; end - def rb_class rb_class_name; @rb_class_c_def[rb_class_name]; end - def rb_module rb_module_name; @rb_module_c_def[rb_module_name]; end - - private - - def analyze - File.open(file).each_line.each_with_index do |line, idx| - line_no = idx.succ - if c_file? - analyze_c_line line, line_no - elsif rb_file? - analyze_rb_line line, line_no - else - raise ArgumentError.new "#{file} is a not supported file type" - end - @last_line = line.strip - end - end - - def c_file?; (name =~ /\.c$/); end - def rb_file?; (name =~ /\.rb$/); end - - RXP_C_VAR = /\s*([^\s]*?)\s*?/ - RXP_C_STR = /\s*?\"(.*?)\"\s*?/ - #RXP_C_ISO = /\s*\;\s*[\/\*]*\s*.*?([15\.]{0,3}[0-9\.]*)\s*[\\\\\*]*/ - RXP_C_ISO = /\s*;\s*[\/\*]*[\sa-zA-Z]*([\d\.]*)[\sa-zA-Z]*[\*\/]*/ - - def analyze_c_line line, line_no - case line.strip - when /^([a-zA-Z\_][a-zA-Z\_0-9]*?)\((.*?)\)\s*?$/ - # assuming c method definition - @c_funcs[$1] = {:line_no => line_no, :args => $2, :return => @last_line} - when /mrb_define_class\(.*?\,#{RXP_C_STR}\,#{RXP_C_VAR}\)#{RXP_C_ISO}/ - # assuming ruby class definition in c - class_name = $1.clone - iso = $3.clone - iso.strip! - @rb_class_c_def[class_name] = {:c_object => $2, :iso => iso} - assigns = line.split '=' - if assigns.size > 1 - assigns[0..-2].each do |v| - @assignments[v.strip] = class_name - end - end - when /mrb_define_module\(.*?\,#{RXP_C_STR}\)#{RXP_C_ISO}/ - # assuming ruby class definition in c - module_name = $1.clone - iso = $2.clone - iso.strip! - @rb_module_c_def[module_name] = {:iso => iso} - assigns = line.split '=' - if assigns.size > 1 - assigns[0..-2].each do |v| - @assignments[v.strip] = module_name - end - end - when /mrb_define_method\(.*?\,#{RXP_C_VAR}\,#{RXP_C_STR}\,#{RXP_C_VAR}\,#{RXP_C_VAR}\)#{RXP_C_ISO}/ - # assuming ruby method definition in c - name = $1.clone - name = resolve_obj(name) - iso = $5.clone - iso.strip! - @rb_method_c_def["#{name}_#{$2}"] = {:c_func => $3, :args => $4, :rb_class => name, :iso => iso} - when /mrb_define_class_method\(.*?\,#{RXP_C_VAR}\,#{RXP_C_STR}\,#{RXP_C_VAR}\,#{RXP_C_VAR}\)#{RXP_C_ISO}/ - # assuming ruby class method definition in c - class_name = $1.clone - class_name = resolve_obj(class_name) - iso = $5.clone - iso.strip! - @rb_class_method_c_def["#{class_name}_#{$2}"] = {:c_func => $3, :args => $4, :rb_class => class_name, :iso => iso} - when /mrb_name_class\(.*?\,#{RXP_C_VAR}\,\s*mrb_intern\(.*?,#{RXP_C_STR}\)\)#{RXP_C_ISO}/ - class_name = $2.clone - iso = $3.clone - iso.strip! - @rb_class_c_def[class_name] = {:c_object => $1, :iso => iso} - @assignments[$1] = class_name - when /mrb_include_module\(.*?\,#{RXP_C_VAR}\,\s*mrb_class_get\(.*?\,#{RXP_C_STR}\)\)/ - class_name = resolve_obj($1) - mod = $2.clone - @rb_class_c_def[class_name][:include] = [] unless @rb_class_c_def[class_name].has_key? :include - @rb_class_c_def[class_name][:include] << mod - end - end - - def analyze_rb_line line, line_no - - end - - def resolve_obj c_var - @assignments[c_var] - end -end diff --git a/doc/language/mrbdoc/lib/mrbdoc_docu.rb b/doc/language/mrbdoc/lib/mrbdoc_docu.rb deleted file mode 100644 index f6f327804..000000000 --- a/doc/language/mrbdoc/lib/mrbdoc_docu.rb +++ /dev/null @@ -1,118 +0,0 @@ -class MRBDoc - def write_documentation dir, cfg, &block - block.call "MRBDOC\twrite to #{File.expand_path(dir)}" - - write(dir, cfg) do |progress| - block.call progress - end - end - - private - - def write dir, cfg - File.open(File.expand_path('Core.md', dir), 'wb+') do |io| - print_core_classes(io, cfg) - print_core_modules(io, cfg) - end - end - - def get_core_list id - core_list = {} - each_file do |file_path, mrb_file| - mrb_file.send(id) do |name, cls_hsh| - core_list[name] = {:data => cls_hsh, :methods => {}, :class_methods => {}} - mrb_file.each_method name do |met_name, met_hsh| - core_list[name][:methods][met_name] = met_hsh - end - mrb_file.each_class_method name do |met_name, met_hsh| - core_list[name][:class_methods][met_name] = met_hsh - end - end - end - core_list - end - - def print_core_classes(io, cfg) - core_list = get_core_list :each_class - io.puts "# Core Classes\n\n" - core_list.sort.each do |name, hsh| - file = find_c_file_by_class(name) - file = file.split("#{@dir}/")[1] - iso = hsh[:data][:iso] - iso = 'n/a' if iso.nil? or iso == '' - mixins = hsh[:data][:include].join(', ') unless hsh[:data][:include].nil? - mixins = 'n/a' if mixins.nil? or mixins == '' - - io.puts <<CLASS -## #{name} - -ISO Code | Mixins | Source File ---- | --- | --- -#{iso} | #{mixins} | #{file} - -CLASS - print_class_methods(io, hsh, cfg) - print_methods(io, hsh, cfg) - end - end - - def print_core_modules(io, cfg) - core_list = get_core_list :each_module - io.puts "# Core Modules\n\n" - core_list.sort.each do |name, hsh| - file = find_c_file_by_module(name) - file = file.split("#{@dir}/")[1] - iso = hsh[:data][:iso] - iso = 'n/a' if iso.nil? or iso == '' - - io.puts <<CLASS -## #{name} - -ISO Code | Source File ---- | --- -#{iso} | #{file} - -CLASS - print_class_methods(io, hsh, cfg) - print_methods(io, hsh, cfg) - end - end - - def print_methods(io, hsh, cfg) - return unless hsh[:methods].size > 0 - io.puts "### Methods\n\n" - hsh[:methods].sort.each do |met_name, met_hsh| - print_method(io, met_name, met_hsh, cfg) - end - end - - def print_class_methods(io, hsh, cfg) - return unless hsh[:class_methods].size > 0 - io.puts "### Class Methods\n\n" - hsh[:class_methods].sort.each do |met_name, met_hsh| - print_method(io, met_name, met_hsh, cfg) - end - end - - def print_method(io, met_name, met_hsh, cfg) - if cfg[:print_line_no] - line_no_head = ' | Line' - line_no = " | #{find_c_func(met_hsh[:c_func])[:line_no]}" - else - line_no, line_no_head = '', '' - end - file = find_c_file(met_hsh[:rb_class], met_hsh[:c_func]) - file = file.split("#{@dir}/")[1] - iso = met_hsh[:iso] - iso = 'n/a' if iso.nil? or iso == '' - - io.puts <<METHOD -#### #{met_name} - -ISO Code | Source File | C Function#{line_no_head} ---- | --- | --- -#{iso} | #{file} | #{met_hsh[:c_func]}#{line_no} - -METHOD - end -end diff --git a/doc/language/mrbdoc/mrbdoc.rb b/doc/language/mrbdoc/mrbdoc.rb deleted file mode 100755 index cafdf112a..000000000 --- a/doc/language/mrbdoc/mrbdoc.rb +++ /dev/null @@ -1,38 +0,0 @@ -#!/usr/bin/env ruby - -$: << File.dirname(__FILE__) + '/lib' - -require 'mrbdoc_analyze' -require 'mrbdoc_docu' - -MRUBY_ROOT = ARGV[0] -DOC_ROOT = ARGV[1] -_WRITE_LINE_NO = ARGV[2] -STDOUT.sync = true - -raise ArgumentError.new 'mruby root missing!' if MRUBY_ROOT.nil? -raise ArgumentError.new 'doc root missing!' if DOC_ROOT.nil? - -if _WRITE_LINE_NO.nil? - WRITE_LINE_NO = true -else - case _WRITE_LINE_NO - when 'true' - WRITE_LINE_NO = true - when 'false' - WRITE_LINE_NO = false - else - raise ArgumentError.new 'Line no parameter has to be false or true!' - end -end - -mrbdoc = MRBDoc.new - -mrbdoc.analyze_code MRUBY_ROOT do |progress| - puts progress -end - -cfg = {:print_line_no => WRITE_LINE_NO} -mrbdoc.write_documentation DOC_ROOT, cfg do |progress| - puts progress -end diff --git a/include/mrbconf.h b/include/mrbconf.h index 95d4b3637..a35ca86bb 100644 --- a/include/mrbconf.h +++ b/include/mrbconf.h @@ -26,6 +26,9 @@ /* represent mrb_value as a word (natural unit of data for the processor) */ //#define MRB_WORD_BOXING +/* string class to handle UTF-8 encoding */ +//#define MRB_UTF8_STRING + /* argv max size in mrb_funcall */ //#define MRB_FUNCALL_ARGC_MAX 16 @@ -100,15 +103,4 @@ # define TRUE 1 #endif -#if defined(MRB_BUILD_AS_DLL) - -#if defined(MRB_CORE) || defined(MRB_LIB) -#define MRB_API __declspec(dllexport) -#else -#define MRB_API __declspec(dllimport) -#endif -#else -#define MRB_API extern -#endif - #endif /* MRUBYCONF_H */ diff --git a/include/mruby.h b/include/mruby.h index e5d19b170..0db9002b6 100644 --- a/include/mruby.h +++ b/include/mruby.h @@ -28,24 +28,40 @@ #ifndef MRUBY_H #define MRUBY_H -#if defined(__cplusplus) -extern "C" { -#endif - #include <stdint.h> #include <stddef.h> #include <limits.h> #include "mrbconf.h" +#include "mruby/common.h" #include "mruby/value.h" #include "mruby/version.h" +/** + * MRuby C API entry point + */ +MRB_BEGIN_DECL + typedef uint32_t mrb_code; + +/** + * Required arguments signature type. + */ typedef uint32_t mrb_aspec; + struct mrb_irep; struct mrb_state; +/** + * Function pointer type of custom allocator used in @see 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. + * + * See @see mrb_default_allocf for the default implementation. + */ typedef void* (*mrb_allocf) (struct mrb_state *mrb, void*, size_t, void *ud); #ifndef MRB_GC_ARENA_SIZE @@ -188,34 +204,170 @@ typedef struct mrb_state { mrb_int atexit_stack_len; } mrb_state; -#if __STDC_VERSION__ >= 201112L -# define mrb_noreturn _Noreturn -#elif defined __GNUC__ && !defined __STRICT_ANSI__ -# define mrb_noreturn __attribute__((noreturn)) -# define mrb_deprecated __attribute__((deprecated)) -#elif defined _MSC_VER -# define mrb_noreturn __declspec(noreturn) -# define mrb_deprecated __declspec(deprecated) -#else -# define mrb_noreturn -# define mrb_deprecated -#endif typedef mrb_value (*mrb_func_t)(mrb_state *mrb, mrb_value); -MRB_API struct RClass *mrb_define_class(mrb_state *, const char*, struct RClass*); + +/** + * Defines a new class. + * + * If you're creating a gem it may look something like this: + * + * !!!c + * void mrb_example_gem_init(mrb_state* mrb) { + * struct RClass *example_class; + * example_class = mrb_define_class(mrb, "Example_Class", mrb->object_class); + * } + * + * void mrb_example_gem_final(mrb_state* mrb) { + * //free(TheAnimals); + * } + * + * @param mrb The current mruby state. + * @param name The name of the defined class + * @param super The new class parent + * @return Reference to the newly defined class + * @see mrb_define_class_under + */ +MRB_API struct RClass *mrb_define_class(mrb_state *mrb, const char *name, struct RClass *super); + +/** + * Defines a new module. + * @param mrb_state* The current mruby state. + * @param char* The name of the module. + * @return Reference to the newly defined module. + */ MRB_API struct RClass *mrb_define_module(mrb_state *, const char*); MRB_API mrb_value mrb_singleton_class(mrb_state*, mrb_value); + +/** + * Include a module in another class or module. + * Equivalent to: + * + * module B * + * include A * + * end + * @param mrb_state* The current mruby state. + * @param RClass* A reference to module or a class. + * @param RClass* A reference to the module to be included. + */ MRB_API void mrb_include_module(mrb_state*, struct RClass*, struct RClass*); -MRB_API void mrb_define_method(mrb_state*, struct RClass*, const char*, mrb_func_t, mrb_aspec); +/** + * Prepends a module in another class or module. + * Equivalent to: + * module B + * prepend A + * end + * @param mrb_state* The current mruby state. + * @param RClass* A reference to module or a class. + * @param RClass* A reference to the module to be prepended. + */ +MRB_API void mrb_prepend_module(mrb_state*, struct RClass*, struct RClass*); + +/** + * Defines a global function in ruby. + * + * If you're creating a gem it may look something like this: + * + * !!!c + * mrb_value example_method(mrb_state* mrb, mrb_value self) + * { + * puts("Executing example command!"); + * return self; + * } + * + * void mrb_example_gem_init(mrb_state* mrb) + * { + * mrb_define_method(mrb, mrb->kernel_module, "example_method", example_method, MRB_ARGS_NONE()); + * } + * + * @param mrb The MRuby state reference. + * @param cla The class pointer where the method will be defined. + * @param func The function pointer to the method definition. + * @param aspec The method parameters declaration. + */ +MRB_API void mrb_define_method(mrb_state *mrb, struct RClass *cla, const char *name, mrb_func_t func, mrb_aspec aspec); + +/** + * Defines a class method. + * # Ruby style + * class Foo + * def Foo.bar + * end + * end + * // C style + * mrb_value bar_method(mrb_state* mrb, mrb_value self){ + * return mrb_nil_value(); + * } + * void mrb_example_gem_init(mrb_state* mrb){ + * struct RClass *foo; + * foo = mrb_define_class(mrb, "Foo", mrb->object_class); + * mrb_define_class_method(mrb, foo, "bar", bar_method, MRB_ARGS_NONE()); + * } + * @param mrb_state* The MRuby state reference. + * @param RClass* The class where the class method will be defined. + * @param char* The name of the class method. + * @param mrb_func_t The function pointer to the class method definition. + * @param mrb_aspec The method parameters declaration. + */ MRB_API void mrb_define_class_method(mrb_state *, struct RClass *, const char *, mrb_func_t, mrb_aspec); MRB_API void mrb_define_singleton_method(mrb_state*, struct RObject*, const char*, mrb_func_t, mrb_aspec); + +/** + * Defines a module fuction. + * # Ruby style + * module Foo + * def Foo.bar * end + * end + * // C style + * mrb_value bar_method(mrb_state* mrb, mrb_value self){ + * return mrb_nil_value(); + * } + * void mrb_example_gem_init(mrb_state* mrb){ + * struct RClass *foo; + * foo = mrb_define_module(mrb, "Foo"); + * mrb_define_module_function(mrb, foo, "bar", bar_method, MRB_ARGS_NONE()); + * } + * @param mrb_state* The MRuby state reference. + * @param RClass* The module where the module function will be defined. + * @param char* The name of the module function. + * @param mrb_func_t The function pointer to the module function definition. + * @param mrb_aspec The method parameters declaration. + */ MRB_API void mrb_define_module_function(mrb_state*, struct RClass*, const char*, mrb_func_t, mrb_aspec); + +/** + * Defines a constant. + * # Ruby style + * AGE = 22 + * // C style + * mrb_define_const(mrb, mrb->kernel_module, "AGE", mrb_fixnum_value(22)); + * @param mrb_state* The MRuby state reference. + * @param RClass* A class or module the constant is defined in. + * @param name The name of the constant. + * @param mrb_value The value for the constant. + */ MRB_API void mrb_define_const(mrb_state*, struct RClass*, const char *name, mrb_value); MRB_API void mrb_undef_method(mrb_state*, struct RClass*, const char*); MRB_API void mrb_undef_class_method(mrb_state*, struct RClass*, const char*); + +/** + * Initialize a new object instace of c class. + * + * @param mrb The current mruby state. + * @param c Reference to the class of the new object. + * @param argc Number of arguments in argv + * @param argv Array of mrb_value to initialize the object + * @return The newly initialized object + */ MRB_API mrb_value mrb_obj_new(mrb_state *mrb, struct RClass *c, mrb_int argc, const mrb_value *argv); -#define mrb_class_new_instance(mrb,argc,argv,c) mrb_obj_new(mrb,c,argc,argv) + +/** @see mrb_obj_new */ +MRB_INLINE mrb_value mrb_class_new_instance(mrb_state *mrb, mrb_int argc, const mrb_value *argv, struct RClass *c) +{ + return mrb_obj_new(mrb,c,argc,argv); +} + MRB_API mrb_value mrb_instance_new(mrb_state *mrb, mrb_value cv); MRB_API struct RClass * mrb_class_new(mrb_state *mrb, struct RClass *super); MRB_API struct RClass * mrb_module_new(mrb_state *mrb); @@ -229,31 +381,110 @@ MRB_API mrb_value mrb_notimplement_m(mrb_state*, mrb_value); MRB_API mrb_value mrb_obj_dup(mrb_state *mrb, mrb_value obj); MRB_API mrb_value mrb_check_to_integer(mrb_state *mrb, mrb_value val, const char *method); MRB_API mrb_bool mrb_obj_respond_to(mrb_state *mrb, struct RClass* c, mrb_sym mid); + +/** + * Defines a new class under a given module + * + * @param mrb The current mruby state. + * @param outer Reference to the module under which the new class will be defined + * @param name The name of the defined class + * @param super The new class parent + * @return Reference to the newly defined class + * @see mrb_define_class + */ MRB_API struct RClass * mrb_define_class_under(mrb_state *mrb, struct RClass *outer, const char *name, struct RClass *super); + MRB_API struct RClass * mrb_define_module_under(mrb_state *mrb, struct RClass *outer, const char *name); -/* required arguments */ +/** + * Function requires n arguments. + * + * @param n + * The number of required arguments. + */ #define MRB_ARGS_REQ(n) ((mrb_aspec)((n)&0x1f) << 18) -/* optional arguments */ + +/** + * Funtion takes n optional arguments + * + * @param n + * The number of optional arguments. + */ #define MRB_ARGS_OPT(n) ((mrb_aspec)((n)&0x1f) << 13) -/* mandatory and optinal arguments */ + +/** + * Funtion takes n1 mandatory arguments and n2 optional arguments + * + * @param n1 + * The number of required arguments. + * @param n2 + * The number of optional arguments. + */ #define MRB_ARGS_ARG(n1,n2) (MRB_ARGS_REQ(n1)|MRB_ARGS_OPT(n2)) -/* rest argument */ +/** rest argument */ #define MRB_ARGS_REST() ((mrb_aspec)(1 << 12)) -/* required arguments after rest */ + +/** required arguments after rest */ #define MRB_ARGS_POST(n) ((mrb_aspec)((n)&0x1f) << 7) -/* keyword arguments (n of keys, kdict) */ + +/** keyword arguments (n of keys, kdict) */ #define MRB_ARGS_KEY(n1,n2) ((mrb_aspec)((((n1)&0x1f) << 2) | ((n2)?(1<<1):0))) -/* block argument */ + +/** + * Function takes a block argument + */ #define MRB_ARGS_BLOCK() ((mrb_aspec)1) -/* accept any number of arguments */ +/** + * Function accepts any number of arguments + */ #define MRB_ARGS_ANY() MRB_ARGS_REST() -/* accept no arguments */ + +/** + * Function accepts no arguments + */ #define MRB_ARGS_NONE() ((mrb_aspec)0) -MRB_API mrb_int mrb_get_args(mrb_state *mrb, const char *format, ...); +/** + * Format specifiers for \ref mrb_get_args function + * + * Must be a list of following format specifiers: + * + * | char | mruby type | retrieve types |note | + * |:----:|----------------|---------------------|----------------------------------------------------| + * | o | Object | mrb_value | Could be used to retrieve any type of argument | + * | C | Class/Module | mrb_value | | + * | S | String | mrb_value | when ! follows, the value may be nil | + * | A | Array | mrb_value | when ! follows, the value may be nil | + * | H | Hash | mrb_value | when ! follows, the value may be nil | + * | s | String | char *, mrb_int | Receive two arguments; s! gives (NULL,0) for nil | + * | z | String | char * | NUL terminated string; z! gives NULL for nil | + * | a | Array | mrb_value *, mrb_int | Receive two arguments; a! gives (NULL,0) for nil | + * | 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. | + * | \| | 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. | + */ +typedef const char *mrb_args_format; + +/** + * Retrieve arguments from mrb_state. + * + * When applicable, implicit conversions (such as to_str, to_ary, to_hash) are + * applied to received arguments. + * Use it inside a function pointed by mrb_func_t. + * + * @param mrb The current MRuby state. + * @param format is a list of format specifiers see @ref mrb_args_format + * @param ... The passing variadic arguments must be a pointer of retrieving type. + * @return the number of arguments retrieved. + */ +MRB_API mrb_int mrb_get_args(mrb_state *mrb, mrb_args_format format, ...); static inline mrb_sym mrb_get_mid(mrb_state *mrb) /* get method symbol */ @@ -275,6 +506,9 @@ mrb_get_argc(mrb_state *mrb) /* get argc */ */ #define mrb_strlen_lit(lit) (sizeof(lit "") - 1) +/** + * Call existing ruby functions. + */ MRB_API mrb_value mrb_funcall(mrb_state*, mrb_value, const char*, mrb_int,...); MRB_API mrb_value mrb_funcall_argv(mrb_state*, mrb_value, mrb_sym, mrb_int, const mrb_value*); MRB_API mrb_value mrb_funcall_with_block(mrb_state*, mrb_value, mrb_sym, mrb_int, const mrb_value*, mrb_value); @@ -299,15 +533,74 @@ MRB_API struct RBasic *mrb_obj_alloc(mrb_state*, enum mrb_vtype, struct RClass*) MRB_API void mrb_free(mrb_state*, void*); MRB_API mrb_value mrb_str_new(mrb_state *mrb, const char *p, size_t len); + +/** + * Turns a C string into a Ruby string value. + */ MRB_API mrb_value mrb_str_new_cstr(mrb_state*, const char*); MRB_API mrb_value mrb_str_new_static(mrb_state *mrb, const char *p, size_t len); #define mrb_str_new_lit(mrb, lit) mrb_str_new_static(mrb, (lit), mrb_strlen_lit(lit)) +#ifdef _WIN32 +char* mrb_utf8_from_locale(const char *p, size_t len); +char* mrb_locale_from_utf8(const char *p, size_t len); +#define mrb_locale_free(p) free(p) +#define mrb_utf8_free(p) free(p) +#else +#define mrb_utf8_from_locale(p, l) (p) +#define mrb_locale_from_utf8(p, l) (p) +#define mrb_locale_free(p) +#define mrb_utf8_free(p) +#endif + +/** + * Creates new mrb_state. + * + * @return + * Pointer to the newly created mrb_state. + */ MRB_API mrb_state* mrb_open(void); -MRB_API mrb_state* mrb_open_allocf(mrb_allocf, void *ud); -MRB_API mrb_state* mrb_open_core(mrb_allocf, void *ud); -MRB_API void mrb_close(mrb_state*); +/** + * Create new mrb_state with custom allocators. + * + * @param f + * Reference to the allocation function. + * @param ud + * User data will be passed to custom allocator f. + * If user data isn't required just pass NULL. + * @return + * Pointer to the newly created mrb_state. + */ +MRB_API mrb_state* mrb_open_allocf(mrb_allocf f, void *ud); + +/** + * Create new mrb_state with just the MRuby core + * + * @param f + * Reference to the allocation function. + * Use mrb_default_allocf for the default + * @param ud + * User data will be passed to custom allocator f. + * If user data isn't required just pass NULL. + * @return + * Pointer to the newly created mrb_state. + */ +MRB_API mrb_state* mrb_open_core(mrb_allocf f, void *ud); + +/** + * Closes and frees a mrb_state. + * + * @param mrb + * Pointer to the mrb_state to be closed. + */ +MRB_API void mrb_close(mrb_state *mrb); + +/** + * The default allocation function. + * + * @ref mrb_allocf + */ MRB_API void* mrb_default_allocf(mrb_state*, void*, size_t, void*); MRB_API mrb_value mrb_top_self(mrb_state *); @@ -407,7 +700,13 @@ MRB_API mrb_value mrb_yield(mrb_state *mrb, mrb_value b, mrb_value arg); MRB_API mrb_value mrb_yield_argv(mrb_state *mrb, mrb_value b, mrb_int argc, const mrb_value *argv); MRB_API mrb_value mrb_yield_with_class(mrb_state *mrb, mrb_value b, mrb_int argc, const mrb_value *argv, mrb_value self, struct RClass *c); +/* mrb_gc_protect() leaves the object in the arena */ MRB_API void mrb_gc_protect(mrb_state *mrb, mrb_value obj); +/* mrb_gc_register() keeps the object from GC. */ +MRB_API void mrb_gc_register(mrb_state *mrb, mrb_value obj); +/* mrb_gc_unregister() removes the object from GC root. */ +MRB_API void mrb_gc_unregister(mrb_state *mrb, mrb_value obj); + MRB_API mrb_value mrb_to_int(mrb_state *mrb, mrb_value val); #define mrb_int(mrb, val) mrb_fixnum(mrb_to_int(mrb, val)) MRB_API void mrb_check_type(mrb_state *mrb, mrb_value x, enum mrb_vtype t); @@ -464,8 +763,6 @@ MRB_API void mrb_show_copyright(mrb_state *mrb); MRB_API mrb_value mrb_format(mrb_state *mrb, const char *format, ...); -#if defined(__cplusplus) -} /* extern "C" { */ -#endif +MRB_END_DECL #endif /* MRUBY_H */ diff --git a/include/mruby/array.h b/include/mruby/array.h index 0b17b47fd..16f5acee2 100644 --- a/include/mruby/array.h +++ b/include/mruby/array.h @@ -7,9 +7,13 @@ #ifndef MRUBY_ARRAY_H #define MRUBY_ARRAY_H -#if defined(__cplusplus) -extern "C" { -#endif +#include "mruby/common.h" + +/* + * Array class + */ +MRB_BEGIN_DECL + typedef struct mrb_shared_array { int refcnt; @@ -41,15 +45,78 @@ struct RArray { void mrb_ary_decref(mrb_state*, mrb_shared_array*); MRB_API void mrb_ary_modify(mrb_state*, struct RArray*); MRB_API mrb_value mrb_ary_new_capa(mrb_state*, mrb_int); + +/* + * Initializes a new array. + * + * Equivalent to: + * + * Array.new + * + * @param mrb The mruby state reference. + * @return The initialized array + */ MRB_API mrb_value mrb_ary_new(mrb_state *mrb); + MRB_API mrb_value mrb_ary_new_from_values(mrb_state *mrb, mrb_int size, const mrb_value *vals); MRB_API mrb_value mrb_assoc_new(mrb_state *mrb, mrb_value car, mrb_value cdr); MRB_API void mrb_ary_concat(mrb_state*, mrb_value, mrb_value); MRB_API mrb_value mrb_ary_splat(mrb_state*, mrb_value); -MRB_API void mrb_ary_push(mrb_state*, mrb_value, mrb_value); + +/* + * Pushes value into array. + * + * Equivalent to: + * + * ary << value + * + * @param mrb The mruby state reference. + * @param ary The array in which the value will be pushed + * @param value The value to be pushed into array + */ +MRB_API void mrb_ary_push(mrb_state *mrb, mrb_value array, mrb_value value); + +/* + * Pops the last element from the array. + * + * Equivalent to: + * + * ary.pop + * + * @param mrb The mruby state reference. + * @param ary The array from which the value will be poped. + * @return The poped value. + */ MRB_API mrb_value mrb_ary_pop(mrb_state *mrb, mrb_value ary); + +/* + * Returns a reference to an element of the array on the given index. + * + * Equivalent to: + * + * ary[n] + * + * @param mrb The mruby state reference. + * @param ary The target array. + * @param n The array index being referenced + * @return The referenced value. + */ MRB_API mrb_value mrb_ary_ref(mrb_state *mrb, mrb_value ary, mrb_int n); + +/* + * Sets a value on an array at the given index + * + * Equivalent to: + * + * ary[n] = val + * + * @param mrb The mruby state reference. + * @param ary The target array. + * @param n The array index being referenced. + * @param val The value being setted. + */ MRB_API void mrb_ary_set(mrb_state *mrb, mrb_value ary, mrb_int n, mrb_value val); + MRB_API void mrb_ary_replace(mrb_state *mrb, mrb_value a, mrb_value b); MRB_API mrb_value mrb_check_array_type(mrb_state *mrb, mrb_value self); MRB_API mrb_value mrb_ary_unshift(mrb_state *mrb, mrb_value self, mrb_value item); @@ -67,8 +134,6 @@ mrb_ary_len(mrb_state *mrb, mrb_value ary) return RARRAY_LEN(ary); } -#if defined(__cplusplus) -} /* extern "C" { */ -#endif +MRB_END_DECL #endif /* MRUBY_ARRAY_H */ diff --git a/include/mruby/class.h b/include/mruby/class.h index 9d5260a24..09565ec08 100644 --- a/include/mruby/class.h +++ b/include/mruby/class.h @@ -7,9 +7,12 @@ #ifndef MRUBY_CLASS_H #define MRUBY_CLASS_H -#if defined(__cplusplus) -extern "C" { -#endif +#include "mruby/common.h" + +/** + * Class class + */ +MRB_BEGIN_DECL struct RClass { MRB_OBJECT_HEADER; @@ -48,8 +51,20 @@ mrb_class(mrb_state *mrb, mrb_value v) } } -#define MRB_SET_INSTANCE_TT(c, tt) c->flags = ((c->flags & ~0xff) | (char)tt) -#define MRB_INSTANCE_TT(c) (enum mrb_vtype)(c->flags & 0xff) +// TODO: figure out where to put user flags +#define MRB_FLAG_IS_PREPENDED (1 << 19) +#define MRB_FLAG_IS_ORIGIN (1 << 20) +#define MRB_CLASS_ORIGIN(c) do {\ + if (c->flags & MRB_FLAG_IS_PREPENDED) {\ + c = c->super;\ + while (!(c->flags & MRB_FLAG_IS_ORIGIN)) {\ + c = c->super;\ + }\ + }\ +} while (0) +#define MRB_INSTANCE_TT_MASK (0xFF) +#define MRB_SET_INSTANCE_TT(c, tt) c->flags = ((c->flags & ~MRB_INSTANCE_TT_MASK) | (char)tt) +#define MRB_INSTANCE_TT(c) (enum mrb_vtype)(c->flags & MRB_INSTANCE_TT_MASK) MRB_API struct RClass* mrb_define_class_id(mrb_state*, mrb_sym, struct RClass*); MRB_API struct RClass* mrb_define_module_id(mrb_state*, mrb_sym); @@ -69,8 +84,6 @@ void mrb_gc_mark_mt(mrb_state*, struct RClass*); size_t mrb_gc_mark_mt_size(mrb_state*, struct RClass*); void mrb_gc_free_mt(mrb_state*, struct RClass*); -#if defined(__cplusplus) -} /* extern "C" { */ -#endif +MRB_END_DECL #endif /* MRUBY_CLASS_H */ diff --git a/include/mruby/common.h b/include/mruby/common.h new file mode 100644 index 000000000..e7841f38f --- /dev/null +++ b/include/mruby/common.h @@ -0,0 +1,67 @@ +/* +** mruby/common.h - mruby common platform definitions +** +** See Copyright Notice in mruby.h +*/ + +#ifndef MRUBY_COMMON_H +#define MRUBY_COMMON_H + + +#ifdef __cplusplus +# define MRB_BEGIN_DECL extern "C" { +# define MRB_END_DECL } +#else +/** Start declarations in C mode */ +# define MRB_BEGIN_DECL +/** End declarations in C mode */ +# define MRB_END_DECL +#endif + +/** + * Shared compiler macros + */ +MRB_BEGIN_DECL + +/** Declare a function that never returns. */ +#if __STDC_VERSION__ >= 201112L +# define mrb_noreturn _Noreturn +#elif defined __GNUC__ && !defined __STRICT_ANSI__ +# define mrb_noreturn __attribute__((noreturn)) +#elif defined _MSC_VER +# define mrb_noreturn __declspec(noreturn) +#else +# define mrb_noreturn +#endif + +/** Mark a function as deprecated. */ +#if defined __GNUC__ && !defined __STRICT_ANSI__ +# define mrb_deprecated __attribute__((deprecated)) +#elif defined _MSC_VER +# define mrb_deprecated __declspec(deprecated) +#else +# define mrb_deprecated +#endif + +/** Declare a function as always inlined. */ +#if defined(_MSC_VER) +# define MRB_INLINE static __inline +#else +# define MRB_INLINE static inline +#endif + + +/** Declare a public MRuby API function. */ +#if defined(MRB_BUILD_AS_DLL) +#if defined(MRB_CORE) || defined(MRB_LIB) +# define MRB_API __declspec(dllexport) +#else +# define MRB_API __declspec(dllimport) +#endif +#else +# define MRB_API extern +#endif + +MRB_END_DECL + +#endif /* MRUBY_COMMON_H */ diff --git a/include/mruby/compile.h b/include/mruby/compile.h index 1fb81782d..637b469a8 100644 --- a/include/mruby/compile.h +++ b/include/mruby/compile.h @@ -7,9 +7,12 @@ #ifndef MRUBY_COMPILE_H #define MRUBY_COMPILE_H -#if defined(__cplusplus) -extern "C" { -#endif +#include "mruby/common.h" + +/** + * MRuby Compiler + */ +MRB_BEGIN_DECL #include "mruby.h" @@ -183,8 +186,7 @@ MRB_API mrb_value mrb_load_file_cxt(mrb_state*,FILE*, mrbc_context *cxt); MRB_API mrb_value mrb_load_string_cxt(mrb_state *mrb, const char *s, mrbc_context *cxt); MRB_API mrb_value mrb_load_nstring_cxt(mrb_state *mrb, const char *s, int len, mrbc_context *cxt); -#if defined(__cplusplus) -} /* extern "C" { */ -#endif +/** @} */ +MRB_END_DECL #endif /* MRUBY_COMPILE_H */ diff --git a/include/mruby/data.h b/include/mruby/data.h index d457e3722..472829c57 100644 --- a/include/mruby/data.h +++ b/include/mruby/data.h @@ -7,12 +7,23 @@ #ifndef MRUBY_DATA_H #define MRUBY_DATA_H -#if defined(__cplusplus) -extern "C" { -#endif +#include "mruby/common.h" +/** + * Custom C wrapped data. + * + * Defining Ruby wrappers around native objects. + */ +MRB_BEGIN_DECL + +/** + * Custom data type description. + */ typedef struct mrb_data_type { + /** data type name */ const char *struct_name; + + /** data type release function pointer */ void (*dfree)(mrb_state *mrb, void*); } mrb_data_type; @@ -59,8 +70,6 @@ mrb_data_init(mrb_value v, void *ptr, const mrb_data_type *type) DATA_TYPE(v) = type; } -#if defined(__cplusplus) -} /* extern "C" { */ -#endif +MRB_END_DECL #endif /* MRUBY_DATA_H */ diff --git a/include/mruby/debug.h b/include/mruby/debug.h index 0860ba8d5..553968a86 100644 --- a/include/mruby/debug.h +++ b/include/mruby/debug.h @@ -7,9 +7,12 @@ #ifndef MRUBY_DEBUG_H #define MRUBY_DEBUG_H -#if defined(__cplusplus) -extern "C" { -#endif +#include "mruby/common.h" + +/** + * MRuby Debugging. + */ +MRB_BEGIN_DECL typedef enum mrb_debug_line_type { mrb_debug_line_ary = 0, @@ -58,8 +61,6 @@ MRB_API mrb_irep_debug_info_file *mrb_debug_info_append_file( MRB_API mrb_irep_debug_info *mrb_debug_info_alloc(mrb_state *mrb, mrb_irep *irep); MRB_API void mrb_debug_info_free(mrb_state *mrb, mrb_irep_debug_info *d); -#if defined(__cplusplus) -} /* extern "C" { */ -#endif +MRB_END_DECL #endif /* MRUBY_DEBUG_H */ diff --git a/include/mruby/dump.h b/include/mruby/dump.h index 45774d872..6a36cbce6 100644 --- a/include/mruby/dump.h +++ b/include/mruby/dump.h @@ -7,18 +7,20 @@ #ifndef MRUBY_DUMP_H #define MRUBY_DUMP_H -#if defined(__cplusplus) -extern "C" { -#endif - #include "mruby.h" #include "mruby/irep.h" +#include "mruby/common.h" + +/** + * Dumping compiled mruby script. + */ +MRB_BEGIN_DECL #define DUMP_DEBUG_INFO 1 #define DUMP_ENDIAN_BIG 2 #define DUMP_ENDIAN_LIL 4 -#define DUMP_ENDIAN_NAT 6 -#define DUMP_ENDIAN_MASK 6 +#define DUMP_ENDIAN_NAT 6 +#define DUMP_ENDIAN_MASK 6 int mrb_dump_irep(mrb_state *mrb, mrb_irep *irep, uint8_t flags, uint8_t **bin, size_t *bin_size); #ifdef ENABLE_STDIO @@ -185,11 +187,9 @@ bin_to_uint8(const uint8_t *bin) return (uint8_t)bin[0]; } -#if defined(__cplusplus) -} /* extern "C" { */ -#endif +MRB_END_DECL -/* crc.c */ +/** @internal crc.c */ uint16_t calc_crc_16_ccitt(const uint8_t *src, size_t nbytes, uint16_t crc); diff --git a/include/mruby/error.h b/include/mruby/error.h index e3e2b25e2..8b6430137 100644 --- a/include/mruby/error.h +++ b/include/mruby/error.h @@ -7,9 +7,12 @@ #ifndef MRUBY_ERROR_H #define MRUBY_ERROR_H -#if defined(__cplusplus) -extern "C" { -#endif +#include "mruby/common.h" + +/** + * MRuby error handling. + */ +MRB_BEGIN_DECL struct RException { MRB_OBJECT_HEADER; @@ -39,8 +42,6 @@ MRB_API mrb_value mrb_rescue_exceptions(mrb_state *mrb, mrb_func_t body, mrb_val mrb_func_t rescue, mrb_value r_data, mrb_int len, struct RClass **classes); -#if defined(__cplusplus) -} /* extern "C" { */ -#endif +MRB_END_DECL #endif /* MRUBY_ERROR_H */ diff --git a/include/mruby/gc.h b/include/mruby/gc.h index ebc57d2aa..0b33617de 100644 --- a/include/mruby/gc.h +++ b/include/mruby/gc.h @@ -7,16 +7,17 @@ #ifndef MRUBY_GC_H #define MRUBY_GC_H -#if defined(__cplusplus) -extern "C" { -#endif +#include "mruby/common.h" + +/** + * Uncommon memory management stuffs. + */ +MRB_BEGIN_DECL typedef void (mrb_each_object_callback)(mrb_state *mrb, struct RBasic *obj, void *data); void mrb_objspace_each_objects(mrb_state *mrb, mrb_each_object_callback *callback, void *data); MRB_API void mrb_free_context(mrb_state *mrb, struct mrb_context *c); -#if defined(__cplusplus) -} /* extern "C" { */ -#endif +MRB_END_DECL #endif /* MRUBY_GC_H */ diff --git a/include/mruby/hash.h b/include/mruby/hash.h index 5339312c6..542986c92 100644 --- a/include/mruby/hash.h +++ b/include/mruby/hash.h @@ -7,9 +7,12 @@ #ifndef MRUBY_HASH_H #define MRUBY_HASH_H -#if defined(__cplusplus) -extern "C" { -#endif +#include "mruby/common.h" + +/** + * Hash class + */ +MRB_BEGIN_DECL struct RHash { MRB_OBJECT_HEADER; @@ -21,15 +24,39 @@ struct RHash { #define mrb_hash_value(p) mrb_obj_value((void*)(p)) MRB_API mrb_value mrb_hash_new_capa(mrb_state*, int); + +/* + * Initializes a new hash. + */ MRB_API mrb_value mrb_hash_new(mrb_state *mrb); +/* + * Sets a keys and values to hashes. + */ MRB_API void mrb_hash_set(mrb_state *mrb, mrb_value hash, mrb_value key, mrb_value val); + +/* + * Gets a value from a key. + */ MRB_API mrb_value mrb_hash_get(mrb_state *mrb, mrb_value hash, mrb_value key); + MRB_API mrb_value mrb_hash_fetch(mrb_state *mrb, mrb_value hash, mrb_value key, mrb_value def); + +/* + * Deletes hash key and value pair. + */ MRB_API mrb_value mrb_hash_delete_key(mrb_state *mrb, mrb_value hash, mrb_value key); + +/* + * Gets an array of keys. + */ MRB_API mrb_value mrb_hash_keys(mrb_state *mrb, mrb_value hash); MRB_API mrb_value mrb_check_hash_type(mrb_state *mrb, mrb_value hash); MRB_API mrb_value mrb_hash_empty_p(mrb_state *mrb, mrb_value self); + +/* + * Clears the hash. + */ MRB_API mrb_value mrb_hash_clear(mrb_state *mrb, mrb_value hash); /* RHASH_TBL allocates st_table if not available. */ @@ -47,8 +74,6 @@ void mrb_gc_mark_hash(mrb_state*, struct RHash*); size_t mrb_gc_mark_hash_size(mrb_state*, struct RHash*); void mrb_gc_free_hash(mrb_state*, struct RHash*); -#if defined(__cplusplus) -} /* extern "C" { */ -#endif +MRB_END_DECL #endif /* MRUBY_HASH_H */ diff --git a/include/mruby/irep.h b/include/mruby/irep.h index f4061bb54..aaf8bd0ef 100644 --- a/include/mruby/irep.h +++ b/include/mruby/irep.h @@ -7,12 +7,14 @@ #ifndef MRUBY_IREP_H #define MRUBY_IREP_H -#if defined(__cplusplus) -extern "C" { -#endif - +#include "mruby/common.h" #include "mruby/compile.h" +/** + * Compiled mruby scripts. + */ +MRB_BEGIN_DECL + enum irep_pool_type { IREP_TT_STRING, IREP_TT_FIXNUM, @@ -53,8 +55,6 @@ void mrb_irep_free(mrb_state*, struct mrb_irep*); void mrb_irep_incref(mrb_state*, struct mrb_irep*); void mrb_irep_decref(mrb_state*, struct mrb_irep*); -#if defined(__cplusplus) -} /* extern "C" { */ -#endif +MRB_END_DECL #endif /* MRUBY_IREP_H */ diff --git a/include/mruby/khash.h b/include/mruby/khash.h index 6a4861bda..4331ef137 100644 --- a/include/mruby/khash.h +++ b/include/mruby/khash.h @@ -7,12 +7,15 @@ #ifndef MRUBY_KHASH_H #define MRUBY_KHASH_H -#if defined(__cplusplus) -extern "C" { -#endif +#include <string.h> #include "mruby.h" -#include <string.h> +#include "mruby/common.h" + +/** + * khash definitions used in mruby's hash table. + */ +MRB_BEGIN_DECL typedef uint32_t khint_t; typedef khint_t khiter_t; @@ -266,8 +269,6 @@ static inline khint_t __ac_X31_hash_string(const char *s) typedef const char *kh_cstr_t; -#if defined(__cplusplus) -} /* extern "C" { */ -#endif +MRB_END_DECL #endif /* MRUBY_KHASH_H */ diff --git a/include/mruby/numeric.h b/include/mruby/numeric.h index 237119bf2..6366e8674 100644 --- a/include/mruby/numeric.h +++ b/include/mruby/numeric.h @@ -7,9 +7,14 @@ #ifndef MRUBY_NUMERIC_H #define MRUBY_NUMERIC_H -#if defined(__cplusplus) -extern "C" { -#endif +#include "mruby/common.h" + +/** + * Numeric class and it's sub-classes. + * + * Integer, Float and Fixnum + */ +MRB_BEGIN_DECL #define POSFIXABLE(f) ((f) <= MRB_INT_MAX) #define NEGFIXABLE(f) ((f) >= MRB_INT_MIN) @@ -104,8 +109,6 @@ mrb_int_sub_overflow(mrb_int minuend, mrb_int subtrahend, mrb_int *difference) #undef MRB_UINT_MAKE #undef MRB_UINT_MAKE2 -#if defined(__cplusplus) -} /* extern "C" { */ -#endif +MRB_END_DECL #endif /* MRUBY_NUMERIC_H */ diff --git a/include/mruby/object.h b/include/mruby/object.h index fe55620fe..6633a23e8 100644 --- a/include/mruby/object.h +++ b/include/mruby/object.h @@ -14,6 +14,8 @@ struct RClass *c;\ struct RBasic *gcnext +#define MRB_FLAG_TEST(obj, flag) ((obj)->flags & flag) + /* white: 011, black: 100, gray: 000 */ #define MRB_GC_GRAY 0 #define MRB_GC_WHITE_A 1 diff --git a/include/mruby/proc.h b/include/mruby/proc.h index 5441cf767..85096fe5e 100644 --- a/include/mruby/proc.h +++ b/include/mruby/proc.h @@ -7,11 +7,13 @@ #ifndef MRUBY_PROC_H #define MRUBY_PROC_H +#include "mruby/common.h" #include "mruby/irep.h" -#if defined(__cplusplus) -extern "C" { -#endif +/** + * Proc class + */ +MRB_BEGIN_DECL struct REnv { MRB_OBJECT_HEADER; @@ -69,8 +71,6 @@ MRB_API mrb_value mrb_proc_cfunc_env_get(mrb_state*, mrb_int); #include "mruby/khash.h" KHASH_DECLARE(mt, mrb_sym, struct RProc*, TRUE) -#if defined(__cplusplus) -} /* extern "C" { */ -#endif +MRB_END_DECL #endif /* MRUBY_PROC_H */ diff --git a/include/mruby/range.h b/include/mruby/range.h index 079ed3bf4..008698fac 100644 --- a/include/mruby/range.h +++ b/include/mruby/range.h @@ -7,9 +7,12 @@ #ifndef MRUBY_RANGE_H #define MRUBY_RANGE_H -#if defined(__cplusplus) -extern "C" { -#endif +#include "mruby/common.h" + +/** + * Range class + */ +MRB_BEGIN_DECL typedef struct mrb_range_edges { mrb_value beg; @@ -25,12 +28,21 @@ struct RRange { #define mrb_range_ptr(v) ((struct RRange*)(mrb_ptr(v))) #define mrb_range_value(p) mrb_obj_value((void*)(p)) -MRB_API mrb_value mrb_range_new(mrb_state*, mrb_value, mrb_value, mrb_bool); +/* + * Initializes a Range. + * + * If the third parameter is FALSE then it includes the last value in the range. + * If the third parameter is TRUE then it excludes the last value in the range. + * + * @param start the beginning value. + * @param end the ending value. + * @param exclude represents the inclusion or exclusion of the last value. + */ +MRB_API mrb_value mrb_range_new(mrb_state *mrb, mrb_value start, mrb_value end, mrb_bool exclude); + MRB_API mrb_bool mrb_range_beg_len(mrb_state *mrb, mrb_value range, mrb_int *begp, mrb_int *lenp, mrb_int len); mrb_value mrb_get_values_at(mrb_state *mrb, mrb_value obj, mrb_int olen, mrb_int argc, const mrb_value *argv, mrb_value (*func)(mrb_state*, mrb_value, mrb_int)); -#if defined(__cplusplus) -} /* extern "C" { */ -#endif +MRB_END_DECL #endif /* MRUBY_RANGE_H */ diff --git a/include/mruby/string.h b/include/mruby/string.h index 5228dcbca..c049a72c7 100644 --- a/include/mruby/string.h +++ b/include/mruby/string.h @@ -7,9 +7,12 @@ #ifndef MRUBY_STRING_H #define MRUBY_STRING_H -#if defined(__cplusplus) -extern "C" { -#endif +#include "mruby/common.h" + +/** + * String class + */ +MRB_BEGIN_DECL extern const char mrb_digitmap[]; @@ -59,52 +62,130 @@ struct RString { #define RSTR_SET_NOFREE_FLAG(s) ((s)->flags |= MRB_STR_NOFREE) #define RSTR_UNSET_NOFREE_FLAG(s) ((s)->flags &= ~MRB_STR_NOFREE) +#define RSTR_FROZEN_P(s) ((s)->flags & MRB_STR_FROZEN) +#define RSTR_SET_FROZEN_FLAG(s) ((s)->flags |= MRB_STR_FROZEN) +#define RSTR_UNSET_FROZEN_FLAG(s) ((s)->flags &= ~MRB_STR_FROZEN) + +/* + * Returns a pointer from a Ruby string + */ #define mrb_str_ptr(s) ((struct RString*)(mrb_ptr(s))) #define RSTRING(s) mrb_str_ptr(s) #define RSTRING_PTR(s) RSTR_PTR(RSTRING(s)) #define RSTRING_EMBED_LEN(s) RSTR_ENBED_LEN(RSTRING(s)) -#define RSTRING_LEN(s) RSTR_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 #define MRB_STR_NOFREE 2 -#define MRB_STR_EMBED 4 -#define MRB_STR_EMBED_LEN_MASK 0xf8 -#define MRB_STR_EMBED_LEN_SHIFT 3 +#define MRB_STR_FROZEN 4 +#define MRB_STR_EMBED 8 +#define MRB_STR_EMBED_LEN_MASK 0x1f0 +#define MRB_STR_EMBED_LEN_SHIFT 4 void mrb_gc_free_str(mrb_state*, struct RString*); MRB_API void mrb_str_modify(mrb_state*, struct RString*); MRB_API void mrb_str_concat(mrb_state*, mrb_value, mrb_value); + +/* + * Adds two strings together. + */ MRB_API mrb_value mrb_str_plus(mrb_state*, mrb_value, mrb_value); + +/* + * Converts pointer into a Ruby string. + */ MRB_API mrb_value mrb_ptr_to_str(mrb_state *, void*); + +/* + * Returns an object as a Ruby string. + */ MRB_API mrb_value mrb_obj_as_string(mrb_state *mrb, mrb_value obj); + +/* + * Resizes the string's length. + */ MRB_API mrb_value mrb_str_resize(mrb_state *mrb, mrb_value str, mrb_int len); + +/* + * Returns a sub string. + */ MRB_API mrb_value mrb_str_substr(mrb_state *mrb, mrb_value str, mrb_int beg, mrb_int len); + +/* + * Returns a Ruby string type. + */ MRB_API mrb_value mrb_string_type(mrb_state *mrb, mrb_value str); + MRB_API mrb_value mrb_check_string_type(mrb_state *mrb, mrb_value str); MRB_API mrb_value mrb_str_buf_new(mrb_state *mrb, size_t capa); MRB_API const char *mrb_string_value_cstr(mrb_state *mrb, mrb_value *ptr); MRB_API const char *mrb_string_value_ptr(mrb_state *mrb, mrb_value ptr); + +/* + * Duplicates a string object. + */ MRB_API mrb_value mrb_str_dup(mrb_state *mrb, mrb_value str); + +/* + * Returns a symbol from a passed in string. + */ MRB_API mrb_value mrb_str_intern(mrb_state *mrb, mrb_value self); + MRB_API mrb_value mrb_str_to_inum(mrb_state *mrb, mrb_value str, mrb_int base, mrb_bool badcheck); MRB_API double mrb_str_to_dbl(mrb_state *mrb, mrb_value str, mrb_bool badcheck); + +/* + * Returns a converted string type. + */ MRB_API mrb_value mrb_str_to_str(mrb_state *mrb, mrb_value str); + +/* + * Returns true if the strings match and false if the strings don't match. + */ MRB_API mrb_bool mrb_str_equal(mrb_state *mrb, mrb_value str1, mrb_value str2); + +/* + * Returns a concated string comprised of a Ruby string and a C string. + * + * @see mrb_str_cat_cstr + */ MRB_API mrb_value mrb_str_cat(mrb_state *mrb, mrb_value str, const char *ptr, size_t len); + +/* + * Returns a concated string comprised of a Ruby string and a C string. + * + * @see mrb_str_cat + */ MRB_API mrb_value mrb_str_cat_cstr(mrb_state *mrb, mrb_value str, const char *ptr); MRB_API mrb_value mrb_str_cat_str(mrb_state *mrb, mrb_value str, mrb_value str2); #define mrb_str_cat_lit(mrb, str, lit) mrb_str_cat(mrb, str, lit, mrb_strlen_lit(lit)) + +/* + * Adds str2 to the end of str1. + */ MRB_API mrb_value mrb_str_append(mrb_state *mrb, mrb_value str, mrb_value str2); +/* + * Returns 0 if both Ruby strings are equal. Returns a value < 0 if Ruby str1 is less than Ruby str2. Returns a value > 0 if Ruby str2 is greater than Ruby str1. + */ MRB_API int mrb_str_cmp(mrb_state *mrb, mrb_value str1, mrb_value str2); + +/* + * Returns a C string from a Ruby string. + */ MRB_API char *mrb_str_to_cstr(mrb_state *mrb, mrb_value str); + mrb_value mrb_str_pool(mrb_state *mrb, mrb_value str); mrb_int mrb_str_hash(mrb_state *mrb, mrb_value str); mrb_value mrb_str_dump(mrb_state *mrb, mrb_value str); + +/* + * Returns a printable version of str, surrounded by quote marks, with special characters escaped. + */ mrb_value mrb_str_inspect(mrb_state *mrb, mrb_value str); void mrb_noregexp(mrb_state *mrb, mrb_value self); @@ -115,8 +196,6 @@ void mrb_regexp_check(mrb_state *mrb, mrb_value obj); #define mrb_str_buf_cat(mrb, str, ptr, len) mrb_str_cat(mrb, str, ptr, len) #define mrb_str_buf_append(mrb, str, str2) mrb_str_cat_str(mrb, str, str2) -#if defined(__cplusplus) -} /* extern "C" { */ -#endif +MRB_END_DECL #endif /* MRUBY_STRING_H */ diff --git a/include/mruby/value.h b/include/mruby/value.h index 9fff3f616..dfad3ec73 100644 --- a/include/mruby/value.h +++ b/include/mruby/value.h @@ -7,6 +7,13 @@ #ifndef MRUBY_VALUE_H #define MRUBY_VALUE_H +#include "mruby/common.h" + +/** + * MRuby Value definition functions and macros. + */ +MRB_BEGIN_DECL + typedef uint32_t mrb_sym; typedef uint8_t mrb_bool; struct mrb_state; @@ -121,8 +128,10 @@ enum mrb_vtype { #define mrb_test(o) mrb_bool(o) MRB_API mrb_bool mrb_regexp_p(struct mrb_state*, mrb_value); -static inline mrb_value -mrb_float_value(struct mrb_state *mrb, mrb_float f) +/* + * Returns a float in Ruby. + */ +MRB_INLINE mrb_value mrb_float_value(struct mrb_state *mrb, mrb_float f) { mrb_value v; (void) mrb; @@ -139,8 +148,10 @@ mrb_cptr_value(struct mrb_state *mrb, void *p) return v; } -static inline mrb_value -mrb_fixnum_value(mrb_int i) +/* + * Returns a fixnum in Ruby. + */ +MRB_INLINE mrb_value mrb_fixnum_value(mrb_int i) { mrb_value v; SET_INT_VALUE(v, i); @@ -163,24 +174,34 @@ mrb_obj_value(void *p) return v; } -static inline mrb_value -mrb_nil_value(void) + +/* + * Get a nil mrb_value object. + * + * @return + * nil mrb_value object reference. + */ +MRB_INLINE mrb_value mrb_nil_value(void) { mrb_value v; SET_NIL_VALUE(v); return v; } -static inline mrb_value -mrb_false_value(void) +/* + * Returns false in Ruby. + */ +MRB_INLINE mrb_value mrb_false_value(void) { mrb_value v; SET_FALSE_VALUE(v); return v; } -static inline mrb_value -mrb_true_value(void) +/* + * Returns true in Ruby. + */ +MRB_INLINE mrb_value mrb_true_value(void) { mrb_value v; SET_TRUE_VALUE(v); @@ -226,4 +247,6 @@ mrb_ro_data_p(const char *p) # define mrb_ro_data_p(p) FALSE #endif +MRB_END_DECL + #endif /* MRUBY_VALUE_H */ diff --git a/include/mruby/variable.h b/include/mruby/variable.h index 7785a8ce2..063d65b71 100644 --- a/include/mruby/variable.h +++ b/include/mruby/variable.h @@ -7,9 +7,12 @@ #ifndef MRUBY_VARIABLE_H #define MRUBY_VARIABLE_H -#if defined(__cplusplus) -extern "C" { -#endif +#include "mruby/common.h" + +/** + * Functions to access mruby variables. + */ +MRB_BEGIN_DECL typedef struct global_variable { int counter; @@ -74,8 +77,6 @@ void mrb_gc_mark_iv(mrb_state*, struct RObject*); size_t mrb_gc_mark_iv_size(mrb_state*, struct RObject*); void mrb_gc_free_iv(mrb_state*, struct RObject*); -#if defined(__cplusplus) -} /* extern "C" { */ -#endif +MRB_END_DECL #endif /* MRUBY_VARIABLE_H */ diff --git a/include/mruby/version.h b/include/mruby/version.h index ea044d6da..733863511 100644 --- a/include/mruby/version.h +++ b/include/mruby/version.h @@ -7,34 +7,104 @@ #ifndef MRUBY_VERSION_H #define MRUBY_VERSION_H +#include "mruby/common.h" + +/** + * mruby version definition macros + */ +MRB_BEGIN_DECL + +/* + * A passed in expression. + */ +#define MRB_STRINGIZE0(expr) #expr + +/* + * Passes in an expression to MRB_STRINGIZE0. + */ +#define MRB_STRINGIZE(expr) MRB_STRINGIZE0(expr) + +/* + * The version of Ruby used by mruby. + */ #define MRUBY_RUBY_VERSION "1.9" + +/* + * Ruby engine. + */ #define MRUBY_RUBY_ENGINE "mruby" -#define MRUBY_VERSION "1.1.0" +/* + * Major release version number. + */ #define MRUBY_RELEASE_MAJOR 1 + +/* + * Minor release version number. + */ #define MRUBY_RELEASE_MINOR 1 + +/* + * Tiny release version number. + */ #define MRUBY_RELEASE_TEENY 1 -#define MRUBY_RELEASE_NO 10101 -#define MRUBY_RELEASE_DATE "2014-11-19" + +/* + * The mruby version. + */ +#define MRUBY_VERSION MRB_STRINGIZE(MRUBY_RELEASE_MAJOR) "." MRB_STRINGIZE(MRUBY_RELEASE_MINOR) "." MRB_STRINGIZE(MRUBY_RELEASE_TEENY) + +/* + * Release number. + */ +#define MRUBY_RELEASE_NO (MRUBY_RELEASE_MAJOR * 100 * 100 + MRUBY_RELEASE_MINOR * 100 + MRUBY_RELEASE_TEENY) + +/* + * Release year. + */ #define MRUBY_RELEASE_YEAR 2014 + +/* + * Release month. + */ #define MRUBY_RELEASE_MONTH 11 + +/* + * Release day. + */ #define MRUBY_RELEASE_DAY 19 +/* + * Release date as a string. + */ +#define MRUBY_RELEASE_DATE MRB_STRINGIZE(MRUBY_RELEASE_YEAR) "-" MRB_STRINGIZE(MRUBY_RELEASE_MONTH) "-" MRB_STRINGIZE(MRUBY_RELEASE_DAY) + +/* + * The year mruby was first created. + */ #define MRUBY_BIRTH_YEAR 2010 +/* + * MRuby's authors. + */ #define MRUBY_AUTHOR "mruby developers" -#define MRB_STRINGIZE0(expr) #expr -#define MRB_STRINGIZE(expr) MRB_STRINGIZE0(expr) - +/* + * mruby's version, and release date. + */ #define MRUBY_DESCRIPTION \ "mruby " MRUBY_VERSION \ " (" MRUBY_RELEASE_DATE ") " \ +/* + * mruby's copyright information. + */ #define MRUBY_COPYRIGHT \ "mruby - Copyright (c) " \ MRB_STRINGIZE(MRUBY_BIRTH_YEAR)"-" \ MRB_STRINGIZE(MRUBY_RELEASE_YEAR)" " \ MRUBY_AUTHOR \ +MRB_END_DECL + #endif /* MRUBY_VERSION_H */ diff --git a/lib/mruby/source.rb b/lib/mruby/source.rb new file mode 100644 index 000000000..5819a322b --- /dev/null +++ b/lib/mruby/source.rb @@ -0,0 +1,30 @@ +require "pathname" + +module MRuby + module Source + # MRuby's source root directory + ROOT = Pathname.new(File.expand_path('../../../',__FILE__)) + + # Reads a constant defined at version.h + MRUBY_READ_VERSION_CONSTANT = Proc.new { |name| ROOT.join('include','mruby','version.h').read.match(/^#define #{name} +"?([\w\. ]+)"?$/)[1] } + + MRUBY_RUBY_VERSION = MRUBY_READ_VERSION_CONSTANT['MRUBY_RUBY_VERSION'] + MRUBY_RUBY_ENGINE = MRUBY_READ_VERSION_CONSTANT['MRUBY_RUBY_ENGINE'] + + MRUBY_RELEASE_MAJOR = Integer(MRUBY_READ_VERSION_CONSTANT['MRUBY_RELEASE_MAJOR']) + MRUBY_RELEASE_MINOR = Integer(MRUBY_READ_VERSION_CONSTANT['MRUBY_RELEASE_MINOR']) + MRUBY_RELEASE_TEENY = Integer(MRUBY_READ_VERSION_CONSTANT['MRUBY_RELEASE_TEENY']) + + MRUBY_VERSION = [MRUBY_RELEASE_MAJOR,MRUBY_RELEASE_MINOR,MRUBY_RELEASE_TEENY].join('.') + MRUBY_RELEASE_NO = (MRUBY_RELEASE_MAJOR * 100 * 100 + MRUBY_RELEASE_MINOR * 100 + MRUBY_RELEASE_TEENY) + + MRUBY_RELEASE_YEAR = Integer(MRUBY_READ_VERSION_CONSTANT['MRUBY_RELEASE_YEAR']) + MRUBY_RELEASE_MONTH = Integer(MRUBY_READ_VERSION_CONSTANT['MRUBY_RELEASE_MONTH']) + MRUBY_RELEASE_DAY = Integer(MRUBY_READ_VERSION_CONSTANT['MRUBY_RELEASE_DAY']) + MRUBY_RELEASE_DATE = [MRUBY_RELEASE_YEAR,MRUBY_RELEASE_MONTH,MRUBY_RELEASE_DAY].join('.') + + MRUBY_BIRTH_YEAR = Integer(MRUBY_READ_VERSION_CONSTANT['MRUBY_BIRTH_YEAR']) + + MRUBY_AUTHOR = MRUBY_READ_VERSION_CONSTANT['MRUBY_AUTHOR'] + end +end diff --git a/mrbgems/full-core.gembox b/mrbgems/full-core.gembox index d1ff5f414..9a5b7081b 100644 --- a/mrbgems/full-core.gembox +++ b/mrbgems/full-core.gembox @@ -4,6 +4,6 @@ MRuby::GemBox.new do |conf| Dir.glob("#{root}/mrbgems/mruby-*/mrbgem.rake") do |x| g = File.basename File.dirname x - conf.gem :core => g unless g =~ /^mruby-(print|sprintf|bin-debugger)$/ + conf.gem :core => g unless g =~ /^mruby-(print|sprintf|bin-debugger|test)$/ end end diff --git a/mrbgems/mruby-array-ext/mrblib/array.rb b/mrbgems/mruby-array-ext/mrblib/array.rb index fd80fa0bb..35be79339 100644 --- a/mrbgems/mruby-array-ext/mrblib/array.rb +++ b/mrbgems/mruby-array-ext/mrblib/array.rb @@ -217,7 +217,7 @@ class Array # [ "a", "b", "c" ].compact! #=> nil # def compact! - result = self.select { |e| e != nil } + result = self.select { |e| !e.nil? } if result.size == self.size nil else @@ -262,7 +262,7 @@ class Array # def fetch(n=nil, ifnone=NONE, &block) - warn "block supersedes default value argument" if n != nil && ifnone != NONE && block + warn "block supersedes default value argument" if !n.nil? && ifnone != NONE && block idx = n if idx < 0 @@ -312,51 +312,51 @@ class Array # def fill(arg0=nil, arg1=nil, arg2=nil, &block) - if arg0 == nil && arg1 == nil && arg2 == nil && !block + if arg0.nil? && arg1.nil? && arg2.nil? && !block raise ArgumentError, "wrong number of arguments (0 for 1..3)" end beg = len = 0 ary = [] if block - if arg0 == nil && arg1 == nil && arg2 == nil + if arg0.nil? && arg1.nil? && arg2.nil? # ary.fill { |index| block } -> ary beg = 0 len = self.size - elsif arg0 != nil && arg0.kind_of?(Range) + elsif !arg0.nil? && arg0.kind_of?(Range) # ary.fill(range) { |index| block } -> ary beg = arg0.begin beg += self.size if beg < 0 len = arg0.end len += self.size if len < 0 len += 1 unless arg0.exclude_end? - elsif arg0 != nil + elsif !arg0.nil? # ary.fill(start [, length] ) { |index| block } -> ary beg = arg0 beg += self.size if beg < 0 - if arg1 == nil + if arg1.nil? len = self.size else len = arg0 + arg1 end end else - if arg0 != nil && arg1 == nil && arg2 == nil + if !arg0.nil? && arg1.nil? && arg2.nil? # ary.fill(obj) -> ary beg = 0 len = self.size - elsif arg0 != nil && arg1 != nil && arg1.kind_of?(Range) + elsif !arg0.nil? && !arg1.nil? && arg1.kind_of?(Range) # ary.fill(obj, range ) -> ary beg = arg1.begin beg += self.size if beg < 0 len = arg1.end len += self.size if len < 0 len += 1 unless arg1.exclude_end? - elsif arg0 != nil && arg1 != nil + elsif !arg0.nil? && !arg1.nil? # ary.fill(obj, start [, length]) -> ary beg = arg1 beg += self.size if beg < 0 - if arg2 == nil + if arg2.nil? len = self.size else len = beg + arg2 @@ -582,7 +582,7 @@ class Array elsif v == true satisfied = true smaller = true - elsif v == false || v == nil + elsif v == false || v.nil? smaller = false end if smaller @@ -681,4 +681,32 @@ class Array return nil if self.size == result.size self.replace(result) end + + ## + # call-seq: + # ary.index(val) -> int or nil + # ary.index {|item| block } -> int or nil + # + # Returns the _index_ of the first object in +ary+ such that the object is + # <code>==</code> to +obj+. + # + # If a block is given instead of an argument, returns the _index_ of the + # first object for which the block returns +true+. Returns +nil+ if no + # match is found. + # + # ISO 15.2.12.5.14 + def index(val=NONE, &block) + return to_enum(:find_index, val) if !block && val == NONE + + if block + idx = 0 + self.each do |*e| + return idx if block.call(*e) + idx += 1 + end + else + return self.__ary_index(val) + end + nil + end end diff --git a/mrbgems/mruby-array-ext/test/array.rb b/mrbgems/mruby-array-ext/test/array.rb index 8c919f7e0..6c2f52379 100644 --- a/mrbgems/mruby-array-ext/test/array.rb +++ b/mrbgems/mruby-array-ext/test/array.rb @@ -293,3 +293,8 @@ assert('Array#to_h') do assert_raise(TypeError) { [1].to_h } assert_raise(ArgumentError) { [[1]].to_h } end + +assert("Array#index (block)") do + assert_nil (1..10).to_a.index { |i| i % 5 == 0 and i % 7 == 0 } + assert_equal 34, (1..100).to_a.index { |i| i % 5 == 0 and i % 7 == 0 } +end diff --git a/mrbgems/mruby-bin-debugger/mrbgem.rake b/mrbgems/mruby-bin-debugger/mrbgem.rake index b9d664779..764f431af 100755 --- a/mrbgems/mruby-bin-debugger/mrbgem.rake +++ b/mrbgems/mruby-bin-debugger/mrbgem.rake @@ -1,7 +1,7 @@ MRuby::Gem::Specification.new('mruby-bin-debugger') do |spec| spec.license = 'MIT' spec.author = 'mruby developers' - spec.summary = 'mruby debuggeer command' + spec.summary = 'mruby debugger command' spec.add_dependency('mruby-eval', :core => 'mruby-eval') diff --git a/mrbgems/mruby-bin-mirb/tools/mirb/mirb.c b/mrbgems/mruby-bin-mirb/tools/mirb/mirb.c index 0f3649a35..37fda352c 100644 --- a/mrbgems/mruby-bin-mirb/tools/mirb/mirb.c +++ b/mrbgems/mruby-bin-mirb/tools/mirb/mirb.c @@ -366,6 +366,8 @@ main(int argc, char **argv) ai = mrb_gc_arena_save(mrb); while (TRUE) { + char *utf8; + #ifndef ENABLE_READLINE print_cmdline(code_block_open); @@ -415,17 +417,21 @@ main(int argc, char **argv) strcpy(ruby_code, last_code_line); } + utf8 = mrb_utf8_from_locale(ruby_code, -1); + if (!utf8) abort(); + /* parse code */ parser = mrb_parser_new(mrb); if (parser == NULL) { fputs("create parser state error\n", stderr); break; } - parser->s = ruby_code; - parser->send = ruby_code + strlen(ruby_code); + parser->s = utf8; + parser->send = utf8 + strlen(utf8); parser->lineno = cxt->lineno; mrb_parser_parse(parser, cxt); code_block_open = is_code_block_open(parser); + mrb_utf8_free(utf8); if (code_block_open) { /* no evaluation of code */ diff --git a/mrbgems/mruby-bin-mrbc/tools/mrbc/mrbc.c b/mrbgems/mruby-bin-mrbc/tools/mrbc/mrbc.c index 301dde1c6..2f507904a 100644 --- a/mrbgems/mruby-bin-mrbc/tools/mrbc/mrbc.c +++ b/mrbgems/mruby-bin-mrbc/tools/mrbc/mrbc.c @@ -119,10 +119,10 @@ parse_args(mrb_state *mrb, int argc, char **argv, struct mrbc_args *args) args->flags |= DUMP_DEBUG_INFO; break; case 'E': - args->flags = DUMP_ENDIAN_BIG | (args->flags & DUMP_DEBUG_INFO); + args->flags = DUMP_ENDIAN_BIG | (args->flags & ~DUMP_ENDIAN_MASK); break; case 'e': - args->flags = DUMP_ENDIAN_LIL | (args->flags & DUMP_DEBUG_INFO); + args->flags = DUMP_ENDIAN_LIL | (args->flags & ~DUMP_ENDIAN_MASK); break; case 'h': return -1; diff --git a/mrbgems/mruby-bin-mruby/bintest/mruby.rb b/mrbgems/mruby-bin-mruby/bintest/mruby.rb index de211c1ba..01fc94632 100644 --- a/mrbgems/mruby-bin-mruby/bintest/mruby.rb +++ b/mrbgems/mruby-bin-mruby/bintest/mruby.rb @@ -1,17 +1,17 @@ require 'tempfile' assert('regression for #1564') do - o = `bin/mruby -e '<<' 2>&1` + o = `#{cmd('mruby')} -e #{shellquote('<<')} 2>&1` assert_equal o, "-e:1:2: syntax error, unexpected tLSHFT\n" - o = `bin/mruby -e '<<-' 2>&1` + o = `#{cmd('mruby')} -e #{shellquote('<<-')} 2>&1` assert_equal o, "-e:1:3: syntax error, unexpected tLSHFT\n" end assert('regression for #1572') do script, bin = Tempfile.new('test.rb'), Tempfile.new('test.mrb') - system "echo 'p \"ok\"' > #{script.path}" - system "bin/mrbc -g -o #{bin.path} #{script.path}" - o = `bin/mruby -b #{bin.path}`.strip + File.write script.path, 'p "ok"' + system "#{cmd('mrbc')} -g -o #{bin.path} #{script.path}" + o = `#{cmd('mruby')} -b #{bin.path}`.strip assert_equal o, '"ok"' end @@ -21,14 +21,14 @@ assert '$0 value' do # .rb script script.write "p $0\n" script.flush - assert_equal "\"#{script.path}\"", `./bin/mruby "#{script.path}"`.chomp + assert_equal "\"#{script.path}\"", `#{cmd('mruby')} "#{script.path}"`.chomp # .mrb file - `./bin/mrbc -o "#{bin.path}" "#{script.path}"` - assert_equal "\"#{bin.path}\"", `./bin/mruby -b "#{bin.path}"`.chomp + `#{cmd('mrbc')} -o "#{bin.path}" "#{script.path}"` + assert_equal "\"#{bin.path}\"", `#{cmd('mruby')} -b "#{bin.path}"`.chomp # one liner - assert_equal '"-e"', `./bin/mruby -e 'p $0'`.chomp + assert_equal '"-e"', `#{cmd('mruby')} -e #{shellquote('p $0')}`.chomp end assert '__END__', '8.6' do @@ -42,5 +42,5 @@ __END__ p 'legend' EOS script.flush - assert_equal "\"test\"\n\"fin\"\n", `./bin/mruby #{script.path}` + assert_equal "\"test\"\n\"fin\"\n", `#{cmd('mruby')} #{script.path}` end diff --git a/mrbgems/mruby-bin-mruby/tools/mruby/mruby.c b/mrbgems/mruby-bin-mruby/tools/mruby/mruby.c index 5ca744388..cc1ca3055 100644 --- a/mrbgems/mruby-bin-mruby/tools/mruby/mruby.c +++ b/mrbgems/mruby-bin-mruby/tools/mruby/mruby.c @@ -191,7 +191,11 @@ main(int argc, char **argv) ARGV = mrb_ary_new_capa(mrb, args.argc); for (i = 0; i < args.argc; i++) { - mrb_ary_push(mrb, ARGV, mrb_str_new_cstr(mrb, args.argv[i])); + char* utf8 = mrb_utf8_from_locale(args.argv[i], -1); + if (utf8) { + mrb_ary_push(mrb, ARGV, mrb_str_new_cstr(mrb, utf8)); + mrb_utf8_free(utf8); + } } mrb_define_global_const(mrb, "ARGV", ARGV); @@ -222,7 +226,10 @@ main(int argc, char **argv) v = mrb_load_file_cxt(mrb, args.rfp, c); } else { - v = mrb_load_string_cxt(mrb, args.cmdline, c); + char* utf8 = mrb_utf8_from_locale(args.cmdline, -1); + if (!utf8) abort(); + v = mrb_load_string_cxt(mrb, utf8, c); + mrb_utf8_free(utf8); } mrbc_context_free(mrb, c); diff --git a/mrbgems/mruby-bin-strip/bintest/mruby-strip.rb b/mrbgems/mruby-bin-strip/bintest/mruby-strip.rb index e74a74f10..bb664a2b1 100644 --- a/mrbgems/mruby-bin-strip/bintest/mruby-strip.rb +++ b/mrbgems/mruby-bin-strip/bintest/mruby-strip.rb @@ -1,13 +1,13 @@ require 'tempfile' assert('no files') do - o = `bin/mruby-strip 2>&1` + o = `#{cmd('mruby-strip')} 2>&1` assert_equal 1, $?.exitstatus assert_equal "no files to strip", o.split("\n")[0] end assert('file not found') do - o = `bin/mruby-strip not_found.mrb 2>&1` + o = `#{cmd('mruby-strip')} not_found.mrb 2>&1` assert_equal 1, $?.exitstatus assert_equal "can't open file for reading not_found.mrb\n", o end @@ -16,7 +16,7 @@ assert('not irep file') do t = Tempfile.new('script.rb') t.write 'p test\n' t.flush - o = `bin/mruby-strip #{t.path} 2>&1` + o = `#{cmd('mruby-strip')} #{t.path} 2>&1` assert_equal 1, $?.exitstatus assert_equal "can't read irep file #{t.path}\n", o end @@ -26,15 +26,15 @@ assert('success') do Tempfile.new('script.rb'), Tempfile.new('c1.mrb'), Tempfile.new('c2.mrb') script_file.write "p 'test'\n" script_file.flush - `bin/mrbc -g -o #{compiled1.path} #{script_file.path}` - `bin/mrbc -g -o #{compiled2.path} #{script_file.path}` + `#{cmd('mrbc')} -g -o #{compiled1.path} #{script_file.path}` + `#{cmd('mrbc')} -g -o #{compiled2.path} #{script_file.path}` - o = `bin/mruby-strip #{compiled1.path}` + o = `#{cmd('mruby-strip')} #{compiled1.path}` assert_equal 0, $?.exitstatus assert_equal "", o - assert_equal `bin/mruby #{script_file.path}`, `bin/mruby -b #{compiled1.path}` + assert_equal `#{cmd('mruby')} #{script_file.path}`, `#{cmd('mruby')} -b #{compiled1.path}` - o = `bin/mruby-strip #{compiled1.path} #{compiled2.path}` + o = `#{cmd('mruby-strip')} #{compiled1.path} #{compiled2.path}` assert_equal 0, $?.exitstatus assert_equal "", o end @@ -44,12 +44,12 @@ assert('check debug section') do Tempfile.new('script.rb'), Tempfile.new('c1.mrb'), Tempfile.new('c2.mrb') script_file.write "p 'test'\n" script_file.flush - `bin/mrbc -o #{without_debug.path} #{script_file.path}` - `bin/mrbc -g -o #{with_debug.path} #{script_file.path}` + `#{cmd('mrbc')} -o #{without_debug.path} #{script_file.path}` + `#{cmd('mrbc')} -g -o #{with_debug.path} #{script_file.path}` assert_true with_debug.size >= without_debug.size - `bin/mruby-strip #{with_debug.path}` + `#{cmd('mruby-strip')} #{with_debug.path}` assert_equal without_debug.size, with_debug.size end @@ -62,12 +62,12 @@ a += b p Kernel.local_variables EOS script_file.flush - `bin/mrbc -o #{with_lv.path} #{script_file.path}` - `bin/mrbc -o #{without_lv.path} #{script_file.path}` + `#{cmd('mrbc')} -o #{with_lv.path} #{script_file.path}` + `#{cmd('mrbc')} -o #{without_lv.path} #{script_file.path}` - `bin/mruby-strip -l #{without_lv.path}` + `#{cmd('mruby-strip')} -l #{without_lv.path}` assert_true without_lv.size < with_lv.size - assert_equal '[:a, :b]', `bin/mruby -b #{with_lv.path}`.chomp - assert_equal '[]', `bin/mruby -b #{without_lv.path}`.chomp + assert_equal '[:a, :b]', `#{cmd('mruby')} -b #{with_lv.path}`.chomp + assert_equal '[]', `#{cmd('mruby')} -b #{without_lv.path}`.chomp end diff --git a/mrbgems/mruby-compiler/bintest/mrbc.rb b/mrbgems/mruby-compiler/bintest/mrbc.rb index b016378a1..e4dc6a9a8 100644 --- a/mrbgems/mruby-compiler/bintest/mrbc.rb +++ b/mrbgems/mruby-compiler/bintest/mrbc.rb @@ -6,7 +6,7 @@ assert('Compiling multiple files without new line in last line. #2361') do 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 + result = `#{cmd('mrbc')} -c -o #{out.path} #{a.path} #{b.path} 2>&1` + assert_equal "#{cmd('mrbc')}:#{a.path}:Syntax OK", result.chomp assert_equal 0, $?.exitstatus end diff --git a/mrbgems/mruby-compiler/core/codegen.c b/mrbgems/mruby-compiler/core/codegen.c index 3853814ec..d9a8e463e 100644 --- a/mrbgems/mruby-compiler/core/codegen.c +++ b/mrbgems/mruby-compiler/core/codegen.c @@ -1330,6 +1330,17 @@ codegen(codegen_scope *s, node *tree, int val) int pos1, pos2; node *e = tree->cdr->cdr->car; + switch ((intptr_t)tree->car->car) { + case NODE_TRUE: + case NODE_INT: + case NODE_STR: + codegen(s, tree->cdr->car, val); + return; + case NODE_FALSE: + case NODE_NIL: + codegen(s, e, val); + return; + } codegen(s, tree->car, VAL); pop(); pos1 = genop_peep(s, MKOP_AsBx(OP_JMPNOT, cursp(), 0), NOVAL); @@ -2216,7 +2227,8 @@ codegen(codegen_scope *s, node *tree, int val) case NODE_REGX: if (val) { char *p1 = (char*)tree->car; - char *p2 = (char*)tree->cdr; + char *p2 = (char*)tree->cdr->car; + char *p3 = (char*)tree->cdr->cdr; int ai = mrb_gc_arena_save(s->mrb); int sym = new_sym(s, mrb_intern_lit(s->mrb, REGEXP_CLASS)); int off = new_lit(s, mrb_str_new_cstr(s->mrb, p1)); @@ -2226,11 +2238,22 @@ codegen(codegen_scope *s, node *tree, int val) genop(s, MKOP_ABx(OP_GETMCNST, cursp(), sym)); push(); genop(s, MKOP_ABx(OP_STRING, cursp(), off)); - if (p2) { + if (p2 || p3) { push(); - off = new_lit(s, mrb_str_new_cstr(s->mrb, p2)); - genop(s, MKOP_ABx(OP_STRING, cursp(), off)); + if (p2) { + off = new_lit(s, mrb_str_new_cstr(s->mrb, p2)); + genop(s, MKOP_ABx(OP_STRING, cursp(), off)); + } else { + genop(s, MKOP_A(OP_LOADNIL, cursp())); + } argc++; + if (p3) { + push(); + off = new_lit(s, mrb_str_new(s->mrb, p3, 1)); + genop(s, MKOP_ABx(OP_STRING, cursp(), off)); + argc++; + pop(); + } pop(); } pop(); diff --git a/mrbgems/mruby-compiler/core/parse.y b/mrbgems/mruby-compiler/core/parse.y index 097d63ac4..2b0591769 100644 --- a/mrbgems/mruby-compiler/core/parse.y +++ b/mrbgems/mruby-compiler/core/parse.y @@ -761,9 +761,9 @@ new_dsym(parser_state *p, node *a) /* (:str . (a . a)) */ static node* -new_regx(parser_state *p, const char *p1, const char* p2) +new_regx(parser_state *p, const char *p1, const char* p2, const char* p3) { - return cons((node*)NODE_REGX, cons((node*)p1, (node*)p2)); + return cons((node*)NODE_REGX, cons((node*)p1, cons((node*)p2, (node*)p3))); } /* (:dregx . a) */ @@ -3986,6 +3986,8 @@ parse_string(parser_state *p) char *s = strndup(tok(p), toklen(p)); char flags[3]; char *flag = flags; + char enc = '\0'; + char *encp; char *dup; newtok(p); @@ -3994,6 +3996,8 @@ parse_string(parser_state *p) case 'i': f |= 1; break; case 'x': f |= 2; break; case 'm': f |= 4; break; + case 'u': f |= 16; break; + case 'n': f |= 32; break; default: tokadd(p, re_opt); break; } } @@ -4009,12 +4013,20 @@ parse_string(parser_state *p) if (f & 1) *flag++ = 'i'; if (f & 2) *flag++ = 'x'; if (f & 4) *flag++ = 'm'; - dup = strndup(flags, (size_t)(flag - flags)); + if (f & 16) enc = 'u'; + if (f & 32) enc = 'n'; } - else { + if (flag > flags) { + dup = strndup(flags, (size_t)(flag - flags)); + } else { dup = NULL; } - yylval.nd = new_regx(p, s, dup); + if (enc) { + encp = strndup(&enc, 1); + } else { + encp = NULL; + } + yylval.nd = new_regx(p, s, dup, encp); return tREGEXP; } diff --git a/mrbgems/mruby-enum-ext/mrblib/enum.rb b/mrbgems/mruby-enum-ext/mrblib/enum.rb index f6629ed79..d469f0651 100644 --- a/mrbgems/mruby-enum-ext/mrblib/enum.rb +++ b/mrbgems/mruby-enum-ext/mrblib/enum.rb @@ -116,6 +116,7 @@ module Enumerable raise TypeError, "no implicit conversion of #{n.class} into Integer" unless n.respond_to?(:to_int) raise ArgumentError, "invalid size" if n <= 0 + return to_enum(:each_cons,n) unless block ary = [] n = n.to_int self.each do |*val| @@ -143,6 +144,7 @@ module Enumerable raise TypeError, "no implicit conversion of #{n.class} into Integer" unless n.respond_to?(:to_int) raise ArgumentError, "invalid slice size" if n <= 0 + return to_enum(:each_slice,n) unless block ary = [] n = n.to_int self.each do |*val| @@ -515,7 +517,7 @@ module Enumerable # def each_with_object(obj=nil, &block) - raise ArgumentError, "wrong number of arguments (0 for 1)" if obj == nil + raise ArgumentError, "wrong number of arguments (0 for 1)" if obj.nil? return to_enum(:each_with_object, obj) unless block @@ -574,10 +576,10 @@ module Enumerable # def cycle(n=nil, &block) - return to_enum(:cycle, n) if !block && n == nil + return to_enum(:cycle, n) if !block && n.nil? ary = [] - if n == nil + if n.nil? self.each do|*val| ary.push val block.call(*val) diff --git a/mrbgems/mruby-eval/src/eval.c b/mrbgems/mruby-eval/src/eval.c index 8bfa2f112..dd5fd5024 100644 --- a/mrbgems/mruby-eval/src/eval.c +++ b/mrbgems/mruby-eval/src/eval.c @@ -235,12 +235,12 @@ f_instance_eval(mrb_state *mrb, mrb_value self) mrb_int len; char *file = NULL; mrb_int line = 1; + mrb_value cv; mrb_get_args(mrb, "s|zi", &s, &len, &file, &line); c->ci->acc = CI_ACC_SKIP; - if (c->ci->target_class->tt == MRB_TT_ICLASS) { - c->ci->target_class = c->ci->target_class->c; - } + cv = mrb_singleton_class(mrb, self); + c->ci->target_class = mrb_class_ptr(cv); return mrb_run(mrb, create_proc_from_string(mrb, s, len, mrb_nil_value(), file, line), self); } else { diff --git a/mrbgems/mruby-eval/test/eval.rb b/mrbgems/mruby-eval/test/eval.rb index e5d3bde18..17860b132 100644 --- a/mrbgems/mruby-eval/test/eval.rb +++ b/mrbgems/mruby-eval/test/eval.rb @@ -78,3 +78,22 @@ assert('Kernel.#eval(string) context') do assert_equal('class') { obj.const_string } end +assert('Object#instance_eval with begin-rescue-ensure execution order') do + class HellRaiser + def raise_hell + order = [:enter_raise_hell] + begin + order.push :begin + self.instance_eval("raise 'error'") + rescue + order.push :rescue + ensure + order.push :ensure + end + order + end + end + + hell_raiser = HellRaiser.new + assert_equal([:enter_raise_hell, :begin, :rescue, :ensure], hell_raiser.raise_hell) +end diff --git a/mrbgems/mruby-hash-ext/mrblib/hash.rb b/mrbgems/mruby-hash-ext/mrblib/hash.rb index c970b9d02..ec8bd05fb 100644 --- a/mrbgems/mruby-hash-ext/mrblib/hash.rb +++ b/mrbgems/mruby-hash-ext/mrblib/hash.rb @@ -126,11 +126,11 @@ class Hash def fetch(key, none=NONE, &block) unless self.key?(key) if block - block.call + block.call(key) elsif none != NONE none else - raise RuntimeError, "Key not found: #{key}" + raise KeyError, "Key not found: #{key}" end else self[key] @@ -156,7 +156,7 @@ class Hash self.each do |k, v| self.delete(k) if block.call(k, v) - end + end self end diff --git a/mrbgems/mruby-hash-ext/test/hash.rb b/mrbgems/mruby-hash-ext/test/hash.rb index e1afdaaa3..01a9c5622 100644 --- a/mrbgems/mruby-hash-ext/test/hash.rb +++ b/mrbgems/mruby-hash-ext/test/hash.rb @@ -75,10 +75,9 @@ assert('Hash#fetch') do assert_equal "feline", h.fetch("cat") assert_equal "mickey", h.fetch("mouse", "mickey") assert_equal "minny", h.fetch("mouse"){"minny"} - begin + assert_equal "mouse", h.fetch("mouse"){|k| k} + assert_raise(KeyError) do h.fetch("gnu") - rescue => e - assert_kind_of(StandardError, e); end end diff --git a/mrbgems/mruby-print/src/print.c b/mrbgems/mruby-print/src/print.c index 673ba2172..9b09b12cc 100644 --- a/mrbgems/mruby-print/src/print.c +++ b/mrbgems/mruby-print/src/print.c @@ -1,17 +1,34 @@ #include "mruby.h" #include "mruby/string.h" #include <stdio.h> +#include <string.h> +#include <stdlib.h> +#if defined(__MINGW32__) || defined(__MINGW64__) +# include <windows.h> +# include <io.h> +#endif static void printstr(mrb_state *mrb, mrb_value obj) { - char *s; - mrb_int len; - if (mrb_string_p(obj)) { - s = RSTRING_PTR(obj); - len = RSTRING_LEN(obj); - fwrite(s, len, 1, stdout); +#if defined(__MINGW32__) || defined(__MINGW64__) + if (isatty(fileno(stdout))) { + DWORD written; + int mlen = RSTRING_LEN(obj); + char* utf8 = RSTRING_PTR(obj); + int wlen = MultiByteToWideChar(CP_UTF8, 0, utf8, mlen, NULL, 0); + wchar_t* utf16 = mrb_malloc(mrb, (wlen+1) * sizeof(wchar_t)); + if (utf16 == NULL) return; + if (MultiByteToWideChar(CP_UTF8, 0, utf8, mlen, utf16, wlen) > 0) { + utf16[wlen] = 0; + WriteConsoleW(GetStdHandle(STD_OUTPUT_HANDLE), + utf16, wlen, &written, NULL); + } + mrb_free(mrb, utf16); + } else +#endif + fwrite(RSTRING_PTR(obj), RSTRING_LEN(obj), 1, stdout); } } diff --git a/mrbgems/mruby-sprintf/src/sprintf.c b/mrbgems/mruby-sprintf/src/sprintf.c index de216f69f..81b48b10d 100644 --- a/mrbgems/mruby-sprintf/src/sprintf.c +++ b/mrbgems/mruby-sprintf/src/sprintf.c @@ -73,7 +73,7 @@ mrb_fix2binstr(mrb_state *mrb, mrb_value x, int base) { char buf[64], *b = buf + sizeof buf; mrb_int num = mrb_fixnum(x); - unsigned long val = (unsigned long)num; + uint64_t val = (uint64_t)num; char d; if (base != 2) { diff --git a/mrbgems/mruby-string-ext/mrblib/string.rb b/mrbgems/mruby-string-ext/mrblib/string.rb index 2b61fdb48..4c8a2ce3b 100644 --- a/mrbgems/mruby-string-ext/mrblib/string.rb +++ b/mrbgems/mruby-string-ext/mrblib/string.rb @@ -164,9 +164,9 @@ class String # string #=> "thsa sting" # def slice!(arg1, arg2=nil) - raise "wrong number of arguments (for 1..2)" if arg1 == nil && arg2 == nil + raise "wrong number of arguments (for 1..2)" if arg1.nil? && arg2.nil? - 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 @@ -196,8 +196,8 @@ class String return nil end end - unless str == nil || str == "" - if arg1 != nil && arg2 !=nil + unless str.nil? || str == "" + if !arg1.nil? && !arg2.nil? idx = arg1 >= 0 ? arg1 : self.size+arg1 str2 = self[0...idx] + self[idx+arg2..-1].to_s else @@ -207,13 +207,13 @@ class String str2 = self[0...idx] + self[idx2+1..-1].to_s elsif arg1.kind_of?(String) idx = self.index(arg1) - str2 = self[0...idx] + self[idx+arg1.size..-1] unless idx == nil + 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].to_s end end - self.replace(str2) unless str2 == nil + self.replace(str2) unless str2.nil? end str end @@ -310,4 +310,30 @@ class String return self if excl && str == other_str end end + + def chars(&block) + if block_given? + self.split('').map do |i| + block.call(i) + end + self + else + self.split('') + end + end + alias each_char chars + + def codepoints(&block) + len = self.size + + if block_given? + self.split('').map do|x| + block.call(x.ord) + end + self + else + self.split('').map{|x| x.ord} + end + end + alias each_codepoint codepoints end diff --git a/mrbgems/mruby-string-ext/src/string.c b/mrbgems/mruby-string-ext/src/string.c index 12657e129..0afc53386 100644 --- a/mrbgems/mruby-string-ext/src/string.c +++ b/mrbgems/mruby-string-ext/src/string.c @@ -245,6 +245,51 @@ mrb_str_chr(mrb_state *mrb, mrb_value self) return mrb_str_substr(mrb, self, 0, 1); } +static mrb_value +mrb_fixnum_chr(mrb_state *mrb, mrb_value num) +{ + mrb_int cp = mrb_fixnum(num); +#ifdef MRB_UTF8_STRING + char utf8[4]; + mrb_int len; + + if (cp < 0 || 0x10FFFF < cp) { + mrb_raisef(mrb, E_RANGE_ERROR, "%S out of char range", num); + } + if (cp < 0x80) { + utf8[0] = (char)cp; + len = 1; + } + else if (cp < 0x800) { + utf8[0] = (char)(0xC0 | (cp >> 6)); + utf8[1] = (char)(0x80 | (cp & 0x3F)); + len = 2; + } + else if (cp < 0x10000) { + utf8[0] = (char)(0xE0 | (cp >> 12)); + utf8[1] = (char)(0x80 | ((cp >> 6) & 0x3F)); + utf8[2] = (char)(0x80 | ( cp & 0x3F)); + len = 3; + } + else { + utf8[0] = (char)(0xF0 | (cp >> 18)); + utf8[1] = (char)(0x80 | ((cp >> 12) & 0x3F)); + utf8[2] = (char)(0x80 | ((cp >> 6) & 0x3F)); + utf8[3] = (char)(0x80 | ( cp & 0x3F)); + len = 4; + } + return mrb_str_new(mrb, utf8, len); +#else + char c; + + if (cp < 0 || 0xff < cp) { + mrb_raisef(mrb, E_RANGE_ERROR, "%S out of char range", num); + } + c = (char)cp; + return mrb_str_new(mrb, &c, 1); +#endif +} + /* * call-seq: * string.lines -> array of string @@ -422,6 +467,72 @@ mrb_str_prepend(mrb_state *mrb, mrb_value self) return self; } +#ifdef MRB_UTF8_STRING +static const char utf8len_codepage_zero[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, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 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 mrb_int +utf8code(unsigned char* p) +{ + mrb_int len; + + if (p[0] < 0x80) + return p[0]; + + len = utf8len_codepage_zero[p[0]]; + if (len > 1 && (p[1] & 0xc0) == 0x80) { + if (len == 2) + return ((p[0] & 0x1f) << 6) + (p[1] & 0x3f); + if ((p[2] & 0xc0) == 0x80) { + if (len == 3) + return ((p[0] & 0x0f) << 12) + ((p[1] & 0x3f) << 6) + + (p[2] & 0x3f); + if ((p[3] & 0xc0) == 0x80) { + if (len == 4) + return ((p[0] & 0x07) << 18) + ((p[1] & 0x3f) << 12) + + ((p[2] & 0x3f) << 6) + (p[3] & 0x3f); + if ((p[4] & 0xc0) == 0x80) { + if (len == 5) + return ((p[0] & 0x03) << 24) + ((p[1] & 0x3f) << 18) + + ((p[2] & 0x3f) << 12) + ((p[3] & 0x3f) << 6) + + (p[4] & 0x3f); + if ((p[5] & 0xc0) == 0x80 && len == 6) + return ((p[0] & 0x01) << 30) + ((p[1] & 0x3f) << 24) + + ((p[2] & 0x3f) << 18) + ((p[3] & 0x3f) << 12) + + ((p[4] & 0x3f) << 6) + (p[5] & 0x3f); + } + } + } + } + return p[0]; +} + +static mrb_value +mrb_str_ord(mrb_state* mrb, mrb_value str) +{ + if (RSTRING_LEN(str) == 0) + mrb_raise(mrb, E_ARGUMENT_ERROR, "empty string"); + return mrb_fixnum_value(utf8code((unsigned char*) RSTRING_PTR(str))); +} +#else +static mrb_value +mrb_str_ord(mrb_state* mrb, mrb_value str) +{ + if (RSTRING_LEN(str) == 0) + mrb_raise(mrb, E_ARGUMENT_ERROR, "empty string"); + return mrb_fixnum_value(RSTRING_PTR(str)[0]); +} +#endif + void mrb_mruby_string_ext_gem_init(mrb_state* mrb) { @@ -446,6 +557,9 @@ mrb_mruby_string_ext_gem_init(mrb_state* mrb) mrb_define_method(mrb, s, "prepend", mrb_str_prepend, MRB_ARGS_REQ(1)); mrb_alias_method(mrb, s, mrb_intern_lit(mrb, "next"), mrb_intern_lit(mrb, "succ")); mrb_alias_method(mrb, s, mrb_intern_lit(mrb, "next!"), mrb_intern_lit(mrb, "succ!")); + mrb_define_method(mrb, s, "ord", mrb_str_ord, MRB_ARGS_NONE()); + + mrb_define_method(mrb, mrb->fixnum_class, "chr", mrb_fixnum_chr, MRB_ARGS_NONE()); } void diff --git a/mrbgems/mruby-string-ext/test/string.rb b/mrbgems/mruby-string-ext/test/string.rb index 5e4847f05..8324a1613 100644 --- a/mrbgems/mruby-string-ext/test/string.rb +++ b/mrbgems/mruby-string-ext/test/string.rb @@ -1,6 +1,8 @@ ## # String(Ext) Test +UTF8STRING = ("\343\201\202".size == 1) + assert('String#getbyte') do str1 = "hello" bytes1 = [104, 101, 108, 108, 111] @@ -180,6 +182,8 @@ end assert('String#chr') do assert_equal "a", "abcde".chr + # test Fixnum#chr as well + assert_equal "a", 97.chr end assert('String#lines') do @@ -374,8 +378,8 @@ assert('String#succ') do assert_equal "-b-", a a = "-z-"; a.succ! assert_equal "-aa-", a - a = "あa"; a.succ! - assert_equal "あb", a + a = "あb"; a.succ! + assert_equal "あc", a a = "あaz"; a.succ! assert_equal "あba", a end @@ -471,3 +475,96 @@ assert('String#upto') do }) assert_equal(2, count) end + +assert('String#ord') do + got = "hello!".split('').map {|x| x.ord} + expect = [104, 101, 108, 108, 111, 33] + assert_equal expect, got +end + +assert('String#ord(UTF-8)') do + got = "こんにちは世界!".split('').map {|x| x.ord} + expect = [0x3053,0x3093,0x306b,0x3061,0x306f,0x4e16,0x754c,0x21] + assert_equal expect, got +end if UTF8STRING + +assert('String#chr') do + assert_equal "h", "hello!".chr +end +assert('String#chr(UTF-8)') do + assert_equal "こ", "こんにちは世界!".chr +end if UTF8STRING + +assert('String#chars') do + expect = ["h", "e", "l", "l", "o", "!"] + assert_equal expect, "hello!".chars + s = "" + "hello!".chars do |x| + s += x + end + assert_equal "hello!", s +end + +assert('String#chars(UTF-8)') do + expect = ['こ', 'ん', 'に', 'ち', 'は', '世', '界', '!'] + assert_equal expect, "こんにちは世界!".chars + s = "" + "こんにちは世界!".chars do |x| + s += x + end + assert_equal "こんにちは世界!", s +end if UTF8STRING + +assert('String#each_char') do + s = "" + "hello!".each_char do |x| + s += x + end + assert_equal "hello!", s +end + +assert('String#each_char(UTF-8)') do + s = "" + "こんにちは世界!".each_char do |x| + s += x + end + assert_equal "こんにちは世界!", s +end if UTF8STRING + +assert('String#codepoints') do + expect = [104, 101, 108, 108, 111, 33] + assert_equal expect, "hello!".codepoints + cp = [] + "hello!".codepoints do |x| + cp << x + end + assert_equal expect, cp +end + +assert('String#codepoints(UTF-8)') 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 if UTF8STRING + +assert('String#each_codepoint') do + expect = [104, 101, 108, 108, 111, 33] + cp = [] + "hello!".each_codepoint do |x| + cp << x + end + assert_equal expect, cp +end + +assert('String#each_codepoint(UTF-8)') do + expect = [12371, 12435, 12395, 12385, 12399, 19990, 30028, 33] + cp = [] + "こんにちは世界!".each_codepoint do |x| + cp << x + end + assert_equal expect, cp +end if UTF8STRING diff --git a/mrbgems/mruby-string-utf8/mrbgem.rake b/mrbgems/mruby-string-utf8/mrbgem.rake deleted file mode 100644 index 7642d4e07..000000000 --- a/mrbgems/mruby-string-utf8/mrbgem.rake +++ /dev/null @@ -1,6 +0,0 @@ -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 deleted file mode 100644 index 25a638ea3..000000000 --- a/mrbgems/mruby-string-utf8/src/string.c +++ /dev/null @@ -1,731 +0,0 @@ -#include "mruby.h" -#include "mruby/array.h" -#include "mruby/class.h" -#include "mruby/string.h" -#include "mruby/range.h" -#include "mruby/numeric.h" -#include "mruby/re.h" -#include <string.h> - -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, - 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, - 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, - 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, - 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, - 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, - 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, - 3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,4,4,4,4,4,1,1,1,1,1,1,1,1,1,1,1, -}; - -static const char utf8len_codepage_zero[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, - 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, - 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, - 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, - 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 mrb_int -utf8code(unsigned char* p) -{ - mrb_int len; - - if (p[0] < 0x80) - return p[0]; - - len = utf8len_codepage_zero[p[0]]; - if (len > 1 && (p[1] & 0xc0) == 0x80) { - if (len == 2) - return ((p[0] & 0x1f) << 6) + (p[1] & 0x3f); - if ((p[2] & 0xc0) == 0x80) { - if (len == 3) - return ((p[0] & 0x0f) << 12) + ((p[1] & 0x3f) << 6) - + (p[2] & 0x3f); - if ((p[3] & 0xc0) == 0x80) { - if (len == 4) - return ((p[0] & 0x07) << 18) + ((p[1] & 0x3f) << 12) - + ((p[2] & 0x3f) << 6) + (p[3] & 0x3f); - if ((p[4] & 0xc0) == 0x80) { - if (len == 5) - return ((p[0] & 0x03) << 24) + ((p[1] & 0x3f) << 18) - + ((p[2] & 0x3f) << 12) + ((p[3] & 0x3f) << 6) - + (p[4] & 0x3f); - if ((p[5] & 0xc0) == 0x80 && len == 6) - return ((p[0] & 0x01) << 30) + ((p[1] & 0x3f) << 24) - + ((p[2] & 0x3f) << 18) + ((p[3] & 0x3f) << 12) - + ((p[4] & 0x3f) << 6) + (p[5] & 0x3f); - } - } - } - } - return p[0]; -} - -static mrb_value mrb_fixnum_chr(mrb_state*, mrb_value); - -static mrb_int -utf8len(unsigned char* p) -{ - mrb_int len; - mrb_int i; - - if (*p == 0) - return 1; - len = utf8len_codepage[*p]; - for (i = 1; i < len; ++i) - if ((p[i] & 0xc0) != 0x80) - return 1; - return len; -} - -static mrb_int -mrb_utf8_strlen(mrb_value str, mrb_int len) -{ - mrb_int total = 0; - unsigned char* p = (unsigned char*) RSTRING_PTR(str); - unsigned char* e = p; - e += len < 0 ? RSTRING_LEN(str) : len; - while (p<e) { - p += utf8len(p); - total++; - } - return total; -} - -static mrb_value -mrb_str_size(mrb_state *mrb, mrb_value str) -{ - return mrb_fixnum_value(mrb_utf8_strlen(str, -1)); -} - -#define RSTRING_LEN_UTF8(s) mrb_utf8_strlen(s, -1) - -static inline mrb_int -mrb_memsearch_qs(const unsigned char *xs, mrb_int m, const unsigned char *ys, mrb_int n) -{ - const unsigned char *x = xs, *xe = xs + m; - const unsigned char *y = ys; - int i, qstable[256]; - - /* Preprocessing */ - for (i = 0; i < 256; ++i) - qstable[i] = m + 1; - for (; x < xe; ++x) - qstable[*x] = xe - x; - /* Searching */ - for (; y + m <= ys + n; y += *(qstable + y[m])) { - if (*xs == *y && memcmp(xs, y, m) == 0) - return y - ys; - } - return -1; -} -static mrb_int -mrb_memsearch(const void *x0, mrb_int m, const void *y0, mrb_int n) -{ - const unsigned char *x = (const unsigned char *)x0, *y = (const unsigned char *)y0; - - if (m > n) return -1; - else if (m == n) { - return memcmp(x0, y0, m) == 0 ? 0 : -1; - } - else if (m < 1) { - return 0; - } - else if (m == 1) { - const unsigned char *ys = y, *ye = ys + n; - for (; y < ye; ++y) { - if (*x == *y) - return y - ys; - } - return -1; - } - return mrb_memsearch_qs((const unsigned char *)x0, m, (const unsigned char *)y0, n); -} - -static mrb_value -str_subseq(mrb_state *mrb, mrb_value str, mrb_int beg, mrb_int len) -{ - mrb_int i; - unsigned char *p = (unsigned char*) RSTRING_PTR(str), *t; - unsigned char *e = p + RSTRING_LEN(str); - - for (i = 0; i < beg && p<e; i++) { - p += utf8len(p); - } - t = p; - for (i = 0; i < len && t<e; i++) { - t += utf8len(t); - } - return mrb_str_new(mrb, (const char*)p, (size_t)(t - p)); -} - -static mrb_value -str_substr(mrb_state *mrb, mrb_value str, mrb_int beg, mrb_int len) -{ - mrb_value str2; - mrb_int len8 = RSTRING_LEN_UTF8(str); - - if (len < 0) return mrb_nil_value(); - if (len8 == 0) { - len = 0; - } - else if (beg < 0) { - beg = len8 + beg; - } - if (beg > len8) return mrb_nil_value(); - if (beg < 0) { - beg += len8; - if (beg < 0) return mrb_nil_value(); - } - if (beg + len > len8) - len = len8 - beg; - if (len <= 0) { - len = 0; - } - str2 = str_subseq(mrb, str, beg, len); - - return str2; -} - -static mrb_int -str_index(mrb_state *mrb, mrb_value str, mrb_value sub, mrb_int offset) -{ - mrb_int pos; - char *s, *sptr; - mrb_int len, slen; - - len = RSTRING_LEN(str); - slen = RSTRING_LEN(sub); - if (offset < 0) { - offset += len; - if (offset < 0) return -1; - } - if (len - offset < slen) return -1; - s = RSTRING_PTR(str); - if (offset) { - s += offset; - } - if (slen == 0) return offset; - /* need proceed one character at a time */ - sptr = RSTRING_PTR(sub); - slen = RSTRING_LEN(sub); - len = RSTRING_LEN(str) - offset; - pos = mrb_memsearch(sptr, slen, s, len); - if (pos < 0) return pos; - return pos + offset; -} - -static mrb_int -str_rindex(mrb_state *mrb, mrb_value str, mrb_value sub, mrb_int pos) -{ - char *s, *sbeg, *t; - struct RString *ps = mrb_str_ptr(str); - mrb_int len = RSTRING_LEN(sub); - - /* substring longer than string */ - if (RSTR_LEN(ps) < len) return -1; - if (RSTR_LEN(ps) - pos < len) { - pos = RSTR_LEN(ps) - len; - } - 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 - RSTR_PTR(ps); - } - s--; - } - return -1; - } - else { - return pos; - } -} - -static mrb_value -mrb_str_aref(mrb_state *mrb, mrb_value str, mrb_value indx) -{ - mrb_int idx; - - mrb_regexp_check(mrb, indx); - switch (mrb_type(indx)) { - case MRB_TT_FLOAT: - indx = mrb_flo_to_fixnum(mrb, indx); - /* fall through */ - case MRB_TT_FIXNUM: - idx = mrb_fixnum(indx); - -num_index: - str = str_substr(mrb, str, idx, 1); - if (!mrb_nil_p(str) && RSTRING_LEN(str) == 0) return mrb_nil_value(); - return str; - - case MRB_TT_STRING: - if (str_index(mrb, str, indx, 0) != -1) - return mrb_str_dup(mrb, indx); - return mrb_nil_value(); - - case MRB_TT_RANGE: - /* check if indx is Range */ - { - mrb_int beg, len; - mrb_value tmp; - - len = RSTRING_LEN_UTF8(str); - if (mrb_range_beg_len(mrb, indx, &beg, &len, len)) { - tmp = str_subseq(mrb, str, beg, len); - return tmp; - } - else { - return mrb_nil_value(); - } - } - default: - idx = mrb_fixnum(indx); - goto num_index; - } - return mrb_nil_value(); /* not reached */ -} - -static mrb_value -mrb_str_aref_m(mrb_state *mrb, mrb_value str) -{ - mrb_value a1, a2; - int argc; - - argc = mrb_get_args(mrb, "o|o", &a1, &a2); - if (argc == 2) { - mrb_regexp_check(mrb, a1); - return str_substr(mrb, str, mrb_fixnum(a1), mrb_fixnum(a2)); - } - if (argc != 1) { - mrb_raisef(mrb, E_ARGUMENT_ERROR, "wrong number of arguments (%S for 1)", mrb_fixnum_value(argc)); - } - return mrb_str_aref(mrb, str, a1); -} - -static mrb_value -mrb_str_index_m(mrb_state *mrb, mrb_value str) -{ - mrb_value *argv; - mrb_int argc; - - mrb_value sub; - mrb_int pos; - - mrb_get_args(mrb, "*", &argv, &argc); - if (argc == 2) { - pos = mrb_fixnum(argv[1]); - sub = argv[0]; - } - else { - pos = 0; - if (argc > 0) - sub = argv[0]; - else - sub = mrb_nil_value(); - - } - mrb_regexp_check(mrb, sub); - if (pos < 0) { - pos += RSTRING_LEN(str); - if (pos < 0) { - return mrb_nil_value(); - } - } - - if (mrb_type(sub) == MRB_TT_FIXNUM) { - sub = mrb_fixnum_chr(mrb, sub); - } - - switch (mrb_type(sub)) { - default: { - mrb_value tmp; - - tmp = mrb_check_string_type(mrb, sub); - if (mrb_nil_p(tmp)) { - mrb_raisef(mrb, E_TYPE_ERROR, "type mismatch: %S given", sub); - } - sub = tmp; - } - /* fall through */ - case MRB_TT_STRING: - pos = str_index(mrb, str, sub, pos); - break; - } - - if (pos == -1) return mrb_nil_value(); - return mrb_fixnum_value(mrb_utf8_strlen(str, pos)); -} - -static mrb_value -mrb_str_reverse_bang(mrb_state *mrb, mrb_value str) -{ - mrb_int utf8_len = mrb_utf8_strlen(str, -1); - if (utf8_len > 1) { - mrb_int len; - char *buf; - unsigned char *p, *e, *r; - - mrb_str_modify(mrb, mrb_str_ptr(str)); - len = RSTRING_LEN(str); - buf = (char *)mrb_malloc(mrb, (size_t)len); - p = (unsigned char*)buf; - e = (unsigned char*)buf + len; - - memcpy(buf, RSTRING_PTR(str), len); - r = (unsigned char*)RSTRING_PTR(str) + len; - - while (p<e) { - mrb_int clen = utf8len(p); - r -= clen; - memcpy(r, p, clen); - p += clen; - } - mrb_free(mrb, buf); - } - - return str; -} - -static mrb_value -mrb_str_rindex_m(mrb_state *mrb, mrb_value str) -{ - mrb_value *argv; - mrb_int argc; - mrb_value sub; - mrb_value vpos; - mrb_int pos, len = RSTRING_LEN(str); - - mrb_get_args(mrb, "*", &argv, &argc); - if (argc == 2) { - sub = argv[0]; - vpos = argv[1]; - pos = mrb_fixnum(vpos); - if (pos < 0) { - pos += len; - if (pos < 0) { - mrb_regexp_check(mrb, sub); - return mrb_nil_value(); - } - } - if (pos > len) pos = len; - } - else { - pos = len; - if (argc > 0) - sub = argv[0]; - else - sub = mrb_nil_value(); - } - mrb_regexp_check(mrb, sub); - - if (mrb_type(sub) == MRB_TT_FIXNUM) { - sub = mrb_fixnum_chr(mrb, sub); - } - - switch (mrb_type(sub)) { - default: { - mrb_value tmp; - - tmp = mrb_check_string_type(mrb, sub); - if (mrb_nil_p(tmp)) { - mrb_raisef(mrb, E_TYPE_ERROR, "type mismatch: %S given", sub); - } - sub = tmp; - } - /* fall through */ - case MRB_TT_STRING: - pos = str_rindex(mrb, str, sub, pos); - break; - } - - if (pos == -1) return mrb_nil_value(); - return mrb_fixnum_value(mrb_utf8_strlen(str, pos)); -} - -static mrb_value -mrb_str_reverse(mrb_state *mrb, mrb_value str) -{ - return mrb_str_reverse_bang(mrb, mrb_str_dup(mrb, str)); -} - -static mrb_value -mrb_fixnum_chr(mrb_state *mrb, mrb_value num) -{ - mrb_int cp = mrb_fixnum(num); - char utf8[4]; - mrb_int len; - - if (cp < 0 || 0x10FFFF < cp) { - mrb_raisef(mrb, E_RANGE_ERROR, "%S out of char range", num); - } - if (cp < 0x80) { - utf8[0] = (char)cp; - len = 1; - } - else if (cp < 0x800) { - utf8[0] = (char)(0xC0 | (cp >> 6)); - utf8[1] = (char)(0x80 | (cp & 0x3F)); - len = 2; - } - else if (cp < 0x10000) { - utf8[0] = (char)(0xE0 | (cp >> 12)); - utf8[1] = (char)(0x80 | ((cp >> 6) & 0x3F)); - utf8[2] = (char)(0x80 | ( cp & 0x3F)); - len = 3; - } - else { - utf8[0] = (char)(0xF0 | (cp >> 18)); - utf8[1] = (char)(0x80 | ((cp >> 12) & 0x3F)); - utf8[2] = (char)(0x80 | ((cp >> 6) & 0x3F)); - utf8[3] = (char)(0x80 | ( cp & 0x3F)); - len = 4; - } - return mrb_str_new(mrb, utf8, len); -} - -static mrb_value -mrb_str_ord(mrb_state* mrb, mrb_value str) -{ - mrb_int len = RSTRING_LEN(str); - - if (len == 0) mrb_raise(mrb, E_ARGUMENT_ERROR, "empty string"); - return mrb_fixnum_value(utf8code((unsigned char*) RSTRING_PTR(str))); -} - -static mrb_value -mrb_str_split_m(mrb_state *mrb, mrb_value str) -{ - int argc; - mrb_value spat = mrb_nil_value(); - enum {awk, string, regexp} split_type = string; - long i = 0, lim_p; - mrb_int beg; - mrb_int end; - mrb_int lim = 0; - mrb_value result, tmp; - - argc = mrb_get_args(mrb, "|oi", &spat, &lim); - lim_p = (lim > 0 && argc == 2); - if (argc == 2) { - if (lim == 1) { - if (RSTRING_LEN(str) == 0) - return mrb_ary_new_capa(mrb, 0); - return mrb_ary_new_from_values(mrb, 1, &str); - } - i = 1; - } - - if (argc == 0 || mrb_nil_p(spat)) { - split_type = awk; - } - else { - if (mrb_string_p(spat)) { - split_type = string; - if (RSTRING_LEN(spat) == 1 && RSTRING_PTR(spat)[0] == ' '){ - split_type = awk; - } - } - else { - mrb_noregexp(mrb, str); - } - } - - result = mrb_ary_new(mrb); - beg = 0; - if (split_type == awk) { - char *ptr = RSTRING_PTR(str); - char *eptr = RSTRING_END(str); - char *bptr = ptr; - int skip = 1; - unsigned int c; - - end = beg; - while (ptr < eptr) { - int ai = mrb_gc_arena_save(mrb); - c = (unsigned char)*ptr++; - if (skip) { - if (ISSPACE(c)) { - beg = ptr - bptr; - } - else { - end = ptr - bptr; - skip = 0; - if (lim_p && lim <= i) break; - } - } - else if (ISSPACE(c)) { - mrb_ary_push(mrb, result, str_subseq(mrb, str, beg, end-beg)); - mrb_gc_arena_restore(mrb, ai); - skip = 1; - beg = ptr - bptr; - if (lim_p) ++i; - } - else { - end = ptr - bptr; - } - } - } - else if (split_type == string) { - char *ptr = RSTRING_PTR(str); // s->as.ary - char *temp = ptr; - char *eptr = RSTRING_END(str); - mrb_int slen = RSTRING_LEN(spat); - - if (slen == 0) { - int ai = mrb_gc_arena_save(mrb); - while (ptr < eptr) { - mrb_ary_push(mrb, result, str_subseq(mrb, str, ptr-temp, 1)); - mrb_gc_arena_restore(mrb, ai); - ptr++; - if (lim_p && lim <= ++i) break; - } - } - else { - char *sptr = RSTRING_PTR(spat); - int ai = mrb_gc_arena_save(mrb); - - while (ptr < eptr && - (end = mrb_memsearch(sptr, slen, ptr, eptr - ptr)) >= 0) { - /* mrb_ary_push(mrb, result, str_subseq(mrb, str, ptr - temp, end)); */ - mrb_ary_push(mrb, result, mrb_str_new(mrb, ptr, end)); - mrb_gc_arena_restore(mrb, ai); - ptr += end + slen; - if (lim_p && lim <= ++i) break; - } - } - beg = ptr - temp; - } - else { - mrb_noregexp(mrb, str); - } - if (RSTRING_LEN(str) > 0 && (lim_p || RSTRING_LEN(str) > beg || lim < 0)) { - if (RSTRING_LEN(str) == beg) { - tmp = mrb_str_new_lit(mrb, ""); - } - else { - tmp = mrb_str_new(mrb, RSTRING_PTR(str)+beg, RSTRING_LEN(str)-beg); - } - mrb_ary_push(mrb, result, tmp); - } - if (!lim_p && lim == 0) { - mrb_int len; - while ((len = RARRAY_LEN(result)) > 0 && - (tmp = RARRAY_PTR(result)[len-1], RSTRING_LEN(tmp) == 0)) - mrb_ary_pop(mrb, result); - } - - 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) -{ - struct RClass * s = mrb->string_class; - - mrb_define_method(mrb, s, "size", mrb_str_size, MRB_ARGS_NONE()); - mrb_define_method(mrb, s, "length", mrb_str_size, MRB_ARGS_NONE()); - mrb_define_method(mrb, s, "index", mrb_str_index_m, MRB_ARGS_ANY()); - mrb_define_method(mrb, s, "[]", mrb_str_aref_m, MRB_ARGS_ANY()); - mrb_define_method(mrb, s, "ord", mrb_str_ord, MRB_ARGS_NONE()); - mrb_define_method(mrb, s, "slice", mrb_str_aref_m, MRB_ARGS_ANY()); - mrb_define_method(mrb, s, "split", mrb_str_split_m, MRB_ARGS_ANY()); - 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()); -} - -void -mrb_mruby_string_utf8_gem_final(mrb_state* mrb) -{ -} diff --git a/mrbgems/mruby-string-utf8/test/string.rb b/mrbgems/mruby-string-utf8/test/string.rb deleted file mode 100644 index 551273106..000000000 --- a/mrbgems/mruby-string-utf8/test/string.rb +++ /dev/null @@ -1,110 +0,0 @@ -# -*- coding: utf-8 -*- -## -# String(utf8) Test - -assert('String#[]') do - assert_equal "ち", "こんにちは世界"[3] - assert_equal nil, "こんにちは世界"[20] - assert_equal "世", "こんにちは世界"[-2] - assert_equal "世界", "こんにちは世界"[-2..-1] - assert_equal "んに", "こんにちは世界"[1,2] - assert_equal "世", "こんにちは世界"["世"] - assert_equal 'b', 'abc'[1.1] -end - -assert('String#reverse', '15.2.10.5.29') do - a = 'こんにちは世界!' - a.reverse - - assert_equal 'こんにちは世界!', a - assert_equal '!界世はちにんこ', 'こんにちは世界!'.reverse -end - -assert('String#reverse!', '15.2.10.5.30') do - a = 'こんにちは世界!' - a.reverse! - - assert_equal '!界世はちにんこ', a - assert_equal '!界世はちにんこ', 'こんにちは世界!'.reverse! -end - -assert('Invalid sequence') do - assert_equal 5, "\xF8\x88\x80\x80\x80".size - assert_equal 6, "\xFC\x84\x80\x80\x80\x80".size -end - -assert('String#size') do - str = 'こんにちは世界!' - assert_equal 8, str.size - assert_not_equal str.bytesize, str.size - assert_equal 2, str[1, 2].size -end - -assert('String#index') do - str = "こんにちは世界!\nこんにちは世界!" - assert_nil str.index('さ') - assert_equal 3, str.index('ち') - assert_equal 12, str.index('ち', 10) - assert_equal nil, str.index("さ") -end - -assert('String#ord') do - got = "こんにちは世界!".split('').map {|x| x.ord} - expect = [0x3053,0x3093,0x306b,0x3061,0x306f,0x4e16,0x754c,0x21] - assert_equal expect, got -end - -assert('String#split') do - got = "こんにちは世界!".split('') - assert_equal ['こ', 'ん', 'に', 'ち', 'は', '世', '界', '!'], got - got = "こんにちは世界!".split('に') - assert_equal ['こん', 'ちは世界!'], got -end - -assert('String#rindex') do - str = "こんにちは世界!\nこんにちは世界!" - assert_nil str.index('さ') - 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/mrblib/struct.rb b/mrbgems/mruby-struct/mrblib/struct.rb index 57f100acd..1445392f3 100644 --- a/mrbgems/mruby-struct/mrblib/struct.rb +++ b/mrbgems/mruby-struct/mrblib/struct.rb @@ -47,7 +47,12 @@ if Object.const_defined?(:Struct) end def _inspect - str = "#<struct #{self.class.to_s} " + name = self.class.to_s + if name[0] == "#" + str = "#<struct " + else + str = "#<struct #{name} " + end buf = [] self.each_pair do |k,v| buf.push [k.to_s + "=" + v._inspect] diff --git a/mrbgems/mruby-struct/src/struct.c b/mrbgems/mruby-struct/src/struct.c index ce8d8d832..342e3eb5e 100644 --- a/mrbgems/mruby-struct/src/struct.c +++ b/mrbgems/mruby-struct/src/struct.c @@ -40,9 +40,9 @@ struct_ivar_get(mrb_state *mrb, mrb_value c, mrb_sym id) } static mrb_value -mrb_struct_s_members(mrb_state *mrb, mrb_value klass) +struct_s_members(mrb_state *mrb, struct RClass *klass) { - mrb_value members = struct_ivar_get(mrb, klass, mrb_intern_lit(mrb, "__members__")); + mrb_value members = struct_ivar_get(mrb, mrb_obj_value(klass), mrb_intern_lit(mrb, "__members__")); if (mrb_nil_p(members)) { mrb_raise(mrb, E_TYPE_ERROR, "uninitialized struct"); @@ -54,15 +54,16 @@ mrb_struct_s_members(mrb_state *mrb, mrb_value klass) } 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))); - if (!strcmp(mrb_class_name(mrb, mrb_obj_class(mrb, s)), "Struct")) { - if (RSTRUCT_LEN(s) != RARRAY_LEN(members)) { - mrb_raisef(mrb, E_TYPE_ERROR, - "struct size differs (%S required %S given)", - mrb_fixnum_value(RARRAY_LEN(members)), mrb_fixnum_value(RSTRUCT_LEN(s))); - } +struct_members(mrb_state *mrb, mrb_value s) +{ + mrb_value members = struct_s_members(mrb, mrb_obj_class(mrb, s)); + if (!mrb_array_p(s)) { + mrb_raise(mrb, E_TYPE_ERROR, "corrupted struct"); + } + if (RSTRUCT_LEN(s) != RARRAY_LEN(members)) { + mrb_raisef(mrb, E_TYPE_ERROR, + "struct size differs (%S required %S given)", + mrb_fixnum_value(RARRAY_LEN(members)), mrb_fixnum_value(RSTRUCT_LEN(s))); } return members; } @@ -72,7 +73,7 @@ mrb_struct_s_members_m(mrb_state *mrb, mrb_value klass) { mrb_value members, ary; - members = mrb_struct_s_members(mrb, klass); + members = struct_s_members(mrb, mrb_class_ptr(klass)); ary = mrb_ary_new_capa(mrb, RARRAY_LEN(members)); mrb_ary_replace(mrb, ary, members); return ary; @@ -92,7 +93,7 @@ mrb_struct_s_members_m(mrb_state *mrb, mrb_value klass) */ static mrb_value -mrb_struct_members_m(mrb_state *mrb, mrb_value obj) +mrb_struct_members(mrb_state *mrb, mrb_value obj) { return mrb_struct_s_members_m(mrb, mrb_obj_value(mrb_obj_class(mrb, obj))); } @@ -105,7 +106,7 @@ mrb_struct_getmember(mrb_state *mrb, mrb_value obj, mrb_sym id) mrb_int i, len; ptr = RSTRUCT_PTR(obj); - members = mrb_struct_members(mrb, obj); + members = struct_members(mrb, obj); ptr_members = RARRAY_PTR(members); slot = mrb_symbol_value(id); len = RARRAY_LEN(members); @@ -183,7 +184,7 @@ mrb_struct_set(mrb_state *mrb, mrb_value obj, mrb_value val) name = mrb_sym2name_len(mrb, mrb->c->ci->mid, &slen); mid = mrb_intern(mrb, name, slen-1); /* omit last "=" */ - members = mrb_struct_members(mrb, obj); + members = struct_members(mrb, obj); ptr_members = RARRAY_PTR(members); len = RARRAY_LEN(members); ptr = RSTRUCT_PTR(obj); @@ -402,7 +403,7 @@ mrb_struct_initialize_withArg(mrb_state *mrb, mrb_int argc, mrb_value *argv, mrb } static mrb_value -mrb_struct_initialize_m(mrb_state *mrb, /*int argc, mrb_value *argv,*/ mrb_value self) +mrb_struct_initialize(mrb_state *mrb, mrb_value self) { mrb_value *argv; mrb_int argc; @@ -446,7 +447,7 @@ struct_aref_sym(mrb_state *mrb, mrb_value s, mrb_sym id) mrb_int i, len; ptr = RSTRUCT_PTR(s); - members = mrb_struct_members(mrb, s); + members = struct_members(mrb, s); ptr_members = RARRAY_PTR(members); len = RARRAY_LEN(members); for (i=0; i<len; i++) { @@ -519,7 +520,7 @@ mrb_struct_aset_sym(mrb_state *mrb, mrb_value s, mrb_sym id, mrb_value val) const mrb_value *ptr_members; mrb_int i, len; - members = mrb_struct_members(mrb, s); + members = struct_members(mrb, s); len = RARRAY_LEN(members); if (RSTRUCT_LEN(s) != len) { mrb_raisef(mrb, E_TYPE_ERROR, @@ -625,8 +626,7 @@ mrb_struct_equal(mrb_state *mrb, mrb_value s) if (mrb_obj_equal(mrb, s, s2)) { return mrb_true_value(); } - if (!strcmp(mrb_class_name(mrb, mrb_obj_class(mrb, s)), "Struct") || - mrb_obj_class(mrb, s) != mrb_obj_class(mrb, s2)) { + if (mrb_obj_class(mrb, s) != mrb_obj_class(mrb, s2)) { return mrb_false_value(); } if (RSTRUCT_LEN(s) != RSTRUCT_LEN(s2)) { @@ -663,8 +663,7 @@ mrb_struct_eql(mrb_state *mrb, mrb_value s) if (mrb_obj_equal(mrb, s, s2)) { return mrb_true_value(); } - if (strcmp(mrb_class_name(mrb, mrb_obj_class(mrb, s2)), "Struct") || - mrb_obj_class(mrb, s) != mrb_obj_class(mrb, s2)) { + if (mrb_obj_class(mrb, s) != mrb_obj_class(mrb, s2)) { return mrb_false_value(); } if (RSTRUCT_LEN(s) != RSTRUCT_LEN(s2)) { @@ -720,7 +719,7 @@ mrb_struct_to_h(mrb_state *mrb, mrb_value self) mrb_value members, ret; mrb_int i; - members = mrb_struct_s_members(mrb, mrb_obj_value(mrb_class(mrb, self))); + members = struct_s_members(mrb, mrb_class(mrb, self)); ret = mrb_hash_new_capa(mrb, RARRAY_LEN(members)); for (i = 0; i < RARRAY_LEN(members); ++i) { @@ -767,8 +766,8 @@ mrb_mruby_struct_gem_init(mrb_state* mrb) mrb_define_method(mrb, st, "==", mrb_struct_equal, MRB_ARGS_REQ(1)); /* 15.2.18.4.1 */ mrb_define_method(mrb, st, "[]", mrb_struct_aref, MRB_ARGS_REQ(1)); /* 15.2.18.4.2 */ mrb_define_method(mrb, st, "[]=", mrb_struct_aset, MRB_ARGS_REQ(2)); /* 15.2.18.4.3 */ - mrb_define_method(mrb, st, "members", mrb_struct_members_m, MRB_ARGS_NONE()); /* 15.2.18.4.6 */ - mrb_define_method(mrb, st, "initialize", mrb_struct_initialize_m,MRB_ARGS_ANY()); /* 15.2.18.4.8 */ + mrb_define_method(mrb, st, "members", mrb_struct_members, MRB_ARGS_NONE()); /* 15.2.18.4.6 */ + mrb_define_method(mrb, st, "initialize", mrb_struct_initialize, MRB_ARGS_ANY()); /* 15.2.18.4.8 */ mrb_define_method(mrb, st, "initialize_copy", mrb_struct_init_copy, MRB_ARGS_REQ(1)); /* 15.2.18.4.9 */ mrb_define_method(mrb, st, "eql?", mrb_struct_eql, MRB_ARGS_REQ(1)); /* 15.2.18.4.12(x) */ diff --git a/mrbgems/mruby-struct/test/struct.rb b/mrbgems/mruby-struct/test/struct.rb index 911e657bd..2db8f5d32 100644 --- a/mrbgems/mruby-struct/test/struct.rb +++ b/mrbgems/mruby-struct/test/struct.rb @@ -103,7 +103,7 @@ end assert('struct inspect') do c = Struct.new(:m1, :m2, :m3, :m4, :m5) cc = c.new(1,2,3,4,5) - assert_equal "#<struct #{c.inspect} m1=1, m2=2, m3=3, m4=4, m5=5>", cc.inspect + assert_equal "#<struct m1=1, m2=2, m3=3, m4=4, m5=5>", cc.inspect end assert('Struct#length, Struct#size') do diff --git a/test/README.md b/mrbgems/mruby-test/README.md index fa4b91e3a..fa4b91e3a 100644 --- a/test/README.md +++ b/mrbgems/mruby-test/README.md diff --git a/test/driver.c b/mrbgems/mruby-test/driver.c index 7f0633723..7f0633723 100644 --- a/test/driver.c +++ b/mrbgems/mruby-test/driver.c diff --git a/test/init_mrbtest.c b/mrbgems/mruby-test/init_mrbtest.c index 1e2ba92bd..1e2ba92bd 100644 --- a/test/init_mrbtest.c +++ b/mrbgems/mruby-test/init_mrbtest.c diff --git a/tasks/mrbgems_test.rake b/mrbgems/mruby-test/mrbgem.rake index 0ee508360..b6b247ff6 100644 --- a/tasks/mrbgems_test.rake +++ b/mrbgems/mruby-test/mrbgem.rake @@ -1,13 +1,46 @@ -MRuby.each_target do - gem_table = gems.generate_gem_table self +MRuby::Gem::Specification.new('mruby-test') do |spec| + spec.license = 'MIT' + spec.author = 'mruby developers' + spec.summary = 'mruby test' - gems.each do |g| + build.bins << 'mrbtest' + spec.add_dependency('mruby-compiler', :core => 'mruby-compiler') + + clib = "#{build_dir}/mrbtest.c" + mlib = clib.ext(exts.object) + mrbs = Dir.glob("#{MRUBY_ROOT}/test/t/*.rb") + exec = exefile("#{build.build_dir}/bin/mrbtest") + + libmruby = libfile("#{build.build_dir}/lib/libmruby") + libmruby_core = libfile("#{build.build_dir}/lib/libmruby_core") + + mrbtest_lib = libfile("#{build_dir}/mrbtest") + mrbtest_objs = [] + + driver_obj = objfile("#{build_dir}/driver") + driver = "#{spec.dir}/driver.c" + + assert_c = "#{build_dir}/assert.c" + assert_rb = "#{MRUBY_ROOT}/test/assert.rb" + assert_lib = assert_c.ext(exts.object) + mrbtest_objs << assert_lib + + file assert_lib => assert_c + file assert_c => [build.mrbcfile, assert_rb] do |t| + open(t.name, 'w') do |f| + mrbc.run f, assert_rb, 'mrbtest_assert_irep' + end + end + + gem_table = build.gems.generate_gem_table build + + build.gems.each do |g| test_rbobj = g.test_rbireps.ext(exts.object) g.test_objs << test_rbobj - dep_list = gems.tsort_dependencies(g.test_dependencies, gem_table).select(&:generate_functions) + dep_list = build.gems.tsort_dependencies(g.test_dependencies, gem_table).select(&:generate_functions) file test_rbobj => g.test_rbireps - file g.test_rbireps => [g.test_rbfiles].flatten + [File.join(g.dir, 'mrbgem.rake'), g.build.mrbcfile, __FILE__, "#{MRUBY_ROOT}/tasks/mrbgem_spec.rake"] do |t| + file g.test_rbireps => [g.test_rbfiles].flatten + [File.join(g.dir, 'mrbgem.rake'), g.build.mrbcfile, "#{MRUBY_ROOT}/tasks/mrbgem_spec.rake"] do |t| FileUtils.mkdir_p File.dirname(t.name) open(t.name, 'w') do |f| g.print_gem_test_header(f) @@ -91,4 +124,51 @@ MRuby.each_target do end end end + + build.gems.each do |v| + mrbtest_objs.concat v.test_objs + end + + file mrbtest_lib => mrbtest_objs do |t| + build.archiver.run t.name, t.prerequisites + end + + unless build.build_mrbtest_lib_only? + file exec => [driver_obj, mlib, mrbtest_lib, libmruby_core, libmruby] do |t| + gem_flags = build.gems.map { |g| g.linker.flags } + gem_flags_before_libraries = build.gems.map { |g| g.linker.flags_before_libraries } + gem_flags_after_libraries = build.gems.map { |g| g.linker.flags_after_libraries } + gem_libraries = build.gems.map { |g| g.linker.libraries } + gem_library_paths = build.gems.map { |g| g.linker.library_paths } + build.linker.run t.name, t.prerequisites, gem_libraries, gem_library_paths, gem_flags, gem_flags_before_libraries + end + end + + init = "#{spec.dir}/init_mrbtest.c" + file mlib => clib + file clib => [build.mrbcfile, init] do |t| + _pp "GEN", "*.rb", "#{clib.relative_path}" + FileUtils.mkdir_p File.dirname(clib) + open(clib, 'w') do |f| + f.puts %Q[/*] + f.puts %Q[ * This file contains a list of all] + f.puts %Q[ * test functions.] + f.puts %Q[ *] + f.puts %Q[ * IMPORTANT:] + f.puts %Q[ * This file was generated!] + f.puts %Q[ * All manual changes will get lost.] + f.puts %Q[ */] + f.puts %Q[] + f.puts IO.read(init) + mrbc.run f, mrbs, 'mrbtest_irep' + build.gems.each do |g| + f.puts %Q[void GENERATED_TMP_mrb_#{g.funcname}_gem_test(mrb_state *mrb);] + end + f.puts %Q[void mrbgemtest_init(mrb_state* mrb) {] + build.gems.each do |g| + f.puts %Q[ GENERATED_TMP_mrb_#{g.funcname}_gem_test(mrb);] + end + f.puts %Q[}] + end + end end diff --git a/mrblib/array.rb b/mrblib/array.rb index 933f822db..65dd0d665 100644 --- a/mrblib/array.rb +++ b/mrblib/array.rb @@ -16,7 +16,7 @@ class Array while idx < length and length <= self.length and length = self.length-1 elm = self[idx += 1] unless elm - if elm == nil and length >= self.length + if elm.nil? and length >= self.length break end end @@ -50,9 +50,7 @@ class Array def collect!(&block) return to_enum :collect! unless block_given? - self.each_index{|idx| - self[idx] = block.call(self[idx]) - } + self.each_index { |idx| self[idx] = block.call(self[idx]) } self end @@ -72,7 +70,7 @@ class Array self.clear if size > 0 - self[size - 1] = nil # allocate + self[size - 1] = nil # allocate idx = 0 while idx < size @@ -158,14 +156,11 @@ class Array len = self.size n = other.size - if len > n - len = n - end + len = n if len > n i = 0 while i < len n = (self[i] <=> other[i]) - return n if n == nil - return n if n != 0 + return n if n.nil? || n != 0 i += 1 end len = self.size - other.size @@ -185,20 +180,14 @@ class Array self.delete_at(i) ret = key end - if ret == nil && block - block.call - else - ret - end + return block.call if ret.nil? && block + ret end # internal method to convert multi-value to single value def __svalue - if self.size < 2 - self.first - else - self - end + return self.first if self.size < 2 + self end end diff --git a/mrblib/enum.rb b/mrblib/enum.rb index e979fe90e..f0c9a4884 100644 --- a/mrblib/enum.rb +++ b/mrblib/enum.rb @@ -24,17 +24,9 @@ module Enumerable # ISO 15.3.2.2.1 def all?(&block) if block - self.each{|*val| - unless block.call(*val) - return false - end - } + self.each{|*val| return false unless block.call(*val)} else - self.each{|*val| - unless val.__svalue - return false - end - } + self.each{|*val| return false unless val.__svalue} end true end @@ -49,17 +41,9 @@ module Enumerable # ISO 15.3.2.2.2 def any?(&block) if block - self.each{|*val| - if block.call(*val) - return true - end - } + self.each{|*val| return true if block.call(*val)} else - self.each{|*val| - if val.__svalue - return true - end - } + self.each{|*val| return true if val.__svalue} end false end @@ -75,9 +59,7 @@ module Enumerable return to_enum :collect unless block ary = [] - self.each{|*val| - ary.push(block.call(*val)) - } + self.each{|*val| ary.push(block.call(*val))} ary end @@ -183,9 +165,7 @@ module Enumerable # ISO 15.3.2.2.10 def include?(obj) self.each{|*val| - if val.__svalue == obj - return true - end + return true if val.__svalue == obj } false end diff --git a/mrblib/error.rb b/mrblib/error.rb index d76dd9c56..2674af7a2 100644 --- a/mrblib/error.rb +++ b/mrblib/error.rb @@ -1,18 +1,3 @@ -## -# Exception -# -# ISO 15.2.22 -class Exception - - ## - # Raise an exception. - # - # ISO 15.2.22.4.1 - def self.exception(*args, &block) - self.new(*args, &block) - end -end - # ISO 15.2.24 class ArgumentError < StandardError end diff --git a/mrblib/hash.rb b/mrblib/hash.rb index cb52c1ffe..48ac96e56 100644 --- a/mrblib/hash.rb +++ b/mrblib/hash.rb @@ -319,6 +319,29 @@ class Hash h end + ## + # call-seq: + # hsh.rehash -> hsh + # + # Rebuilds the hash based on the current hash values for each key. If + # values of key objects have changed since they were inserted, this + # method will reindex <i>hsh</i>. + # + # h = {"AAA" => "b"} + # h.keys[0].chop! + # h #=> {"AA"=>"b"} + # h["AA"] #=> nil + # h.rehash #=> {"AA"=>"b"} + # h["AA"] #=> "b" + # + def rehash + h = {} + self.each{|k,v| + h[k] = v + } + self.replace(h) + end + def __update(h) h.each_key{|k| self[k] = h[k]} self diff --git a/mrblib/numeric.rb b/mrblib/numeric.rb index cf608b04b..6e4c5027f 100644 --- a/mrblib/numeric.rb +++ b/mrblib/numeric.rb @@ -100,7 +100,7 @@ module Integral # Calls the given block from +self+ to +num+ # incremented by +step+ (default 1). # - def step(num, step=1, &block) + def step(num, step = 1, &block) raise ArgumentError, "step can't be 0" if step == 0 return to_enum(:step, num, step) unless block_given? @@ -165,16 +165,30 @@ class Float # floats should be compatible to integers. def >> other n = self.to_i - other.to_i.times { - n /= 2 - } - n + other = other.to_i + if other < 0 + n << -other + else + other.times { n /= 2 } + if n.abs < 1 + if n >= 0 + 0 + else + -1 + end + else + n.to_i + end + end end def << other n = self.to_i - other.to_i.times { - n *= 2 - } - n.to_i + other = other.to_i + if other < 0 + n >> -other + else + other.times { n *= 2 } + n + end end end diff --git a/mrblib/range.rb b/mrblib/range.rb index 64fa0cb6c..5e5fd9bdc 100644 --- a/mrblib/range.rb +++ b/mrblib/range.rb @@ -26,9 +26,7 @@ class Range return self end - unless val.respond_to? :succ - raise TypeError, "can't iterate" - end + raise TypeError, "can't iterate" unless val.respond_to? :succ return self if (val <=> last) > 0 @@ -37,18 +35,14 @@ class Range val = val.succ end - if not exclude_end? and (val <=> last) == 0 - block.call(val) - end + block.call(val) if !exclude_end? && (val <=> last) == 0 self end # redefine #hash 15.3.1.3.15 def hash h = first.hash ^ last.hash - if self.exclude_end? - h += 1 - end + h += 1 if self.exclude_end? h end end diff --git a/mrblib/string.rb b/mrblib/string.rb index 23cf02e97..37441ec98 100644 --- a/mrblib/string.rb +++ b/mrblib/string.rb @@ -10,9 +10,8 @@ class String # # ISO 15.2.10.5.15 def each_line(&block) - # expect that str.index accepts an Integer for 1st argument as a byte data offset = 0 - while pos = self.index(0x0a, offset) + while pos = self.index("\n", offset) block.call(self[offset, pos + 1 - offset]) offset = pos + 1 end @@ -80,12 +79,8 @@ class String # ISO 15.2.10.5.19 def gsub!(*args, &block) str = self.gsub(*args, &block) - if str != self - self.replace(str) - self - else - nil - end + return nil if str == self + self.replace(str) end ## @@ -129,12 +124,8 @@ class String # ISO 15.2.10.5.37 def sub!(*args, &block) str = self.sub(*args, &block) - if str != self - self.replace(str) - self - else - nil - end + return nil if str == self + self.replace(str) end ## @@ -162,23 +153,54 @@ class String end ## - # Modify +self+ by replacing the content of +self+ - # at the position +pos+ with +value+. - def []=(pos, value) - if pos < 0 - pos += self.length + # Modify +self+ by replacing the content of +self+. + # The portion of the string affected is determined using the same criteria as +String#[]+. + def []=(*args) + anum = args.size + if anum == 2 + pos, value = args + if pos.kind_of? String + posnum = self.index(pos) + if posnum + b = self[0, posnum.to_i] + a = self[(posnum + pos.length)..-1] + self.replace([b, value, a].join('')) + return value + else + raise IndexError, "string not matched" + end + else + pos += self.length if pos < 0 + if pos < 0 || pos > self.length + raise IndexError, "index #{args[0]} out of string" + end + b = self[0, pos.to_i] + a = self[pos + 1..-1] + self.replace([b, value, a].join('')) + return value + end + elsif anum == 3 + pos, len, value = args + pos += self.length if pos < 0 + if pos < 0 || pos > self.length + raise IndexError, "index #{args[0]} out of string" + end + if len < 0 + raise IndexError, "negative length #{len}" + end + b = self[0, pos.to_i] + a = self[pos + len..-1] + self.replace([b, value, a].join('')) + return value + else + raise ArgumentError, "wrong number of arguments (#{anum} for 2..3)" end - b = self[0, pos] - a = self[pos+1..-1] - self.replace([b, value, a].join('')) end ## # ISO 15.2.10.5.3 def =~(re) - if re.respond_to? :to_str - raise TypeError, "type mismatch: String given" - end + raise TypeError, "type mismatch: String given" if re.respond_to? :to_str re =~ self end diff --git a/mruby-source.gemspec b/mruby-source.gemspec new file mode 100644 index 000000000..62d4c0d12 --- /dev/null +++ b/mruby-source.gemspec @@ -0,0 +1,18 @@ +# coding: utf-8 +lib = File.expand_path('../lib', __FILE__) +$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib) +require 'mruby/source' + +Gem::Specification.new do |spec| + spec.name = "mruby-source" + spec.version = MRuby::Source::MRUBY_VERSION + spec.authors = [ MRuby::Source::MRUBY_AUTHOR ] + + spec.summary = %q{MRuby source code wrapper.} + spec.description = %q{MRuby source code wrapper for use with Ruby libs.} + spec.homepage = "http://www.mruby.org/" + spec.license = "MIT" + + spec.files = `git ls-files -z`.split("\x0") + spec.require_paths = ["lib"] +end diff --git a/src/array.c b/src/array.c index 2622ee528..aa914952a 100644 --- a/src/array.c +++ b/src/array.c @@ -1101,4 +1101,5 @@ mrb_init_array(mrb_state *mrb) mrb_define_method(mrb, a, "__ary_eq", mrb_ary_eq, MRB_ARGS_REQ(1)); mrb_define_method(mrb, a, "__ary_cmp", mrb_ary_cmp, MRB_ARGS_REQ(1)); + mrb_define_method(mrb, a, "__ary_index", mrb_ary_index_m, MRB_ARGS_REQ(1)); /* kept for mruby-array-ext */ } diff --git a/src/class.c b/src/class.c index 33fb61211..c3c3e0b8f 100644 --- a/src/class.c +++ b/src/class.c @@ -76,7 +76,7 @@ prepare_singleton_class(mrb_state *mrb, struct RBasic *o) if (o->c->tt == MRB_TT_SCLASS) return; sc = (struct RClass*)mrb_obj_alloc(mrb, MRB_TT_SCLASS, mrb->class_class); - sc->mt = 0; + sc->mt = kh_init(mt, mrb); sc->iv = 0; if (o->tt == MRB_TT_CLASS) { c = (struct RClass*)o; @@ -188,12 +188,20 @@ mrb_define_module_under(mrb_state *mrb, struct RClass *outer, const char *name) } static struct RClass* +find_origin(struct RClass *c) +{ + MRB_CLASS_ORIGIN(c); + return c; +} + +static struct RClass* define_class(mrb_state *mrb, mrb_sym name, struct RClass *super, struct RClass *outer) { struct RClass * c; if (mrb_const_defined_at(mrb, mrb_obj_value(outer), name)) { c = class_from_sym(mrb, outer, name); + MRB_CLASS_ORIGIN(c); if (super && mrb_class_real(c->super) != super) { mrb_raisef(mrb, E_TYPE_ERROR, "superclass mismatch for Class %S (%S not %S)", mrb_sym2str(mrb, name), @@ -323,8 +331,10 @@ mrb_define_class_under(mrb_state *mrb, struct RClass *outer, const char *name, s MRB_API void mrb_define_method_raw(mrb_state *mrb, struct RClass *c, mrb_sym mid, struct RProc *p) { - khash_t(mt) *h = c->mt; + khash_t(mt) *h; khiter_t k; + MRB_CLASS_ORIGIN(c); + h = c->mt; if (!h) h = c->mt = kh_init(mt, mrb); k = kh_put(mt, mrb, h, mid); @@ -817,47 +827,130 @@ boot_defclass(mrb_state *mrb, struct RClass *super) return c; } -MRB_API void -mrb_include_module(mrb_state *mrb, struct RClass *c, struct RClass *m) +static void +boot_initmod(mrb_state *mrb, struct RClass *mod) { - struct RClass *ins_pos; + mod->mt = kh_init(mt, mrb); +} + +static struct RClass* +include_class_new(mrb_state *mrb, struct RClass *m, struct RClass *super) +{ + struct RClass *ic = (struct RClass*)mrb_obj_alloc(mrb, MRB_TT_ICLASS, mrb->class_class); + if (m->tt == MRB_TT_ICLASS) { + m = m->c; + } + MRB_CLASS_ORIGIN(m); + ic->iv = m->iv; + ic->mt = m->mt; + ic->super = super; + if (m->tt == MRB_TT_ICLASS) { + ic->c = m->c; + } else { + ic->c = m; + } + return ic; +} + +static int +include_module_at(mrb_state *mrb, struct RClass *c, struct RClass *ins_pos, struct RClass *m, int search_super) +{ + struct RClass *p, *ic; + void *klass_mt = find_origin(c)->mt; - ins_pos = c; while (m) { - struct RClass *p = c, *ic; int superclass_seen = 0; - if (c->mt && c->mt == m->mt) { - mrb_raise(mrb, E_ARGUMENT_ERROR, "cyclic include detected"); - } - while (p) { - if (c != p && p->tt == MRB_TT_CLASS) { - superclass_seen = 1; - } - else if (p->mt == m->mt) { - if (p->tt == MRB_TT_ICLASS && !superclass_seen) { - ins_pos = p; + if (m->flags & MRB_FLAG_IS_PREPENDED) + goto skip; + + if (klass_mt && klass_mt == m->mt) + return -1; + + p = c->super; + while(p) { + if (p->tt == MRB_TT_ICLASS) { + if (p->mt == m->mt) { + if (!superclass_seen) { + ins_pos = p; // move insert point + } + goto skip; } - goto skip; + } else if (p->tt == MRB_TT_CLASS) { + if (!search_super) break; + superclass_seen = 1; } p = p->super; } - ic = (struct RClass*)mrb_obj_alloc(mrb, MRB_TT_ICLASS, mrb->class_class); - if (m->tt == MRB_TT_ICLASS) { - ic->c = m->c; - } - else { - ic->c = m; - } - ic->mt = m->mt; - ic->iv = m->iv; - ic->super = ins_pos->super; + + ic = include_class_new(mrb, m, ins_pos->super); ins_pos->super = ic; - mrb_field_write_barrier(mrb, (struct RBasic*)ins_pos, (struct RBasic*)ic); + mrb_field_write_barrier(mrb, (struct RBasic*)ins_pos, (struct RBasic*)ins_pos->super); ins_pos = ic; skip: m = m->super; } + return 0; +} + +MRB_API void +mrb_include_module(mrb_state *mrb, struct RClass *c, struct RClass *m) +{ + int changed = include_module_at(mrb, c, find_origin(c), m, 1); + if (changed < 0) { + mrb_raise(mrb, E_ARGUMENT_ERROR, "cyclic include detected"); + } +} + +MRB_API void +mrb_prepend_module(mrb_state *mrb, struct RClass *c, struct RClass *m) +{ + struct RClass *origin; + int changed = 0; + + if (!(c->flags & MRB_FLAG_IS_PREPENDED)) { + origin = (struct RClass*)mrb_obj_alloc(mrb, MRB_TT_ICLASS, c); + origin->flags |= MRB_FLAG_IS_ORIGIN; + origin->super = c->super; + c->super = origin; + origin->mt = c->mt; + c->mt = kh_init(mt, mrb); + mrb_field_write_barrier(mrb, (struct RBasic*)c, (struct RBasic*)origin); + c->flags |= MRB_FLAG_IS_PREPENDED; + } + changed = include_module_at(mrb, c, c, m, 0); + if (changed < 0) { + mrb_raise(mrb, E_ARGUMENT_ERROR, "cyclic prepend detected"); + } +} + +static mrb_value +mrb_mod_prepend_features(mrb_state *mrb, mrb_value mod) +{ + mrb_value klass; + + mrb_check_type(mrb, mod, MRB_TT_MODULE); + mrb_get_args(mrb, "C", &klass); + mrb_prepend_module(mrb, mrb_class_ptr(klass), mrb_class_ptr(mod)); + return mod; +} + +static mrb_value +mrb_mod_prepend(mrb_state *mrb, mrb_value klass) +{ + mrb_value *argv; + mrb_int argc, i; + + mrb_get_args(mrb, "*", &argv, &argc); + for (i=0; i<argc; i++) { + mrb_check_type(mrb, argv[i], MRB_TT_MODULE); + } + while (argc--) { + mrb_funcall(mrb, argv[argc], "prepend_features", 1, klass); + mrb_funcall(mrb, argv[argc], "prepended", 1, klass); + } + + return klass; } static mrb_value @@ -931,15 +1024,12 @@ mrb_mod_ancestors(mrb_state *mrb, mrb_value self) { mrb_value result; struct RClass *c = mrb_class_ptr(self); - result = mrb_ary_new(mrb); - mrb_ary_push(mrb, result, mrb_obj_value(c)); - c = c->super; while (c) { if (c->tt == MRB_TT_ICLASS) { mrb_ary_push(mrb, result, mrb_obj_value(c->c)); } - else if (c->tt != MRB_TT_SCLASS) { + else if (!(c->flags & MRB_FLAG_IS_PREPENDED)) { mrb_ary_push(mrb, result, mrb_obj_value(c)); } c = c->super; @@ -964,11 +1054,15 @@ mrb_mod_included_modules(mrb_state *mrb, mrb_value self) { mrb_value result; struct RClass *c = mrb_class_ptr(self); + struct RClass *origin = c; + MRB_CLASS_ORIGIN(origin); result = mrb_ary_new(mrb); while (c) { - if (c->tt == MRB_TT_ICLASS) { - mrb_ary_push(mrb, result, mrb_obj_value(c->c)); + if (c != origin && c->tt == MRB_TT_ICLASS) { + if (c->c->tt == MRB_TT_MODULE) { + mrb_ary_push(mrb, result, mrb_obj_value(c->c)); + } } c = c->super; } @@ -980,10 +1074,11 @@ static mrb_value mrb_mod_initialize(mrb_state *mrb, mrb_value mod) { mrb_value b; - - mrb_get_args(mrb, "&", &b); + struct RClass *m = mrb_class_ptr(mod); + boot_initmod(mrb, m); // bootstrap a newly initialized module + mrb_get_args(mrb, "|&", &b); if (!mrb_nil_p(b)) { - mrb_yield_with_class(mrb, b, 1, &mod, mod, mrb_class_ptr(mod)); + mrb_yield_with_class(mrb, b, 1, &mod, mod, m); } return mod; } @@ -1300,9 +1395,9 @@ mrb_class_superclass(mrb_state *mrb, mrb_value klass) struct RClass *c; c = mrb_class_ptr(klass); - c = c->super; + c = find_origin(c)->super; while (c && c->tt == MRB_TT_ICLASS) { - c = c->super; + c = find_origin(c)->super; } if (!c) return mrb_nil_value(); return mrb_obj_value(c); @@ -1540,8 +1635,7 @@ MRB_API struct RClass* mrb_module_new(mrb_state *mrb) { struct RClass *m = (struct RClass*)mrb_obj_alloc(mrb, MRB_TT_MODULE, mrb->module_class); - m->mt = kh_init(mt, mrb); - + boot_initmod(mrb, m); return m; } @@ -1900,13 +1994,14 @@ static void remove_method(mrb_state *mrb, mrb_value mod, mrb_sym mid) { struct RClass *c = mrb_class_ptr(mod); - khash_t(mt) *h = c->mt; + khash_t(mt) *h = find_origin(c)->mt; khiter_t k; if (h) { k = kh_get(mt, mrb, h, mid); if (k != kh_end(h)) { kh_del(mt, mrb, h, k); + mrb_funcall(mrb, mod, "method_removed", 1, mrb_symbol_value(mid)); return; } } @@ -2140,6 +2235,9 @@ mrb_init_class(mrb_state *mrb) mrb_define_method(mrb, mod, "class_variable_set", mrb_mod_cvar_set, MRB_ARGS_REQ(2)); /* 15.2.2.4.18 */ mrb_define_method(mrb, mod, "extend_object", mrb_mod_extend_object, MRB_ARGS_REQ(1)); /* 15.2.2.4.25 */ mrb_define_method(mrb, mod, "extended", mrb_bob_init, MRB_ARGS_REQ(1)); /* 15.2.2.4.26 */ + mrb_define_method(mrb, mod, "prepend", mrb_mod_prepend, MRB_ARGS_ANY()); + mrb_define_method(mrb, mod, "prepended", mrb_bob_init, MRB_ARGS_REQ(1)); + mrb_define_method(mrb, mod, "prepend_features", mrb_mod_prepend_features, MRB_ARGS_REQ(1)); mrb_define_method(mrb, mod, "include", mrb_mod_include, MRB_ARGS_ANY()); /* 15.2.2.4.27 */ mrb_define_method(mrb, mod, "include?", mrb_mod_include_p, MRB_ARGS_REQ(1)); /* 15.2.2.4.28 */ mrb_define_method(mrb, mod, "append_features", mrb_mod_append_features, MRB_ARGS_REQ(1)); /* 15.2.2.4.10 */ @@ -2156,6 +2254,7 @@ mrb_init_class(mrb_state *mrb) mrb_define_method(mrb, mod, "public", mrb_mod_dummy_visibility, MRB_ARGS_ANY()); /* 15.2.2.4.38 */ mrb_define_method(mrb, mod, "remove_class_variable", mrb_mod_remove_cvar, MRB_ARGS_REQ(1)); /* 15.2.2.4.39 */ mrb_define_method(mrb, mod, "remove_method", mrb_mod_remove_method, MRB_ARGS_ANY()); /* 15.2.2.4.41 */ + mrb_define_method(mrb, mod, "method_removed", mrb_bob_init, MRB_ARGS_REQ(1)); mrb_define_method(mrb, mod, "attr_reader", mrb_mod_attr_reader, MRB_ARGS_ANY()); /* 15.2.2.4.13 */ mrb_define_method(mrb, mod, "attr_writer", mrb_mod_attr_writer, MRB_ARGS_ANY()); /* 15.2.2.4.14 */ mrb_define_method(mrb, mod, "to_s", mrb_mod_to_s, MRB_ARGS_NONE()); diff --git a/src/dump.c b/src/dump.c index 462e036b4..734f38043 100644 --- a/src/dump.c +++ b/src/dump.c @@ -16,7 +16,7 @@ #define FLAG_BYTEORDER_NONATIVE 0 #ifdef MRB_USE_FLOAT -#define MRB_FLOAT_FMT "%.9e" +#define MRB_FLOAT_FMT "%.8e" #else #define MRB_FLOAT_FMT "%.16e" #endif @@ -385,6 +385,7 @@ gc_protect(mrb_state *mrb, struct RBasic *p) mrb->arena[mrb->arena_idx++] = p; } +/* mrb_gc_protect() leaves the object in the arena */ MRB_API void mrb_gc_protect(mrb_state *mrb, mrb_value obj) { @@ -392,6 +393,53 @@ mrb_gc_protect(mrb_state *mrb, mrb_value obj) gc_protect(mrb, mrb_basic_ptr(obj)); } +#define GC_ROOT_NAME "_gc_root_" + +/* mrb_gc_register() keeps the object from GC. + + Register your object when it's exported to C world, + without reference from Ruby world, e.g. callback + arguments. Don't forget to remove the obejct using + mrb_gc_unregister, otherwise your object will leak. +*/ + +MRB_API void +mrb_gc_register(mrb_state *mrb, mrb_value obj) +{ + mrb_sym root = mrb_intern_lit(mrb, GC_ROOT_NAME); + mrb_value table = mrb_gv_get(mrb, root); + + if (mrb_nil_p(table) || mrb_type(table) != MRB_TT_ARRAY) { + table = mrb_ary_new(mrb); + mrb_gv_set(mrb, root, table); + } + mrb_ary_push(mrb, table, obj); +} + +/* mrb_gc_unregister() removes the object from GC root. */ +MRB_API void +mrb_gc_unregister(mrb_state *mrb, mrb_value obj) +{ + mrb_sym root = mrb_intern_lit(mrb, GC_ROOT_NAME); + mrb_value table = mrb_gv_get(mrb, root); + struct RArray *a; + mrb_int i, j; + + if (mrb_nil_p(table)) return; + if (mrb_type(table) != MRB_TT_ARRAY) { + mrb_gv_set(mrb, root, mrb_nil_value()); + return; + } + a = mrb_ary_ptr(table); + mrb_ary_modify(mrb, a); + for (i=j=0; i<a->len; i++) { + if (!mrb_obj_eq(mrb, a->ptr[i], obj)) { + a->ptr[j++] = a->ptr[i]; + } + } + a->len = j; +} + MRB_API struct RBasic* mrb_obj_alloc(mrb_state *mrb, enum mrb_vtype ttype, struct RClass *cls) { @@ -498,7 +546,12 @@ gc_mark_children(mrb_state *mrb, struct RBasic *obj) mrb_gc_mark(mrb, (struct RBasic*)obj->c); switch (obj->tt) { case MRB_TT_ICLASS: - mrb_gc_mark(mrb, (struct RBasic*)((struct RClass*)obj)->super); + { + struct RClass *c = (struct RClass*)obj; + if (MRB_FLAG_TEST(c, MRB_FLAG_IS_ORIGIN)) + mrb_gc_mark_mt(mrb, c); + mrb_gc_mark(mrb, (struct RBasic*)((struct RClass*)obj)->super); + } break; case MRB_TT_CLASS: @@ -624,7 +677,10 @@ obj_free(mrb_state *mrb, struct RBasic *obj) mrb_gc_free_mt(mrb, (struct RClass*)obj); mrb_gc_free_iv(mrb, (struct RObject*)obj); break; - + case MRB_TT_ICLASS: + if (MRB_FLAG_TEST(obj, MRB_FLAG_IS_ORIGIN)) + mrb_gc_free_mt(mrb, (struct RClass*)obj); + break; case MRB_TT_ENV: { struct REnv *e = (struct REnv*)obj; diff --git a/src/hash.c b/src/hash.c index 0bda2b48b..ffb8bd931 100644 --- a/src/hash.c +++ b/src/hash.c @@ -104,10 +104,11 @@ static void mrb_hash_modify(mrb_state *mrb, mrb_value hash); static inline mrb_value mrb_hash_ht_key(mrb_state *mrb, mrb_value key) { - if (mrb_string_p(key)) - return mrb_str_dup(mrb, key); - else - return key; + if (mrb_string_p(key) && !RSTR_FROZEN_P(mrb_str_ptr(key))) { + key = mrb_str_dup(mrb, key); + RSTR_SET_FROZEN_FLAG(mrb_str_ptr(key)); + } + return key; } #define KEY(key) mrb_hash_ht_key(mrb, key) diff --git a/src/kernel.c b/src/kernel.c index b5b13f874..759dc42b7 100644 --- a/src/kernel.c +++ b/src/kernel.c @@ -240,14 +240,12 @@ mrb_singleton_class_clone(mrb_state *mrb, mrb_value obj) /* copy singleton(unnamed) class */ struct RClass *clone = (struct RClass*)mrb_obj_alloc(mrb, klass->tt, mrb->class_class); - if ((mrb_type(obj) == MRB_TT_CLASS) || - (mrb_type(obj) == MRB_TT_SCLASS)) { /* BUILTIN_TYPE(obj) == T_CLASS */ + if ((mrb_type(obj) == MRB_TT_CLASS) || (mrb_type(obj) == MRB_TT_SCLASS)) { clone->c = clone; } else { clone->c = mrb_singleton_class_clone(mrb, mrb_obj_value(klass)); } - clone->super = klass->super; if (klass->iv) { mrb_iv_copy(mrb, mrb_obj_value(clone), mrb_obj_value(klass)); @@ -269,6 +267,21 @@ copy_class(mrb_state *mrb, mrb_value dst, mrb_value src) { struct RClass *dc = mrb_class_ptr(dst); struct RClass *sc = mrb_class_ptr(src); + /* if the origin is not the same as the class, then the origin and + the current class need to be copied */ + if (sc->flags & MRB_FLAG_IS_PREPENDED) { + struct RClass *c0 = sc->super; + struct RClass *c1 = dc; + + /* copy prepended iclasses */ + while (!(c0->flags & MRB_FLAG_IS_ORIGIN)) { + c1->super = mrb_class_ptr(mrb_obj_dup(mrb, mrb_obj_value(c0))); + c1 = c1->super; + c0 = c0->super; + } + c1->super = mrb_class_ptr(mrb_obj_dup(mrb, mrb_obj_value(c0))); + c1->super->flags |= MRB_FLAG_IS_ORIGIN; + } dc->mt = kh_copy(mt, mrb, sc->mt); dc->super = sc->super; } @@ -641,13 +654,19 @@ mrb_class_instance_method_list(mrb_state *mrb, mrb_bool recur, struct RClass* kl { khint_t i; mrb_value ary; + mrb_bool prepended = FALSE; struct RClass* oldklass; khash_t(st)* set = kh_init(st, mrb); + if (!recur && (klass->flags & MRB_FLAG_IS_PREPENDED)) { + MRB_CLASS_ORIGIN(klass); + prepended = TRUE; + } + oldklass = 0; while (klass && (klass != oldklass)) { method_entry_loop(mrb, klass, set); - if ((klass->tt == MRB_TT_ICLASS) || + if ((klass->tt == MRB_TT_ICLASS && !prepended) || (klass->tt == MRB_TT_SCLASS)) { } else { diff --git a/src/load.c b/src/load.c index a9f1641bf..36fae9aee 100644 --- a/src/load.c +++ b/src/load.c @@ -529,7 +529,7 @@ read_binary_header(const uint8_t *bin, size_t *bin_size, uint16_t *crc, uint8_t else if (memcmp(header->binary_ident, RITE_BINARY_IDENT_LIL, sizeof(header->binary_ident)) == 0) { if (bigendian_p()) *flags |= FLAG_BYTEORDER_LIL; - else + else *flags |= FLAG_BYTEORDER_NATIVE; } else { diff --git a/src/numeric.c b/src/numeric.c index 14c0b76a6..1a3c903f0 100644 --- a/src/numeric.c +++ b/src/numeric.c @@ -820,11 +820,13 @@ fix_xor(mrb_state *mrb, mrb_value x) static mrb_value lshift(mrb_state *mrb, mrb_int val, mrb_int width) { - mrb_assert(width >= 0); + mrb_assert(width > 0); if (width > NUMERIC_SHIFT_WIDTH_MAX) { - mrb_raisef(mrb, E_RANGE_ERROR, "width(%S) > (%S:MRB_INT_BIT-1)", - mrb_fixnum_value(width), - mrb_fixnum_value(NUMERIC_SHIFT_WIDTH_MAX)); + mrb_float f = (mrb_float)val; + while (width--) { + f *= 2; + } + return mrb_float_value(mrb, f); } return mrb_fixnum_value(val << width); } @@ -832,7 +834,7 @@ lshift(mrb_state *mrb, mrb_int val, mrb_int width) static mrb_value rshift(mrb_int val, mrb_int width) { - mrb_assert(width >= 0); + mrb_assert(width > 0); if (width >= NUMERIC_SHIFT_WIDTH_MAX) { if (val < 0) { return mrb_fixnum_value(-1); diff --git a/src/object.c b/src/object.c index f8f41bfe8..2e0bd245f 100644 --- a/src/object.c +++ b/src/object.c @@ -487,6 +487,7 @@ mrb_obj_is_kind_of(mrb_state *mrb, mrb_value obj, struct RClass *c) mrb_raise(mrb, E_TYPE_ERROR, "class or module required"); } + MRB_CLASS_ORIGIN(c); while (cl) { if (cl == c || cl->mt == c->mt) return TRUE; diff --git a/src/state.c b/src/state.c index 2efd34334..bfd99e4c3 100644 --- a/src/state.c +++ b/src/state.c @@ -234,6 +234,7 @@ mrb_free_context(mrb_state *mrb, struct mrb_context *c) MRB_API void mrb_close(mrb_state *mrb) { + if (!mrb) return; if (mrb->atexit_stack_len > 0) { mrb_int i; for (i = mrb->atexit_stack_len; i > 0; --i) { diff --git a/src/string.c b/src/string.c index 45ba38c9d..b597c3da9 100644 --- a/src/string.c +++ b/src/string.c @@ -16,8 +16,6 @@ #include "mruby/string.h" #include "mruby/re.h" -const char mrb_digitmap[] = "0123456789abcdefghijklmnopqrstuvwxyz"; - typedef struct mrb_shared_string { mrb_bool nofree : 1; int refcnt; @@ -25,117 +23,7 @@ typedef struct mrb_shared_string { mrb_int len; } mrb_shared_string; -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_API mrb_int -mrb_str_strlen(mrb_state *mrb, struct RString *s) -{ - mrb_int i, max = RSTR_LEN(s); - char *p = RSTR_PTR(s); - - if (!p) return 0; - for (i=0; i<max; i++) { - if (p[i] == '\0') { - mrb_raise(mrb, E_ARGUMENT_ERROR, "string contains null byte"); - } - } - return max; -} - -static inline void -resize_capa(mrb_state *mrb, struct RString *s, mrb_int capacity) -{ - if (RSTR_EMBED_P(s)) { - if (RSTRING_EMBED_LEN_MAX < capacity) { - char *const tmp = (char *)mrb_malloc(mrb, capacity+1); - const mrb_int len = RSTR_EMBED_LEN(s); - memcpy(tmp, s->as.ary, len); - 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, RSTR_PTR(s), capacity+1); - s->as.heap.aux.capa = capacity; - } -} - -static void -str_decref(mrb_state *mrb, mrb_shared_string *shared) -{ - shared->refcnt--; - if (shared->refcnt == 0) { - if (!shared->nofree) { - mrb_free(mrb, shared->ptr); - } - mrb_free(mrb, shared); - } -} - -MRB_API void -mrb_str_modify(mrb_state *mrb, struct RString *s) -{ - if (RSTR_SHARED_P(s)) { - mrb_shared_string *shared = s->as.heap.aux.shared; - - if (shared->refcnt == 1 && s->as.heap.ptr == shared->ptr) { - s->as.heap.ptr = shared->ptr; - s->as.heap.aux.capa = shared->len; - RSTR_PTR(s)[s->as.heap.len] = '\0'; - mrb_free(mrb, shared); - } - else { - char *ptr, *p; - mrb_int len; - - p = RSTR_PTR(s); - len = s->as.heap.len; - ptr = (char *)mrb_malloc(mrb, (size_t)len + 1); - if (p) { - memcpy(ptr, p, len); - } - ptr[len] = '\0'; - s->as.heap.ptr = ptr; - s->as.heap.aux.capa = len; - str_decref(mrb, shared); - } - RSTR_UNSET_SHARED_FLAG(s); - return; - } - 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(RSTR_PTR(s), p, s->as.heap.len); - } - RSTR_PTR(s)[s->as.heap.len] = '\0'; - s->as.heap.aux.capa = s->as.heap.len; - RSTR_UNSET_NOFREE_FLAG(s); - return; - } -} - -MRB_API mrb_value -mrb_str_resize(mrb_state *mrb, mrb_value str, mrb_int len) -{ - mrb_int slen; - struct RString *s = mrb_str_ptr(str); - - mrb_str_modify(mrb, s); - slen = RSTR_LEN(s); - if (len != slen) { - if (slen < len || slen - len > 256) { - resize_capa(mrb, s, len); - } - RSTR_SET_LEN(s, len); - RSTR_PTR(s)[len] = '\0'; /* sentinel */ - } - return str; -} +const char mrb_digitmap[] = "0123456789abcdefghijklmnopqrstuvwxyz"; #define mrb_obj_alloc_string(mrb) ((struct RString*)mrb_obj_alloc((mrb), MRB_TT_STRING, (mrb)->string_class)) @@ -226,6 +114,26 @@ mrb_str_buf_new(mrb_state *mrb, size_t capa) return mrb_obj_value(s); } +static inline void +resize_capa(mrb_state *mrb, struct RString *s, mrb_int capacity) +{ + if (RSTR_EMBED_P(s)) { + if (RSTRING_EMBED_LEN_MAX < capacity) { + char *const tmp = (char *)mrb_malloc(mrb, capacity+1); + const mrb_int len = RSTR_EMBED_LEN(s); + memcpy(tmp, s->as.ary, len); + 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, RSTR_PTR(s), capacity+1); + s->as.heap.aux.capa = capacity; + } +} + static void str_buf_cat(mrb_state *mrb, struct RString *s, const char *ptr, size_t len) { @@ -305,6 +213,18 @@ mrb_str_new_static(mrb_state *mrb, const char *p, size_t len) return mrb_obj_value(s); } +static void +str_decref(mrb_state *mrb, mrb_shared_string *shared) +{ + shared->refcnt--; + if (shared->refcnt == 0) { + if (!shared->nofree) { + mrb_free(mrb, shared->ptr); + } + mrb_free(mrb, shared); + } +} + void mrb_gc_free_str(mrb_state *mrb, struct RString *str) { @@ -316,20 +236,126 @@ mrb_gc_free_str(mrb_state *mrb, struct RString *str) mrb_free(mrb, str->as.heap.ptr); } -MRB_API char* -mrb_str_to_cstr(mrb_state *mrb, mrb_value str0) +#ifdef MRB_UTF8_STRING +static const char utf8len_codepage[256] = { - struct RString *s; + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,4,4,4,4,4,1,1,1,1,1,1,1,1,1,1,1, +}; - if (!mrb_string_p(str0)) { - mrb_raise(mrb, E_TYPE_ERROR, "expected String"); +static mrb_int +utf8len(const char* p, const char* e) +{ + mrb_int len; + mrb_int i; + + len = utf8len_codepage[(unsigned char)*p]; + if (p + len > e) return 1; + for (i = 1; i < len; ++i) + if ((p[i] & 0xc0) != 0x80) + return 1; + return len; +} + +static mrb_int +utf8_strlen(mrb_value str, mrb_int len) +{ + mrb_int total = 0; + char* p = RSTRING_PTR(str); + char* e = p; + e += len < 0 ? RSTRING_LEN(str) : len; + while (p<e) { + p += utf8len(p, e); + total++; } + return total; +} - s = str_new(mrb, RSTRING_PTR(str0), RSTRING_LEN(str0)); - if ((strlen(RSTR_PTR(s)) ^ RSTR_LEN(s)) != 0) { - mrb_raise(mrb, E_ARGUMENT_ERROR, "string contains null byte"); +#define RSTRING_CHAR_LEN(s) utf8_strlen(s, -1) + +/* map character index to byte offset index */ +static mrb_int +chars2bytes(mrb_value s, mrb_int off, mrb_int idx) +{ + mrb_int i, b, n; + const char *p = RSTRING_PTR(s) + off; + const char *e = RSTRING_END(s); + + for (b=i=0; p<e && i<idx; i++) { + n = utf8len(p, e); + b += n; + p += n; } - return RSTR_PTR(s); + return b; +} + +/* map byte offset to character index */ +static mrb_int +bytes2chars(char *p, mrb_int bi) +{ + mrb_int i, b, n; + + for (b=i=0; b<bi; i++) { + n = utf8len(p, p+bi); + b += n; + p += n; + } + return i; +} + +#else +#define RSTRING_CHAR_LEN(s) RSTRING_LEN(s) +#define chars2bytes(p, off, ci) (ci) +#define bytes2chars(p, bi) (bi) +#endif + +static inline mrb_int +mrb_memsearch_qs(const unsigned char *xs, mrb_int m, const unsigned char *ys, mrb_int n) +{ + const unsigned char *x = xs, *xe = xs + m; + const unsigned char *y = ys; + int i, qstable[256]; + + /* Preprocessing */ + for (i = 0; i < 256; ++i) + qstable[i] = m + 1; + for (; x < xe; ++x) + qstable[*x] = xe - x; + /* Searching */ + for (; y + m <= ys + n; y += *(qstable + y[m])) { + if (*xs == *y && memcmp(xs, y, m) == 0) + return y - ys; + } + return -1; +} + +static mrb_int +mrb_memsearch(const void *x0, mrb_int m, const void *y0, mrb_int n) +{ + const unsigned char *x = (const unsigned char *)x0, *y = (const unsigned char *)y0; + + if (m > n) return -1; + else if (m == n) { + return memcmp(x0, y0, m) == 0 ? 0 : -1; + } + else if (m < 1) { + return 0; + } + else if (m == 1) { + const unsigned char *ys = y, *ye = ys + n; + for (; y < ye; ++y) { + if (*x == *y) + return y - ys; + } + return -1; + } + return mrb_memsearch_qs((const unsigned char *)x0, m, (const unsigned char *)y0, n); } static void @@ -370,6 +396,339 @@ str_make_shared(mrb_state *mrb, struct RString *s) } } +static mrb_value +byte_subseq(mrb_state *mrb, mrb_value str, mrb_int beg, mrb_int len) +{ + struct RString *orig, *s; + mrb_shared_string *shared; + + orig = mrb_str_ptr(str); + if (RSTR_EMBED_P(orig)) { + s = str_new(mrb, orig->as.ary+beg, len); + } + else { + str_make_shared(mrb, orig); + shared = orig->as.heap.aux.shared; + s = mrb_obj_alloc_string(mrb); + s->as.heap.ptr = orig->as.heap.ptr + beg; + s->as.heap.len = len; + s->as.heap.aux.shared = shared; + RSTR_SET_SHARED_FLAG(s); + shared->refcnt++; + } + + return mrb_obj_value(s); +} +#ifdef MRB_UTF8_STRING +static inline mrb_value +str_subseq(mrb_state *mrb, mrb_value str, mrb_int beg, mrb_int len) +{ + beg = chars2bytes(str, 0, beg); + len = chars2bytes(str, beg, len); + + return byte_subseq(mrb, str, beg, len); +} +#else +#define str_subseq(mrb, str, beg, len) byte_subseq(mrb, str, beg, len) +#endif + +static mrb_value +str_substr(mrb_state *mrb, mrb_value str, mrb_int beg, mrb_int len) +{ + mrb_int clen = RSTRING_CHAR_LEN(str); + + if (len < 0) return mrb_nil_value(); + if (clen == 0) { + len = 0; + } + else if (beg < 0) { + beg = clen + beg; + } + if (beg > clen) return mrb_nil_value(); + if (beg < 0) { + beg += clen; + if (beg < 0) return mrb_nil_value(); + } + if (beg + len > clen) + len = clen - beg; + if (len <= 0) { + len = 0; + } + return str_subseq(mrb, str, beg, len); +} + +static mrb_int +str_index(mrb_state *mrb, mrb_value str, mrb_value sub, mrb_int offset) +{ + mrb_int pos; + char *s, *sptr; + mrb_int len, slen; + + len = RSTRING_LEN(str); + slen = RSTRING_LEN(sub); + if (offset < 0) { + offset += len; + if (offset < 0) return -1; + } + if (len - offset < slen) return -1; + s = RSTRING_PTR(str); + if (offset) { + s += offset; + } + if (slen == 0) return offset; + /* need proceed one character at a time */ + sptr = RSTRING_PTR(sub); + slen = RSTRING_LEN(sub); + len = RSTRING_LEN(str) - offset; + pos = mrb_memsearch(sptr, slen, s, len); + if (pos < 0) return pos; + return pos + offset; +} + +static void +check_frozen(mrb_state *mrb, struct RString *s) +{ + if (RSTR_FROZEN_P(s)) { + mrb_raise(mrb, E_RUNTIME_ERROR, "can't modify frozen string"); + } +} + +static mrb_value +str_replace(mrb_state *mrb, struct RString *s1, struct RString *s2) +{ + long len; + + check_frozen(mrb, 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; + RSTR_SET_SHARED_FLAG(s1); + s1->as.heap.aux.shared->refcnt++; + } + else { + if (len <= RSTRING_EMBED_LEN_MAX) { + 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); + goto L_SHARE; + } + } + + return mrb_obj_value(s1); +} + +static mrb_int +str_rindex(mrb_state *mrb, mrb_value str, mrb_value sub, mrb_int pos) +{ + char *s, *sbeg, *t; + struct RString *ps = mrb_str_ptr(str); + mrb_int len = RSTRING_LEN(sub); + + /* substring longer than string */ + if (RSTR_LEN(ps) < len) return -1; + if (RSTR_LEN(ps) - pos < len) { + pos = RSTR_LEN(ps) - len; + } + 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 - RSTR_PTR(ps); + } + s--; + } + return -1; + } + else { + return pos; + } +} + +MRB_API mrb_int +mrb_str_strlen(mrb_state *mrb, struct RString *s) +{ + mrb_int i, max = RSTR_LEN(s); + char *p = RSTR_PTR(s); + + if (!p) return 0; + for (i=0; i<max; i++) { + if (p[i] == '\0') { + mrb_raise(mrb, E_ARGUMENT_ERROR, "string contains null byte"); + } + } + return max; +} + +#ifdef _WIN32 +#include <windows.h> + +char* +mrb_utf8_from_locale(const char *str, size_t len) +{ + wchar_t* wcsp; + char* mbsp; + size_t mbssize, wcssize; + + if (len == 0) + return strdup(""); + if (len == -1) + len = strlen(str); + wcssize = MultiByteToWideChar(GetACP(), 0, str, len, NULL, 0); + wcsp = (wchar_t*) malloc((wcssize + 1) * sizeof(wchar_t)); + if (!wcsp) + return NULL; + wcssize = MultiByteToWideChar(GetACP(), 0, str, len, wcsp, wcssize + 1); + wcsp[wcssize] = 0; + + mbssize = WideCharToMultiByte(CP_UTF8, 0, (LPCWSTR) wcsp, -1, NULL, 0, NULL, NULL); + mbsp = (char*) malloc((mbssize + 1)); + if (!mbsp) { + free(wcsp); + return NULL; + } + mbssize = WideCharToMultiByte(CP_UTF8, 0, (LPCWSTR) wcsp, -1, mbsp, mbssize, NULL, NULL); + mbsp[mbssize] = 0; + free(wcsp); + return mbsp; +} + +char* +mrb_locale_from_utf8(const char *utf8, size_t len) +{ + wchar_t* wcsp; + char* mbsp; + size_t mbssize, wcssize; + + if (len == 0) + return strdup(""); + if (len == -1) + len = strlen(utf8); + wcssize = MultiByteToWideChar(CP_UTF8, 0, utf8, len, NULL, 0); + wcsp = (wchar_t*) malloc((wcssize + 1) * sizeof(wchar_t)); + if (!wcsp) + return NULL; + wcssize = MultiByteToWideChar(CP_UTF8, 0, utf8, len, wcsp, wcssize + 1); + wcsp[wcssize] = 0; + mbssize = WideCharToMultiByte(GetACP(), 0, (LPCWSTR) wcsp, -1, NULL, 0, NULL, NULL); + mbsp = (char*) malloc((mbssize + 1)); + if (!mbsp) { + free(wcsp); + return NULL; + } + mbssize = WideCharToMultiByte(GetACP(), 0, (LPCWSTR) wcsp, -1, mbsp, mbssize, NULL, NULL); + mbsp[mbssize] = 0; + free(wcsp); + return mbsp; +} +#endif + +MRB_API void +mrb_str_modify(mrb_state *mrb, struct RString *s) +{ + check_frozen(mrb, 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; + RSTR_PTR(s)[s->as.heap.len] = '\0'; + mrb_free(mrb, shared); + } + else { + char *ptr, *p; + mrb_int len; + + p = RSTR_PTR(s); + len = s->as.heap.len; + ptr = (char *)mrb_malloc(mrb, (size_t)len + 1); + if (p) { + memcpy(ptr, p, len); + } + ptr[len] = '\0'; + s->as.heap.ptr = ptr; + s->as.heap.aux.capa = len; + str_decref(mrb, shared); + } + RSTR_UNSET_SHARED_FLAG(s); + return; + } + 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(RSTR_PTR(s), p, s->as.heap.len); + } + RSTR_PTR(s)[s->as.heap.len] = '\0'; + s->as.heap.aux.capa = s->as.heap.len; + RSTR_UNSET_NOFREE_FLAG(s); + return; + } +} + +static mrb_value +mrb_str_freeze(mrb_state *mrb, mrb_value str) +{ + struct RString *s = mrb_str_ptr(str); + + RSTR_SET_FROZEN_FLAG(s); + return str; +} + +MRB_API mrb_value +mrb_str_resize(mrb_state *mrb, mrb_value str, mrb_int len) +{ + mrb_int slen; + struct RString *s = mrb_str_ptr(str); + + mrb_str_modify(mrb, s); + slen = RSTR_LEN(s); + if (len != slen) { + if (slen < len || slen - len > 256) { + resize_capa(mrb, s, len); + } + RSTR_SET_LEN(s, len); + RSTR_PTR(s)[len] = '\0'; /* sentinel */ + } + return str; +} + +MRB_API char* +mrb_str_to_cstr(mrb_state *mrb, mrb_value str0) +{ + struct RString *s; + + if (!mrb_string_p(str0)) { + mrb_raise(mrb, E_TYPE_ERROR, "expected String"); + } + + s = str_new(mrb, RSTRING_PTR(str0), RSTRING_LEN(str0)); + if ((strlen(RSTR_PTR(s)) ^ RSTR_LEN(s)) != 0) { + mrb_raise(mrb, E_ARGUMENT_ERROR, "string contains null byte"); + } + return RSTR_PTR(s); +} + /* * call-seq: (Caution! String("abcd") change) * String("abcdefg") = String("abcd") + String("efg") @@ -438,15 +797,22 @@ mrb_str_plus_m(mrb_state *mrb, mrb_value self) /* 15.2.10.5.33 */ /* * call-seq: - * len = strlen(String("abcd")) + * "abcd".size => int * * Returns the length of string. */ static mrb_value mrb_str_size(mrb_state *mrb, mrb_value self) { - struct RString *s = mrb_str_ptr(self); - return mrb_fixnum_value(RSTR_LEN(s)); + mrb_int len = RSTRING_CHAR_LEN(self); + return mrb_fixnum_value(len); +} + +static mrb_value +mrb_str_bytesize(mrb_state *mrb, mrb_value self) +{ + mrb_int len = RSTRING_LEN(self); + return mrb_fixnum_value(len); } /* 15.2.10.5.1 */ @@ -661,77 +1027,6 @@ mrb_regexp_check(mrb_state *mrb, mrb_value obj) } } -static inline mrb_int -mrb_memsearch_qs(const unsigned char *xs, mrb_int m, const unsigned char *ys, mrb_int n) -{ - const unsigned char *x = xs, *xe = xs + m; - const unsigned char *y = ys; - int i, qstable[256]; - - /* Preprocessing */ - for (i = 0; i < 256; ++i) - qstable[i] = m + 1; - for (; x < xe; ++x) - qstable[*x] = xe - x; - /* Searching */ - for (; y + m <= ys + n; y += *(qstable + y[m])) { - if (*xs == *y && memcmp(xs, y, m) == 0) - return y - ys; - } - return -1; -} - -static mrb_int -mrb_memsearch(const void *x0, mrb_int m, const void *y0, mrb_int n) -{ - const unsigned char *x = (const unsigned char *)x0, *y = (const unsigned char *)y0; - - if (m > n) return -1; - else if (m == n) { - return memcmp(x0, y0, m) == 0 ? 0 : -1; - } - else if (m < 1) { - return 0; - } - else if (m == 1) { - const unsigned char *ys = y, *ye = ys + n; - for (; y < ye; ++y) { - if (*x == *y) - return y - ys; - } - return -1; - } - return mrb_memsearch_qs((const unsigned char *)x0, m, (const unsigned char *)y0, n); -} - -static mrb_int -mrb_str_index(mrb_state *mrb, mrb_value str, mrb_value sub, mrb_int offset) -{ - mrb_int pos; - char *s, *sptr; - mrb_int len, slen; - - len = RSTRING_LEN(str); - slen = RSTRING_LEN(sub); - if (offset < 0) { - offset += len; - if (offset < 0) return -1; - } - if (len - offset < slen) return -1; - s = RSTRING_PTR(str); - if (offset) { - s += offset; - } - if (slen == 0) return offset; - /* need proceed one character at a time */ - sptr = RSTRING_PTR(sub); - slen = RSTRING_LEN(sub); - len = RSTRING_LEN(str) - offset; - pos = mrb_memsearch(sptr, slen, s, len); - if (pos < 0) return pos; - return pos + offset; -} - MRB_API mrb_value mrb_str_dup(mrb_state *mrb, mrb_value str) { @@ -753,12 +1048,12 @@ mrb_str_aref(mrb_state *mrb, mrb_value str, mrb_value indx) idx = mrb_fixnum(indx); num_index: - str = mrb_str_substr(mrb, str, idx, 1); + str = str_substr(mrb, str, idx, 1); if (!mrb_nil_p(str) && RSTRING_LEN(str) == 0) return mrb_nil_value(); return str; case MRB_TT_STRING: - if (mrb_str_index(mrb, str, indx, 0) != -1) + if (str_index(mrb, str, indx, 0) != -1) return mrb_str_dup(mrb, indx); return mrb_nil_value(); @@ -767,9 +1062,9 @@ num_index: { mrb_int beg, len; - len = RSTRING_LEN(str); + len = RSTRING_CHAR_LEN(str); if (mrb_range_beg_len(mrb, indx, &beg, &len, len)) { - return mrb_str_subseq(mrb, str, beg, len); + return str_subseq(mrb, str, beg, len); } else { return mrb_nil_value(); @@ -836,7 +1131,7 @@ mrb_str_aref_m(mrb_state *mrb, mrb_value str) argc = mrb_get_args(mrb, "o|o", &a1, &a2); if (argc == 2) { mrb_regexp_check(mrb, a1); - return mrb_str_substr(mrb, str, mrb_fixnum(a1), mrb_fixnum(a2)); + return str_substr(mrb, str, mrb_fixnum(a1), mrb_fixnum(a2)); } if (argc != 1) { mrb_raisef(mrb, E_ARGUMENT_ERROR, "wrong number of arguments (%S for 1)", mrb_fixnum_value(argc)); @@ -906,7 +1201,7 @@ mrb_str_capitalize(mrb_state *mrb, mrb_value self) /* 15.2.10.5.10 */ /* * call-seq: - * str.chomp!(separator=$/) => str or nil + * str.chomp!(separator="\n") => str or nil * * Modifies <i>str</i> in place as described for <code>String#chomp</code>, * returning <i>str</i>, or <code>nil</code> if no modifications were made. @@ -980,7 +1275,7 @@ mrb_str_chomp_bang(mrb_state *mrb, mrb_value str) /* 15.2.10.5.9 */ /* * call-seq: - * str.chomp(separator=$/) => new_str + * str.chomp(separator="\n") => new_str * * Returns a new <code>String</code> with the given record separator removed * from the end of <i>str</i> (if present). If <code>$/</code> has not been @@ -1023,7 +1318,18 @@ mrb_str_chop_bang(mrb_state *mrb, mrb_value str) mrb_str_modify(mrb, s); if (RSTR_LEN(s) > 0) { mrb_int len; +#ifdef MRB_UTF8_STRING + const char* t = RSTR_PTR(s), *p = t; + const char* e = p + RSTR_LEN(s); + while (p<e) { + mrb_int clen = utf8len(p, e); + if (p + clen>=e) break; + p += clen; + } + len = p - t; +#else len = RSTR_LEN(s) - 1; +#endif if (RSTR_PTR(s)[len] == '\n') { if (len > 0 && RSTR_PTR(s)[len-1] == '\r') { @@ -1151,47 +1457,10 @@ mrb_str_eql(mrb_state *mrb, mrb_value self) return mrb_bool_value(eql_p); } -static mrb_value -mrb_str_subseq(mrb_state *mrb, mrb_value str, mrb_int beg, mrb_int len) -{ - struct RString *orig, *s; - mrb_shared_string *shared; - - orig = mrb_str_ptr(str); - if (RSTR_EMBED_P(orig)) { - s = str_new(mrb, orig->as.ary+beg, len); - } else { - str_make_shared(mrb, orig); - shared = orig->as.heap.aux.shared; - s = mrb_obj_alloc_string(mrb); - s->as.heap.ptr = orig->as.heap.ptr + beg; - s->as.heap.len = len; - s->as.heap.aux.shared = shared; - RSTR_SET_SHARED_FLAG(s); - shared->refcnt++; - } - - return mrb_obj_value(s); -} - MRB_API mrb_value mrb_str_substr(mrb_state *mrb, mrb_value str, mrb_int beg, mrb_int len) { - if (len < 0) return mrb_nil_value(); - if (!RSTRING_LEN(str)) { - len = 0; - } - if (beg > RSTRING_LEN(str)) return mrb_nil_value(); - if (beg < 0) { - beg += RSTRING_LEN(str); - if (beg < 0) return mrb_nil_value(); - } - if (beg + len > RSTRING_LEN(str)) - len = RSTRING_LEN(str) - beg; - if (len <= 0) { - len = 0; - } - return mrb_str_subseq(mrb, str, beg, len); + return str_substr(mrb, str, beg, len); } mrb_int @@ -1250,7 +1519,7 @@ mrb_str_include(mrb_state *mrb, mrb_value self) } else { str2 = mrb_str_to_str(mrb, str2); - i = mrb_str_index(mrb, self, str2, 0); + i = str_index(mrb, self, str2, 0); include_p = (i != -1); } @@ -1280,12 +1549,12 @@ mrb_str_include(mrb_state *mrb, mrb_value self) * "hello".index(/[aeiou]/, -3) #=> 4 */ static mrb_value -mrb_str_index_m(mrb_state *mrb, mrb_value str) +mrb_str_index(mrb_state *mrb, mrb_value str) { mrb_value *argv; mrb_int argc; mrb_value sub; - mrb_int pos; + mrb_int pos, clen; mrb_get_args(mrb, "*", &argv, &argc); if (argc == 2) { @@ -1300,25 +1569,17 @@ mrb_str_index_m(mrb_state *mrb, mrb_value str) sub = mrb_nil_value(); } mrb_regexp_check(mrb, sub); + clen = RSTRING_CHAR_LEN(str); if (pos < 0) { - pos += RSTRING_LEN(str); + pos += clen; if (pos < 0) { return mrb_nil_value(); } } + if (pos >= clen) return mrb_nil_value(); + pos = chars2bytes(str, 0, pos); switch (mrb_type(sub)) { - case MRB_TT_FIXNUM: { - mrb_int c = mrb_fixnum(sub); - mrb_int len = RSTRING_LEN(str); - unsigned char *p = (unsigned char*)RSTRING_PTR(str); - - for (;pos<len;pos++) { - if (p[pos] == c) return mrb_fixnum_value(pos); - } - return mrb_nil_value(); - } - default: { mrb_value tmp; @@ -1330,56 +1591,17 @@ mrb_str_index_m(mrb_state *mrb, mrb_value str) } /* fall through */ case MRB_TT_STRING: - pos = mrb_str_index(mrb, str, sub, pos); + pos = str_index(mrb, str, sub, pos); break; } if (pos == -1) return mrb_nil_value(); + pos = bytes2chars(RSTRING_PTR(str), pos); return mrb_fixnum_value(pos); } #define STR_REPLACE_SHARED_MIN 10 -static mrb_value -str_replace(mrb_state *mrb, struct RString *s1, struct RString *s2) -{ - long len; - - 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; - RSTR_SET_SHARED_FLAG(s1); - s1->as.heap.aux.shared->refcnt++; - } - else { - if (len <= RSTRING_EMBED_LEN_MAX) { - 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); - goto L_SHARE; - } - } - - return mrb_obj_value(s1); -} - /* 15.2.10.5.24 */ /* 15.2.10.5.28 */ /* @@ -1499,107 +1721,81 @@ mrb_check_string_type(mrb_state *mrb, mrb_value str) return mrb_check_convert_type(mrb, str, MRB_TT_STRING, "String", "to_str"); } -/* ---------------------------------- */ -/* 15.2.10.5.29 */ +/* 15.2.10.5.30 */ /* * call-seq: - * str.reverse => new_str - * - * Returns a new string with the characters from <i>str</i> in reverse order. + * str.reverse! => str * - * "stressed".reverse #=> "desserts" + * Reverses <i>str</i> in place. */ static mrb_value -mrb_str_reverse(mrb_state *mrb, mrb_value str) +mrb_str_reverse_bang(mrb_state *mrb, mrb_value str) { - struct RString *s2; - char *s, *e, *p; +#ifdef MRB_UTF8_STRING + mrb_int utf8_len = RSTRING_CHAR_LEN(str); + mrb_int len = RSTRING_LEN(str); - if (RSTRING_LEN(str) <= 1) return mrb_str_dup(mrb, str); + if (utf8_len == len) goto bytes; + if (utf8_len > 1) { + char *buf; + char *p, *e, *r; - s2 = str_new(mrb, 0, RSTRING_LEN(str)); - str_with_class(mrb, s2, str); - s = RSTRING_PTR(str); e = RSTRING_END(str) - 1; - p = RSTR_PTR(s2); + mrb_str_modify(mrb, mrb_str_ptr(str)); + len = RSTRING_LEN(str); + buf = mrb_malloc(mrb, (size_t)len); + p = buf; + e = buf + len; + + memcpy(buf, RSTRING_PTR(str), len); + r = RSTRING_PTR(str) + len; - while (e >= s) { - *p++ = *e--; + while (p<e) { + mrb_int clen = utf8len(p, e); + r -= clen; + memcpy(r, p, clen); + p += clen; + } + mrb_free(mrb, buf); } - return mrb_obj_value(s2); -} + return str; -/* 15.2.10.5.30 */ -/* - * call-seq: - * str.reverse! => str - * - * Reverses <i>str</i> in place. - */ -static mrb_value -mrb_str_reverse_bang(mrb_state *mrb, mrb_value str) -{ - struct RString *s = mrb_str_ptr(str); - char *p, *e; - char c; + bytes: +#endif + { + struct RString *s = mrb_str_ptr(str); + char *p, *e; + char c; - mrb_str_modify(mrb, s); - if (RSTR_LEN(s) > 1) { - p = RSTR_PTR(s); - e = p + RSTR_LEN(s) - 1; - while (p < e) { + mrb_str_modify(mrb, s); + if (RSTR_LEN(s) > 1) { + p = RSTR_PTR(s); + e = p + RSTR_LEN(s) - 1; + while (p < e) { c = *p; *p++ = *e; *e-- = c; + } } + return str; } - return str; } +/* ---------------------------------- */ +/* 15.2.10.5.29 */ /* * call-seq: - * str.rindex(substring [, fixnum]) => fixnum or nil - * str.rindex(fixnum [, fixnum]) => fixnum or nil - * str.rindex(regexp [, fixnum]) => fixnum or nil + * str.reverse => new_str * - * Returns the index of the last occurrence of the given <i>substring</i>, - * character (<i>fixnum</i>), or pattern (<i>regexp</i>) in <i>str</i>. Returns - * <code>nil</code> if not found. If the second parameter is present, it - * specifies the position in the string to end the search---characters beyond - * this point will not be considered. + * Returns a new string with the characters from <i>str</i> in reverse order. * - * "hello".rindex('e') #=> 1 - * "hello".rindex('l') #=> 3 - * "hello".rindex('a') #=> nil - * "hello".rindex(101) #=> 1 - * "hello".rindex(/[aeiou]/, -2) #=> 1 + * "stressed".reverse #=> "desserts" */ -static mrb_int -mrb_str_rindex(mrb_state *mrb, mrb_value str, mrb_value sub, mrb_int pos) +static mrb_value +mrb_str_reverse(mrb_state *mrb, mrb_value str) { - char *s, *sbeg, *t; - struct RString *ps = mrb_str_ptr(str); - mrb_int len = RSTRING_LEN(sub); - - /* substring longer than string */ - if (RSTR_LEN(ps) < len) return -1; - if (RSTR_LEN(ps) - pos < len) { - pos = RSTR_LEN(ps) - len; - } - 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 - RSTR_PTR(ps); - } - s--; - } - return -1; - } - else { - return pos; - } + mrb_value str2 = mrb_str_dup(mrb, str); + mrb_str_reverse_bang(mrb, str2); + return str2; } /* 15.2.10.5.31 */ @@ -1622,13 +1818,13 @@ mrb_str_rindex(mrb_state *mrb, mrb_value str, mrb_value sub, mrb_int pos) * "hello".rindex(/[aeiou]/, -2) #=> 1 */ static mrb_value -mrb_str_rindex_m(mrb_state *mrb, mrb_value str) +mrb_str_rindex(mrb_state *mrb, mrb_value str) { mrb_value *argv; mrb_int argc; mrb_value sub; mrb_value vpos; - mrb_int pos, len = RSTRING_LEN(str); + mrb_int pos, len = RSTRING_CHAR_LEN(str); mrb_get_args(mrb, "*", &argv, &argc); if (argc == 2) { @@ -1651,19 +1847,11 @@ mrb_str_rindex_m(mrb_state *mrb, mrb_value str) else sub = mrb_nil_value(); } + pos = chars2bytes(str, 0, pos); + len = chars2bytes(str, pos, len); mrb_regexp_check(mrb, sub); switch (mrb_type(sub)) { - case MRB_TT_FIXNUM: { - mrb_int c = mrb_fixnum(sub); - unsigned char *p = (unsigned char*)RSTRING_PTR(str); - - for (pos=len-1;pos>=0;pos--) { - if (p[pos] == c) return mrb_fixnum_value(pos); - } - return mrb_nil_value(); - } - default: { mrb_value tmp; @@ -1675,8 +1863,11 @@ mrb_str_rindex_m(mrb_state *mrb, mrb_value str) } /* fall through */ case MRB_TT_STRING: - pos = mrb_str_rindex(mrb, str, sub, pos); - if (pos >= 0) return mrb_fixnum_value(pos); + pos = str_rindex(mrb, str, sub, pos); + if (pos >= 0) { + pos = bytes2chars(RSTRING_PTR(str), pos); + return mrb_fixnum_value(pos); + } break; } /* end of switch (TYPE(sub)) */ @@ -1687,7 +1878,7 @@ mrb_str_rindex_m(mrb_state *mrb, mrb_value str) /* * call-seq: - * str.split(pattern=$;, [limit]) => anArray + * str.split(pattern="\n", [limit]) => anArray * * Divides <i>str</i> into substrings based on a delimiter, returning an array * of these substrings. @@ -1785,7 +1976,7 @@ mrb_str_split_m(mrb_state *mrb, mrb_value str) } } else if (ISSPACE(c)) { - mrb_ary_push(mrb, result, mrb_str_subseq(mrb, str, beg, end-beg)); + mrb_ary_push(mrb, result, byte_subseq(mrb, str, beg, end-beg)); mrb_gc_arena_restore(mrb, ai); skip = TRUE; beg = idx; @@ -1807,9 +1998,9 @@ mrb_str_split_m(mrb_state *mrb, mrb_value str) end = mrb_memsearch(RSTRING_PTR(spat), pat_len, RSTRING_PTR(str)+idx, str_len - idx); if (end < 0) break; } else { - end = 1; + end = chars2bytes(str, idx, 1); } - mrb_ary_push(mrb, result, mrb_str_subseq(mrb, str, idx, end)); + mrb_ary_push(mrb, result, byte_subseq(mrb, str, idx, end)); mrb_gc_arena_restore(mrb, ai); idx += end + pat_len; if (lim_p && lim <= ++i) break; @@ -1824,7 +2015,7 @@ mrb_str_split_m(mrb_state *mrb, mrb_value str) tmp = mrb_str_new_empty(mrb, str); } else { - tmp = mrb_str_subseq(mrb, str, beg, RSTRING_LEN(str)-beg); + tmp = byte_subseq(mrb, str, beg, RSTRING_LEN(str)-beg); } mrb_ary_push(mrb, result, tmp); } @@ -1844,7 +2035,7 @@ mrb_cstr_to_inum(mrb_state *mrb, const char *str, int base, int badcheck) const char *p; char sign = 1; int c, uscore; - unsigned long n = 0; + uint64_t n = 0; mrb_int val; #define conv_digit(c) \ @@ -1964,9 +2155,9 @@ mrb_cstr_to_inum(mrb_state *mrb, const char *str, int base, int badcheck) } n *= base; n += c; - } - if (n > MRB_INT_MAX) { - mrb_raisef(mrb, E_ARGUMENT_ERROR, "string (%S) too big for integer", mrb_str_new_cstr(mrb, str)); + if (n > MRB_INT_MAX) { + mrb_raisef(mrb, E_ARGUMENT_ERROR, "string (%S) too big for integer", mrb_str_new_cstr(mrb, str)); + } } val = n; if (badcheck) { @@ -2366,10 +2557,10 @@ mrb_str_cat_str(mrb_state *mrb, mrb_value str, mrb_value str2) } MRB_API mrb_value -mrb_str_append(mrb_state *mrb, mrb_value str, mrb_value str2) +mrb_str_append(mrb_state *mrb, mrb_value str1, mrb_value str2) { str2 = mrb_str_to_str(mrb, str2); - return mrb_str_cat_str(mrb, str, str2); + return mrb_str_cat_str(mrb, str1, str2); } #define CHAR_ESC_LEN 13 /* sizeof(\x{ hex of 32bit unsigned int } \0) */ @@ -2395,7 +2586,21 @@ mrb_str_inspect(mrb_state *mrb, mrb_value str) p = RSTRING_PTR(str); pend = RSTRING_END(str); for (;p < pend; p++) { unsigned char c, cc; +#ifdef MRB_UTF8_STRING + mrb_int clen; + + clen = utf8len(p, pend); + if (clen > 1) { + mrb_int i; + for (i=0; i<clen; i++) { + buf[i] = p[i]; + } + mrb_str_cat(mrb, result, buf, clen); + p += clen-1; + continue; + } +#endif c = *p; if (c == '"'|| c == '\\' || (c == '#' && IS_EVSTR(p, pend))) { buf[0] = '\\'; buf[1] = c; @@ -2472,7 +2677,7 @@ mrb_init_string(mrb_state *mrb) s = mrb->string_class = mrb_define_class(mrb, "String", mrb->object_class); /* 15.2.10 */ MRB_SET_INSTANCE_TT(s, MRB_TT_STRING); - mrb_define_method(mrb, s, "bytesize", mrb_str_size, MRB_ARGS_NONE()); + mrb_define_method(mrb, s, "bytesize", mrb_str_bytesize, MRB_ARGS_NONE()); mrb_define_method(mrb, s, "<=>", mrb_str_cmp_m, MRB_ARGS_REQ(1)); /* 15.2.10.5.1 */ mrb_define_method(mrb, s, "==", mrb_str_equal_m, MRB_ARGS_REQ(1)); /* 15.2.10.5.2 */ @@ -2492,7 +2697,7 @@ mrb_init_string(mrb_state *mrb) mrb_define_method(mrb, s, "hash", mrb_str_hash_m, MRB_ARGS_NONE()); /* 15.2.10.5.20 */ mrb_define_method(mrb, s, "include?", mrb_str_include, MRB_ARGS_REQ(1)); /* 15.2.10.5.21 */ - mrb_define_method(mrb, s, "index", mrb_str_index_m, MRB_ARGS_ANY()); /* 15.2.10.5.22 */ + mrb_define_method(mrb, s, "index", mrb_str_index, MRB_ARGS_ANY()); /* 15.2.10.5.22 */ mrb_define_method(mrb, s, "initialize", mrb_str_init, MRB_ARGS_REQ(1)); /* 15.2.10.5.23 */ mrb_define_method(mrb, s, "initialize_copy", mrb_str_replace, MRB_ARGS_REQ(1)); /* 15.2.10.5.24 */ mrb_define_method(mrb, s, "intern", mrb_str_intern, MRB_ARGS_NONE()); /* 15.2.10.5.25 */ @@ -2500,7 +2705,7 @@ mrb_init_string(mrb_state *mrb) mrb_define_method(mrb, s, "replace", mrb_str_replace, MRB_ARGS_REQ(1)); /* 15.2.10.5.28 */ mrb_define_method(mrb, s, "reverse", mrb_str_reverse, MRB_ARGS_NONE()); /* 15.2.10.5.29 */ mrb_define_method(mrb, s, "reverse!", mrb_str_reverse_bang, MRB_ARGS_NONE()); /* 15.2.10.5.30 */ - mrb_define_method(mrb, s, "rindex", mrb_str_rindex_m, MRB_ARGS_ANY()); /* 15.2.10.5.31 */ + mrb_define_method(mrb, s, "rindex", mrb_str_rindex, MRB_ARGS_ANY()); /* 15.2.10.5.31 */ mrb_define_method(mrb, s, "size", mrb_str_size, MRB_ARGS_NONE()); /* 15.2.10.5.33 */ mrb_define_method(mrb, s, "slice", mrb_str_aref_m, MRB_ARGS_ANY()); /* 15.2.10.5.34 */ mrb_define_method(mrb, s, "split", mrb_str_split_m, MRB_ARGS_ANY()); /* 15.2.10.5.35 */ @@ -2514,4 +2719,6 @@ mrb_init_string(mrb_state *mrb) mrb_define_method(mrb, s, "upcase!", mrb_str_upcase_bang, MRB_ARGS_NONE()); /* 15.2.10.5.43 */ mrb_define_method(mrb, s, "inspect", mrb_str_inspect, MRB_ARGS_NONE()); /* 15.2.10.5.46(x) */ mrb_define_method(mrb, s, "bytes", mrb_str_bytes, MRB_ARGS_NONE()); + + mrb_define_method(mrb, s, "freeze", mrb_str_freeze, MRB_ARGS_NONE()); } diff --git a/src/version.c b/src/version.c index 7aac44d62..fc3b2fc7a 100644 --- a/src/version.c +++ b/src/version.c @@ -7,6 +7,7 @@ mrb_init_version(mrb_state* mrb) mrb_define_global_const(mrb, "RUBY_VERSION", mrb_str_new_lit(mrb, MRUBY_RUBY_VERSION)); mrb_define_global_const(mrb, "RUBY_ENGINE", mrb_str_new_lit(mrb, MRUBY_RUBY_ENGINE)); mrb_define_global_const(mrb, "MRUBY_VERSION", mrb_str_new_lit(mrb, MRUBY_VERSION)); + mrb_define_global_const(mrb, "MRUBY_RELEASE_NO", mrb_fixnum_value(MRUBY_RELEASE_NO)); mrb_define_global_const(mrb, "MRUBY_RELEASE_DATE", mrb_str_new_lit(mrb, MRUBY_RELEASE_DATE)); mrb_define_global_const(mrb, "MRUBY_DESCRIPTION", mrb_str_new_lit(mrb, MRUBY_DESCRIPTION)); mrb_define_global_const(mrb, "MRUBY_COPYRIGHT", mrb_str_new_lit(mrb, MRUBY_COPYRIGHT)); @@ -1486,9 +1486,6 @@ RETRY_TRY_BLOCK: if (ci->ridx == 0) goto L_STOP; goto L_RESCUE; } - while (eidx > ci[-1].eidx) { - ecall(mrb, --eidx); - } while (ci[0].ridx == ci[-1].ridx) { cipop(mrb); ci = mrb->c->ci; diff --git a/tasks/mrbgem_spec.rake b/tasks/mrbgem_spec.rake index d13e7733f..4fcfc8f83 100644 --- a/tasks/mrbgem_spec.rake +++ b/tasks/mrbgem_spec.rake @@ -52,9 +52,9 @@ module MRuby MRuby::Build::COMMANDS.each do |command| instance_variable_set("@#{command}", @build.send(command).clone) end - @linker = LinkerConfig.new([], [], [], []) + @linker = LinkerConfig.new([], [], [], [], []) - @rbfiles = Dir.glob("#{dir}/mrblib/*.rb").sort + @rbfiles = Dir.glob("#{dir}/mrblib/**/*.rb").sort @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 @@ -62,7 +62,7 @@ module MRuby @generate_functions = !(@rbfiles.empty? && @objs.empty?) @objs << objfile("#{build_dir}/gem_init") if @generate_functions - @test_rbfiles = Dir.glob("#{dir}/test/*.rb") + @test_rbfiles = Dir.glob("#{dir}/test/**/*.rb") @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 @@ -103,6 +103,10 @@ module MRuby @dependencies << {:gem => name, :requirements => requirements, :default => default_gem} end + def add_test_dependency(*args) + add_dependency(*args) if build.test_enabled? + end + def add_conflict(name, *req) @conflicts << {:gem => name, :requirements => req.empty? ? nil : req} end diff --git a/tasks/mruby_build.rake b/tasks/mruby_build.rake index 9f8b4eda5..cff45ddf8 100644 --- a/tasks/mruby_build.rake +++ b/tasks/mruby_build.rake @@ -26,8 +26,8 @@ module MRuby MRuby::Toolchain.toolchains[@name] = self end - def setup(conf) - conf.instance_eval(&@initializer) + def setup(conf,params={}) + conf.instance_exec(conf, params, &@initializer) end def self.load @@ -46,7 +46,7 @@ module MRuby include LoadGems attr_accessor :name, :bins, :exts, :file_separator, :build_dir, :gem_clone_dir attr_reader :libmruby, :gems, :toolchains - attr_writer :enable_bintest + attr_writer :enable_bintest, :enable_test COMPILERS = %w(cc cxx objc asm) COMMANDS = COMPILERS + %w(linker archiver yacc gperf git exts mrbc) @@ -85,6 +85,8 @@ module MRuby @build_mrbtest_lib_only = false @cxx_abi_enabled = false @cxx_exception_disabled = false + @enable_bintest = false + @enable_test = false @toolchains = [] MRuby.targets[@name] = self @@ -94,6 +96,7 @@ module MRuby MRuby.targets[@name].instance_eval(&block) build_mrbc_exec if name == 'host' + build_mrbtest if test_enabled? end def enable_debug @@ -155,10 +158,10 @@ EOS @enable_bintest end - def toolchain(name) + def toolchain(name, params={}) tc = Toolchain.toolchains[name.to_s] fail "Unknown #{name} toolchain" unless tc - tc.setup(self) + tc.setup(self, params) @toolchains.unshift name.to_s end @@ -170,6 +173,18 @@ EOS MRUBY_ROOT end + def enable_test + @enable_test = true + end + + def test_enabled? + @enable_test + end + + def build_mrbtest + gem :core => 'mruby-test' + end + def build_mrbc_exec gem :core => 'mruby-bin-mrbc' end @@ -249,10 +264,10 @@ EOS def run_test puts ">>> Test #{name} <<<" - mrbtest = exefile("#{build_dir}/test/mrbtest") + mrbtest = exefile("#{build_dir}/bin/mrbtest") sh "#{filename mrbtest.relative_path}#{$verbose ? ' -v' : ''}" puts - run_bintest if @enable_bintest + run_bintest if bintest_enabled? end def run_bintest @@ -297,7 +312,7 @@ EOS end def run_test - mrbtest = exefile("#{build_dir}/test/mrbtest") + mrbtest = exefile("#{build_dir}/bin/mrbtest") if (@test_runner.command == nil) puts "You should run #{mrbtest} on target device." puts diff --git a/tasks/toolchains/androidndk.rake b/tasks/toolchains/androidndk.rake new file mode 100644 index 000000000..ccf1a0c4d --- /dev/null +++ b/tasks/toolchains/androidndk.rake @@ -0,0 +1,215 @@ + +class MRuby::Toolchain::AndroidNDK + DEFAULT_ARCH = 'armeabi' + DEFAULT_PLATFORM = 'android-14' + DEFAULT_TOOLCHAIN = :gcc + DEFAULT_NDK_HOMES = %w{ + /usr/local/opt/android-ndk + } + TOOLCHAINS = [:gcc, :clang] + ARCHITECTURES = %w{ + armeabi armeabi-v7a arm64-v8a + mips mips64 + x86 x86_64 + } + + class AndroidNDKHomeNotFound < StandardError + def message + <<-EOM +Couldn't find Android NDK Home. +Set ANDROID_NDK_HOME environment variable or set :ndk_home parameter + EOM + end + end + + attr_reader :params + + def initialize(params) + @params = params + end + + def home_path + @home_path ||= Pathname( + params[:ndk_home] || + ENV['ANDROID_NDK_HOME'] || + DEFAULT_NDK_HOMES.find{ |path| File.directory?(path) } || + raise(AndroidNDKHomeNotFound) + ) + end + + def arch + params.fetch(:arch){ DEFAULT_ARCH } + end + + def platform + params.fetch(:platform){ DEFAULT_PLATFORM } + end + + def toolchain + params.fetch(:toolchain){ DEFAULT_TOOLCHAIN } + end + + def toolchain_version + params.fetch(:toolchain_version) do + test = case toolchain + when :gcc + case arch + when /armeabi/ + 'arm-linux-androideabi-*' + when /arm64/ + 'aarch64-linux-android-*' + when /mips64/ + 'mips64el-linux-android-*' + when /mips/ + 'mipsel-linux-android-*' + when /x86_64/ + 'x86_64-*' + when /x86/ + 'x86-*' + end + when :clang + 'llvm-*' + end + + Dir[home_path.join('toolchains',test)].map{|t| t.match(/-(\d+\.\d+)$/); $1.to_f }.max + end + end + + def toolchain_path + prefix = case toolchain + when :clang then 'llvm-' + when :gcc + case arch + when /armeabi/ then 'arm-linux-androideabi-' + when /arm64/ then 'aarch64-linux-android-' + when /x86_64/ then 'x86_64-' + when /x86/ then 'x86-' + when /mips64/ then 'mips64el-linux-android-' + when /mips/ then 'mipsel-linux-android-' + end + end + home_path.join('toolchains', prefix + toolchain_version.to_s, 'prebuilt', host_platform) + end + + def sysroot + path = case arch + when /armeabi/ then 'arch-arm' + when /arm64/ then 'arch-arm64' + when /x86_64/ then 'arch-x86_64' + when /x86/ then 'arch-x86' + when /mips64/ then 'arch-mips64' + when /mips/ then 'arch-mips' + end + + home_path.join('platforms', platform, path).to_s + end + + def bin(command) + command = command.to_s + + if toolchain == :gcc + command = case arch + when /armeabi/ then 'arm-linux-androideabi-' + when /arm64/ then 'aarch64-linux-android-' + when /x86_64/ then 'x86_64-linux-android-' + when /x86/ then 'i686-linux-android-' + when /mips64/ then 'mips64el-linux-android-' + when /mips/ then 'mipsel-linux-android-' + end + command + end + + toolchain_path.join('bin',command).to_s + end + + def cc + case toolchain + when :gcc then bin(:gcc) + when :clang then bin(:clang) + end + end + + def cflags + flags = [] + + case toolchain + when :gcc + flags += %W(-ffunction-sections -funwind-tables -no-canonical-prefixes) + flags += %W(-D__android__ -mandroid --sysroot="#{sysroot}") + case arch + when /arm64/ + flags += %W(-fpic -fstack-protector-strong) + when 'armeabi-v7a-hard' + flags += %W(-fpic -fstack-protector-strong -march=armv7-a -mhard-float -D_NDK_MATH_NO_SOFTFP=1 -mfpu=vfpv3-d16) + when 'armeabi-v7a' + flags += %W(-fpic -fstack-protector-strong -march=armv7-a -mfloat-abi=softfp -mfpu=vfpv3-d16) + when /arm/ + flags += %W(-fpic -fstack-protector-strong -march=armv5te -mtune=xscale -msoft-float) + when /mips/ + flags += %W(-fpic -fno-strict-aliasing -finline-functions -fmessage-length=0 -fno-inline-functions-called-once -fgcse-after-reload -frerun-cse-after-loop -frename-registers) + when /x86/ + flags += %W(-fstack-protector-strong) + end + when :clang + end + + flags + end + + def ld + cc + end + + def ldflags + flags = [] + case toolchain + when :gcc + flags += %W(-no-canonical-prefixes) + flags += %W(-D__android__ -mandroid --sysroot="#{sysroot}") + case arch + when 'armeabi-v7a-hard' + flags += %W(-march=armv7-a -Wl,--fix-cortex-a8 -Wl,--no-warn-mismatch -lm_hard) + when 'armeabi-v7a' + flags += %W(-march=armv7-a -Wl,--fix-cortex-a8) + end + end + end + + def ar + case toolchain + when :gcc then bin(:ar) + when :clang then bin('llvm-ar') + end + end + + def host_platform + case RUBY_PLATFORM + when /cygwin|mswin|mingw|bccwin|wince|emx/i + 'windows' + when /x86_64-darwin/i + 'darwin-x86_64' + when /darwin/i + 'darwin-x86' + when /x86_64-linux/i + 'linux-x86_64' + when /linux/i + 'linux-x86' + else + raise NotImplementedError, "Unknown host platform (#{RUBY_PLATFORM})" + end + end +end + +MRuby::Toolchain.new(:androidndk) do |conf, params| + ndk = MRuby::Toolchain::AndroidNDK.new(params) + + toolchain ndk.toolchain + + [conf.cc, conf.cxx, conf.objc, conf.asm].each do |cc| + cc.command = ndk.cc + cc.flags = ndk.cflags + end + conf.linker.command = ndk.ld + conf.linker.flags = ndk.ldflags + conf.archiver.command = ndk.ar +end + diff --git a/test/bintest.rb b/test/bintest.rb index 0ff3341a0..e6d122047 100644 --- a/test/bintest.rb +++ b/test/bintest.rb @@ -1,8 +1,16 @@ $:.unshift File.dirname(File.dirname(File.expand_path(__FILE__))) require 'test/assert.rb' +def cmd(s) + ENV['SHELL'] ? "bin/#{s}" : "bin\\#{s}.exe" +end + +def shellquote(s) + ENV['SHELL'] ? "'#{s}'" : "\"#{s}\"" +end + ARGV.each do |gem| - Dir["#{gem}/bintest/*.rb"].each do |file| + Dir["#{gem}/bintest/**/*.rb"].each do |file| load file end end diff --git a/test/mrbtest.rake b/test/mrbtest.rake deleted file mode 100644 index b9616fe9d..000000000 --- a/test/mrbtest.rake +++ /dev/null @@ -1,69 +0,0 @@ -MRuby.each_target do - current_dir = File.dirname(__FILE__).relative_path_from(Dir.pwd) - relative_from_root = File.dirname(__FILE__).relative_path_from(MRUBY_ROOT) - current_build_dir = "#{build_dir}/#{relative_from_root}" - - exec = exefile("#{current_build_dir}/mrbtest") - clib = "#{current_build_dir}/mrbtest.c" - mlib = clib.ext(exts.object) - mrbs = Dir.glob("#{current_dir}/t/*.rb") - init = "#{current_dir}/init_mrbtest.c" - ass_c = "#{current_build_dir}/assert.c" - ass_lib = ass_c.ext(exts.object) - - mrbtest_lib = libfile("#{current_build_dir}/mrbtest") - mrbtest_objs = [mlib, ass_lib] - gems.each do |v| - mrbtest_objs.concat v.test_objs - end - file mrbtest_lib => mrbtest_objs do |t| - archiver.run t.name, t.prerequisites - end - - unless build_mrbtest_lib_only? - driver_obj = objfile("#{current_build_dir}/driver") - file exec => [driver_obj, mrbtest_lib, libfile("#{build_dir}/lib/libmruby")] do |t| - gem_flags = gems.map { |g| g.linker.flags } - gem_flags_before_libraries = gems.map { |g| g.linker.flags_before_libraries } - gem_flags_after_libraries = gems.map { |g| g.linker.flags_after_libraries } - gem_libraries = gems.map { |g| g.linker.libraries } - gem_library_paths = gems.map { |g| g.linker.library_paths } - linker.run t.name, t.prerequisites, gem_libraries, gem_library_paths, gem_flags, gem_flags_before_libraries - end - end - - file ass_lib => ass_c - 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.first], 'mrbtest_assert_irep' - end - end - - file mlib => clib - 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| - f.puts %Q[/*] - f.puts %Q[ * This file contains a list of all] - f.puts %Q[ * test functions.] - f.puts %Q[ *] - f.puts %Q[ * IMPORTANT:] - f.puts %Q[ * This file was generated!] - f.puts %Q[ * All manual changes will get lost.] - f.puts %Q[ */] - f.puts %Q[] - f.puts IO.read(init) - mrbc.run f, mrbs, 'mrbtest_irep' - gems.each do |g| - f.puts %Q[void GENERATED_TMP_mrb_#{g.funcname}_gem_test(mrb_state *mrb);] - end - f.puts %Q[void mrbgemtest_init(mrb_state* mrb) {] - gems.each do |g| - f.puts %Q[ GENERATED_TMP_mrb_#{g.funcname}_gem_test(mrb);] - end - f.puts %Q[}] - end - end -end diff --git a/test/t/float.rb b/test/t/float.rb index d45709173..0aab0b1f2 100644 --- a/test/t/float.rb +++ b/test/t/float.rb @@ -178,3 +178,25 @@ assert('Float#nan?') do assert_false (1.0/0.0).nan? assert_false (-1.0/0.0).nan? end + +assert('Float#<<') do + # Left Shift by one + assert_equal 46, 23.0 << 1 + + # Left Shift by a negative is Right Shift + assert_equal 23, 46.0 << -1 +end + +assert('Float#>>') do + # Right Shift by one + assert_equal 23, 46.0 >> 1 + + # Right Shift by a negative is Left Shift + assert_equal 46, 23.0 >> -1 + + # Don't raise on large Right Shift + assert_equal 0, 23.0 >> 128 + + # Don't raise on large Right Shift + assert_equal -1, -23.0 >> 128 +end diff --git a/test/t/hash.rb b/test/t/hash.rb index eee7c7b6a..3196cc97a 100644 --- a/test/t/hash.rb +++ b/test/t/hash.rb @@ -342,3 +342,12 @@ assert('Hash#inspect') do assert_include ret, '"a"=>100' assert_include ret, '"d"=>400' end + +assert('Hash#rehash') do + h = {[:a] => "b"} + # hash key modified + h.keys[0][0] = :b + # h[[:b]] => nil + h.rehash + assert_equal("b", h[[:b]]) +end diff --git a/test/t/integer.rb b/test/t/integer.rb index 6b8cc308d..be3c13db2 100644 --- a/test/t/integer.rb +++ b/test/t/integer.rb @@ -147,11 +147,6 @@ assert('Integer#<<', '15.2.8.3.12') do # Left Shift by a negative is Right Shift assert_equal 23, 46 << -1 - - # Raise when shift is too large - assert_raise(RangeError) do - 2 << 128 - end end assert('Integer#>>', '15.2.8.3.13') do @@ -165,11 +160,6 @@ assert('Integer#>>', '15.2.8.3.13') do # Don't raise on large Right Shift assert_equal 0, 23 >> 128 - - # Raise when shift is too large - assert_raise(RangeError) do - 2 >> -128 - end end assert('Integer#ceil', '15.2.8.3.14') do diff --git a/test/t/module.rb b/test/t/module.rb index ecb969475..4bde20fbe 100644 --- a/test/t/module.rb +++ b/test/t/module.rb @@ -1,6 +1,26 @@ ## # Module ISO Test +def labeled_module(name, &block) + Module.new do + singleton_class.class_eval do + define_method(:to_s) { name } + alias_method :inspect, :to_s + end + class_eval(&block) if block + end +end + +def labeled_class(name, supklass = Object, &block) + Class.new(supklass) do + singleton_class.class_eval do + define_method(:to_s) { name } + alias_method :inspect, :to_s + end + class_eval(&block) if block + end +end + assert('Module', '15.2.2') do assert_equal Class, Module.class end @@ -474,6 +494,286 @@ end # Not ISO specified +# @!group prepend + assert('Module#prepend') do + module M0 + def m1; [:M0] end + end + module M1 + def m1; [:M1, super, :M1] end + end + module M2 + def m1; [:M2, super, :M2] end + end + M3 = Module.new do + def m1; [:M3, super, :M3] end + end + module M4 + def m1; [:M4, super, :M4] end + end + + class P0 + include M0 + prepend M1 + def m1; [:C0, super, :C0] end + end + class P1 < P0 + prepend M2, M3 + include M4 + def m1; [:C1, super, :C1] end + end + + obj = P1.new + expected = [:M2,[:M3,[:C1,[:M4,[:M1,[:C0,[:M0],:C0],:M1],:M4],:C1],:M3],:M2] + assert_equal(expected, obj.m1) + end + + # mruby shouldn't be affected by this since there is + # no visibility control (yet) + assert('Module#prepend public') do + assert_nothing_raised('ruby/ruby #8846') do + Class.new.prepend(Module.new) + end + end + + assert('Module#prepend inheritance') do + bug6654 = '[ruby-core:45914]' + a = labeled_module('a') + b = labeled_module('b') { include a } + c = labeled_module('c') { prepend b } + + #assert bug6654 do + # the Module#< operator should be used here instead, but we don't have it + assert_include(c.ancestors, a) + assert_include(c.ancestors, b) + #end + + bug8357 = '[ruby-core:54736] [Bug #8357]' + b = labeled_module('b') { prepend a } + c = labeled_class('c') { include b } + + #assert bug8357 do + # the Module#< operator should be used here instead, but we don't have it + assert_include(c.ancestors, a) + assert_include(c.ancestors, b) + #end + + bug8357 = '[ruby-core:54742] [Bug #8357]' + assert_kind_of(b, c.new, bug8357) + end + + assert('Moduler#prepend + #instance_methods') do + bug6655 = '[ruby-core:45915]' + assert_equal(Object.instance_methods, Class.new {prepend Module.new}.instance_methods, bug6655) + end + + assert 'Module#prepend + #singleton_methods' do + o = Object.new + o.singleton_class.class_eval {prepend Module.new} + assert_equal([], o.singleton_methods) + end + + assert 'Module#prepend + #remove_method' do + c = Class.new do + prepend Module.new { def foo; end } + end + assert_raise(NameError) do + c.class_eval do + remove_method(:foo) + end + end + c.class_eval do + def foo; end + end + removed = nil + c.singleton_class.class_eval do + define_method(:method_removed) {|id| removed = id} + end + assert_nothing_raised(NoMethodError, NameError, '[Bug #7843]') do + c.class_eval do + remove_method(:foo) + end + end + assert_equal(:foo, removed) + end + + assert 'Module#prepend + Class#ancestors' do + bug6658 = '[ruby-core:45919]' + m = labeled_module("m") + c = labeled_class("c") {prepend m} + assert_equal([m, c], c.ancestors[0, 2], bug6658) + + bug6662 = '[ruby-dev:45868]' + c2 = labeled_class("c2", c) + anc = c2.ancestors + assert_equal([c2, m, c, Object], anc[0..anc.index(Object)], bug6662) + end + + assert 'Module#prepend + Module#ancestors' do + bug6659 = '[ruby-dev:45861]' + m0 = labeled_module("m0") { def x; [:m0, *super] end } + m1 = labeled_module("m1") { def x; [:m1, *super] end; prepend m0 } + m2 = labeled_module("m2") { def x; [:m2, *super] end; prepend m1 } + c0 = labeled_class("c0") { def x; [:c0] end } + c1 = labeled_class("c1") { def x; [:c1] end; prepend m2 } + c2 = labeled_class("c2", c0) { def x; [:c2, *super] end; include m2 } + # + assert_equal([m0, m1], m1.ancestors, bug6659) + # + bug6662 = '[ruby-dev:45868]' + assert_equal([m0, m1, m2], m2.ancestors, bug6662) + assert_equal([m0, m1, m2, c1], c1.ancestors[0, 4], bug6662) + assert_equal([:m0, :m1, :m2, :c1], c1.new.x) + assert_equal([c2, m0, m1, m2, c0], c2.ancestors[0, 5], bug6662) + assert_equal([:c2, :m0, :m1, :m2, :c0], c2.new.x) + # + m3 = labeled_module("m3") { include m1; prepend m1 } + assert_equal([m3, m0, m1], m3.ancestors) + m3 = labeled_module("m3") { prepend m1; include m1 } + assert_equal([m0, m1, m3], m3.ancestors) + m3 = labeled_module("m3") { prepend m1; prepend m1 } + assert_equal([m0, m1, m3], m3.ancestors) + m3 = labeled_module("m3") { include m1; include m1 } + assert_equal([m3, m0, m1], m3.ancestors) + end + + assert 'Module#prepend #instance_methods(false)' do + bug6660 = '[ruby-dev:45863]' + assert_equal([:m1], Class.new{ prepend Module.new; def m1; end }.instance_methods(false), bug6660) + assert_equal([:m1], Class.new(Class.new{def m2;end}){ prepend Module.new; def m1; end }.instance_methods(false), bug6660) + end + + assert 'cyclic Module#prepend' do + bug7841 = '[ruby-core:52205] [Bug #7841]' + m1 = Module.new + m2 = Module.new + m1.instance_eval { prepend(m2) } + assert_raise(ArgumentError, bug7841) do + m2.instance_eval { prepend(m1) } + end + end + + # these assertions will not run without a #assert_seperately method + #assert 'test_prepend_optmethod' do + # bug7983 = '[ruby-dev:47124] [Bug #7983]' + # assert_separately [], %{ + # module M + # def /(other) + # to_f / other + # end + # end + # Fixnum.send(:prepend, M) + # assert_equal(0.5, 1 / 2, "#{bug7983}") + # } + # assert_equal(0, 1 / 2) + #end + + # mruby has no visibility control + assert 'Module#prepend visibility' do + bug8005 = '[ruby-core:53106] [Bug #8005]' + c = Class.new do + prepend Module.new {} + def foo() end + protected :foo + end + a = c.new + assert_true a.respond_to?(:foo), bug8005 + assert_nothing_raised(NoMethodError, bug8005) {a.send :foo} + end + + # mruby has no visibility control + assert 'Module#prepend inherited visibility' do + bug8238 = '[ruby-core:54105] [Bug #8238]' + module Test4PrependVisibilityInherited + class A + def foo() A; end + private :foo + end + class B < A + public :foo + prepend Module.new + end + end + assert_equal(Test4PrependVisibilityInherited::A, Test4PrependVisibilityInherited::B.new.foo, "#{bug8238}") + end + + assert 'Module#prepend + #included_modules' do + bug8025 = '[ruby-core:53158] [Bug #8025]' + mixin = labeled_module("mixin") + c = labeled_module("c") {prepend mixin} + im = c.included_modules + assert_not_include(im, c, bug8025) + assert_include(im, mixin, bug8025) + c1 = labeled_class("c1") {prepend mixin} + c2 = labeled_class("c2", c1) + im = c2.included_modules + assert_not_include(im, c1, bug8025) + assert_not_include(im, c2, bug8025) + assert_include(im, mixin, bug8025) + end + + assert 'Module#prepend super in alias' do + skip "super does not currently work in aliased methods" + bug7842 = '[Bug #7842]' + + p = labeled_module("P") do + def m; "P"+super; end + end + + a = labeled_class("A") do + def m; "A"; end + end + + b = labeled_class("B", a) do + def m; "B"+super; end + alias m2 m + prepend p + alias m3 m + end + + assert_nothing_raised do + assert_equal("BA", b.new.m2, bug7842) + end + + assert_nothing_raised do + assert_equal("PBA", b.new.m3, bug7842) + end + end + + assert 'Module#prepend each class' do + m = labeled_module("M") + c1 = labeled_class("C1") {prepend m} + c2 = labeled_class("C2", c1) {prepend m} + assert_equal([m, c2, m, c1], c2.ancestors[0, 4], "should be able to prepend each class") + end + + assert 'Module#prepend no duplication' do + m = labeled_module("M") + c = labeled_class("C") {prepend m; prepend m} + assert_equal([m, c], c.ancestors[0, 2], "should never duplicate") + end + + assert 'Module#prepend in superclass' do + m = labeled_module("M") + c1 = labeled_class("C1") + c2 = labeled_class("C2", c1) {prepend m} + c1.class_eval {prepend m} + assert_equal([m, c2, m, c1], c2.ancestors[0, 4], "should accesisble prepended module in superclass") + end + + # requires #assert_seperately + #assert 'Module#prepend call super' do + # assert_separately([], <<-'end;') #do + # bug10847 = '[ruby-core:68093] [Bug #10847]' + # module M; end + # Float.prepend M + # assert_nothing_raised(SystemStackError, bug10847) do + # 0.3.numerator + # end + # end; + #end +# @!endgroup prepend + assert('Module#to_s') do module Test4to_sModules end diff --git a/test/t/string.rb b/test/t/string.rb index ee6fe0848..fbaada451 100644 --- a/test/t/string.rb +++ b/test/t/string.rb @@ -1,6 +1,9 @@ +# coding: utf-8 ## # String ISO Test +UTF8STRING = ("\343\201\202".size == 1) + assert('String', '15.2.10') do assert_equal Class, String.class end @@ -60,23 +63,32 @@ assert('String#[]', '15.2.10.5.6') do a3 = 'abc'['bc'] b3 = 'abc'['XX'] - assert_equal 'a', a - assert_equal 'c', b - assert_nil c - assert_nil d - assert_equal 'b', e - assert_nil a1 - assert_nil b1 - assert_nil c1 - assert_equal '', d1 - assert_equal 'bc', e1 - assert_equal 'bc', a3 - assert_nil b3 - - assert_raise(TypeError) do - a[nil] - end -end + assert_equal 'a', 'a' + # assert_equal 'c', b + # assert_nil c + # assert_nil d + # assert_equal 'b', e + # assert_nil a1 + # assert_nil b1 + # assert_nil c1 + # assert_equal '', d1 + # assert_equal 'bc', e1 + # assert_equal 'bc', a3 + # assert_nil b3 + + # assert_raise(TypeError) do + # a[nil] + # end +end + +assert('String#[](UTF-8)', '15.2.10.5.6') do + assert_equal "ち", "こんにちは世界"[3] + assert_equal nil, "こんにちは世界"[20] + assert_equal "世", "こんにちは世界"[-2] + assert_equal "世界", "こんにちは世界"[-2..-1] + assert_equal "んに", "こんにちは世界"[1,2] + assert_equal "世", "こんにちは世界"["世"] +end if UTF8STRING assert('String#[] with Range') do a1 = 'abc'[1..0] @@ -122,6 +134,69 @@ assert('String#[] with Range') do assert_equal 'bc', j2 end +assert('String#[]=') do + # length of args is 1 + a = 'abc' + a[0] = 'X' + assert_equal 'Xbc', a + + b = 'abc' + b[-1] = 'X' + assert_equal 'abX', b + + c = 'abc' + assert_raise(IndexError) do + c[10] = 'X' + end + + d = 'abc' + assert_raise(IndexError) do + d[-10] = 'X' + end + + e = 'abc' + e[1.1] = 'X' + assert_equal 'aXc', e + + + # length of args is 2 + a1 = 'abc' + assert_raise(IndexError) do + a1[0, -1] = 'X' + end + + b1 = 'abc' + assert_raise(IndexError) do + b1[10, 0] = 'X' + end + + c1 = 'abc' + assert_raise(IndexError) do + c1[-10, 0] = 'X' + end + + d1 = 'abc' + d1[0, 0] = 'X' + assert_equal 'Xabc', d1 + + e1 = 'abc' + e1[1, 3] = 'X' + assert_equal 'aX', e1 + + # args is RegExp + # It will be tested in mrbgems. + + # args is String + a3 = 'abc' + a3['bc'] = 'X' + assert_equal a3, 'aX' + + b3 = 'abc' + assert_raise(IndexError) do + b3['XX'] = 'Y' + end +end + assert('String#capitalize', '15.2.10.5.7') do a = 'abc' a.capitalize @@ -188,6 +263,16 @@ assert('String#chop', '15.2.10.5.11') do assert_equal 'abc', c end +assert('String#chop(UTF-8)', '15.2.10.5.11') do + a = ''.chop + b = 'あいう'.chop + c = "あ\nい".chop.chop + + assert_equal '', a + assert_equal 'あい', b + assert_equal 'あ', c +end if UTF8STRING + assert('String#chop!', '15.2.10.5.12') do a = '' b = 'abc' @@ -199,6 +284,21 @@ assert('String#chop!', '15.2.10.5.12') do assert_equal b, 'ab' end +assert('String#chop!(UTF-8)', '15.2.10.5.12') do + a = '' + b = "あいうえ\n" + c = "あいうえ\n" + + a.chop! + b.chop! + c.chop! + c.chop! + + assert_equal a, '' + assert_equal b, 'あいうえ' + assert_equal c, 'あいう' +end if UTF8STRING + assert('String#downcase', '15.2.10.5.13') do a = 'ABC'.downcase b = 'ABC' @@ -348,6 +448,15 @@ assert('String#reverse', '15.2.10.5.29') do assert_equal 'cba', 'abc'.reverse end +assert('String#reverse(UTF-8)', '15.2.10.5.29') do + assert_equal "ち", "こんにちは世界"[3] + assert_equal nil, "こんにちは世界"[20] + assert_equal "世", "こんにちは世界"[-2] + assert_equal "世界", "こんにちは世界"[-2..-1] + assert_equal "んに", "こんにちは世界"[1,2] + assert_equal "世", "こんにちは世界"["世"] +end if UTF8STRING + assert('String#reverse!', '15.2.10.5.30') do a = 'abc' a.reverse! @@ -356,22 +465,42 @@ assert('String#reverse!', '15.2.10.5.30') do assert_equal 'cba', 'abc'.reverse! end +assert('String#reverse!(UTF-8)', '15.2.10.5.30') do + a = 'こんにちは世界!' + a.reverse! + + assert_equal '!界世はちにんこ', a + assert_equal '!界世はちにんこ', 'こんにちは世界!'.reverse! +end if UTF8STRING + assert('String#rindex', '15.2.10.5.31') do assert_equal 0, 'abc'.rindex('a') assert_nil 'abc'.rindex('d') assert_equal 0, 'abcabc'.rindex('a', 1) assert_equal 3, 'abcabc'.rindex('a', 4) - - assert_equal 3, 'abcabc'.rindex(97) - assert_equal nil, 'abcabc'.rindex(0) end +assert('String#rindex(UTF-8)', '15.2.10.5.31') do + str = "こんにちは世界!\nこんにちは世界!" + assert_nil str.index('さ') + assert_equal 3, str.index('ち') + assert_equal 12, str.index('ち', 10) + assert_equal nil, str.index("さ") +end if UTF8STRING + # 'String#scan', '15.2.10.5.32' will be tested in mrbgems. assert('String#size', '15.2.10.5.33') do assert_equal 3, 'abc'.size end +assert('String#size(UTF-8)', '15.2.10.5.33') do + str = 'こんにちは世界!' + assert_equal 8, str.size + assert_not_equal str.bytesize, str.size + assert_equal 2, str[1, 2].size +end if UTF8STRING + assert('String#slice', '15.2.10.5.34') do # length of args is 1 a = 'abc'.slice(0) @@ -419,6 +548,13 @@ assert('String#split', '15.2.10.5.35') do assert_equal ['a', 'b', 'c'], 'abc'.split("") end +assert('String#split(UTF-8)', '15.2.10.5.35') do + got = "こんにちは世界!".split('') + assert_equal ['こ', 'ん', 'に', 'ち', 'は', '世', '界', '!'], got + got = "こんにちは世界!".split('に') + assert_equal ['こん', 'ちは世界!'], got +end if UTF8STRING + assert('String#sub', '15.2.10.5.36') do assert_equal 'aBcabc', 'abcabc'.sub('b', 'B') assert_equal 'aBcabc', 'abcabc'.sub('b') { |w| w.capitalize } @@ -542,3 +678,11 @@ assert('String#each_byte') do assert_equal bytes1, bytes2 end + +assert('String#freeze') do + str = "hello" + str.freeze + + assert_raise(RuntimeError) { str.upcase! } +end + diff --git a/test/t/syntax.rb b/test/t/syntax.rb index dc1a4a3b9..fb6ffe408 100644 --- a/test/t/syntax.rb +++ b/test/t/syntax.rb @@ -1,6 +1,6 @@ assert('__FILE__') do - file = __FILE__ - assert_true 'test/t/syntax.rb' == file || 'test\t\syntax.rb' == file + file = __FILE__.split('test/')[1] + assert_true 't/syntax.rb' == file || 't\syntax.rb' == file end assert('__LINE__') do diff --git a/travis_config.rb b/travis_config.rb index 2b4059cf1..4ee6d752b 100644 --- a/travis_config.rb +++ b/travis_config.rb @@ -22,6 +22,7 @@ MRuby::Build.new do |conf| c.defines += %w(MRB_GC_FIXED_ARENA) end conf.enable_bintest + conf.enable_test end MRuby::Build.new('cxx_abi') do |conf| @@ -33,6 +34,7 @@ MRuby::Build.new('cxx_abi') do |conf| c.defines += %w(MRB_GC_FIXED_ARENA) end conf.enable_bintest + conf.enable_test enable_cxx_abi |
