summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--include/mruby/range.h3
-rw-r--r--include/mruby/value.h5
-rw-r--r--mrbgems/mruby-compiler/core/codegen.c12
-rw-r--r--mrbgems/mruby-compiler/core/parse.y56
-rw-r--r--mrbgems/mruby-enumerator/mrblib/enumerator.rb14
-rw-r--r--mrbgems/mruby-enumerator/test/enumerator.rb3
-rw-r--r--mrbgems/mruby-range-ext/src/range.c6
-rw-r--r--src/array.c11
-rw-r--r--src/class.c2
-rw-r--r--src/object.c2
-rw-r--r--src/proc.c2
-rw-r--r--src/range.c39
-rw-r--r--src/string.c242
-rw-r--r--src/vm.c47
-rw-r--r--test/t/codegen.rb9
-rw-r--r--test/t/kernel.rb15
-rw-r--r--test/t/nomethoderror.rb31
-rw-r--r--test/t/proc.rb16
18 files changed, 441 insertions, 74 deletions
diff --git a/include/mruby/range.h b/include/mruby/range.h
index cf42ce133..fb602b3f3 100644
--- a/include/mruby/range.h
+++ b/include/mruby/range.h
@@ -25,7 +25,8 @@ struct RRange {
mrb_bool excl : 1;
};
-#define mrb_range_ptr(v) ((struct RRange*)(mrb_ptr(v)))
+MRB_API struct RRange* mrb_range_ptr(mrb_state *mrb, mrb_value v);
+#define mrb_range_raw_ptr(v) ((struct RRange*)mrb_ptr(v))
#define mrb_range_value(p) mrb_obj_value((void*)(p))
/*
diff --git a/include/mruby/value.h b/include/mruby/value.h
index 61110e3dd..54d197f8f 100644
--- a/include/mruby/value.h
+++ b/include/mruby/value.h
@@ -62,12 +62,12 @@ struct mrb_state;
# define MRB_PRIx PRIx32
#endif
+
+MRB_API double mrb_float_read(const char*, char**);
#ifdef MRB_USE_FLOAT
typedef float mrb_float;
-# define str_to_mrb_float(buf) strtof(buf, NULL)
#else
typedef double mrb_float;
-# define str_to_mrb_float(buf) strtod(buf, NULL)
#endif
#if defined _MSC_VER && _MSC_VER < 1900
@@ -85,7 +85,6 @@ MRB_API int mrb_msvc_snprintf(char *s, size_t n, const char *format, ...);
# define isnan _isnan
# define isinf(n) (!_finite(n) && !_isnan(n))
# define signbit(n) (_copysign(1.0, (n)) < 0.0)
-# define strtof (float)strtod
static const unsigned int IEEE754_INFINITY_BITS_SINGLE = 0x7F800000;
# define INFINITY (*(float *)&IEEE754_INFINITY_BITS_SINGLE)
# define NAN ((float)(INFINITY - INFINITY))
diff --git a/mrbgems/mruby-compiler/core/codegen.c b/mrbgems/mruby-compiler/core/codegen.c
index 553baa116..b2cd12225 100644
--- a/mrbgems/mruby-compiler/core/codegen.c
+++ b/mrbgems/mruby-compiler/core/codegen.c
@@ -1061,7 +1061,9 @@ gen_vmassignment(codegen_scope *s, node *tree, int rhs, int val)
n++;
}
}
- push();
+ if (!val) {
+ push();
+ }
}
}
@@ -1518,7 +1520,9 @@ codegen(codegen_scope *s, node *tree, int val)
genop(s, MKOP_A(OP_LOADNIL, cursp()));
if (pos3) dispatch_linked(s, pos3);
if (head) pop();
- genop(s, MKOP_AB(OP_MOVE, cursp(), pos));
+ if (cursp() != pos) {
+ genop(s, MKOP_AB(OP_MOVE, cursp(), pos));
+ }
push();
}
else {
@@ -2207,7 +2211,7 @@ codegen(codegen_scope *s, node *tree, int val)
case NODE_FLOAT:
if (val) {
char *p = (char*)tree;
- mrb_float f = str_to_mrb_float(p);
+ mrb_float f = mrb_float_read(p, NULL);
int off = new_lit(s, mrb_float_value(s->mrb, f));
genop(s, MKOP_ABx(OP_LOADL, cursp(), off));
@@ -2223,7 +2227,7 @@ codegen(codegen_scope *s, node *tree, int val)
case NODE_FLOAT:
{
char *p = (char*)tree;
- mrb_float f = str_to_mrb_float(p);
+ mrb_float f = mrb_float_read(p, NULL);
int off = new_lit(s, mrb_float_value(s->mrb, -f));
genop(s, MKOP_ABx(OP_LOADL, cursp(), off));
diff --git a/mrbgems/mruby-compiler/core/parse.y b/mrbgems/mruby-compiler/core/parse.y
index bf893fb37..05b948f8e 100644
--- a/mrbgems/mruby-compiler/core/parse.y
+++ b/mrbgems/mruby-compiler/core/parse.y
@@ -871,6 +871,7 @@ call_with_block(parser_state *p, node *a, node *b)
break;
case NODE_CALL:
case NODE_FCALL:
+ case NODE_SCALL:
n = a->cdr->cdr->cdr;
if (!n->car) n->car = cons(0, b);
else {
@@ -1917,7 +1918,7 @@ aref_args : none
$$ = $1;
NODE_LINENO($$, $1);
}
- | args ',' assocs trailer
+ | args comma assocs trailer
{
$$ = push($1, new_hash(p, $3));
}
@@ -1957,7 +1958,7 @@ opt_call_args : none
$$ = cons($1,0);
NODE_LINENO($$, $1);
}
- | args ',' assocs ','
+ | args comma assocs ','
{
$$ = cons(push($1, new_hash(p, $3)), 0);
NODE_LINENO($$, $1);
@@ -1984,7 +1985,7 @@ call_args : command
$$ = cons(list1(new_hash(p, $1)), $2);
NODE_LINENO($$, $1);
}
- | args ',' assocs opt_block_arg
+ | args comma assocs opt_block_arg
{
$$ = cons(push($1, new_hash(p, $3)), $4);
NODE_LINENO($$, $1);
@@ -2013,7 +2014,7 @@ block_arg : tAMPER arg
}
;
-opt_block_arg : ',' block_arg
+opt_block_arg : comma block_arg
{
$$ = $2;
}
@@ -2023,6 +2024,10 @@ opt_block_arg : ',' block_arg
}
;
+comma : ','
+ | ',' heredoc_bodies
+ ;
+
args : arg
{
void_expr_error(p, $1);
@@ -2035,34 +2040,24 @@ args : arg
$$ = cons(new_splat(p, $2), 0);
NODE_LINENO($$, $2);
}
- | args ',' arg
+ | args comma arg
{
void_expr_error(p, $3);
$$ = push($1, $3);
}
- | args ',' tSTAR arg
+ | args comma tSTAR arg
{
void_expr_error(p, $4);
$$ = push($1, new_splat(p, $4));
}
- | args ',' heredoc_bodies arg
- {
- void_expr_error(p, $4);
- $$ = push($1, $4);
- }
- | args ',' heredoc_bodies tSTAR arg
- {
- void_expr_error(p, $5);
- $$ = push($1, new_splat(p, $5));
- }
;
-mrhs : args ',' arg
+mrhs : args comma arg
{
void_expr_error(p, $3);
$$ = push($1, $3);
}
- | args ',' tSTAR arg
+ | args comma tSTAR arg
{
void_expr_error(p, $4);
$$ = push($1, new_splat(p, $4));
@@ -2786,10 +2781,6 @@ regexp : tREGEXP_BEG tREGEXP
heredoc : tHEREDOC_BEG
;
-opt_heredoc_bodies : /* none */
- | heredoc_bodies
- ;
-
heredoc_bodies : heredoc_body
| heredoc_bodies heredoc_body
;
@@ -3311,11 +3302,12 @@ rbracket : opt_nl ']'
trailer : /* none */
| nl
- | ','
+ | comma
;
term : ';' {yyerrok;}
| nl
+ | heredoc_body
;
nl : '\n'
@@ -3323,10 +3315,10 @@ nl : '\n'
p->lineno++;
p->column = 0;
}
- opt_heredoc_bodies
+ ;
terms : term
- | terms ';' {yyerrok;}
+ | terms term
;
none : /* none */
@@ -4911,7 +4903,7 @@ parser_yylex(parser_state *p)
char *endp;
errno = 0;
- d = strtod(tok(p), &endp);
+ d = mrb_float_read(tok(p), &endp);
if (d == 0 && endp == tok(p)) {
yywarning_s(p, "corrupted float value %s", tok(p));
}
@@ -6067,7 +6059,17 @@ mrb_parser_dump(mrb_state *mrb, node *tree, int offset)
case NODE_FCALL:
case NODE_CALL:
- printf("NODE_CALL:\n");
+ case NODE_SCALL:
+ switch (nodetype) {
+ case NODE_FCALL:
+ printf("NODE_FCALL:\n"); break;
+ case NODE_CALL:
+ printf("NODE_CALL(.):\n"); break;
+ case NODE_SCALL:
+ printf("NODE_SCALL(&.):\n"); break;
+ default:
+ break;
+ }
mrb_parser_dump(mrb, tree->car, offset+1);
dump_prefix(tree, offset+1);
printf("method='%s' (%d)\n",
diff --git a/mrbgems/mruby-enumerator/mrblib/enumerator.rb b/mrbgems/mruby-enumerator/mrblib/enumerator.rb
index 1515a7a89..89b66cd45 100644
--- a/mrbgems/mruby-enumerator/mrblib/enumerator.rb
+++ b/mrbgems/mruby-enumerator/mrblib/enumerator.rb
@@ -153,12 +153,18 @@ class Enumerator
#
def with_index(offset=0)
return to_enum :with_index, offset unless block_given?
- raise TypeError, "no implicit conversion of #{offset.class} into Integer" unless offset.respond_to?(:to_int)
+ offset = if offset.nil?
+ 0
+ elsif offset.respond_to?(:to_int)
+ offset.to_int
+ else
+ raise TypeError, "no implicit conversion of #{offset.class} into Integer"
+ end
- n = offset.to_int - 1
- enumerator_block_call do |i|
+ n = offset - 1
+ enumerator_block_call do |*i|
n += 1
- yield [i,n]
+ yield i.__svalue, n
end
end
diff --git a/mrbgems/mruby-enumerator/test/enumerator.rb b/mrbgems/mruby-enumerator/test/enumerator.rb
index 2e45dae4b..e86e874f0 100644
--- a/mrbgems/mruby-enumerator/test/enumerator.rb
+++ b/mrbgems/mruby-enumerator/test/enumerator.rb
@@ -50,6 +50,9 @@ end
assert 'Enumerator#with_index' do
assert_equal([[1,0],[2,1],[3,2]], @obj.to_enum(:foo, 1, 2, 3).with_index.to_a)
assert_equal([[1,5],[2,6],[3,7]], @obj.to_enum(:foo, 1, 2, 3).with_index(5).to_a)
+ a = []
+ @obj.to_enum(:foo, 1, 2, 3).with_index(10).with_index(20) { |*i| a << i }
+ assert_equal [[[1, 10], 20], [[2, 11], 21], [[3, 12], 22]], a
end
assert 'Enumerator#with_index nonnum offset' do
diff --git a/mrbgems/mruby-range-ext/src/range.c b/mrbgems/mruby-range-ext/src/range.c
index ccb5a9e45..8aa1379b0 100644
--- a/mrbgems/mruby-range-ext/src/range.c
+++ b/mrbgems/mruby-range-ext/src/range.c
@@ -44,7 +44,7 @@ static mrb_value
mrb_range_cover(mrb_state *mrb, mrb_value range)
{
mrb_value val;
- struct RRange *r = mrb_range_ptr(range);
+ struct RRange *r = mrb_range_ptr(mrb, range);
mrb_value beg, end;
mrb_get_args(mrb, "o", &val);
@@ -87,7 +87,7 @@ mrb_range_last(mrb_state *mrb, mrb_value range)
{
mrb_value num;
mrb_value array;
- struct RRange *r = mrb_range_ptr(range);
+ struct RRange *r = mrb_range_ptr(mrb, range);
if (mrb_get_args(mrb, "|o", &num) == 0) {
return r->edges->end;
@@ -111,7 +111,7 @@ mrb_range_last(mrb_state *mrb, mrb_value range)
static mrb_value
mrb_range_size(mrb_state *mrb, mrb_value range)
{
- struct RRange *r = mrb_range_ptr(range);
+ struct RRange *r = mrb_range_ptr(mrb, range);
mrb_value beg, end;
mrb_float beg_f, end_f;
mrb_bool num_p = TRUE;
diff --git a/src/array.c b/src/array.c
index 9013492fb..f6599bd5b 100644
--- a/src/array.c
+++ b/src/array.c
@@ -173,11 +173,13 @@ ary_expand_capa(mrb_state *mrb, struct RArray *a, mrb_int len)
capa = ARY_DEFAULT_LEN;
}
while (capa < len) {
- capa *= 2;
+ if (capa <= ARY_MAX_SIZE / 2) {
+ capa *= 2;
+ } else {
+ capa = ARY_MAX_SIZE;
+ }
}
- if (capa > ARY_MAX_SIZE) capa = ARY_MAX_SIZE; /* len <= capa <= ARY_MAX_SIZE */
-
if (capa > a->aux.capa) {
mrb_value *expanded_ptr = (mrb_value *)mrb_realloc(mrb, a->ptr, sizeof(mrb_value)*capa);
@@ -503,6 +505,9 @@ mrb_ary_unshift_m(mrb_state *mrb, mrb_value self)
mrb_int len;
mrb_get_args(mrb, "*", &vals, &len);
+ if (len > ARY_MAX_SIZE - a->len) {
+ mrb_raise(mrb, E_ARGUMENT_ERROR, "array size too big");
+ }
if (ARY_SHARED_P(a)
&& a->aux.shared->refcnt == 1 /* shared only referenced from this array */
&& a->ptr - a->aux.shared->ptr >= len) /* there's room for unshifted item */ {
diff --git a/src/class.c b/src/class.c
index bac1d2984..e724dbb91 100644
--- a/src/class.c
+++ b/src/class.c
@@ -1817,7 +1817,7 @@ mrb_mod_undef(mrb_state *mrb, mrb_value mod)
mrb_get_args(mrb, "*", &argv, &argc);
while (argc--) {
- undef_method(mrb, c, mrb_symbol(*argv));
+ undef_method(mrb, c, to_sym(mrb, *argv));
argv++;
}
return mrb_nil_value();
diff --git a/src/object.c b/src/object.c
index f76ee68a2..eb2c23e63 100644
--- a/src/object.c
+++ b/src/object.c
@@ -444,7 +444,7 @@ mrb_any_to_s(mrb_state *mrb, mrb_value obj)
mrb_str_cat_lit(mrb, str, "#<");
mrb_str_cat_cstr(mrb, str, cname);
mrb_str_cat_lit(mrb, str, ":");
- mrb_str_concat(mrb, str, mrb_ptr_to_str(mrb, mrb_cptr(obj)));
+ mrb_str_concat(mrb, str, mrb_ptr_to_str(mrb, mrb_ptr(obj)));
mrb_str_cat_lit(mrb, str, ">");
return str;
diff --git a/src/proc.c b/src/proc.c
index 4f770932b..470547094 100644
--- a/src/proc.c
+++ b/src/proc.c
@@ -140,7 +140,7 @@ mrb_proc_copy(struct RProc *a, struct RProc *b)
{
a->flags = b->flags;
a->body = b->body;
- if (!MRB_PROC_CFUNC_P(a)) {
+ if (!MRB_PROC_CFUNC_P(a) && a->body.irep) {
a->body.irep->refcnt++;
}
a->target_class = b->target_class;
diff --git a/src/range.c b/src/range.c
index f0a976e53..417957420 100644
--- a/src/range.c
+++ b/src/range.c
@@ -12,6 +12,17 @@
#define RANGE_CLASS (mrb_class_get(mrb, "Range"))
+MRB_API struct RRange*
+mrb_range_ptr(mrb_state *mrb, mrb_value v)
+{
+ struct RRange *r = (struct RRange*)mrb_ptr(v);
+
+ if (r->edges == NULL) {
+ mrb_raise(mrb, E_ARGUMENT_ERROR, "uninitialized range");
+ }
+ return r;
+}
+
static void
range_check(mrb_state *mrb, mrb_value a, mrb_value b)
{
@@ -57,7 +68,7 @@ mrb_range_new(mrb_state *mrb, mrb_value beg, mrb_value end, mrb_bool excl)
mrb_value
mrb_range_beg(mrb_state *mrb, mrb_value range)
{
- struct RRange *r = mrb_range_ptr(range);
+ struct RRange *r = mrb_range_ptr(mrb, range);
return r->edges->beg;
}
@@ -76,7 +87,7 @@ mrb_range_beg(mrb_state *mrb, mrb_value range)
mrb_value
mrb_range_end(mrb_state *mrb, mrb_value range)
{
- struct RRange *r = mrb_range_ptr(range);
+ struct RRange *r = mrb_range_ptr(mrb, range);
return r->edges->end;
}
@@ -90,7 +101,7 @@ mrb_range_end(mrb_state *mrb, mrb_value range)
mrb_value
mrb_range_excl(mrb_state *mrb, mrb_value range)
{
- struct RRange *r = mrb_range_ptr(range);
+ struct RRange *r = mrb_range_ptr(mrb, range);
return mrb_bool_value(r->excl);
}
@@ -98,7 +109,7 @@ mrb_range_excl(mrb_state *mrb, mrb_value range)
static void
range_init(mrb_state *mrb, mrb_value range, mrb_value beg, mrb_value end, mrb_bool exclude_end)
{
- struct RRange *r = mrb_range_ptr(range);
+ struct RRange *r = mrb_range_raw_ptr(range);
range_check(mrb, beg, end);
r->excl = exclude_end;
@@ -129,7 +140,7 @@ mrb_range_initialize(mrb_state *mrb, mrb_value range)
exclusive = FALSE;
}
/* Ranges are immutable, so that they should be initialized only once. */
- if (mrb_range_ptr(range)->edges) {
+ if (mrb_range_raw_ptr(range)->edges) {
mrb_name_error(mrb, mrb_intern_lit(mrb, "initialize"), "`initialize' called twice");
}
range_init(mrb, range, beg, end, exclusive);
@@ -164,8 +175,8 @@ mrb_range_eq(mrb_state *mrb, mrb_value range)
return mrb_false_value();
}
- rr = mrb_range_ptr(range);
- ro = mrb_range_ptr(obj);
+ rr = mrb_range_ptr(mrb, range);
+ ro = mrb_range_ptr(mrb, obj);
v1 = mrb_funcall(mrb, rr->edges->beg, "==", 1, ro->edges->beg);
v2 = mrb_funcall(mrb, rr->edges->end, "==", 1, ro->edges->end);
if (!mrb_bool(v1) || !mrb_bool(v2) || rr->excl != ro->excl) {
@@ -222,7 +233,7 @@ mrb_value
mrb_range_include(mrb_state *mrb, mrb_value range)
{
mrb_value val;
- struct RRange *r = mrb_range_ptr(range);
+ struct RRange *r = mrb_range_ptr(mrb, range);
mrb_value beg, end;
mrb_bool include_p;
@@ -241,7 +252,7 @@ static mrb_bool
range_beg_len(mrb_state *mrb, mrb_value range, mrb_int *begp, mrb_int *lenp, mrb_int len, mrb_bool trunc)
{
mrb_int beg, end;
- struct RRange *r = mrb_range_ptr(range);
+ struct RRange *r = mrb_range_ptr(mrb, range);
if (mrb_type(range) != MRB_TT_RANGE) return FALSE;
@@ -287,7 +298,7 @@ static mrb_value
range_to_s(mrb_state *mrb, mrb_value range)
{
mrb_value str, str2;
- struct RRange *r = mrb_range_ptr(range);
+ struct RRange *r = mrb_range_ptr(mrb, range);
str = mrb_obj_as_string(mrb, r->edges->beg);
str2 = mrb_obj_as_string(mrb, r->edges->end);
@@ -312,7 +323,7 @@ static mrb_value
range_inspect(mrb_state *mrb, mrb_value range)
{
mrb_value str, str2;
- struct RRange *r = mrb_range_ptr(range);
+ struct RRange *r = mrb_range_ptr(mrb, range);
str = mrb_inspect(mrb, r->edges->beg);
str2 = mrb_inspect(mrb, r->edges->end);
@@ -352,8 +363,8 @@ range_eql(mrb_state *mrb, mrb_value range)
}
if (mrb_type(obj) != MRB_TT_RANGE) return mrb_false_value();
- r = mrb_range_ptr(range);
- o = mrb_range_ptr(obj);
+ r = mrb_range_ptr(mrb, range);
+ o = mrb_range_ptr(mrb, obj);
if (!mrb_eql(mrb, r->edges->beg, o->edges->beg) ||
!mrb_eql(mrb, r->edges->end, o->edges->end) ||
(r->excl != o->excl)) {
@@ -376,7 +387,7 @@ range_initialize_copy(mrb_state *mrb, mrb_value copy)
mrb_raise(mrb, E_TYPE_ERROR, "wrong argument class");
}
- r = mrb_range_ptr(src);
+ r = mrb_range_ptr(mrb, src);
range_init(mrb, copy, r->edges->beg, r->edges->end, r->excl);
return copy;
diff --git a/src/string.c b/src/string.c
index f47294291..4f722b273 100644
--- a/src/string.c
+++ b/src/string.c
@@ -2286,7 +2286,7 @@ mrb_cstr_to_dbl(mrb_state *mrb, const char * p, mrb_bool badcheck)
if (!badcheck && p[0] == '0' && (p[1] == 'x' || p[1] == 'X')) {
return 0.0;
}
- d = strtod(p, &end);
+ d = mrb_float_read(p, &end);
if (p == end) {
if (badcheck) {
bad:
@@ -2324,7 +2324,7 @@ bad:
return 0.0;
}
- d = strtod(p, &end);
+ d = mrb_float_read(p, &end);
if (badcheck) {
if (!end || p == end) goto bad;
while (*end && ISSPACE(*end)) end++;
@@ -2749,3 +2749,241 @@ mrb_init_string(mrb_state *mrb)
mrb_define_method(mrb, s, "freeze", mrb_str_freeze, MRB_ARGS_NONE());
}
+
+/*
+ * Source code for the "strtod" library procedure.
+ *
+ * Copyright (c) 1988-1993 The Regents of the University of California.
+ * Copyright (c) 1994 Sun Microsystems, Inc.
+ *
+ * Permission to use, copy, modify, and distribute this
+ * software and its documentation for any purpose and without
+ * fee is hereby granted, provided that the above copyright
+ * notice appear in all copies. The University of California
+ * makes no representations about the suitability of this
+ * software for any purpose. It is provided "as is" without
+ * express or implied warranty.
+ *
+ * RCS: @(#) $Id: strtod.c 11708 2007-02-12 23:01:19Z shyouhei $
+ */
+
+#include <ctype.h>
+#include <errno.h>
+extern int errno;
+
+#ifndef __STDC__
+# ifdef __GNUC__
+# define const __const__
+# else
+# define const
+# endif
+#endif
+
+static const int maxExponent = 511; /* Largest possible base 10 exponent. Any
+ * exponent larger than this will already
+ * produce underflow or overflow, so there's
+ * no need to worry about additional digits.
+ */
+static const double powersOf10[] = {/* Table giving binary powers of 10. Entry */
+ 10., /* is 10^2^i. Used to convert decimal */
+ 100., /* exponents into floating-point numbers. */
+ 1.0e4,
+ 1.0e8,
+ 1.0e16,
+ 1.0e32,
+ 1.0e64,
+ 1.0e128,
+ 1.0e256
+};
+
+MRB_API double
+mrb_float_read(const char *string, char **endPtr)
+/* const char *string; A decimal ASCII floating-point number,
+ * optionally preceded by white space.
+ * Must have form "-I.FE-X", where I is the
+ * integer part of the mantissa, F is the
+ * fractional part of the mantissa, and X
+ * is the exponent. Either of the signs
+ * may be "+", "-", or omitted. Either I
+ * or F may be omitted, or both. The decimal
+ * point isn't necessary unless F is present.
+ * The "E" may actually be an "e". E and X
+ * may both be omitted (but not just one).
+ */
+/* char **endPtr; If non-NULL, store terminating character's
+ * address here. */
+{
+ int sign, expSign = FALSE;
+ double fraction, dblExp;
+ const double *d;
+ register const char *p;
+ register int c;
+ int exp = 0; /* Exponent read from "EX" field. */
+ int fracExp = 0; /* Exponent that derives from the fractional
+ * part. Under normal circumstatnces, it is
+ * the negative of the number of digits in F.
+ * However, if I is very long, the last digits
+ * of I get dropped (otherwise a long I with a
+ * large negative exponent could cause an
+ * unnecessary overflow on I alone). In this
+ * case, fracExp is incremented one for each
+ * dropped digit. */
+ int mantSize; /* Number of digits in mantissa. */
+ int decPt; /* Number of mantissa digits BEFORE decimal
+ * point. */
+ const char *pExp; /* Temporarily holds location of exponent
+ * in string. */
+
+ /*
+ * Strip off leading blanks and check for a sign.
+ */
+
+ p = string;
+ while (isspace(*p)) {
+ p += 1;
+ }
+ if (*p == '-') {
+ sign = TRUE;
+ p += 1;
+ } else {
+ if (*p == '+') {
+ p += 1;
+ }
+ sign = FALSE;
+ }
+
+ /*
+ * Count the number of digits in the mantissa (including the decimal
+ * point), and also locate the decimal point.
+ */
+
+ decPt = -1;
+ for (mantSize = 0; ; mantSize += 1)
+ {
+ c = *p;
+ if (!isdigit(c)) {
+ if ((c != '.') || (decPt >= 0)) {
+ break;
+ }
+ decPt = mantSize;
+ }
+ p += 1;
+ }
+
+ /*
+ * Now suck up the digits in the mantissa. Use two integers to
+ * collect 9 digits each (this is faster than using floating-point).
+ * If the mantissa has more than 18 digits, ignore the extras, since
+ * they can't affect the value anyway.
+ */
+
+ pExp = p;
+ p -= mantSize;
+ if (decPt < 0) {
+ decPt = mantSize;
+ } else {
+ mantSize -= 1; /* One of the digits was the point. */
+ }
+ if (mantSize > 18) {
+ fracExp = decPt - 18;
+ mantSize = 18;
+ } else {
+ fracExp = decPt - mantSize;
+ }
+ if (mantSize == 0) {
+ fraction = 0.0;
+ p = string;
+ goto done;
+ } else {
+ int frac1, frac2;
+ frac1 = 0;
+ for ( ; mantSize > 9; mantSize -= 1)
+ {
+ c = *p;
+ p += 1;
+ if (c == '.') {
+ c = *p;
+ p += 1;
+ }
+ frac1 = 10*frac1 + (c - '0');
+ }
+ frac2 = 0;
+ for (; mantSize > 0; mantSize -= 1)
+ {
+ c = *p;
+ p += 1;
+ if (c == '.') {
+ c = *p;
+ p += 1;
+ }
+ frac2 = 10*frac2 + (c - '0');
+ }
+ fraction = (1.0e9 * frac1) + frac2;
+ }
+
+ /*
+ * Skim off the exponent.
+ */
+
+ p = pExp;
+ if ((*p == 'E') || (*p == 'e')) {
+ p += 1;
+ if (*p == '-') {
+ expSign = TRUE;
+ p += 1;
+ } else {
+ if (*p == '+') {
+ p += 1;
+ }
+ expSign = FALSE;
+ }
+ while (isdigit(*p)) {
+ exp = exp * 10 + (*p - '0');
+ p += 1;
+ }
+ }
+ if (expSign) {
+ exp = fracExp - exp;
+ } else {
+ exp = fracExp + exp;
+ }
+
+ /*
+ * Generate a floating-point number that represents the exponent.
+ * Do this by processing the exponent one bit at a time to combine
+ * many powers of 2 of 10. Then combine the exponent with the
+ * fraction.
+ */
+
+ if (exp < 0) {
+ expSign = TRUE;
+ exp = -exp;
+ } else {
+ expSign = FALSE;
+ }
+ if (exp > maxExponent) {
+ exp = maxExponent;
+ errno = ERANGE;
+ }
+ dblExp = 1.0;
+ for (d = powersOf10; exp != 0; exp >>= 1, d += 1) {
+ if (exp & 01) {
+ dblExp *= *d;
+ }
+ }
+ if (expSign) {
+ fraction /= dblExp;
+ } else {
+ fraction *= dblExp;
+ }
+
+done:
+ if (endPtr != NULL) {
+ *endPtr = (char *) p;
+ }
+
+ if (sign) {
+ return -fraction;
+ }
+ return fraction;
+}
diff --git a/src/vm.c b/src/vm.c
index f0dc338d0..76f56298f 100644
--- a/src/vm.c
+++ b/src/vm.c
@@ -54,6 +54,10 @@ The value below allows about 60000 recursive calls in the simplest case. */
#define ARENA_RESTORE(mrb,ai) (mrb)->gc.arena_idx = (ai)
+#define CALL_MAXARGS 127
+
+void mrb_method_missing(mrb_state *mrb, mrb_sym name, mrb_value self, mrb_value args);
+
static inline void
stack_clear(mrb_value *from, size_t count)
{
@@ -362,9 +366,13 @@ mrb_funcall_with_block(mrb_state *mrb, mrb_value self, mrb_sym mid, mrb_int argc
c = mrb_class(mrb, self);
p = mrb_method_search_vm(mrb, &c, mid);
if (!p) {
+ mrb_sym missing = mrb_intern_lit(mrb, "method_missing");
+ p = mrb_method_search_vm(mrb, &c, missing);
+ if (!p) {
+ mrb_value args = mrb_ary_new_from_values(mrb, argc, argv);
+ mrb_method_missing(mrb, mid, self, args);
+ }
undef = mid;
- mid = mrb_intern_lit(mrb, "method_missing");
- p = mrb_method_search_vm(mrb, &c, mid);
n++; argc++;
}
ci = cipush(mrb);
@@ -749,10 +757,6 @@ argnum_error(mrb_state *mrb, mrb_int num)
#endif
-#define CALL_MAXARGS 127
-
-void mrb_method_missing(mrb_state *mrb, mrb_sym name, mrb_value self, mrb_value args);
-
MRB_API mrb_value
mrb_vm_run(mrb_state *mrb, struct RProc *proc, mrb_value self, unsigned int stack_keep)
{
@@ -1290,8 +1294,20 @@ RETRY_TRY_BLOCK:
c = mrb->c->ci->target_class->super;
m = mrb_method_search_vm(mrb, &c, mid);
if (!m) {
- mid = mrb_intern_lit(mrb, "method_missing");
- m = mrb_method_search_vm(mrb, &c, mid);
+ mrb_sym missing = mrb_intern_lit(mrb, "method_missing");
+ m = mrb_method_search_vm(mrb, &c, missing);
+ if (!m) {
+ mrb_value args;
+
+ if (n == CALL_MAXARGS) {
+ args = regs[a+1];
+ }
+ else {
+ args = mrb_ary_new_from_values(mrb, n, regs+a+1);
+ }
+ mrb_method_missing(mrb, mid, recv, args);
+ }
+ mid = missing;
if (n == CALL_MAXARGS) {
mrb_ary_unshift(mrb, regs[a+1], mrb_symbol_value(ci->mid));
}
@@ -1681,9 +1697,20 @@ RETRY_TRY_BLOCK:
m = mrb_method_search_vm(mrb, &c, mid);
if (!m) {
mrb_value sym = mrb_symbol_value(mid);
+ mrb_sym missing = mrb_intern_lit(mrb, "method_missing");
+ m = mrb_method_search_vm(mrb, &c, missing);
+ if (!m) {
+ mrb_value args;
- mid = mrb_intern_lit(mrb, "method_missing");
- m = mrb_method_search_vm(mrb, &c, mid);
+ if (n == CALL_MAXARGS) {
+ args = regs[a+1];
+ }
+ else {
+ args = mrb_ary_new_from_values(mrb, n, regs+a+1);
+ }
+ mrb_method_missing(mrb, mid, recv, args);
+ }
+ mid = missing;
if (n == CALL_MAXARGS) {
mrb_ary_unshift(mrb, regs[a+1], sym);
}
diff --git a/test/t/codegen.rb b/test/t/codegen.rb
index 1ac689a82..bb0f5c306 100644
--- a/test/t/codegen.rb
+++ b/test/t/codegen.rb
@@ -54,3 +54,12 @@ A
B
assert_equal "\n", a
end
+
+assert('splat in case splat') do
+ a = *case
+ when 0
+ * = 1
+ end
+
+ assert_equal [1], a
+end
diff --git a/test/t/kernel.rb b/test/t/kernel.rb
index d240e59dc..e59bd6a10 100644
--- a/test/t/kernel.rb
+++ b/test/t/kernel.rb
@@ -520,6 +520,21 @@ assert('Kernel#to_s', '15.3.1.3.46') do
assert_equal to_s.class, String
end
+assert('Kernel#to_s on primitives') do
+ begin
+ Fixnum.alias_method :to_s_, :to_s
+ Fixnum.remove_method :to_s
+
+ assert_nothing_raised do
+ # segfaults if mrb_cptr is used
+ 1.to_s
+ end
+ ensure
+ Fixnum.alias_method :to_s, :to_s_
+ Fixnum.remove_method :to_s_
+ end
+end
+
assert('Kernel.local_variables', '15.3.1.2.7') do
a, b = 0, 1
a += b
diff --git a/test/t/nomethoderror.rb b/test/t/nomethoderror.rb
index 5fed79689..1c09bc20e 100644
--- a/test/t/nomethoderror.rb
+++ b/test/t/nomethoderror.rb
@@ -20,3 +20,34 @@ assert('NoMethodError#args', '15.2.32.2.1') do
end
end
end
+
+assert('Can still raise when BasicObject#method_missing is removed') do
+ assert_raise(NoMethodError) do
+ begin
+ BasicObject.alias_method(:old_method_missing, :method_missing)
+ BasicObject.remove_method(:method_missing)
+ 1.__send__(:foo)
+ ensure
+ BasicObject.alias_method(:method_missing, :old_method_missing)
+ BasicObject.remove_method(:old_method_missing)
+ end
+ end
+end
+
+assert('Can still call super when BasicObject#method_missing is removed') do
+ assert_raise(NoMethodError) do
+ class A
+ def foo
+ super
+ end
+ end
+ begin
+ BasicObject.alias_method(:old_method_missing, :method_missing)
+ BasicObject.remove_method(:method_missing)
+ A.new.foo
+ ensure
+ BasicObject.alias_method(:method_missing, :old_method_missing)
+ BasicObject.remove_method(:old_method_missing)
+ end
+ end
+end
diff --git a/test/t/proc.rb b/test/t/proc.rb
index bc9821f7c..29530e8dd 100644
--- a/test/t/proc.rb
+++ b/test/t/proc.rb
@@ -163,3 +163,19 @@ assert('&obj call to_proc if defined') do
assert_raise(TypeError){ mock(&(Object.new)) }
end
+
+assert('initialize_copy works when initialize is removed') do
+ begin
+ Proc.alias_method(:old_initialize, :initialize)
+ Proc.remove_method(:initialize)
+
+ a = Proc.new {}
+ b = Proc.new {}
+ assert_nothing_raised do
+ a.initialize_copy(b)
+ end
+ ensure
+ Proc.alias_method(:initialize, :old_initialize)
+ Proc.remove_method(:old_initialize)
+ end
+end