summaryrefslogtreecommitdiffhomepage
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/array.c908
-rw-r--r--src/backtrace.c345
-rw-r--r--src/cdump.c445
-rw-r--r--src/class.c2637
-rw-r--r--src/codedump.c595
-rw-r--r--src/codegen.c3156
-rw-r--r--src/compar.c2
-rw-r--r--src/crc.c39
-rw-r--r--src/debug.c118
-rw-r--r--src/dump.c560
-rw-r--r--src/enum.c22
-rw-r--r--src/error.c520
-rw-r--r--src/error.h2
-rw-r--r--src/etc.c143
-rw-r--r--src/ext/.gitkeep0
-rw-r--r--src/fmt_fp.c364
-rw-r--r--src/gc.c1315
-rw-r--r--src/hash.c1706
-rw-r--r--src/init.c6
-rw-r--r--src/kernel.c917
-rw-r--r--src/keywords50
-rw-r--r--src/lex.def212
-rw-r--r--src/load.c590
-rw-r--r--src/mrb_throw.h41
-rw-r--r--src/mruby_core.rake78
-rw-r--r--src/node.h117
-rw-r--r--src/numeric.c1657
-rw-r--r--src/object.c335
-rw-r--r--src/opcode.h2
-rw-r--r--src/parse.y6407
-rw-r--r--src/pool.c19
-rw-r--r--src/print.c94
-rw-r--r--src/proc.c423
-rw-r--r--src/range.c497
-rw-r--r--src/readflt.c120
-rw-r--r--src/readint.c30
-rw-r--r--src/state.c233
-rw-r--r--src/string.c2672
-rw-r--r--src/symbol.c414
-rw-r--r--src/value_array.h3
-rw-r--r--src/variable.c912
-rw-r--r--src/version.c10
-rw-r--r--src/vm.c3266
43 files changed, 13608 insertions, 18374 deletions
diff --git a/src/array.c b/src/array.c
index 48dc1ff10..dd669a0b1 100644
--- a/src/array.c
+++ b/src/array.c
@@ -4,46 +4,40 @@
** See Copyright Notice in mruby.h
*/
-#include "mruby.h"
-#include "mruby/array.h"
-#include "mruby/class.h"
-#include "mruby/string.h"
-#include "mruby/range.h"
+#include <mruby.h>
+#include <mruby/array.h>
+#include <mruby/class.h>
+#include <mruby/string.h>
+#include <mruby/range.h>
+#include <mruby/proc.h>
+#include <mruby/presym.h>
#include "value_array.h"
#define ARY_DEFAULT_LEN 4
#define ARY_SHRINK_RATIO 5 /* must be larger than 2 */
#define ARY_C_MAX_SIZE (SIZE_MAX / sizeof(mrb_value))
-#define ARY_MAX_SIZE ((ARY_C_MAX_SIZE < (size_t)MRB_INT_MAX) ? (mrb_int)ARY_C_MAX_SIZE : MRB_INT_MAX-1)
-
-static inline mrb_value
-ary_elt(mrb_value ary, mrb_int offset)
-{
- if (RARRAY_LEN(ary) == 0) return mrb_nil_value();
- if (offset < 0 || RARRAY_LEN(ary) <= offset) {
- return mrb_nil_value();
- }
- return RARRAY_PTR(ary)[offset];
-}
+#define ARY_MAX_SIZE ((mrb_int)((ARY_C_MAX_SIZE < (size_t)MRB_INT_MAX) ? ARY_C_MAX_SIZE : MRB_INT_MAX-1))
static struct RArray*
ary_new_capa(mrb_state *mrb, mrb_int capa)
{
struct RArray *a;
- mrb_int blen;
+ size_t blen;
if (capa > ARY_MAX_SIZE) {
mrb_raise(mrb, E_ARGUMENT_ERROR, "array size too big");
}
blen = capa * sizeof(mrb_value);
- if (blen < capa) {
- mrb_raise(mrb, E_ARGUMENT_ERROR, "array size too big");
- }
- a = (struct RArray*)mrb_obj_alloc(mrb, MRB_TT_ARRAY, mrb->array_class);
- a->ptr = (mrb_value *)mrb_malloc(mrb, blen);
- a->aux.capa = capa;
- a->len = 0;
+ a = MRB_OBJ_ALLOC(mrb, MRB_TT_ARRAY, mrb->array_class);
+ if (capa <= MRB_ARY_EMBED_LEN_MAX) {
+ ARY_SET_EMBED_LEN(a, 0);
+ }
+ else {
+ a->as.heap.ptr = (mrb_value *)mrb_malloc(mrb, blen);
+ a->as.heap.aux.capa = capa;
+ a->as.heap.len = 0;
+ }
return a;
}
@@ -62,18 +56,17 @@ mrb_ary_new(mrb_state *mrb)
}
/*
- * to copy array, use this instead of memcpy because of portability
+ * To copy array, use this instead of memcpy because of portability
* * gcc on ARM may fail optimization of memcpy
- * http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.faqs/ka3934.html
+ * https://gcc.gnu.org/bugzilla/show_bug.cgi?id=56620
* * gcc on MIPS also fail
- * http://gcc.gnu.org/bugzilla/show_bug.cgi?id=39755
+ * https://gcc.gnu.org/bugzilla/show_bug.cgi?id=39755
* * memcpy doesn't exist on freestanding environment
*
* If you optimize for binary size, use memcpy instead of this at your own risk
* of above portability issue.
*
- * see also http://togetter.com/li/462898
- *
+ * See also https://togetter.com/li/462898 (Japanese)
*/
static inline void
array_copy(mrb_value *dst, const mrb_value *src, mrb_int size)
@@ -85,18 +78,22 @@ array_copy(mrb_value *dst, const mrb_value *src, mrb_int size)
}
}
-MRB_API mrb_value
-mrb_ary_new_from_values(mrb_state *mrb, mrb_int size, const mrb_value *vals)
+static struct RArray*
+ary_new_from_values(mrb_state *mrb, mrb_int size, const mrb_value *vals)
{
- mrb_value ary;
- struct RArray *a;
+ struct RArray *a = ary_new_capa(mrb, size);
- ary = mrb_ary_new_capa(mrb, size);
- a = mrb_ary_ptr(ary);
- array_copy(a->ptr, vals, size);
- a->len = size;
+ array_copy(ARY_PTR(a), vals, size);
+ ARY_SET_LEN(a, size);
- return ary;
+ return a;
+}
+
+MRB_API mrb_value
+mrb_ary_new_from_values(mrb_state *mrb, mrb_int size, const mrb_value *vals)
+{
+ struct RArray *a = ary_new_from_values(mrb, size, vals);
+ return mrb_obj_value(a);
}
MRB_API mrb_value
@@ -105,9 +102,9 @@ mrb_assoc_new(mrb_state *mrb, mrb_value car, mrb_value cdr)
struct RArray *a;
a = ary_new_capa(mrb, 2);
- a->ptr[0] = car;
- a->ptr[1] = cdr;
- a->len = 2;
+ ARY_PTR(a)[0] = car;
+ ARY_PTR(a)[1] = cdr;
+ ARY_SET_LEN(a, 2);
return mrb_obj_value(a);
}
@@ -122,28 +119,36 @@ ary_fill_with_nil(mrb_value *ptr, mrb_int size)
}
static void
+ary_modify_check(mrb_state *mrb, struct RArray *a)
+{
+ mrb_check_frozen(mrb, a);
+}
+
+static void
ary_modify(mrb_state *mrb, struct RArray *a)
{
+ ary_modify_check(mrb, a);
+
if (ARY_SHARED_P(a)) {
- mrb_shared_array *shared = a->aux.shared;
+ mrb_shared_array *shared = a->as.heap.aux.shared;
- if (shared->refcnt == 1 && a->ptr == shared->ptr) {
- a->ptr = shared->ptr;
- a->aux.capa = a->len;
+ if (shared->refcnt == 1 && a->as.heap.ptr == shared->ptr) {
+ a->as.heap.ptr = shared->ptr;
+ a->as.heap.aux.capa = a->as.heap.len;
mrb_free(mrb, shared);
}
else {
mrb_value *ptr, *p;
mrb_int len;
- p = a->ptr;
- len = a->len * sizeof(mrb_value);
+ p = a->as.heap.ptr;
+ len = a->as.heap.len * sizeof(mrb_value);
ptr = (mrb_value *)mrb_malloc(mrb, len);
if (p) {
- array_copy(ptr, p, a->len);
+ array_copy(ptr, p, a->as.heap.len);
}
- a->ptr = ptr;
- a->aux.capa = a->len;
+ a->as.heap.ptr = ptr;
+ a->as.heap.aux.capa = a->as.heap.len;
mrb_ary_decref(mrb, shared);
}
ARY_UNSET_SHARED_FLAG(a);
@@ -160,18 +165,20 @@ mrb_ary_modify(mrb_state *mrb, struct RArray* a)
static void
ary_make_shared(mrb_state *mrb, struct RArray *a)
{
- if (!ARY_SHARED_P(a)) {
+ if (!ARY_SHARED_P(a) && !ARY_EMBED_P(a)) {
mrb_shared_array *shared = (mrb_shared_array *)mrb_malloc(mrb, sizeof(mrb_shared_array));
+ mrb_value *ptr = a->as.heap.ptr;
+ mrb_int len = a->as.heap.len;
shared->refcnt = 1;
- if (a->aux.capa > a->len) {
- a->ptr = shared->ptr = (mrb_value *)mrb_realloc(mrb, a->ptr, sizeof(mrb_value)*a->len+1);
+ if (a->as.heap.aux.capa > len) {
+ a->as.heap.ptr = shared->ptr = (mrb_value *)mrb_realloc(mrb, ptr, sizeof(mrb_value)*len+1);
}
else {
- shared->ptr = a->ptr;
+ shared->ptr = ptr;
}
- shared->len = a->len;
- a->aux.shared = shared;
+ shared->len = len;
+ a->as.heap.aux.shared = shared;
ARY_SET_SHARED_FLAG(a);
}
}
@@ -179,36 +186,58 @@ ary_make_shared(mrb_state *mrb, struct RArray *a)
static void
ary_expand_capa(mrb_state *mrb, struct RArray *a, mrb_int len)
{
- mrb_int capa = a->aux.capa;
+ mrb_int capa = ARY_CAPA(a);
- if (len > ARY_MAX_SIZE) {
+ if (len > ARY_MAX_SIZE || len < 0) {
+ size_error:
mrb_raise(mrb, E_ARGUMENT_ERROR, "array size too big");
}
- if (capa == 0) {
+ if (capa < ARY_DEFAULT_LEN) {
capa = ARY_DEFAULT_LEN;
}
while (capa < len) {
- capa *= 2;
+ if (capa <= ARY_MAX_SIZE / 2) {
+ capa *= 2;
+ }
+ else {
+ capa = len;
+ }
+ }
+ if (capa < len || capa > ARY_MAX_SIZE) {
+ goto size_error;
}
- if (capa > ARY_MAX_SIZE) capa = ARY_MAX_SIZE; /* len <= capa <= ARY_MAX_SIZE */
+ if (ARY_EMBED_P(a)) {
+ mrb_value *ptr = ARY_EMBED_PTR(a);
+ mrb_int len = ARY_EMBED_LEN(a);
+ mrb_value *expanded_ptr = (mrb_value *)mrb_malloc(mrb, sizeof(mrb_value)*capa);
- if (capa > a->aux.capa) {
- mrb_value *expanded_ptr = (mrb_value *)mrb_realloc(mrb, a->ptr, sizeof(mrb_value)*capa);
+ ARY_UNSET_EMBED_FLAG(a);
+ array_copy(expanded_ptr, ptr, len);
+ a->as.heap.len = len;
+ a->as.heap.aux.capa = capa;
+ a->as.heap.ptr = expanded_ptr;
+ }
+ else if (capa > a->as.heap.aux.capa) {
+ mrb_value *expanded_ptr = (mrb_value *)mrb_realloc(mrb, a->as.heap.ptr, sizeof(mrb_value)*capa);
- a->aux.capa = capa;
- a->ptr = expanded_ptr;
+ a->as.heap.aux.capa = capa;
+ a->as.heap.ptr = expanded_ptr;
}
}
static void
ary_shrink_capa(mrb_state *mrb, struct RArray *a)
{
- mrb_int capa = a->aux.capa;
+ mrb_int capa;
+
+ if (ARY_EMBED_P(a)) return;
+
+ capa = a->as.heap.aux.capa;
if (capa < ARY_DEFAULT_LEN * 2) return;
- if (capa <= a->len * ARY_SHRINK_RATIO) return;
+ if (capa <= a->as.heap.len * ARY_SHRINK_RATIO) return;
do {
capa /= 2;
@@ -216,11 +245,11 @@ ary_shrink_capa(mrb_state *mrb, struct RArray *a)
capa = ARY_DEFAULT_LEN;
break;
}
- } while (capa > a->len * ARY_SHRINK_RATIO);
+ } while (capa > a->as.heap.len * ARY_SHRINK_RATIO);
- if (capa > a->len && capa < a->aux.capa) {
- a->aux.capa = capa;
- a->ptr = (mrb_value *)mrb_realloc(mrb, a->ptr, sizeof(mrb_value)*capa);
+ if (capa > a->as.heap.len && capa < a->as.heap.aux.capa) {
+ a->as.heap.aux.capa = capa;
+ a->as.heap.ptr = (mrb_value *)mrb_realloc(mrb, a->as.heap.ptr, sizeof(mrb_value)*capa);
}
}
@@ -233,40 +262,58 @@ mrb_ary_resize(mrb_state *mrb, mrb_value ary, mrb_int new_len)
ary_modify(mrb, a);
old_len = RARRAY_LEN(ary);
if (old_len != new_len) {
- a->len = new_len;
if (new_len < old_len) {
ary_shrink_capa(mrb, a);
}
else {
ary_expand_capa(mrb, a, new_len);
- ary_fill_with_nil(a->ptr + old_len, new_len - old_len);
+ ary_fill_with_nil(ARY_PTR(a) + old_len, new_len - old_len);
}
+ ARY_SET_LEN(a, new_len);
}
return ary;
}
static mrb_value
-mrb_ary_s_create(mrb_state *mrb, mrb_value self)
+mrb_ary_s_create(mrb_state *mrb, mrb_value klass)
{
- mrb_value *vals;
+ mrb_value ary;
+ const mrb_value *vals;
mrb_int len;
+ struct RArray *a;
- mrb_get_args(mrb, "*", &vals, &len);
+ mrb_get_args(mrb, "*!", &vals, &len);
+ ary = mrb_ary_new_from_values(mrb, len, vals);
+ a = mrb_ary_ptr(ary);
+ a->c = mrb_class_ptr(klass);
- return mrb_ary_new_from_values(mrb, len, vals);
+ return ary;
}
+static void ary_replace(mrb_state*, struct RArray*, struct RArray*);
+
static void
-ary_concat(mrb_state *mrb, struct RArray *a, mrb_value *ptr, mrb_int blen)
+ary_concat(mrb_state *mrb, struct RArray *a, struct RArray *a2)
{
- mrb_int len = a->len + blen;
+ mrb_int len;
+
+ if (ARY_LEN(a) == 0) {
+ ary_replace(mrb, a, a2);
+ return;
+ }
+ if (ARY_LEN(a2) > ARY_MAX_SIZE - ARY_LEN(a)) {
+ mrb_raise(mrb, E_ARGUMENT_ERROR, "array size too big");
+ }
+ len = ARY_LEN(a) + ARY_LEN(a2);
ary_modify(mrb, a);
- if (a->aux.capa < len) ary_expand_capa(mrb, a, len);
- array_copy(a->ptr+a->len, ptr, blen);
+ if (ARY_CAPA(a) < len) {
+ ary_expand_capa(mrb, a, len);
+ }
+ array_copy(ARY_PTR(a)+ARY_LEN(a), ARY_PTR(a2), ARY_LEN(a2));
mrb_write_barrier(mrb, (struct RBasic*)a);
- a->len = len;
+ ARY_SET_LEN(a, len);
}
MRB_API void
@@ -274,17 +321,16 @@ mrb_ary_concat(mrb_state *mrb, mrb_value self, mrb_value other)
{
struct RArray *a2 = mrb_ary_ptr(other);
- ary_concat(mrb, mrb_ary_ptr(self), a2->ptr, a2->len);
+ ary_concat(mrb, mrb_ary_ptr(self), a2);
}
static mrb_value
mrb_ary_concat_m(mrb_state *mrb, mrb_value self)
{
- mrb_value *ptr;
- mrb_int blen;
+ mrb_value ary;
- mrb_get_args(mrb, "a", &ptr, &blen);
- ary_concat(mrb, mrb_ary_ptr(self), ptr, blen);
+ mrb_get_args(mrb, "A", &ary);
+ mrb_ary_concat(mrb, self, ary);
return self;
}
@@ -293,37 +339,74 @@ mrb_ary_plus(mrb_state *mrb, mrb_value self)
{
struct RArray *a1 = mrb_ary_ptr(self);
struct RArray *a2;
- mrb_value ary;
- mrb_value *ptr;
- mrb_int blen;
+ const mrb_value *ptr;
+ mrb_int blen, len1;
mrb_get_args(mrb, "a", &ptr, &blen);
- ary = mrb_ary_new_capa(mrb, a1->len + blen);
- a2 = mrb_ary_ptr(ary);
- array_copy(a2->ptr, a1->ptr, a1->len);
- array_copy(a2->ptr + a1->len, ptr, blen);
- a2->len = a1->len + blen;
+ if (ARY_MAX_SIZE - blen < ARY_LEN(a1)) {
+ mrb_raise(mrb, E_ARGUMENT_ERROR, "array size too big");
+ }
+ len1 = ARY_LEN(a1);
+ a2 = ary_new_capa(mrb, len1 + blen);
+ array_copy(ARY_PTR(a2), ARY_PTR(a1), len1);
+ array_copy(ARY_PTR(a2) + len1, ptr, blen);
+ ARY_SET_LEN(a2, len1+blen);
- return ary;
+ return mrb_obj_value(a2);
}
+#define ARY_REPLACE_SHARED_MIN 20
+
static void
-ary_replace(mrb_state *mrb, struct RArray *a, mrb_value *argv, mrb_int len)
+ary_replace(mrb_state *mrb, struct RArray *a, struct RArray *b)
{
- ary_modify(mrb, a);
- if (a->aux.capa < len)
+ mrb_int len = ARY_LEN(b);
+
+ ary_modify_check(mrb, a);
+ if (a == b) return;
+ if (ARY_SHARED_P(a)) {
+ mrb_ary_decref(mrb, a->as.heap.aux.shared);
+ a->as.heap.aux.capa = 0;
+ a->as.heap.len = 0;
+ a->as.heap.ptr = NULL;
+ ARY_UNSET_SHARED_FLAG(a);
+ }
+ if (ARY_SHARED_P(b)) {
+ shared_b:
+ if (ARY_EMBED_P(a)) {
+ ARY_UNSET_EMBED_FLAG(a);
+ }
+ else {
+ mrb_free(mrb, a->as.heap.ptr);
+ }
+ a->as.heap.ptr = b->as.heap.ptr;
+ a->as.heap.len = len;
+ a->as.heap.aux.shared = b->as.heap.aux.shared;
+ a->as.heap.aux.shared->refcnt++;
+ ARY_SET_SHARED_FLAG(a);
+ mrb_write_barrier(mrb, (struct RBasic*)a);
+ return;
+ }
+ if (!mrb_frozen_p(b) && len > ARY_REPLACE_SHARED_MIN) {
+ ary_make_shared(mrb, b);
+ goto shared_b;
+ }
+ if (ARY_CAPA(a) < len)
ary_expand_capa(mrb, a, len);
- array_copy(a->ptr, argv, len);
+ array_copy(ARY_PTR(a), ARY_PTR(b), len);
mrb_write_barrier(mrb, (struct RBasic*)a);
- a->len = len;
+ ARY_SET_LEN(a, len);
}
MRB_API void
mrb_ary_replace(mrb_state *mrb, mrb_value self, mrb_value other)
{
+ struct RArray *a1 = mrb_ary_ptr(self);
struct RArray *a2 = mrb_ary_ptr(other);
- ary_replace(mrb, mrb_ary_ptr(self), a2->ptr, a2->len);
+ if (a1 != a2) {
+ ary_replace(mrb, a1, a2);
+ }
}
static mrb_value
@@ -342,39 +425,41 @@ mrb_ary_times(mrb_state *mrb, mrb_value self)
{
struct RArray *a1 = mrb_ary_ptr(self);
struct RArray *a2;
- mrb_value ary;
mrb_value *ptr;
- mrb_int times;
+ mrb_int times, len1;
mrb_get_args(mrb, "i", &times);
if (times < 0) {
mrb_raise(mrb, E_ARGUMENT_ERROR, "negative argument");
}
if (times == 0) return mrb_ary_new(mrb);
-
- ary = mrb_ary_new_capa(mrb, a1->len * times);
- a2 = mrb_ary_ptr(ary);
- ptr = a2->ptr;
+ if (ARY_MAX_SIZE / times < ARY_LEN(a1)) {
+ mrb_raise(mrb, E_ARGUMENT_ERROR, "array size too big");
+ }
+ len1 = ARY_LEN(a1);
+ a2 = ary_new_capa(mrb, len1 * times);
+ ARY_SET_LEN(a2, len1 * times);
+ ptr = ARY_PTR(a2);
while (times--) {
- array_copy(ptr, a1->ptr, a1->len);
- ptr += a1->len;
- a2->len += a1->len;
+ array_copy(ptr, ARY_PTR(a1), len1);
+ ptr += len1;
}
- return ary;
+ return mrb_obj_value(a2);
}
static mrb_value
mrb_ary_reverse_bang(mrb_state *mrb, mrb_value self)
{
struct RArray *a = mrb_ary_ptr(self);
+ mrb_int len = ARY_LEN(a);
- if (a->len > 1) {
+ if (len > 1) {
mrb_value *p1, *p2;
ary_modify(mrb, a);
- p1 = a->ptr;
- p2 = a->ptr + a->len - 1;
+ p1 = ARY_PTR(a);
+ p2 = p1 + len - 1;
while (p1 < p2) {
mrb_value tmp = *p1;
@@ -388,48 +473,60 @@ mrb_ary_reverse_bang(mrb_state *mrb, mrb_value self)
static mrb_value
mrb_ary_reverse(mrb_state *mrb, mrb_value self)
{
- struct RArray *a = mrb_ary_ptr(self), *b;
- mrb_value ary;
+ struct RArray *a = mrb_ary_ptr(self), *b = ary_new_capa(mrb, ARY_LEN(a));
+ mrb_int len = ARY_LEN(a);
- ary = mrb_ary_new_capa(mrb, a->len);
- b = mrb_ary_ptr(ary);
- if (a->len > 0) {
+ if (len > 0) {
mrb_value *p1, *p2, *e;
- p1 = a->ptr;
- e = p1 + a->len;
- p2 = b->ptr + a->len - 1;
+ p1 = ARY_PTR(a);
+ e = p1 + len;
+ p2 = ARY_PTR(b) + len - 1;
while (p1 < e) {
*p2-- = *p1++;
}
- b->len = a->len;
+ ARY_SET_LEN(b, len);
}
- return ary;
+ return mrb_obj_value(b);
}
MRB_API void
mrb_ary_push(mrb_state *mrb, mrb_value ary, mrb_value elem)
{
struct RArray *a = mrb_ary_ptr(ary);
+ mrb_int len = ARY_LEN(a);
ary_modify(mrb, a);
- if (a->len == a->aux.capa)
- ary_expand_capa(mrb, a, a->len + 1);
- a->ptr[a->len++] = elem;
+ if (len == ARY_CAPA(a))
+ ary_expand_capa(mrb, a, len + 1);
+ ARY_PTR(a)[len] = elem;
+ ARY_SET_LEN(a, len+1);
mrb_field_write_barrier_value(mrb, (struct RBasic*)a, elem);
}
static mrb_value
mrb_ary_push_m(mrb_state *mrb, mrb_value self)
{
- mrb_value *argv;
- mrb_int len;
+ mrb_int argc;
+ const mrb_value *argv;
+ mrb_int len, len2;
+ struct RArray *a;
- mrb_get_args(mrb, "*", &argv, &len);
- while (len--) {
- mrb_ary_push(mrb, self, *argv++);
+ argc = mrb_get_argc(mrb);
+ argv = mrb_get_argv(mrb);
+ a = mrb_ary_ptr(self);
+ ary_modify(mrb, a);
+ len = ARY_LEN(a);
+ len2 = len + argc;
+ if (ARY_CAPA(a) < len2) {
+ ary_expand_capa(mrb, a, len2);
+ }
+ array_copy(ARY_PTR(a)+len, argv, argc);
+ ARY_SET_LEN(a, len2);
+ while (argc--) {
+ mrb_field_write_barrier_value(mrb, (struct RBasic*)a, *argv);
+ argv++;
}
-
return self;
}
@@ -437,9 +534,12 @@ MRB_API mrb_value
mrb_ary_pop(mrb_state *mrb, mrb_value ary)
{
struct RArray *a = mrb_ary_ptr(ary);
+ mrb_int len = ARY_LEN(a);
- if (a->len == 0) return mrb_nil_value();
- return a->ptr[--a->len];
+ ary_modify_check(mrb, a);
+ if (len == 0) return mrb_nil_value();
+ ARY_SET_LEN(a, len-1);
+ return ARY_PTR(a)[len-1];
}
#define ARY_SHIFT_SHARED_MIN 10
@@ -448,30 +548,74 @@ MRB_API mrb_value
mrb_ary_shift(mrb_state *mrb, mrb_value self)
{
struct RArray *a = mrb_ary_ptr(self);
+ mrb_int len = ARY_LEN(a);
mrb_value val;
- if (a->len == 0) return mrb_nil_value();
+ ary_modify_check(mrb, a);
+ if (len == 0) return mrb_nil_value();
if (ARY_SHARED_P(a)) {
L_SHIFT:
- val = a->ptr[0];
- a->ptr++;
- a->len--;
+ val = a->as.heap.ptr[0];
+ a->as.heap.ptr++;
+ a->as.heap.len--;
return val;
}
- if (a->len > ARY_SHIFT_SHARED_MIN) {
+ if (len > ARY_SHIFT_SHARED_MIN) {
ary_make_shared(mrb, a);
goto L_SHIFT;
}
else {
- mrb_value *ptr = a->ptr;
- mrb_int size = a->len;
+ mrb_value *ptr = ARY_PTR(a);
+ mrb_int size = len;
val = *ptr;
while (--size) {
*ptr = *(ptr+1);
++ptr;
}
- --a->len;
+ ARY_SET_LEN(a, len-1);
+ }
+ return val;
+}
+
+static mrb_value
+mrb_ary_shift_m(mrb_state *mrb, mrb_value self)
+{
+ struct RArray *a = mrb_ary_ptr(self);
+ mrb_int len = ARY_LEN(a);
+ mrb_int n;
+ mrb_value val;
+
+ if (mrb_get_args(mrb, "|i", &n) == 0) {
+ return mrb_ary_shift(mrb, self);
+ };
+ ary_modify_check(mrb, a);
+ if (len == 0 || n == 0) return mrb_ary_new(mrb);
+ if (n < 0) mrb_raise(mrb, E_ARGUMENT_ERROR, "negative array shift");
+ if (n > len) n = len;
+ val = mrb_ary_new_from_values(mrb, n, ARY_PTR(a));
+ if (ARY_SHARED_P(a)) {
+ L_SHIFT:
+ a->as.heap.ptr+=n;
+ a->as.heap.len-=n;
+ return val;
+ }
+ if (len > ARY_SHIFT_SHARED_MIN) {
+ ary_make_shared(mrb, a);
+ goto L_SHIFT;
+ }
+ else if (len == n) {
+ ARY_SET_LEN(a, 0);
+ }
+ else {
+ mrb_value *ptr = ARY_PTR(a);
+ mrb_int size = len-n;
+
+ while (size--) {
+ *ptr = *(ptr+n);
+ ++ptr;
+ }
+ ARY_SET_LEN(a, len-n);
}
return val;
}
@@ -484,21 +628,25 @@ MRB_API mrb_value
mrb_ary_unshift(mrb_state *mrb, mrb_value self, mrb_value item)
{
struct RArray *a = mrb_ary_ptr(self);
+ mrb_int len = ARY_LEN(a);
if (ARY_SHARED_P(a)
- && a->aux.shared->refcnt == 1 /* shared only referenced from this array */
- && a->ptr - a->aux.shared->ptr >= 1) /* there's room for unshifted item */ {
- a->ptr--;
- a->ptr[0] = item;
+ && a->as.heap.aux.shared->refcnt == 1 /* shared only referenced from this array */
+ && a->as.heap.ptr - a->as.heap.aux.shared->ptr >= 1) /* there's room for unshifted item */ {
+ a->as.heap.ptr--;
+ a->as.heap.ptr[0] = item;
}
else {
+ mrb_value *ptr;
+
ary_modify(mrb, a);
- if (a->aux.capa < a->len + 1)
- ary_expand_capa(mrb, a, a->len + 1);
- value_move(a->ptr + 1, a->ptr, a->len);
- a->ptr[0] = item;
+ if (ARY_CAPA(a) < len + 1)
+ ary_expand_capa(mrb, a, len + 1);
+ ptr = ARY_PTR(a);
+ value_move(ptr + 1, ptr, len);
+ ptr[0] = item;
}
- a->len++;
+ ARY_SET_LEN(a, len+1);
mrb_field_write_barrier_value(mrb, (struct RBasic*)a, item);
return self;
@@ -508,121 +656,159 @@ static mrb_value
mrb_ary_unshift_m(mrb_state *mrb, mrb_value self)
{
struct RArray *a = mrb_ary_ptr(self);
- mrb_value *vals;
- mrb_int len;
+ const mrb_value *vals;
+ mrb_value *ptr;
+ mrb_int alen, len;
- mrb_get_args(mrb, "*", &vals, &len);
+ mrb_get_args(mrb, "*!", &vals, &alen);
+ if (alen == 0) {
+ ary_modify_check(mrb, a);
+ return self;
+ }
+ len = ARY_LEN(a);
+ if (alen > ARY_MAX_SIZE - 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 */ {
- a->ptr -= len;
+ && a->as.heap.aux.shared->refcnt == 1 /* shared only referenced from this array */
+ && a->as.heap.ptr - a->as.heap.aux.shared->ptr >= alen) /* there's room for unshifted item */ {
+ ary_modify_check(mrb, a);
+ a->as.heap.ptr -= alen;
+ ptr = a->as.heap.ptr;
}
else {
+ mrb_bool same = vals == ARY_PTR(a);
ary_modify(mrb, a);
- if (len == 0) return self;
- if (a->aux.capa < a->len + len)
- ary_expand_capa(mrb, a, a->len + len);
- value_move(a->ptr + len, a->ptr, a->len);
+ if (ARY_CAPA(a) < len + alen)
+ ary_expand_capa(mrb, a, len + alen);
+ ptr = ARY_PTR(a);
+ value_move(ptr + alen, ptr, len);
+ if (same) vals = ptr;
}
- array_copy(a->ptr, vals, len);
- a->len += len;
- while (len--) {
- mrb_field_write_barrier_value(mrb, (struct RBasic*)a, vals[len]);
+ array_copy(ptr, vals, alen);
+ ARY_SET_LEN(a, len+alen);
+ while (alen--) {
+ mrb_field_write_barrier_value(mrb, (struct RBasic*)a, vals[alen]);
}
return self;
}
-MRB_API mrb_value
-mrb_ary_ref(mrb_state *mrb, mrb_value ary, mrb_int n)
-{
- struct RArray *a = mrb_ary_ptr(ary);
-
- /* range check */
- if (n < 0) n += a->len;
- if (n < 0 || a->len <= n) return mrb_nil_value();
-
- return a->ptr[n];
-}
-
MRB_API void
mrb_ary_set(mrb_state *mrb, mrb_value ary, mrb_int n, mrb_value val)
{
struct RArray *a = mrb_ary_ptr(ary);
+ mrb_int len = ARY_LEN(a);
ary_modify(mrb, a);
/* range check */
if (n < 0) {
- n += a->len;
+ n += len;
if (n < 0) {
- mrb_raisef(mrb, E_INDEX_ERROR, "index %S out of array", mrb_fixnum_value(n - a->len));
+ mrb_raisef(mrb, E_INDEX_ERROR, "index %i out of array", n - len);
}
}
- if (a->len <= n) {
- if (a->aux.capa <= n)
+ if (len <= n) {
+ if (ARY_CAPA(a) <= n)
ary_expand_capa(mrb, a, n + 1);
- ary_fill_with_nil(a->ptr + a->len, n + 1 - a->len);
- a->len = n + 1;
+ ary_fill_with_nil(ARY_PTR(a) + len, n + 1 - len);
+ ARY_SET_LEN(a, n+1);
}
- a->ptr[n] = val;
+ ARY_PTR(a)[n] = val;
mrb_field_write_barrier_value(mrb, (struct RBasic*)a, val);
}
+static struct RArray*
+ary_dup(mrb_state *mrb, struct RArray *a)
+{
+ return ary_new_from_values(mrb, ARY_LEN(a), ARY_PTR(a));
+}
+
MRB_API mrb_value
mrb_ary_splice(mrb_state *mrb, mrb_value ary, mrb_int head, mrb_int len, mrb_value rpl)
{
struct RArray *a = mrb_ary_ptr(ary);
- mrb_int tail, size;
+ mrb_int alen = ARY_LEN(a);
const mrb_value *argv;
- mrb_int i, argc;
+ mrb_int argc;
+ mrb_int tail;
ary_modify(mrb, a);
/* len check */
- if (len < 0) mrb_raisef(mrb, E_INDEX_ERROR, "negative length (%S)", mrb_fixnum_value(len));
+ if (len < 0) mrb_raisef(mrb, E_INDEX_ERROR, "negative length (%i)", len);
/* range check */
if (head < 0) {
- head += a->len;
+ head += alen;
if (head < 0) {
mrb_raise(mrb, E_INDEX_ERROR, "index is out of array");
}
}
- if (a->len < len || a->len < head + len) {
- len = a->len - head;
- }
tail = head + len;
+ if (alen < len || alen < tail) {
+ len = alen - head;
+ }
/* size check */
if (mrb_array_p(rpl)) {
argc = RARRAY_LEN(rpl);
argv = RARRAY_PTR(rpl);
+ if (argv == ARY_PTR(a)) {
+ struct RArray *r;
+
+ if (argc > 32767) {
+ mrb_raise(mrb, E_ARGUMENT_ERROR, "too big recursive splice");
+ }
+ r = ary_dup(mrb, a);
+ argv = ARY_PTR(r);
+ }
+ }
+ else if (mrb_undef_p(rpl)) {
+ argc = 0;
+ argv = NULL;
}
else {
argc = 1;
argv = &rpl;
}
- size = head + argc;
-
- if (tail < a->len) size += a->len - tail;
- if (size > a->aux.capa)
- ary_expand_capa(mrb, a, size);
-
- if (head > a->len) {
- ary_fill_with_nil(a->ptr + a->len, head - a->len);
- }
- else if (head < a->len) {
- value_move(a->ptr + head + argc, a->ptr + tail, a->len - tail);
- }
-
- for (i = 0; i < argc; i++) {
- *(a->ptr + head + i) = *(argv + i);
- mrb_field_write_barrier_value(mrb, (struct RBasic*)a, argv[i]);
+ if (head >= alen) {
+ if (head > ARY_MAX_SIZE - argc) {
+ mrb_raisef(mrb, E_INDEX_ERROR, "index %i too big", head);
+ }
+ len = head + argc;
+ if (len > ARY_CAPA(a)) {
+ ary_expand_capa(mrb, a, head + argc);
+ }
+ ary_fill_with_nil(ARY_PTR(a) + alen, head - alen);
+ if (argc > 0) {
+ array_copy(ARY_PTR(a) + head, argv, argc);
+ }
+ ARY_SET_LEN(a, len);
}
+ else {
+ mrb_int newlen;
- a->len = size;
+ if (alen - len > ARY_MAX_SIZE - argc) {
+ mrb_raisef(mrb, E_INDEX_ERROR, "index %i too big", alen + argc - len);
+ }
+ newlen = alen + argc - len;
+ if (newlen > ARY_CAPA(a)) {
+ ary_expand_capa(mrb, a, newlen);
+ }
+ if (len != argc) {
+ mrb_value *ptr = ARY_PTR(a);
+ tail = head + len;
+ value_move(ptr + head + argc, ptr + tail, alen - tail);
+ ARY_SET_LEN(a, newlen);
+ }
+ if (argc > 0) {
+ value_move(ARY_PTR(a) + head, argv, argc);
+ }
+ }
+ mrb_write_barrier(mrb, (struct RBasic*)a);
return ary;
}
@@ -641,31 +827,43 @@ ary_subseq(mrb_state *mrb, struct RArray *a, mrb_int beg, mrb_int len)
{
struct RArray *b;
+ if (!ARY_SHARED_P(a) && len <= ARY_SHIFT_SHARED_MIN) {
+ return mrb_ary_new_from_values(mrb, len, ARY_PTR(a)+beg);
+ }
ary_make_shared(mrb, a);
- b = (struct RArray*)mrb_obj_alloc(mrb, MRB_TT_ARRAY, mrb->array_class);
- b->ptr = a->ptr + beg;
- b->len = len;
- b->aux.shared = a->aux.shared;
- b->aux.shared->refcnt++;
+ b = MRB_OBJ_ALLOC(mrb, MRB_TT_ARRAY, mrb->array_class);
+ b->as.heap.ptr = a->as.heap.ptr + beg;
+ b->as.heap.len = len;
+ b->as.heap.aux.shared = a->as.heap.aux.shared;
+ b->as.heap.aux.shared->refcnt++;
ARY_SET_SHARED_FLAG(b);
return mrb_obj_value(b);
}
+mrb_value
+mrb_ary_subseq(mrb_state *mrb, mrb_value ary, mrb_int beg, mrb_int len)
+{
+ struct RArray *a = mrb_ary_ptr(ary);
+ return ary_subseq(mrb, a, beg, len);
+}
+
static mrb_int
aget_index(mrb_state *mrb, mrb_value index)
{
- if (mrb_fixnum_p(index)) {
- return mrb_fixnum(index);
+ if (mrb_integer_p(index)) {
+ return mrb_integer(index);
}
+#ifndef MRB_NO_FLOAT
else if (mrb_float_p(index)) {
return (mrb_int)mrb_float(index);
}
+#endif
else {
mrb_int i, argc;
- mrb_value *argv;
+ const mrb_value *argv;
- mrb_get_args(mrb, "i*", &i, &argv, &argc);
+ mrb_get_args(mrb, "i*!", &i, &argv, &argc);
return i;
}
}
@@ -701,32 +899,36 @@ static mrb_value
mrb_ary_aget(mrb_state *mrb, mrb_value self)
{
struct RArray *a = mrb_ary_ptr(self);
- mrb_int i, len;
+ mrb_int i;
+ mrb_int len, alen;
mrb_value index;
- if (mrb_get_args(mrb, "o|i", &index, &len) == 1) {
+ if (mrb_get_argc(mrb) == 1) {
+ index = mrb_get_arg1(mrb);
switch (mrb_type(index)) {
/* a[n..m] */
case MRB_TT_RANGE:
- if (mrb_range_beg_len(mrb, index, &i, &len, a->len)) {
+ if (mrb_range_beg_len(mrb, index, &i, &len, ARY_LEN(a), TRUE) == MRB_RANGE_OK) {
return ary_subseq(mrb, a, i, len);
}
else {
return mrb_nil_value();
}
- case MRB_TT_FIXNUM:
- return mrb_ary_ref(mrb, self, mrb_fixnum(index));
+ case MRB_TT_INTEGER:
+ return mrb_ary_ref(mrb, self, mrb_integer(index));
default:
return mrb_ary_ref(mrb, self, aget_index(mrb, index));
}
}
+ mrb_get_args(mrb, "oi", &index, &len);
i = aget_index(mrb, index);
- if (i < 0) i += a->len;
- if (i < 0 || a->len < i) return mrb_nil_value();
+ alen = ARY_LEN(a);
+ if (i < 0) i += alen;
+ if (i < 0 || alen < i) return mrb_nil_value();
if (len < 0) return mrb_nil_value();
- if (a->len == i) return mrb_ary_new(mrb);
- if (len > a->len - i) len = a->len - i;
+ if (alen == i) return mrb_ary_new(mrb);
+ if (len > alen - i) len = alen - i;
return ary_subseq(mrb, a, i, len);
}
@@ -772,25 +974,27 @@ mrb_ary_aset(mrb_state *mrb, mrb_value self)
mrb_value v1, v2, v3;
mrb_int i, len;
- if (mrb_get_args(mrb, "oo|o", &v1, &v2, &v3) == 2) {
- switch (mrb_type(v1)) {
+ ary_modify(mrb, mrb_ary_ptr(self));
+ if (mrb_get_argc(mrb) == 2) {
+ const mrb_value *vs = mrb_get_argv(mrb);
+ v1 = vs[0]; v2 = vs[1];
+
/* a[n..m] = v */
- case MRB_TT_RANGE:
- if (mrb_range_beg_len(mrb, v1, &i, &len, RARRAY_LEN(self))) {
- mrb_ary_splice(mrb, self, i, len, v2);
- }
+ switch (mrb_range_beg_len(mrb, v1, &i, &len, RARRAY_LEN(self), FALSE)) {
+ case MRB_RANGE_TYPE_MISMATCH:
+ mrb_ary_set(mrb, self, aget_index(mrb, v1), v2);
break;
- /* a[n] = v */
- case MRB_TT_FIXNUM:
- mrb_ary_set(mrb, self, mrb_fixnum(v1), v2);
+ case MRB_RANGE_OK:
+ mrb_ary_splice(mrb, self, i, len, v2);
break;
- default:
- mrb_ary_set(mrb, self, aget_index(mrb, v1), v2);
+ case MRB_RANGE_OUT:
+ mrb_raisef(mrb, E_RANGE_ERROR, "%v out of range", v1);
break;
}
return v2;
}
+ mrb_get_args(mrb, "ooo", &v1, &v2, &v3);
/* a[n,m] = v */
mrb_ary_splice(mrb, self, aget_index(mrb, v1), aget_index(mrb, v2), v3);
return v3;
@@ -803,22 +1007,24 @@ mrb_ary_delete_at(mrb_state *mrb, mrb_value self)
mrb_int index;
mrb_value val;
mrb_value *ptr;
- mrb_int len;
+ mrb_int len, alen;
mrb_get_args(mrb, "i", &index);
- if (index < 0) index += a->len;
- if (index < 0 || a->len <= index) return mrb_nil_value();
+ alen = ARY_LEN(a);
+ if (index < 0) index += alen;
+ if (index < 0 || alen <= index) return mrb_nil_value();
ary_modify(mrb, a);
- val = a->ptr[index];
+ ptr = ARY_PTR(a);
+ val = ptr[index];
- ptr = a->ptr + index;
- len = a->len - index;
+ ptr += index;
+ len = alen - index;
while (--len) {
*ptr = *(ptr+1);
++ptr;
}
- --a->len;
+ ARY_SET_LEN(a, alen-1);
ary_shrink_capa(mrb, a);
@@ -829,48 +1035,52 @@ static mrb_value
mrb_ary_first(mrb_state *mrb, mrb_value self)
{
struct RArray *a = mrb_ary_ptr(self);
- mrb_int size;
+ mrb_int size, alen;
- if (mrb_get_args(mrb, "|i", &size) == 0) {
- return (a->len > 0)? a->ptr[0]: mrb_nil_value();
+ if (mrb_get_argc(mrb) == 0) {
+ return (ARY_LEN(a) > 0)? ARY_PTR(a)[0]: mrb_nil_value();
}
+ mrb_get_args(mrb, "|i", &size);
if (size < 0) {
mrb_raise(mrb, E_ARGUMENT_ERROR, "negative array size");
}
- if (size > a->len) size = a->len;
+ alen = ARY_LEN(a);
+ if (size > alen) size = alen;
if (ARY_SHARED_P(a)) {
return ary_subseq(mrb, a, 0, size);
}
- return mrb_ary_new_from_values(mrb, size, a->ptr);
+ return mrb_ary_new_from_values(mrb, size, ARY_PTR(a));
}
static mrb_value
mrb_ary_last(mrb_state *mrb, mrb_value self)
{
struct RArray *a = mrb_ary_ptr(self);
- mrb_int size;
+ mrb_int n, size, alen;
- if (mrb_get_args(mrb, "|i", &size) == 0)
- return (a->len > 0)? a->ptr[a->len - 1]: mrb_nil_value();
+ n = mrb_get_args(mrb, "|i", &size);
+ alen = ARY_LEN(a);
+ if (n == 0) {
+ return (alen > 0) ? ARY_PTR(a)[alen - 1]: mrb_nil_value();
+ }
if (size < 0) {
mrb_raise(mrb, E_ARGUMENT_ERROR, "negative array size");
}
- if (size > a->len) size = a->len;
+ if (size > alen) size = alen;
if (ARY_SHARED_P(a) || size > ARY_DEFAULT_LEN) {
- return ary_subseq(mrb, a, a->len - size, size);
+ return ary_subseq(mrb, a, alen - size, size);
}
- return mrb_ary_new_from_values(mrb, size, a->ptr + a->len - size);
+ return mrb_ary_new_from_values(mrb, size, ARY_PTR(a) + alen - size);
}
static mrb_value
mrb_ary_index_m(mrb_state *mrb, mrb_value self)
{
- mrb_value obj;
+ mrb_value obj = mrb_get_arg1(mrb);
mrb_int i;
- mrb_get_args(mrb, "o", &obj);
for (i = 0; i < RARRAY_LEN(self); i++) {
if (mrb_equal(mrb, RARRAY_PTR(self)[i], obj)) {
return mrb_fixnum_value(i);
@@ -882,14 +1092,16 @@ mrb_ary_index_m(mrb_state *mrb, mrb_value self)
static mrb_value
mrb_ary_rindex_m(mrb_state *mrb, mrb_value self)
{
- mrb_value obj;
- mrb_int i;
+ mrb_value obj = mrb_get_arg1(mrb);
+ mrb_int i, len;
- mrb_get_args(mrb, "o", &obj);
for (i = RARRAY_LEN(self) - 1; i >= 0; i--) {
if (mrb_equal(mrb, RARRAY_PTR(self)[i], obj)) {
return mrb_fixnum_value(i);
}
+ if (i > (len = RARRAY_LEN(self))) {
+ i = len;
+ }
}
return mrb_nil_value();
}
@@ -897,15 +1109,26 @@ mrb_ary_rindex_m(mrb_state *mrb, mrb_value self)
MRB_API mrb_value
mrb_ary_splat(mrb_state *mrb, mrb_value v)
{
+ mrb_value ary;
+ struct RArray *a;
+
if (mrb_array_p(v)) {
- return v;
+ a = ary_dup(mrb, mrb_ary_ptr(v));
+ return mrb_obj_value(a);
}
- if (mrb_respond_to(mrb, v, mrb_intern_lit(mrb, "to_a"))) {
- return mrb_funcall(mrb, v, "to_a", 0);
+
+ if (!mrb_respond_to(mrb, v, MRB_SYM(to_a))) {
+ return mrb_ary_new_from_values(mrb, 1, &v);
}
- else {
+
+ ary = mrb_funcall_id(mrb, v, MRB_SYM(to_a), 0);
+ if (mrb_nil_p(ary)) {
return mrb_ary_new_from_values(mrb, 1, &v);
}
+ mrb_ensure_array_type(mrb, ary);
+ a = mrb_ary_ptr(ary);
+ a = ary_dup(mrb, a);
+ return mrb_obj_value(a);
}
static mrb_value
@@ -913,7 +1136,7 @@ mrb_ary_size(mrb_state *mrb, mrb_value self)
{
struct RArray *a = mrb_ary_ptr(self);
- return mrb_fixnum_value(a->len);
+ return mrb_fixnum_value(ARY_LEN(a));
}
MRB_API mrb_value
@@ -921,41 +1144,50 @@ mrb_ary_clear(mrb_state *mrb, mrb_value self)
{
struct RArray *a = mrb_ary_ptr(self);
+ ary_modify(mrb, a);
if (ARY_SHARED_P(a)) {
- mrb_ary_decref(mrb, a->aux.shared);
+ mrb_ary_decref(mrb, a->as.heap.aux.shared);
ARY_UNSET_SHARED_FLAG(a);
}
+ else if (!ARY_EMBED_P(a)){
+ mrb_free(mrb, a->as.heap.ptr);
+ }
+ if (MRB_ARY_EMBED_LEN_MAX > 0) {
+ ARY_SET_EMBED_LEN(a, 0);
+ }
else {
- mrb_free(mrb, a->ptr);
+ a->as.heap.ptr = NULL;
+ a->as.heap.aux.capa = 0;
+ ARY_SET_LEN(a, 0);
}
- a->len = 0;
- a->aux.capa = 0;
- a->ptr = 0;
-
return self;
}
static mrb_value
+mrb_ary_clear_m(mrb_state *mrb, mrb_value self)
+{
+ return mrb_ary_clear(mrb, self);
+}
+
+static mrb_value
mrb_ary_empty_p(mrb_state *mrb, mrb_value self)
{
struct RArray *a = mrb_ary_ptr(self);
- return mrb_bool_value(a->len == 0);
+ return mrb_bool_value(ARY_LEN(a) == 0);
}
MRB_API mrb_value
-mrb_check_array_type(mrb_state *mrb, mrb_value ary)
+mrb_ary_entry(mrb_value ary, mrb_int n)
{
- return mrb_check_convert_type(mrb, ary, MRB_TT_ARRAY, "Array", "to_ary");
-}
+ struct RArray *a = mrb_ary_ptr(ary);
+ mrb_int len = ARY_LEN(a);
-MRB_API mrb_value
-mrb_ary_entry(mrb_value ary, mrb_int offset)
-{
- if (offset < 0) {
- offset += RARRAY_LEN(ary);
- }
- return ary_elt(ary, offset);
+ /* range check */
+ if (n < 0) n += len;
+ if (n < 0 || len <= n) return mrb_nil_value();
+
+ return ARY_PTR(a)[n];
}
static mrb_value
@@ -973,7 +1205,7 @@ join_ary(mrb_state *mrb, mrb_value ary, mrb_value sep, mrb_value list)
mrb_ary_push(mrb, list, ary);
- result = mrb_str_buf_new(mrb, 64);
+ result = mrb_str_new_capa(mrb, 64);
for (i=0; i<RARRAY_LEN(ary); i++) {
if (i > 0 && !mrb_nil_p(sep)) {
@@ -993,15 +1225,17 @@ join_ary(mrb_state *mrb, mrb_value ary, mrb_value sep, mrb_value list)
break;
default:
- tmp = mrb_check_string_type(mrb, val);
- if (!mrb_nil_p(tmp)) {
- val = tmp;
- goto str_join;
- }
- tmp = mrb_check_convert_type(mrb, val, MRB_TT_ARRAY, "Array", "to_ary");
- if (!mrb_nil_p(tmp)) {
- val = tmp;
- goto ary_join;
+ if (!mrb_immediate_p(val)) {
+ tmp = mrb_check_string_type(mrb, val);
+ if (!mrb_nil_p(tmp)) {
+ val = tmp;
+ goto str_join;
+ }
+ tmp = mrb_check_array_type(mrb, val);
+ if (!mrb_nil_p(tmp)) {
+ val = tmp;
+ goto ary_join;
+ }
}
val = mrb_obj_as_string(mrb, val);
goto str_join;
@@ -1016,7 +1250,9 @@ join_ary(mrb_state *mrb, mrb_value ary, mrb_value sep, mrb_value list)
MRB_API mrb_value
mrb_ary_join(mrb_state *mrb, mrb_value ary, mrb_value sep)
{
- sep = mrb_obj_as_string(mrb, sep);
+ if (!mrb_nil_p(sep)) {
+ sep = mrb_obj_as_string(mrb, sep);
+ }
return join_ary(mrb, ary, sep, mrb_ary_new(mrb));
}
@@ -1036,18 +1272,16 @@ mrb_ary_join_m(mrb_state *mrb, mrb_value ary)
{
mrb_value sep = mrb_nil_value();
- mrb_get_args(mrb, "|S", &sep);
+ mrb_get_args(mrb, "|S!", &sep);
return mrb_ary_join(mrb, ary, sep);
}
static mrb_value
mrb_ary_eq(mrb_state *mrb, mrb_value ary1)
{
- mrb_value ary2;
+ mrb_value ary2 = mrb_get_arg1(mrb);
- mrb_get_args(mrb, "o", &ary2);
if (mrb_obj_equal(mrb, ary1, ary2)) return mrb_true_value();
- if (mrb_immediate_p(ary2)) return mrb_false_value();
if (!mrb_array_p(ary2)) {
return mrb_false_value();
}
@@ -1059,11 +1293,9 @@ mrb_ary_eq(mrb_state *mrb, mrb_value ary1)
static mrb_value
mrb_ary_cmp(mrb_state *mrb, mrb_value ary1)
{
- mrb_value ary2;
+ mrb_value ary2 = mrb_get_arg1(mrb);
- mrb_get_args(mrb, "o", &ary2);
if (mrb_obj_equal(mrb, ary1, ary2)) return mrb_fixnum_value(0);
- if (mrb_immediate_p(ary2)) return mrb_nil_value();
if (!mrb_array_p(ary2)) {
return mrb_nil_value();
}
@@ -1071,42 +1303,58 @@ mrb_ary_cmp(mrb_state *mrb, mrb_value ary1)
return ary2;
}
+/* internal method to convert multi-value to single value */
+static mrb_value
+mrb_ary_svalue(mrb_state *mrb, mrb_value ary)
+{
+ switch (RARRAY_LEN(ary)) {
+ case 0:
+ return mrb_nil_value();
+ case 1:
+ return RARRAY_PTR(ary)[0];
+ default:
+ return ary;
+ }
+}
+
void
mrb_init_array(mrb_state *mrb)
{
struct RClass *a;
- a = mrb->array_class = mrb_define_class(mrb, "Array", mrb->object_class); /* 15.2.12 */
+ mrb->array_class = a = mrb_define_class(mrb, "Array", mrb->object_class); /* 15.2.12 */
MRB_SET_INSTANCE_TT(a, MRB_TT_ARRAY);
- mrb_define_class_method(mrb, a, "[]", mrb_ary_s_create, MRB_ARGS_ANY()); /* 15.2.12.4.1 */
-
- mrb_define_method(mrb, a, "+", mrb_ary_plus, MRB_ARGS_REQ(1)); /* 15.2.12.5.1 */
- mrb_define_method(mrb, a, "*", mrb_ary_times, MRB_ARGS_REQ(1)); /* 15.2.12.5.2 */
- mrb_define_method(mrb, a, "<<", mrb_ary_push_m, MRB_ARGS_REQ(1)); /* 15.2.12.5.3 */
- mrb_define_method(mrb, a, "[]", mrb_ary_aget, MRB_ARGS_ANY()); /* 15.2.12.5.4 */
- mrb_define_method(mrb, a, "[]=", mrb_ary_aset, MRB_ARGS_ANY()); /* 15.2.12.5.5 */
- mrb_define_method(mrb, a, "clear", mrb_ary_clear, MRB_ARGS_NONE()); /* 15.2.12.5.6 */
- mrb_define_method(mrb, a, "concat", mrb_ary_concat_m, MRB_ARGS_REQ(1)); /* 15.2.12.5.8 */
- mrb_define_method(mrb, a, "delete_at", mrb_ary_delete_at, MRB_ARGS_REQ(1)); /* 15.2.12.5.9 */
- mrb_define_method(mrb, a, "empty?", mrb_ary_empty_p, MRB_ARGS_NONE()); /* 15.2.12.5.12 */
- mrb_define_method(mrb, a, "first", mrb_ary_first, MRB_ARGS_OPT(1)); /* 15.2.12.5.13 */
- mrb_define_method(mrb, a, "index", mrb_ary_index_m, MRB_ARGS_REQ(1)); /* 15.2.12.5.14 */
- mrb_define_method(mrb, a, "initialize_copy", mrb_ary_replace_m, MRB_ARGS_REQ(1)); /* 15.2.12.5.16 */
- mrb_define_method(mrb, a, "join", mrb_ary_join_m, MRB_ARGS_ANY()); /* 15.2.12.5.17 */
- mrb_define_method(mrb, a, "last", mrb_ary_last, MRB_ARGS_ANY()); /* 15.2.12.5.18 */
- mrb_define_method(mrb, a, "length", mrb_ary_size, MRB_ARGS_NONE()); /* 15.2.12.5.19 */
- mrb_define_method(mrb, a, "pop", mrb_ary_pop, MRB_ARGS_NONE()); /* 15.2.12.5.21 */
- mrb_define_method(mrb, a, "push", mrb_ary_push_m, MRB_ARGS_ANY()); /* 15.2.12.5.22 */
- mrb_define_method(mrb, a, "replace", mrb_ary_replace_m, MRB_ARGS_REQ(1)); /* 15.2.12.5.23 */
- mrb_define_method(mrb, a, "reverse", mrb_ary_reverse, MRB_ARGS_NONE()); /* 15.2.12.5.24 */
- mrb_define_method(mrb, a, "reverse!", mrb_ary_reverse_bang, MRB_ARGS_NONE()); /* 15.2.12.5.25 */
- mrb_define_method(mrb, a, "rindex", mrb_ary_rindex_m, MRB_ARGS_REQ(1)); /* 15.2.12.5.26 */
- mrb_define_method(mrb, a, "shift", mrb_ary_shift, MRB_ARGS_NONE()); /* 15.2.12.5.27 */
- mrb_define_method(mrb, a, "size", mrb_ary_size, MRB_ARGS_NONE()); /* 15.2.12.5.28 */
- mrb_define_method(mrb, a, "slice", mrb_ary_aget, MRB_ARGS_ANY()); /* 15.2.12.5.29 */
- mrb_define_method(mrb, a, "unshift", mrb_ary_unshift_m, MRB_ARGS_ANY()); /* 15.2.12.5.30 */
+ mrb_define_class_method(mrb, a, "[]", mrb_ary_s_create, MRB_ARGS_ANY()); /* 15.2.12.4.1 */
+
+ mrb_define_method(mrb, a, "+", mrb_ary_plus, MRB_ARGS_REQ(1)); /* 15.2.12.5.1 */
+ mrb_define_method(mrb, a, "*", mrb_ary_times, MRB_ARGS_REQ(1)); /* 15.2.12.5.2 */
+ mrb_define_method(mrb, a, "<<", mrb_ary_push_m, MRB_ARGS_REQ(1)); /* 15.2.12.5.3 */
+ mrb_define_method(mrb, a, "[]", mrb_ary_aget, MRB_ARGS_ARG(1,1)); /* 15.2.12.5.4 */
+ mrb_define_method(mrb, a, "[]=", mrb_ary_aset, MRB_ARGS_ARG(2,1)); /* 15.2.12.5.5 */
+ mrb_define_method(mrb, a, "clear", mrb_ary_clear_m, MRB_ARGS_NONE()); /* 15.2.12.5.6 */
+ mrb_define_method(mrb, a, "concat", mrb_ary_concat_m, MRB_ARGS_REQ(1)); /* 15.2.12.5.8 */
+ mrb_define_method(mrb, a, "delete_at", mrb_ary_delete_at, MRB_ARGS_REQ(1)); /* 15.2.12.5.9 */
+ mrb_define_method(mrb, a, "empty?", mrb_ary_empty_p, MRB_ARGS_NONE()); /* 15.2.12.5.12 */
+ mrb_define_method(mrb, a, "first", mrb_ary_first, MRB_ARGS_OPT(1)); /* 15.2.12.5.13 */
+ mrb_define_method(mrb, a, "index", mrb_ary_index_m, MRB_ARGS_REQ(1)); /* 15.2.12.5.14 */
+ mrb_define_method(mrb, a, "initialize_copy", mrb_ary_replace_m, MRB_ARGS_REQ(1)); /* 15.2.12.5.16 */
+ mrb_define_method(mrb, a, "join", mrb_ary_join_m, MRB_ARGS_OPT(1)); /* 15.2.12.5.17 */
+ mrb_define_method(mrb, a, "last", mrb_ary_last, MRB_ARGS_OPT(1)); /* 15.2.12.5.18 */
+ mrb_define_method(mrb, a, "length", mrb_ary_size, MRB_ARGS_NONE()); /* 15.2.12.5.19 */
+ mrb_define_method(mrb, a, "pop", mrb_ary_pop, MRB_ARGS_NONE()); /* 15.2.12.5.21 */
+ mrb_define_method(mrb, a, "push", mrb_ary_push_m, MRB_ARGS_ANY()); /* 15.2.12.5.22 */
+ mrb_define_method(mrb, a, "replace", mrb_ary_replace_m, MRB_ARGS_REQ(1)); /* 15.2.12.5.23 */
+ mrb_define_method(mrb, a, "reverse", mrb_ary_reverse, MRB_ARGS_NONE()); /* 15.2.12.5.24 */
+ mrb_define_method(mrb, a, "reverse!", mrb_ary_reverse_bang, MRB_ARGS_NONE()); /* 15.2.12.5.25 */
+ mrb_define_method(mrb, a, "rindex", mrb_ary_rindex_m, MRB_ARGS_REQ(1)); /* 15.2.12.5.26 */
+ mrb_define_method(mrb, a, "shift", mrb_ary_shift_m, MRB_ARGS_OPT(1)); /* 15.2.12.5.27 */
+ mrb_define_method(mrb, a, "size", mrb_ary_size, MRB_ARGS_NONE()); /* 15.2.12.5.28 */
+ mrb_define_method(mrb, a, "slice", mrb_ary_aget, MRB_ARGS_ARG(1,1)); /* 15.2.12.5.29 */
+ mrb_define_method(mrb, a, "unshift", mrb_ary_unshift_m, MRB_ARGS_ANY()); /* 15.2.12.5.30 */
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 */
+ mrb_define_method(mrb, a, "__svalue", mrb_ary_svalue, MRB_ARGS_NONE());
}
diff --git a/src/backtrace.c b/src/backtrace.c
index a82d8f343..7ff1cea6c 100644
--- a/src/backtrace.c
+++ b/src/backtrace.c
@@ -4,198 +4,261 @@
** See Copyright Notice in mruby.h
*/
-#include <stdarg.h>
-#include "mruby.h"
-#include "mruby/variable.h"
-#include "mruby/proc.h"
-#include "mruby/array.h"
-#include "mruby/string.h"
-#include "mruby/class.h"
-#include "mruby/debug.h"
-#include "mruby/error.h"
-
-#ifdef ENABLE_STDIO
-
-typedef void (*output_stream_func)(mrb_state*, void*, int, const char*, ...);
-
-static void
-print_backtrace_i(mrb_state *mrb, void *stream, int level, const char *format, ...)
-{
- va_list ap;
-
- va_start(ap, format);
- vfprintf((FILE*)stream, format, ap);
- va_end(ap);
-}
-
-
-#define MIN_BUFSIZE 127
+#include <mruby.h>
+#include <mruby/variable.h>
+#include <mruby/proc.h>
+#include <mruby/array.h>
+#include <mruby/string.h>
+#include <mruby/class.h>
+#include <mruby/debug.h>
+#include <mruby/error.h>
+#include <mruby/numeric.h>
+#include <mruby/data.h>
+#include <mruby/presym.h>
+
+struct backtrace_location {
+ int32_t lineno;
+ mrb_sym method_id;
+ const char *filename;
+};
+
+typedef void (*each_backtrace_func)(mrb_state*, const struct backtrace_location*, void*);
+
+static const mrb_data_type bt_type = { "Backtrace", mrb_free };
+
+mrb_value mrb_exc_inspect(mrb_state *mrb, mrb_value exc);
+mrb_value mrb_unpack_backtrace(mrb_state *mrb, mrb_value backtrace);
static void
-get_backtrace_i(mrb_state *mrb, void *stream, int level, const char *format, ...)
+each_backtrace(mrb_state *mrb, ptrdiff_t ciidx, each_backtrace_func func, void *data)
{
- va_list ap;
- mrb_value ary, str;
- int ai;
-
- if (level > 0) {
- return;
- }
-
- ai = mrb_gc_arena_save(mrb);
- ary = mrb_obj_value((struct RArray*)stream);
-
- va_start(ap, format);
- str = mrb_str_new(mrb, 0, vsnprintf(NULL, 0, format, ap) + 1);
- va_end(ap);
-
- va_start(ap, format);
- vsnprintf(RSTRING_PTR(str), RSTRING_LEN(str), format, ap);
- va_end(ap);
-
- mrb_str_resize(mrb, str, RSTRING_LEN(str) - 1);
- mrb_ary_push(mrb, ary, str);
- mrb_gc_arena_restore(mrb, ai);
-}
-
-static void
-output_backtrace(mrb_state *mrb, mrb_int ciidx, mrb_code *pc0, output_stream_func func, void *stream)
-{
- mrb_callinfo *ci;
- const char *filename, *method, *sep;
- int i, lineno, tracehead = 1;
-
if (ciidx >= mrb->c->ciend - mrb->c->cibase)
ciidx = 10; /* ciidx is broken... */
- for (i = ciidx; i >= 0; i--) {
+ for (ptrdiff_t i=ciidx; i >= 0; i--) {
+ struct backtrace_location loc;
+ mrb_callinfo *ci;
+ const mrb_irep *irep = 0;
+ const mrb_code *pc;
+ uint32_t idx;
+
ci = &mrb->c->cibase[i];
- filename = NULL;
- if (!ci->proc) continue;
- if (MRB_PROC_CFUNC_P(ci->proc)) {
- continue;
+ if (!ci->proc || MRB_PROC_CFUNC_P(ci->proc)) {
+ loc.lineno = -1;
+ idx = 0;
}
else {
- mrb_irep *irep = ci->proc->body.irep;
- mrb_code *pc;
-
- if (mrb->c->cibase[i].err) {
- pc = mrb->c->cibase[i].err;
- }
- else if (i+1 <= ciidx) {
- pc = mrb->c->cibase[i+1].pc - 1;
+ irep = ci->proc->body.irep;
+ if (!irep) continue;
+ if (mrb->c->cibase[i].pc) {
+ pc = &mrb->c->cibase[i].pc[-1];
}
else {
- pc = pc0;
+ continue;
}
- filename = mrb_debug_get_filename(irep, (uint32_t)(pc - irep->iseq));
- lineno = mrb_debug_get_line(irep, (uint32_t)(pc - irep->iseq));
- }
- if (lineno == -1) continue;
- if (ci->target_class == ci->proc->target_class)
- sep = ".";
- else
- sep = "#";
-
- if (!filename) {
- filename = "(unknown)";
- }
-
- if (tracehead) {
- func(mrb, stream, 1, "trace:\n");
- tracehead = 0;
+ idx = (uint32_t)(pc - irep->iseq);
+ loc.lineno = mrb_debug_get_line(mrb, irep, idx);
}
- method = mrb_sym2name(mrb, ci->mid);
- if (method) {
- const char *cn = mrb_class_name(mrb, ci->proc->target_class);
-
- if (cn) {
- func(mrb, stream, 1, "\t[%d] ", i);
- func(mrb, stream, 0, "%s:%d:in %s%s%s", filename, lineno, cn, sep, method);
- func(mrb, stream, 1, "\n");
- }
- else {
- func(mrb, stream, 1, "\t[%d] ", i);
- func(mrb, stream, 0, "%s:%d:in %s", filename, lineno, method);
- func(mrb, stream, 1, "\n");
+ loc.method_id = ci->mid;
+ if (loc.lineno == -1) {
+ for (ptrdiff_t j=i-1; j >= 0; j--) {
+ ci = &mrb->c->cibase[j];
+
+ if (!ci->proc) continue;
+ if (MRB_PROC_CFUNC_P(ci->proc)) continue;
+
+ irep = ci->proc->body.irep;
+ if (!irep) continue;
+
+ if (mrb->c->cibase[j].pc) {
+ pc = &mrb->c->cibase[j].pc[-1];
+ }
+ else {
+ continue;
+ }
+
+ idx = (uint32_t)(pc - irep->iseq);
+ loc.lineno = mrb_debug_get_line(mrb, irep, idx);
+ if (loc.lineno > 0) break;
}
}
- else {
- func(mrb, stream, 1, "\t[%d] ", i);
- func(mrb, stream, 0, "%s:%d", filename, lineno);
- func(mrb, stream, 1, "\n");
+
+ loc.filename = mrb_debug_get_filename(mrb, irep, idx);
+ if (!loc.filename) {
+ loc.filename = "(unknown)";
}
+
+ func(mrb, &loc, data);
}
}
+#ifndef MRB_NO_STDIO
+
static void
-exc_output_backtrace(mrb_state *mrb, struct RObject *exc, output_stream_func func, void *stream)
+print_backtrace(mrb_state *mrb, struct RObject *exc, mrb_value backtrace)
{
- output_backtrace(mrb, mrb_fixnum(mrb_obj_iv_get(mrb, exc, mrb_intern_lit(mrb, "ciidx"))),
- (mrb_code*)mrb_cptr(mrb_obj_iv_get(mrb, exc, mrb_intern_lit(mrb, "lastpc"))),
- func, stream);
+ mrb_int i;
+ mrb_int n = RARRAY_LEN(backtrace);
+ mrb_value *loc, mesg;
+ FILE *stream = stderr;
+
+ if (n != 0) {
+ if (n > 1) {
+ fprintf(stream, "trace (most recent call last):\n");
+ }
+ for (i=n-1,loc=&RARRAY_PTR(backtrace)[i]; i>0; i--,loc--) {
+ if (mrb_string_p(*loc)) {
+ fprintf(stream, "\t[%d] %.*s\n",
+ (int)i, (int)RSTRING_LEN(*loc), RSTRING_PTR(*loc));
+ }
+ }
+ if (mrb_string_p(*loc)) {
+ fprintf(stream, "%.*s: ", (int)RSTRING_LEN(*loc), RSTRING_PTR(*loc));
+ }
+ }
+ mesg = mrb_exc_inspect(mrb, mrb_obj_value(exc));
+ fprintf(stream, "%.*s\n", (int)RSTRING_LEN(mesg), RSTRING_PTR(mesg));
}
-/* mrb_print_backtrace/mrb_get_backtrace:
+/* mrb_print_backtrace
- function to retrieve backtrace information from the exception.
- note that if you call method after the exception, call stack will be
- overwritten. So invoke these functions just after detecting exceptions.
+ function to retrieve backtrace information from the last exception.
*/
MRB_API void
mrb_print_backtrace(mrb_state *mrb)
{
- if (!mrb->exc || mrb_obj_is_kind_of(mrb, mrb_obj_value(mrb->exc), E_SYSSTACK_ERROR)) {
+ mrb_value backtrace;
+
+ if (!mrb->exc) {
return;
}
- exc_output_backtrace(mrb, mrb->exc, print_backtrace_i, (void*)stderr);
+
+ backtrace = mrb_obj_iv_get(mrb, mrb->exc, MRB_SYM(backtrace));
+ if (mrb_nil_p(backtrace)) return;
+ if (!mrb_array_p(backtrace)) backtrace = mrb_unpack_backtrace(mrb, backtrace);
+ print_backtrace(mrb, mrb->exc, backtrace);
}
+#else
-MRB_API mrb_value
-mrb_exc_backtrace(mrb_state *mrb, mrb_value self)
+MRB_API void
+mrb_print_backtrace(mrb_state *mrb)
{
- mrb_value ary;
+}
+
+#endif
- ary = mrb_ary_new(mrb);
- exc_output_backtrace(mrb, mrb_obj_ptr(self), get_backtrace_i, (void*)mrb_ary_ptr(ary));
+static void
+count_backtrace_i(mrb_state *mrb,
+ const struct backtrace_location *loc,
+ void *data)
+{
+ int *lenp = (int*)data;
- return ary;
+ (*lenp)++;
}
-MRB_API mrb_value
-mrb_get_backtrace(mrb_state *mrb)
+static void
+pack_backtrace_i(mrb_state *mrb,
+ const struct backtrace_location *loc,
+ void *data)
{
- mrb_value ary;
- mrb_callinfo *ci = mrb->c->ci;
- mrb_code *pc = ci->pc;
- mrb_int ciidx = (mrb_int)(ci - mrb->c->cibase - 1);
+ struct backtrace_location **pptr = (struct backtrace_location**)data;
+ struct backtrace_location *ptr = *pptr;
- if (ciidx < 0) ciidx = 0;
- ary = mrb_ary_new(mrb);
- output_backtrace(mrb, ciidx, pc, get_backtrace_i, (void*)mrb_ary_ptr(ary));
+ *ptr = *loc;
+ *pptr = ptr+1;
+}
- return ary;
+static mrb_value
+packed_backtrace(mrb_state *mrb)
+{
+ struct RData *backtrace;
+ ptrdiff_t ciidx = mrb->c->ci - mrb->c->cibase;
+ int len = 0;
+ int size;
+ void *ptr;
+
+ each_backtrace(mrb, ciidx, count_backtrace_i, &len);
+ size = len * sizeof(struct backtrace_location);
+ ptr = mrb_malloc(mrb, size);
+ backtrace = mrb_data_object_alloc(mrb, NULL, ptr, &bt_type);
+ backtrace->flags = (uint32_t)len;
+ each_backtrace(mrb, ciidx, pack_backtrace_i, &ptr);
+ return mrb_obj_value(backtrace);
}
-#else
+void
+mrb_keep_backtrace(mrb_state *mrb, mrb_value exc)
+{
+ mrb_sym sym = MRB_SYM(backtrace);
+ mrb_value backtrace;
+ int ai;
-MRB_API void
-mrb_print_backtrace(mrb_state *mrb)
+ if (mrb_iv_defined(mrb, exc, sym)) return;
+ ai = mrb_gc_arena_save(mrb);
+ backtrace = packed_backtrace(mrb);
+ mrb_iv_set(mrb, exc, sym, backtrace);
+ mrb_gc_arena_restore(mrb, ai);
+}
+
+mrb_value
+mrb_unpack_backtrace(mrb_state *mrb, mrb_value backtrace)
{
+ const struct backtrace_location *bt;
+ mrb_int n, i;
+ int ai;
+
+ if (mrb_nil_p(backtrace)) {
+ empty_backtrace:
+ return mrb_ary_new_capa(mrb, 0);
+ }
+ if (mrb_array_p(backtrace)) return backtrace;
+ bt = (struct backtrace_location*)mrb_data_check_get_ptr(mrb, backtrace, &bt_type);
+ if (bt == NULL) goto empty_backtrace;
+ n = (mrb_int)RDATA(backtrace)->flags;
+ if (n == 0) goto empty_backtrace;
+ backtrace = mrb_ary_new_capa(mrb, n);
+ ai = mrb_gc_arena_save(mrb);
+ for (i = 0; i < n; i++) {
+ const struct backtrace_location *entry = &bt[i];
+ mrb_value btline;
+
+ if (entry->lineno != -1) {//debug info was available
+ btline = mrb_format(mrb, "%s:%d", entry->filename, (int)entry->lineno);
+ }
+ else { //all that was left was the stack frame
+ btline = mrb_format(mrb, "%s:0", entry->filename);
+ }
+ if (entry->method_id != 0) {
+ mrb_str_cat_lit(mrb, btline, ":in ");
+ mrb_str_cat_cstr(mrb, btline, mrb_sym_name(mrb, entry->method_id));
+ }
+ mrb_ary_push(mrb, backtrace, btline);
+ mrb_gc_arena_restore(mrb, ai);
+ }
+
+ return backtrace;
}
-MRB_API mrb_value
-mrb_exc_backtrace(mrb_state *mrb, mrb_value self)
+mrb_value
+mrb_exc_backtrace(mrb_state *mrb, mrb_value exc)
{
- return mrb_ary_new(mrb);
+ mrb_value backtrace;
+
+ backtrace = mrb_iv_get(mrb, exc, MRB_SYM(backtrace));
+ if (mrb_nil_p(backtrace) || mrb_array_p(backtrace)) {
+ return backtrace;
+ }
+ /* unpack packed-backtrace */
+ backtrace = mrb_unpack_backtrace(mrb, backtrace);
+ mrb_iv_set(mrb, exc, MRB_SYM(backtrace), backtrace);
+ return backtrace;
}
-MRB_API mrb_value
+mrb_value
mrb_get_backtrace(mrb_state *mrb)
{
- return mrb_ary_new(mrb);
+ return mrb_unpack_backtrace(mrb, packed_backtrace(mrb));
}
-
-#endif
diff --git a/src/cdump.c b/src/cdump.c
new file mode 100644
index 000000000..9b7040e58
--- /dev/null
+++ b/src/cdump.c
@@ -0,0 +1,445 @@
+/*
+** dump.c - mruby binary dumper (mrbc binary format)
+**
+** See Copyright Notice in mruby.h
+*/
+
+#include <mruby.h>
+#include <mruby/string.h>
+#include <mruby/dump.h>
+#include <mruby/irep.h>
+#include <mruby/debug.h>
+
+#include <string.h>
+
+#ifndef MRB_NO_STDIO
+
+#ifndef MRB_NO_FLOAT
+#include <mruby/endian.h>
+#define MRB_FLOAT_FMT "%.17g"
+#endif
+
+static int
+cdump_pool(mrb_state *mrb, const mrb_pool_value *p, FILE *fp)
+{
+ if (p->tt & IREP_TT_NFLAG) { /* number */
+ switch (p->tt) {
+#ifdef MRB_64BIT
+ case IREP_TT_INT64:
+ if (p->u.i64 < INT32_MIN || INT32_MAX < p->u.i64) {
+ fprintf(fp, "{IREP_TT_INT64, {.i64=%" PRId64 "}},\n", p->u.i64);
+ }
+ else {
+ fprintf(fp, "{IREP_TT_INT32, {.i32=%" PRId32 "}},\n", (int32_t)p->u.i64);
+ }
+ break;
+#endif
+ case IREP_TT_INT32:
+ fprintf(fp, "{IREP_TT_INT32, {.i32=%" PRId32 "}},\n", p->u.i32);
+ break;
+ case IREP_TT_FLOAT:
+#ifndef MRB_NO_FLOAT
+ if (p->u.f == 0) {
+ fprintf(fp, "{IREP_TT_FLOAT, {.f=%#.1f}},\n", p->u.f);
+ }
+ else {
+ fprintf(fp, "{IREP_TT_FLOAT, {.f=" MRB_FLOAT_FMT "}},\n", p->u.f);
+ }
+#endif
+ break;
+ case IREP_TT_BIGINT:
+ {
+ const char *s = p->u.str;
+ int len = s[0]+2;
+ fputs("{IREP_TT_BIGINT, {\"", fp);
+ for (int i=0; i<len; i++) {
+ fprintf(fp, "\\x%02x", (int)s[i]&0xff);
+ }
+ fputs("\"}},\n", fp);
+ }
+ break;
+ }
+ }
+ else { /* string */
+ int i, len = p->tt>>2;
+ const char *s = p->u.str;
+ fprintf(fp, "{IREP_TT_STR|(%d<<2), {\"", len);
+ for (i=0; i<len; i++) {
+ fprintf(fp, "\\x%02x", (int)s[i]&0xff);
+ }
+ fputs("\"}},\n", fp);
+ }
+ return MRB_DUMP_OK;
+}
+
+static mrb_bool
+sym_name_word_p(const char *name, mrb_int len)
+{
+ if (len == 0) return FALSE;
+ if (name[0] != '_' && !ISALPHA(name[0])) return FALSE;
+ for (int i = 1; i < len; i++) {
+ if (name[i] != '_' && !ISALNUM(name[i])) return FALSE;
+ }
+ return TRUE;
+}
+
+static mrb_bool
+sym_name_with_equal_p(const char *name, mrb_int len)
+{
+ return len >= 2 && name[len-1] == '=' && sym_name_word_p(name, len-1);
+}
+
+static mrb_bool
+sym_name_with_question_mark_p(const char *name, mrb_int len)
+{
+ return len >= 2 && name[len-1] == '?' && sym_name_word_p(name, len-1);
+}
+
+static mrb_bool
+sym_name_with_bang_p(const char *name, mrb_int len)
+{
+ return len >= 2 && name[len-1] == '!' && sym_name_word_p(name, len-1);
+}
+
+static mrb_bool
+sym_name_ivar_p(const char *name, mrb_int len)
+{
+ return len >= 2 && name[0] == '@' && sym_name_word_p(name+1, len-1);
+}
+
+static mrb_bool
+sym_name_cvar_p(const char *name, mrb_int len)
+{
+ return len >= 3 && name[0] == '@' && sym_name_ivar_p(name+1, len-1);
+}
+
+#define OPERATOR_SYMBOL(sym_name, name) {name, sym_name, sizeof(sym_name)-1}
+struct operator_symbol {
+ const char *name;
+ const char *sym_name;
+ uint16_t sym_name_len;
+};
+static const struct operator_symbol operator_table[] = {
+ OPERATOR_SYMBOL("!", "not"),
+ OPERATOR_SYMBOL("%", "mod"),
+ OPERATOR_SYMBOL("&", "and"),
+ OPERATOR_SYMBOL("*", "mul"),
+ OPERATOR_SYMBOL("+", "add"),
+ OPERATOR_SYMBOL("-", "sub"),
+ OPERATOR_SYMBOL("/", "div"),
+ OPERATOR_SYMBOL("<", "lt"),
+ OPERATOR_SYMBOL(">", "gt"),
+ OPERATOR_SYMBOL("^", "xor"),
+ OPERATOR_SYMBOL("`", "tick"),
+ OPERATOR_SYMBOL("|", "or"),
+ OPERATOR_SYMBOL("~", "neg"),
+ OPERATOR_SYMBOL("!=", "neq"),
+ OPERATOR_SYMBOL("!~", "nmatch"),
+ OPERATOR_SYMBOL("&&", "andand"),
+ OPERATOR_SYMBOL("**", "pow"),
+ OPERATOR_SYMBOL("+@", "plus"),
+ OPERATOR_SYMBOL("-@", "minus"),
+ OPERATOR_SYMBOL("<<", "lshift"),
+ OPERATOR_SYMBOL("<=", "le"),
+ OPERATOR_SYMBOL("==", "eq"),
+ OPERATOR_SYMBOL("=~", "match"),
+ OPERATOR_SYMBOL(">=", "ge"),
+ OPERATOR_SYMBOL(">>", "rshift"),
+ OPERATOR_SYMBOL("[]", "aref"),
+ OPERATOR_SYMBOL("||", "oror"),
+ OPERATOR_SYMBOL("<=>", "cmp"),
+ OPERATOR_SYMBOL("===", "eqq"),
+ OPERATOR_SYMBOL("[]=", "aset"),
+};
+
+static const char*
+sym_operator_name(const char *sym_name, mrb_int len)
+{
+ mrb_sym table_size = sizeof(operator_table)/sizeof(struct operator_symbol);
+ if (operator_table[table_size-1].sym_name_len < len) return NULL;
+
+ mrb_sym start, idx;
+ int cmp;
+ const struct operator_symbol *op_sym;
+ for (start = 0; table_size != 0; table_size/=2) {
+ idx = start+table_size/2;
+ op_sym = &operator_table[idx];
+ cmp = (int)len-(int)op_sym->sym_name_len;
+ if (cmp == 0) {
+ cmp = memcmp(sym_name, op_sym->sym_name, len);
+ if (cmp == 0) return op_sym->name;
+ }
+ if (0 < cmp) {
+ start = ++idx;
+ --table_size;
+ }
+ }
+ return NULL;
+}
+
+static const char*
+sym_var_name(mrb_state *mrb, const char *initname, const char *key, int n)
+{
+ char buf[32];
+ mrb_value s = mrb_str_new_cstr(mrb, initname);
+ mrb_str_cat_lit(mrb, s, "_");
+ mrb_str_cat_cstr(mrb, s, key);
+ mrb_str_cat_lit(mrb, s, "_");
+ snprintf(buf, sizeof(buf), "%d", n);
+ mrb_str_cat_cstr(mrb, s, buf);
+ return RSTRING_PTR(s);
+}
+
+static int
+cdump_sym(mrb_state *mrb, mrb_sym sym, const char *var_name, int idx, mrb_value init_syms_code, FILE *fp)
+{
+ if (sym == 0) return MRB_DUMP_INVALID_ARGUMENT;
+
+ mrb_int len;
+ const char *name = mrb_sym_name_len(mrb, sym, &len), *op_name;
+ if (!name) return MRB_DUMP_INVALID_ARGUMENT;
+ if (sym_name_word_p(name, len)) {
+ fprintf(fp, "MRB_SYM(%s)", name);
+ }
+ else if (sym_name_with_equal_p(name, len)) {
+ fprintf(fp, "MRB_SYM_E(%.*s)", (int)(len-1), name);
+ }
+ else if (sym_name_with_question_mark_p(name, len)) {
+ fprintf(fp, "MRB_SYM_Q(%.*s)", (int)(len-1), name);
+ }
+ else if (sym_name_with_bang_p(name, len)) {
+ fprintf(fp, "MRB_SYM_B(%.*s)", (int)(len-1), name);
+ }
+ else if (sym_name_ivar_p(name, len)) {
+ fprintf(fp, "MRB_IVSYM(%s)", name+1);
+ }
+ else if (sym_name_cvar_p(name, len)) {
+ fprintf(fp, "MRB_CVSYM(%s)", name+2);
+ }
+ else if ((op_name = sym_operator_name(name, len))) {
+ fprintf(fp, "MRB_OPSYM(%s)", op_name);
+ }
+ else {
+ char buf[32];
+ mrb_value name_obj = mrb_str_new(mrb, name, len);
+ mrb_str_cat_lit(mrb, init_syms_code, " ");
+ mrb_str_cat_cstr(mrb, init_syms_code, var_name);
+ snprintf(buf, sizeof(buf), "[%d] = ", idx);
+ mrb_str_cat_cstr(mrb, init_syms_code, buf);
+ mrb_str_cat_lit(mrb, init_syms_code, "mrb_intern_lit(mrb, ");
+ mrb_str_cat_str(mrb, init_syms_code, mrb_str_dump(mrb, name_obj));
+ mrb_str_cat_lit(mrb, init_syms_code, ");\n");
+ fputs("0", fp);
+ }
+ fputs(", ", fp);
+ return MRB_DUMP_OK;
+}
+
+static int
+cdump_syms(mrb_state *mrb, const char *name, const char *key, int n, int syms_len, const mrb_sym *syms, mrb_value init_syms_code, FILE *fp)
+{
+ int ai = mrb_gc_arena_save(mrb);
+ mrb_int code_len = RSTRING_LEN(init_syms_code);
+ const char *var_name = sym_var_name(mrb, name, key, n);
+ fprintf(fp, "mrb_DEFINE_SYMS_VAR(%s, %d, (", var_name, syms_len);
+ for (int i=0; i<syms_len; i++) {
+ cdump_sym(mrb, syms[i], var_name, i, init_syms_code, fp);
+ }
+ fputs("), ", fp);
+ if (code_len == RSTRING_LEN(init_syms_code)) fputs("const", fp);
+ fputs(");\n", fp);
+ mrb_gc_arena_restore(mrb, ai);
+ return MRB_DUMP_OK;
+}
+
+//Handle the simple/common case of debug_info:
+// - 1 file associated with a single irep
+// - mrb_debug_line_ary format only
+static int
+simple_debug_info(mrb_irep_debug_info *info)
+{
+ if (!info ||
+ info->flen != 1 ||
+ info->files[0]->line_type != mrb_debug_line_ary) {
+ return 0;
+ }
+ return 1;
+}
+
+//Adds debug information to c-structs and
+//adds filenames in init_syms_code block
+static int
+cdump_debug(mrb_state *mrb, const char *name, int n, mrb_irep_debug_info *info,
+ mrb_value init_syms_code, FILE *fp)
+{
+ char buffer[256];
+ const char *filename;
+ mrb_int file_len;
+ int len, i;
+
+ if (!simple_debug_info(info))
+ return MRB_DUMP_INVALID_IREP;
+
+ len = info->files[0]->line_entry_count;
+
+ filename = mrb_sym_name_len(mrb, info->files[0]->filename_sym, &file_len);
+ snprintf(buffer, sizeof(buffer), " %s_debug_file_%d.filename_sym = mrb_intern_lit(mrb,\"",
+ name, n);
+ mrb_str_cat_cstr(mrb, init_syms_code, buffer);
+ mrb_str_cat_cstr(mrb, init_syms_code, filename);
+ mrb_str_cat_cstr(mrb, init_syms_code, "\");\n");
+
+ fprintf(fp, "static uint16_t %s_debug_lines_%d[%d] = {", name, n, len);
+ for (i=0; i<len; i++) {
+ if (i%10 == 0) fputs("\n", fp);
+ fprintf(fp, "0x%04x,", info->files[0]->lines.ary[i]);
+ }
+ fputs("};\n", fp);
+
+ fprintf(fp, "static mrb_irep_debug_info_file %s_debug_file_%d = {\n", name, n);
+ fprintf(fp, "%d, %d, %d, mrb_debug_line_ary, {%s_debug_lines_%d}};\n",
+ info->files[0]->start_pos,
+ info->files[0]->filename_sym,
+ info->files[0]->line_entry_count,
+ name,n);
+ fprintf(fp, "static mrb_irep_debug_info_file *%s_debug_file_%d_ = &%s_debug_file_%d;\n", name, n, name, n);
+
+ fprintf(fp, "static mrb_irep_debug_info %s_debug_%d = {\n", name, n);
+ fprintf(fp, "%d, %d, &%s_debug_file_%d_};\n", info->pc_count, info->flen, name, n);
+
+ return MRB_DUMP_OK;
+}
+
+static int
+cdump_irep_struct(mrb_state *mrb, const mrb_irep *irep, uint8_t flags, FILE *fp, const char *name, int n, mrb_value init_syms_code, int *mp)
+{
+ int i, len;
+ int max = *mp;
+ int debug_available = 0;
+
+ /* dump reps */
+ if (irep->reps) {
+ for (i=0,len=irep->rlen; i<len; i++) {
+ *mp += len;
+ if (cdump_irep_struct(mrb, irep->reps[i], flags, fp, name, max+i, init_syms_code, mp) != MRB_DUMP_OK)
+ return MRB_DUMP_INVALID_ARGUMENT;
+ }
+ fprintf(fp, "static const mrb_irep *%s_reps_%d[%d] = {\n", name, n, len);
+ for (i=0,len=irep->rlen; i<len; i++) {
+ fprintf(fp, " &%s_irep_%d,\n", name, max+i);
+ }
+ fputs("};\n", fp);
+ }
+ /* dump pool */
+ if (irep->pool) {
+ len=irep->plen;
+ fprintf(fp, "static const mrb_pool_value %s_pool_%d[%d] = {\n", name, n, len);
+ for (i=0; i<len; i++) {
+ if (cdump_pool(mrb, &irep->pool[i], fp) != MRB_DUMP_OK)
+ return MRB_DUMP_INVALID_ARGUMENT;
+ }
+ fputs("};\n", fp);
+ }
+ /* dump syms */
+ if (irep->syms) {
+ cdump_syms(mrb, name, "syms", n, irep->slen, irep->syms, init_syms_code, fp);
+ }
+ /* dump iseq */
+ len=irep->ilen+sizeof(struct mrb_irep_catch_handler)*irep->clen;
+ fprintf(fp, "static const mrb_code %s_iseq_%d[%d] = {", name, n, len);
+ for (i=0; i<len; i++) {
+ if (i%20 == 0) fputs("\n", fp);
+ fprintf(fp, "0x%02x,", irep->iseq[i]);
+ }
+ fputs("};\n", fp);
+ /* dump lv */
+ if (irep->lv) {
+ cdump_syms(mrb, name, "lv", n, irep->nlocals-1, irep->lv, init_syms_code, fp);
+ }
+ /* dump debug */
+ if (flags & MRB_DUMP_DEBUG_INFO) {
+ if(cdump_debug(mrb, name, n, irep->debug_info,
+ init_syms_code, fp) == MRB_DUMP_OK) {
+ debug_available = 1;
+ }
+ }
+
+
+ /* dump irep */
+ fprintf(fp, "static const mrb_irep %s_irep_%d = {\n", name, n);
+ fprintf(fp, " %d,%d,%d,\n", irep->nlocals, irep->nregs, irep->clen);
+ fprintf(fp, " MRB_IREP_STATIC,%s_iseq_%d,\n", name, n);
+ if (irep->pool) {
+ fprintf(fp, " %s_pool_%d,", name, n);
+ }
+ else {
+ fputs( " NULL,", fp);
+ }
+ if (irep->syms) {
+ fprintf(fp, "%s_syms_%d,", name, n);
+ }
+ else {
+ fputs( "NULL,", fp);
+ }
+ if (irep->reps) {
+ fprintf(fp, "%s_reps_%d,\n", name, n);
+ }
+ else {
+ fputs( "NULL,\n", fp);
+ }
+ if (irep->lv) {
+ fprintf(fp, " %s_lv_%d,\n", name, n);
+ }
+ else {
+ fputs( " NULL,\t\t\t\t\t/* lv */\n", fp);
+ }
+ if(debug_available) {
+ fprintf(fp, " &%s_debug_%d,\n", name, n);
+ }
+ else {
+ fputs(" NULL,\t\t\t\t\t/* debug_info */\n", fp);
+ }
+ fprintf(fp, " %d,%d,%d,%d,0\n};\n", irep->ilen, irep->plen, irep->slen, irep->rlen);
+
+ return MRB_DUMP_OK;
+}
+
+int
+mrb_dump_irep_cstruct(mrb_state *mrb, const mrb_irep *irep, uint8_t flags, FILE *fp, const char *initname)
+{
+ if (fp == NULL || initname == NULL || initname[0] == '\0') {
+ return MRB_DUMP_INVALID_ARGUMENT;
+ }
+ if (fprintf(fp, "#include <mruby.h>\n"
+ "#include <mruby/irep.h>\n"
+ "#include <mruby/debug.h>\n"
+ "#include <mruby/proc.h>\n"
+ "#include <mruby/presym.h>\n"
+ "\n") < 0) {
+ return MRB_DUMP_WRITE_FAULT;
+ }
+ fputs("#define mrb_BRACED(...) {__VA_ARGS__}\n", fp);
+ fputs("#define mrb_DEFINE_SYMS_VAR(name, len, syms, qualifier) \\\n", fp);
+ fputs(" static qualifier mrb_sym name[len] = mrb_BRACED syms\n", fp);
+ fputs("\n", fp);
+ mrb_value init_syms_code = mrb_str_new_capa(mrb, 0);
+ int max = 1;
+ int n = cdump_irep_struct(mrb, irep, flags, fp, initname, 0, init_syms_code, &max);
+ if (n != MRB_DUMP_OK) return n;
+ fprintf(fp,
+ "%s\n"
+ "const struct RProc %s[] = {{\n",
+ (flags & MRB_DUMP_STATIC) ? "static"
+ : "#ifdef __cplusplus\n"
+ "extern\n"
+ "#endif",
+ initname);
+ fprintf(fp, "NULL,NULL,MRB_TT_PROC,MRB_GC_RED,0,{&%s_irep_0},NULL,{NULL},\n}};\n", initname);
+ fputs("static void\n", fp);
+ fprintf(fp, "%s_init_syms(mrb_state *mrb)\n", initname);
+ fputs("{\n", fp);
+ fputs(RSTRING_PTR(init_syms_code), fp);
+ fputs("}\n", fp);
+ return MRB_DUMP_OK;
+}
+#endif
diff --git a/src/class.c b/src/class.c
index afcc4dc1d..33a610c26 100644
--- a/src/class.c
+++ b/src/class.c
@@ -4,68 +4,333 @@
** See Copyright Notice in mruby.h
*/
-#include <ctype.h>
#include <stdarg.h>
-#include "mruby.h"
-#include "mruby/array.h"
-#include "mruby/class.h"
-#include "mruby/numeric.h"
-#include "mruby/proc.h"
-#include "mruby/string.h"
-#include "mruby/variable.h"
-#include "mruby/error.h"
-#include "mruby/data.h"
-
-KHASH_DEFINE(mt, mrb_sym, struct RProc*, TRUE, kh_int_hash_func, kh_int_hash_equal)
+#include <mruby.h>
+#include <mruby/array.h>
+#include <mruby/hash.h>
+#include <mruby/class.h>
+#include <mruby/numeric.h>
+#include <mruby/proc.h>
+#include <mruby/string.h>
+#include <mruby/variable.h>
+#include <mruby/error.h>
+#include <mruby/data.h>
+#include <mruby/istruct.h>
+#include <mruby/opcode.h>
+#include <mruby/presym.h>
+
+union mt_ptr {
+ struct RProc *proc;
+ mrb_func_t func;
+};
+
+struct mt_elem {
+ union mt_ptr ptr;
+ size_t func_p:1;
+ size_t noarg_p:1;
+ mrb_sym key:sizeof(mrb_sym)*8-2;
+};
+
+/* method table structure */
+typedef struct mt_tbl {
+ size_t size;
+ size_t alloc;
+ struct mt_elem *table;
+} mt_tbl;
+
+/* Creates the method table. */
+static mt_tbl*
+mt_new(mrb_state *mrb)
+{
+ mt_tbl *t;
+
+ t = (mt_tbl*)mrb_malloc(mrb, sizeof(mt_tbl));
+ t->size = 0;
+ t->alloc = 0;
+ t->table = NULL;
+
+ return t;
+}
+
+static struct mt_elem *mt_put(mrb_state *mrb, mt_tbl *t, mrb_sym sym, size_t func_p, size_t noarg_p, union mt_ptr ptr);
+
+static void
+mt_rehash(mrb_state *mrb, mt_tbl *t)
+{
+ size_t old_alloc = t->alloc;
+ size_t new_alloc = old_alloc+1;
+ struct mt_elem *old_table = t->table;
+
+ khash_power2(new_alloc);
+ if (old_alloc == new_alloc) return;
+
+ t->alloc = new_alloc;
+ t->size = 0;
+ t->table = (struct mt_elem*)mrb_calloc(mrb, sizeof(struct mt_elem), new_alloc);
+
+ for (size_t i = 0; i < old_alloc; i++) {
+ struct mt_elem *slot = &old_table[i];
+
+ /* key = 0 means empty or deleted */
+ if (slot->key != 0) {
+ mt_put(mrb, t, slot->key, slot->func_p, slot->noarg_p, slot->ptr);
+ }
+ }
+ mrb_free(mrb, old_table);
+}
+
+#define slot_empty_p(slot) ((slot)->key == 0 && (slot)->func_p == 0)
+
+/* Set the value for the symbol in the method table. */
+static struct mt_elem*
+mt_put(mrb_state *mrb, mt_tbl *t, mrb_sym sym, size_t func_p, size_t noarg_p, union mt_ptr ptr)
+{
+ size_t hash, pos, start;
+ struct mt_elem *dslot = NULL;
+
+ if (t->alloc == 0) {
+ mt_rehash(mrb, t);
+ }
+ hash = kh_int_hash_func(mrb, sym);
+ start = pos = hash & (t->alloc-1);
+ for (;;) {
+ struct mt_elem *slot = &t->table[pos];
+
+ if (slot->key == sym) {
+ slot->func_p = func_p;
+ slot->noarg_p = noarg_p;
+ slot->ptr = ptr;
+ return slot;
+ }
+ else if (slot->key == 0) { /* empty or deleted */
+ if (slot->func_p == 0) { /* empty */
+ t->size++;
+ slot->key = sym;
+ slot->func_p = func_p;
+ slot->noarg_p = noarg_p;
+ slot->ptr = ptr;
+ return slot;
+ }
+ else if (!dslot) { /* deleted */
+ dslot = slot;
+ }
+ }
+ pos = (pos+1) & (t->alloc-1);
+ if (pos == start) { /* not found */
+ if (dslot) {
+ t->size++;
+ dslot->key = sym;
+ dslot->func_p = func_p;
+ dslot->noarg_p = noarg_p;
+ dslot->ptr = ptr;
+ return dslot;
+ }
+ /* no room */
+ mt_rehash(mrb, t);
+ start = pos = hash & (t->alloc-1);
+ }
+ }
+}
+
+/* Get a value for a symbol from the method table. */
+static struct mt_elem*
+mt_get(mrb_state *mrb, mt_tbl *t, mrb_sym sym)
+{
+ size_t hash, pos, start;
+
+ if (t == NULL) return NULL;
+ if (t->alloc == 0) return NULL;
+ if (t->size == 0) return NULL;
+
+ hash = kh_int_hash_func(mrb, sym);
+ start = pos = hash & (t->alloc-1);
+ for (;;) {
+ struct mt_elem *slot = &t->table[pos];
+
+ if (slot->key == sym) {
+ return slot;
+ }
+ else if (slot_empty_p(slot)) {
+ return NULL;
+ }
+ pos = (pos+1) & (t->alloc-1);
+ if (pos == start) { /* not found */
+ return NULL;
+ }
+ }
+}
+
+/* Deletes the value for the symbol from the method table. */
+static mrb_bool
+mt_del(mrb_state *mrb, mt_tbl *t, mrb_sym sym)
+{
+ size_t hash, pos, start;
+
+ if (t == NULL) return FALSE;
+ if (t->alloc == 0) return FALSE;
+ if (t->size == 0) return FALSE;
+
+ hash = kh_int_hash_func(mrb, sym);
+ start = pos = hash & (t->alloc-1);
+ for (;;) {
+ struct mt_elem *slot = &t->table[pos];
+
+ if (slot->key == sym) {
+ t->size--;
+ slot->key = 0;
+ slot->func_p = 1;
+ return TRUE;
+ }
+ else if (slot_empty_p(slot)) {
+ return FALSE;
+ }
+ pos = (pos+1) & (t->alloc-1);
+ if (pos == start) { /* not found */
+ return FALSE;
+ }
+ }
+}
+
+/* Copy the method table. */
+static struct mt_tbl*
+mt_copy(mrb_state *mrb, mt_tbl *t)
+{
+ mt_tbl *t2;
+ size_t i;
+
+ if (t == NULL) return NULL;
+ if (t->alloc == 0) return NULL;
+ if (t->size == 0) return NULL;
+
+ t2 = mt_new(mrb);
+ for (i=0; i<t->alloc; i++) {
+ struct mt_elem *slot = &t->table[i];
+
+ if (slot->key) {
+ mt_put(mrb, t2, slot->key, slot->func_p, slot->noarg_p, slot->ptr);
+ }
+ }
+ return t2;
+}
+
+/* Free memory of the method table. */
+static void
+mt_free(mrb_state *mrb, mt_tbl *t)
+{
+ mrb_free(mrb, t->table);
+ mrb_free(mrb, t);
+}
+
+MRB_API void
+mrb_mt_foreach(mrb_state *mrb, struct RClass *c, mrb_mt_foreach_func *fn, void *p)
+{
+ mt_tbl *t = c->mt;
+ size_t i;
+
+ if (t == NULL) return;
+ if (t->alloc == 0) return;
+ if (t->size == 0) return;
+
+ for (i=0; i<t->alloc; i++) {
+ struct mt_elem *slot = &t->table[i];
+
+ if (slot->key) {
+ mrb_method_t m;
+
+ if (slot->func_p) {
+ MRB_METHOD_FROM_FUNC(m, slot->ptr.func);
+ }
+ else {
+ MRB_METHOD_FROM_PROC(m, slot->ptr.proc);
+ }
+ if (slot->noarg_p) {
+ MRB_METHOD_NOARG_SET(m);
+ }
+
+ if (fn(mrb, slot->key, m, p) != 0)
+ return;
+ }
+ }
+ return;
+}
void
mrb_gc_mark_mt(mrb_state *mrb, struct RClass *c)
{
- khiter_t k;
- khash_t(mt) *h = c->mt;
+ mt_tbl *t = c->mt;
+ size_t i;
- if (!h) return;
- for (k = kh_begin(h); k != kh_end(h); k++) {
- if (kh_exist(h, k)) {
- struct RProc *m = kh_value(h, k);
- if (m) {
- mrb_gc_mark(mrb, (struct RBasic*)m);
- }
+ if (t == NULL) return;
+ if (t->alloc == 0) return;
+ if (t->size == 0) return;
+
+ for (i=0; i<t->alloc; i++) {
+ struct mt_elem *slot = &t->table[i];
+
+ if (slot->key && !slot->func_p) { /* Proc pointer */
+ struct RProc *p = slot->ptr.proc;
+ mrb_gc_mark(mrb, (struct RBasic*)p);
}
}
+ return;
}
size_t
mrb_gc_mark_mt_size(mrb_state *mrb, struct RClass *c)
{
- khash_t(mt) *h = c->mt;
+ struct mt_tbl *h = c->mt;
if (!h) return 0;
- return kh_size(h);
+ return h->size;
}
void
mrb_gc_free_mt(mrb_state *mrb, struct RClass *c)
{
- kh_destroy(mt, mrb, c->mt);
+ if (c->mt) mt_free(mrb, c->mt);
}
-static void
-name_class(mrb_state *mrb, struct RClass *c, mrb_sym name)
+void
+mrb_class_name_class(mrb_state *mrb, struct RClass *outer, struct RClass *c, mrb_sym id)
+{
+ mrb_value name;
+ mrb_sym nsym = MRB_SYM(__classname__);
+
+ if (mrb_obj_iv_defined(mrb, (struct RObject*)c, nsym)) return;
+ if (outer == NULL || outer == mrb->object_class) {
+ name = mrb_symbol_value(id);
+ }
+ else {
+ name = mrb_class_path(mrb, outer);
+ if (mrb_nil_p(name)) { /* unnamed outer class */
+ if (outer != mrb->object_class && outer != c) {
+ mrb_obj_iv_set_force(mrb, (struct RObject*)c, MRB_SYM(__outer__),
+ mrb_obj_value(outer));
+ }
+ return;
+ }
+ else {
+ mrb_int len;
+ const char *n = mrb_sym_name_len(mrb, id, &len);
+
+ mrb_str_cat_lit(mrb, name, "::");
+ mrb_str_cat(mrb, name, n, len);
+ }
+ }
+ mrb_obj_iv_set_force(mrb, (struct RObject*)c, nsym, name);
+}
+
+mrb_bool
+mrb_const_name_p(mrb_state *mrb, const char *name, mrb_int len)
{
- mrb_obj_iv_set(mrb, (struct RObject*)c,
- mrb_intern_lit(mrb, "__classid__"), mrb_symbol_value(name));
+ return len > 0 && ISUPPER(name[0]) && mrb_ident_p(name+1, len-1);
}
static void
setup_class(mrb_state *mrb, struct RClass *outer, struct RClass *c, mrb_sym id)
{
- name_class(mrb, c, id);
+ mrb_class_name_class(mrb, outer, c, id);
mrb_obj_iv_set(mrb, (struct RObject*)outer, id, mrb_obj_value(c));
- if (outer != mrb->object_class) {
- mrb_obj_iv_set(mrb, (struct RObject*)c, mrb_intern_lit(mrb, "__outer__"),
- mrb_obj_value(outer));
- }
}
#define make_metaclass(mrb, c) prepare_singleton_class((mrb), (struct RBasic*)(c))
@@ -76,8 +341,9 @@ prepare_singleton_class(mrb_state *mrb, struct RBasic *o)
struct RClass *sc, *c;
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 = MRB_OBJ_ALLOC(mrb, MRB_TT_SCLASS, mrb->class_class);
+ sc->flags |= MRB_FL_CLASS_IS_INHERITED;
+ sc->mt = mt_new(mrb);
sc->iv = 0;
if (o->tt == MRB_TT_CLASS) {
c = (struct RClass*)o;
@@ -97,14 +363,29 @@ prepare_singleton_class(mrb_state *mrb, struct RBasic *o)
}
else {
sc->super = o->c;
+ prepare_singleton_class(mrb, (struct RBasic*)sc);
}
o->c = sc;
mrb_field_write_barrier(mrb, (struct RBasic*)o, (struct RBasic*)sc);
mrb_field_write_barrier(mrb, (struct RBasic*)sc, (struct RBasic*)o);
- mrb_obj_iv_set(mrb, (struct RObject*)sc, mrb_intern_lit(mrb, "__attached__"), mrb_obj_value(o));
+ mrb_obj_iv_set(mrb, (struct RObject*)sc, MRB_SYM(__attached__), mrb_obj_value(o));
+ sc->flags |= o->flags & MRB_FL_OBJ_IS_FROZEN;
}
-static struct RClass *
+static mrb_value
+class_name_str(mrb_state *mrb, struct RClass* c)
+{
+ mrb_value path = mrb_class_path(mrb, c);
+ if (mrb_nil_p(path)) {
+ path = c->tt == MRB_TT_MODULE ? mrb_str_new_lit(mrb, "#<Module:") :
+ mrb_str_new_lit(mrb, "#<Class:");
+ mrb_str_cat_str(mrb, path, mrb_ptr_to_str(mrb, c));
+ mrb_str_cat_lit(mrb, path, ">");
+ }
+ return path;
+}
+
+static struct RClass*
class_from_sym(mrb_state *mrb, struct RClass *klass, mrb_sym id)
{
mrb_value c = mrb_const_get(mrb, mrb_obj_value(klass), id);
@@ -113,7 +394,7 @@ class_from_sym(mrb_state *mrb, struct RClass *klass, mrb_sym id)
return mrb_class_ptr(c);
}
-static struct RClass *
+static struct RClass*
module_from_sym(mrb_state *mrb, struct RClass *klass, mrb_sym id)
{
mrb_value c = mrb_const_get(mrb, mrb_obj_value(klass), id);
@@ -122,14 +403,25 @@ module_from_sym(mrb_state *mrb, struct RClass *klass, mrb_sym id)
return mrb_class_ptr(c);
}
-MRB_API struct RClass*
-mrb_class_outer_module(mrb_state *mrb, struct RClass *c)
+static mrb_bool
+class_ptr_p(mrb_value obj)
{
- mrb_value outer;
+ switch (mrb_type(obj)) {
+ case MRB_TT_CLASS:
+ case MRB_TT_SCLASS:
+ case MRB_TT_MODULE:
+ return TRUE;
+ default:
+ return FALSE;
+ }
+}
- outer = mrb_obj_iv_get(mrb, (struct RObject*)c, mrb_intern_lit(mrb, "__outer__"));
- if (mrb_nil_p(outer)) return NULL;
- return mrb_class_ptr(outer);
+static void
+check_if_class_or_module(mrb_state *mrb, mrb_value obj)
+{
+ if (!class_ptr_p(obj)) {
+ mrb_raisef(mrb, E_TYPE_ERROR, "%!v is not a class/module", obj);
+ }
}
static struct RClass*
@@ -158,13 +450,31 @@ mrb_define_module(mrb_state *mrb, const char *name)
return define_module(mrb, mrb_intern_cstr(mrb, name), mrb->object_class);
}
-MRB_API struct RClass*
+struct RClass*
mrb_vm_define_module(mrb_state *mrb, mrb_value outer, mrb_sym id)
{
+ check_if_class_or_module(mrb, outer);
+ if (mrb_const_defined_at(mrb, outer, id)) {
+ mrb_value old = mrb_const_get(mrb, outer, id);
+
+ if (!mrb_module_p(old)) {
+ mrb_raisef(mrb, E_TYPE_ERROR, "%!v is not a module", old);
+ }
+ return mrb_class_ptr(old);
+ }
return define_module(mrb, id, mrb_class_ptr(outer));
}
MRB_API struct RClass*
+mrb_define_module_under_id(mrb_state *mrb, struct RClass *outer, mrb_sym name)
+{
+ struct RClass * c = define_module(mrb, name, outer);
+
+ setup_class(mrb, outer, c, name);
+ return c;
+}
+
+MRB_API struct RClass*
mrb_define_module_under(mrb_state *mrb, struct RClass *outer, const char *name)
{
mrb_sym id = mrb_intern_cstr(mrb, name);
@@ -175,16 +485,23 @@ 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),
- mrb_obj_value(c->super), mrb_obj_value(super));
+ mrb_raisef(mrb, E_TYPE_ERROR, "superclass mismatch for Class %n (%C not %C)",
+ name, c->super, super);
}
return c;
}
@@ -199,7 +516,7 @@ MRB_API struct RClass*
mrb_define_class_id(mrb_state *mrb, mrb_sym name, struct RClass *super)
{
if (!super) {
- mrb_warn(mrb, "no super class for `%S', Object assumed", mrb_sym2str(mrb, name));
+ mrb_warn(mrb, "no super class for '%n', Object assumed", name);
}
return define_class(mrb, name, super, mrb->object_class);
}
@@ -210,37 +527,61 @@ mrb_define_class(mrb_state *mrb, const char *name, struct RClass *super)
return mrb_define_class_id(mrb, mrb_intern_cstr(mrb, name), super);
}
+static mrb_value mrb_bob_init(mrb_state *mrb, mrb_value);
+#ifndef MRB_NO_METHOD_CACHE
+static void mc_clear(mrb_state *mrb);
+#else
+#define mc_clear(mrb)
+#endif
+
static void
mrb_class_inherited(mrb_state *mrb, struct RClass *super, struct RClass *klass)
{
+ mrb_value s;
+ mrb_sym mid;
+
if (!super)
super = mrb->object_class;
- mrb_funcall(mrb, mrb_obj_value(super), "inherited", 1, mrb_obj_value(klass));
+ super->flags |= MRB_FL_CLASS_IS_INHERITED;
+ s = mrb_obj_value(super);
+ mrb_mc_clear_by_class(mrb, klass);
+ mid = MRB_SYM(inherited);
+ if (!mrb_func_basic_p(mrb, s, mid, mrb_bob_init)) {
+ mrb_value c = mrb_obj_value(klass);
+ mrb_funcall_argv(mrb, s, mid, 1, &c);
+ }
}
-MRB_API struct RClass*
+struct RClass*
mrb_vm_define_class(mrb_state *mrb, mrb_value outer, mrb_value super, mrb_sym id)
{
struct RClass *s;
struct RClass *c;
if (!mrb_nil_p(super)) {
- if (mrb_type(super) != MRB_TT_CLASS) {
- mrb_raisef(mrb, E_TYPE_ERROR, "superclass must be a Class (%S given)", super);
+ if (!mrb_class_p(super)) {
+ mrb_raisef(mrb, E_TYPE_ERROR, "superclass must be a Class (%!v given)", super);
}
s = mrb_class_ptr(super);
}
else {
s = 0;
}
- switch (mrb_type(outer)) {
- case MRB_TT_CLASS:
- case MRB_TT_SCLASS:
- case MRB_TT_MODULE:
- break;
- default:
- mrb_raisef(mrb, E_TYPE_ERROR, "%S is not a class/module", outer);
- break;
+ check_if_class_or_module(mrb, outer);
+ if (mrb_const_defined_at(mrb, outer, id)) {
+ mrb_value old = mrb_const_get(mrb, outer, id);
+
+ if (!mrb_class_p(old)) {
+ mrb_raisef(mrb, E_TYPE_ERROR, "%!v is not a class", old);
+ }
+ c = mrb_class_ptr(old);
+ if (s) {
+ /* check super class */
+ if (mrb_class_real(c->super) != s) {
+ mrb_raisef(mrb, E_TYPE_ERROR, "superclass mismatch for class %v", old);
+ }
+ }
+ return c;
}
c = define_class(mrb, id, s, mrb_class_ptr(outer));
mrb_class_inherited(mrb, mrb_class_real(c->super), c);
@@ -251,41 +592,102 @@ mrb_vm_define_class(mrb_state *mrb, mrb_value outer, mrb_value super, mrb_sym id
MRB_API mrb_bool
mrb_class_defined(mrb_state *mrb, const char *name)
{
- mrb_value sym = mrb_check_intern_cstr(mrb, name);
- if (mrb_nil_p(sym)) {
- return FALSE;
- }
- return mrb_const_defined(mrb, mrb_obj_value(mrb->object_class), mrb_symbol(sym));
+ mrb_sym sym = mrb_intern_check_cstr(mrb, name);
+ if (!sym) return FALSE;
+ return mrb_const_defined(mrb, mrb_obj_value(mrb->object_class), sym);
+}
+
+MRB_API mrb_bool
+mrb_class_defined_id(mrb_state *mrb, mrb_sym name)
+{
+ return mrb_const_defined(mrb, mrb_obj_value(mrb->object_class), name);
}
-MRB_API struct RClass *
+MRB_API mrb_bool
+mrb_class_defined_under(mrb_state *mrb, struct RClass *outer, const char *name)
+{
+ mrb_sym sym = mrb_intern_check_cstr(mrb, name);
+ if (!sym) return FALSE;
+ return mrb_const_defined_at(mrb, mrb_obj_value(outer), sym);
+}
+
+MRB_API mrb_bool
+mrb_class_defined_under_id(mrb_state *mrb, struct RClass *outer, mrb_sym name)
+{
+ return mrb_const_defined_at(mrb, mrb_obj_value(outer), name);
+}
+
+MRB_API struct RClass*
mrb_class_get_under(mrb_state *mrb, struct RClass *outer, const char *name)
{
return class_from_sym(mrb, outer, mrb_intern_cstr(mrb, name));
}
-MRB_API struct RClass *
+MRB_API struct RClass*
+mrb_class_get_under_id(mrb_state *mrb, struct RClass *outer, mrb_sym name)
+{
+ return class_from_sym(mrb, outer, name);
+}
+
+MRB_API struct RClass*
mrb_class_get(mrb_state *mrb, const char *name)
{
return mrb_class_get_under(mrb, mrb->object_class, name);
}
-MRB_API struct RClass *
+MRB_API struct RClass*
+mrb_class_get_id(mrb_state *mrb, mrb_sym name)
+{
+ return mrb_class_get_under_id(mrb, mrb->object_class, name);
+}
+
+MRB_API struct RClass*
+mrb_exc_get_id(mrb_state *mrb, mrb_sym name)
+{
+ struct RClass *exc, *e;
+ mrb_value c = mrb_const_get(mrb, mrb_obj_value(mrb->object_class), name);
+
+ if (!mrb_class_p(c)) {
+ mrb_raise(mrb, mrb->eException_class, "exception corrupted");
+ }
+ exc = e = mrb_class_ptr(c);
+
+ while (e) {
+ if (e == mrb->eException_class)
+ return exc;
+ e = e->super;
+ }
+ return mrb->eException_class;
+}
+
+MRB_API struct RClass*
mrb_module_get_under(mrb_state *mrb, struct RClass *outer, const char *name)
{
return module_from_sym(mrb, outer, mrb_intern_cstr(mrb, name));
}
-MRB_API struct RClass *
+MRB_API struct RClass*
+mrb_module_get_under_id(mrb_state *mrb, struct RClass *outer, mrb_sym name)
+{
+ return module_from_sym(mrb, outer, name);
+}
+
+MRB_API struct RClass*
mrb_module_get(mrb_state *mrb, const char *name)
{
return mrb_module_get_under(mrb, mrb->object_class, name);
}
+MRB_API struct RClass*
+mrb_module_get_id(mrb_state *mrb, mrb_sym name)
+{
+ return mrb_module_get_under_id(mrb, mrb->object_class, name);
+}
+
/*!
* Defines a class under the namespace of \a outer.
* \param outer a class which contains the new class.
- * \param id name of the new class
+ * \param name name of the new class
* \param super a class from which the new class will derive.
* NULL means \c Object class.
* \return the created class
@@ -298,46 +700,77 @@ mrb_module_get(mrb_state *mrb, const char *name)
* \note if a class named \a name is already defined and its superclass is
* \a super, the function just returns the defined 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_class_under_id(mrb_state *mrb, struct RClass *outer, mrb_sym name, struct RClass *super)
{
- mrb_sym id = mrb_intern_cstr(mrb, name);
struct RClass * c;
#if 0
if (!super) {
- mrb_warn(mrb, "no super class for `%S::%S', Object assumed",
- mrb_obj_value(outer), mrb_sym2str(mrb, id));
+ mrb_warn(mrb, "no super class for '%C::%n', Object assumed", outer, id);
}
#endif
- c = define_class(mrb, id, super, outer);
- setup_class(mrb, outer, c, id);
+ c = define_class(mrb, name, super, outer);
+ setup_class(mrb, outer, c, name);
return c;
}
-MRB_API void
-mrb_define_method_raw(mrb_state *mrb, struct RClass *c, mrb_sym mid, struct RProc *p)
+MRB_API struct RClass*
+mrb_define_class_under(mrb_state *mrb, struct RClass *outer, const char *name, struct RClass *super)
{
- khash_t(mt) *h = c->mt;
- khiter_t k;
+ return mrb_define_class_under_id(mrb, outer, mrb_intern_cstr(mrb, name), super);
+}
- if (!h) h = c->mt = kh_init(mt, mrb);
- k = kh_put(mt, mrb, h, mid);
- kh_value(h, k) = p;
- if (p) {
- mrb_field_write_barrier(mrb, (struct RBasic *)c, (struct RBasic *)p);
+MRB_API void
+mrb_define_method_raw(mrb_state *mrb, struct RClass *c, mrb_sym mid, mrb_method_t m)
+{
+ mt_tbl *h;
+ union mt_ptr ptr;
+
+ MRB_CLASS_ORIGIN(c);
+ h = c->mt;
+ mrb_check_frozen(mrb, c);
+ if (!h) h = c->mt = mt_new(mrb);
+ if (MRB_METHOD_PROC_P(m)) {
+ struct RProc *p = MRB_METHOD_PROC(m);
+
+ ptr.proc = p;
+ if (p) {
+ if (p->color != MRB_GC_RED) {
+ p->flags |= MRB_PROC_SCOPE;
+ p->c = NULL;
+ mrb_field_write_barrier(mrb, (struct RBasic*)c, (struct RBasic*)p);
+ if (!MRB_PROC_ENV_P(p)) {
+ MRB_PROC_SET_TARGET_CLASS(p, c);
+ }
+ }
+ else {
+ mrb_assert(MRB_FROZEN_P(p) && MRB_PROC_SCOPE_P(p));
+ mrb_assert(p->c == NULL && p->upper == NULL && p->e.target_class == NULL);
+ }
+ }
}
+ else {
+ ptr.func = MRB_METHOD_FUNC(m);
+ }
+ mt_put(mrb, h, mid, MRB_METHOD_FUNC_P(m), MRB_METHOD_NOARG_P(m), ptr);
+ mc_clear(mrb);
}
MRB_API void
mrb_define_method_id(mrb_state *mrb, struct RClass *c, mrb_sym mid, mrb_func_t func, mrb_aspec aspec)
{
- struct RProc *p;
+ mrb_method_t m;
int ai = mrb_gc_arena_save(mrb);
- p = mrb_proc_new_cfunc(mrb, func);
- p->target_class = c;
- mrb_define_method_raw(mrb, c, mid, p);
+ MRB_METHOD_FROM_FUNC(m, func);
+#ifndef MRB_USE_METHOD_T_STRUCT
+ mrb_assert(MRB_METHOD_FUNC(m) == func);
+#endif
+ if (aspec == MRB_ARGS_NONE()) {
+ MRB_METHOD_NOARG_SET(m);
+ }
+ mrb_define_method_raw(mrb, c, mid, m);
mrb_gc_arena_restore(mrb, ai);
}
@@ -347,35 +780,14 @@ mrb_define_method(mrb_state *mrb, struct RClass *c, const char *name, mrb_func_t
mrb_define_method_id(mrb, c, mrb_intern_cstr(mrb, name), func, aspec);
}
-MRB_API void
-mrb_define_method_vm(mrb_state *mrb, struct RClass *c, mrb_sym name, mrb_value body)
-{
- khash_t(mt) *h = c->mt;
- khiter_t k;
- struct RProc *p;
-
- if (!h) h = c->mt = kh_init(mt, mrb);
- k = kh_put(mt, mrb, h, name);
- p = mrb_proc_ptr(body);
- kh_value(h, k) = p;
- if (p) {
- mrb_field_write_barrier(mrb, (struct RBasic *)c, (struct RBasic *)p);
- }
-}
-
/* a function to raise NotImplementedError with current method name */
MRB_API void
mrb_notimplement(mrb_state *mrb)
{
- const char *str;
- mrb_int len;
mrb_callinfo *ci = mrb->c->ci;
if (ci->mid) {
- str = mrb_sym2name_len(mrb, ci->mid, &len);
- mrb_raisef(mrb, E_NOTIMP_ERROR,
- "%S() function is unimplemented on this machine",
- mrb_str_new_static(mrb, str, (size_t)len));
+ mrb_raisef(mrb, E_NOTIMP_ERROR, "%n() function is unimplemented on this machine", ci->mid);
}
}
@@ -389,50 +801,77 @@ mrb_notimplement_m(mrb_state *mrb, mrb_value self)
}
static mrb_value
-check_type(mrb_state *mrb, mrb_value val, enum mrb_vtype t, const char *c, const char *m)
+to_ary(mrb_state *mrb, mrb_value val)
{
- mrb_value tmp;
-
- tmp = mrb_check_convert_type(mrb, val, t, c, m);
- if (mrb_nil_p(tmp)) {
- mrb_raisef(mrb, E_TYPE_ERROR, "expected %S", mrb_str_new_cstr(mrb, c));
- }
- return tmp;
+ mrb_check_type(mrb, val, MRB_TT_ARRAY);
+ return val;
}
static mrb_value
-to_str(mrb_state *mrb, mrb_value val)
+to_hash(mrb_state *mrb, mrb_value val)
{
- return check_type(mrb, val, MRB_TT_STRING, "String", "to_str");
+ mrb_check_type(mrb, val, MRB_TT_HASH);
+ return val;
}
-static mrb_value
-to_ary(mrb_state *mrb, mrb_value val)
+#define to_sym(mrb, ss) mrb_obj_to_sym(mrb, ss)
+
+MRB_API mrb_int
+mrb_get_argc(mrb_state *mrb)
{
- return check_type(mrb, val, MRB_TT_ARRAY, "Array", "to_ary");
+ mrb_int argc = mrb->c->ci->argc;
+
+ if (argc < 0) {
+ struct RArray *a = mrb_ary_ptr(mrb->c->ci->stack[1]);
+
+ argc = ARY_LEN(a);
+ }
+ return argc;
}
-static mrb_value
-to_hash(mrb_state *mrb, mrb_value val)
+MRB_API const mrb_value*
+mrb_get_argv(mrb_state *mrb)
{
- return check_type(mrb, val, MRB_TT_HASH, "Hash", "to_hash");
+ mrb_int argc = mrb->c->ci->argc;
+ mrb_value *array_argv = mrb->c->ci->stack + 1;
+ if (argc < 0) {
+ struct RArray *a = mrb_ary_ptr(*array_argv);
+
+ array_argv = ARY_PTR(a);
+ }
+ return array_argv;
}
-static mrb_sym
-to_sym(mrb_state *mrb, mrb_value ss)
+MRB_API mrb_value
+mrb_get_arg1(mrb_state *mrb)
{
- if (mrb_type(ss) == MRB_TT_SYMBOL) {
- return mrb_symbol(ss);
+ mrb_int argc = mrb->c->ci->argc;
+ mrb_value *array_argv = mrb->c->ci->stack + 1;
+ if (argc < 0) {
+ struct RArray *a = mrb_ary_ptr(*array_argv);
+
+ argc = ARY_LEN(a);
+ array_argv = ARY_PTR(a);
}
- else if (mrb_string_p(ss)) {
- return mrb_intern_str(mrb, to_str(mrb, ss));
- }
- else {
- mrb_value obj = mrb_funcall(mrb, ss, "inspect", 0);
- mrb_raisef(mrb, E_TYPE_ERROR, "%S is not a symbol", obj);
+ if (argc != 1) {
+ mrb_argnum_error(mrb, argc, 1, 1);
}
+ return array_argv[0];
}
+MRB_API mrb_bool
+mrb_block_given_p(mrb_state *mrb)
+{
+ const mrb_callinfo *ci = mrb->c->ci;
+ int argc = ci->argc;
+ int idx = (argc < 0) ? 2 : argc + 1;
+ mrb_value b = ci->stack[idx];
+
+ return !mrb_nil_p(b);
+}
+
+void mrb_hash_check_kdict(mrb_state *mrb, mrb_value self);
+
/*
retrieve arguments from mrb_state.
@@ -445,44 +884,94 @@ to_sym(mrb_state *mrb, mrb_value ss)
string mruby type C type note
----------------------------------------------------------------------------------------------
o: Object [mrb_value]
- C: class/module [mrb_value]
- S: String [mrb_value]
- A: Array [mrb_value]
- H: Hash [mrb_value]
- s: String [char*,mrb_int] Receive two arguments.
- z: String [char*] NUL terminated string.
- a: Array [mrb_value*,mrb_int] Receive two arguments.
- f: Float [mrb_float]
- i: Integer [mrb_int]
- b: Boolean [mrb_bool]
- n: Symbol [mrb_sym]
- d: Data [void*,mrb_data_type const] 2nd argument will be used to check data type so it won't be modified
- &: Block [mrb_value]
- *: rest argument [mrb_value*,mrb_int] Receive the rest of the arguments as an array.
- |: optional Next argument of '|' and later are optional.
- ?: optional given [mrb_bool] true if preceding argument (optional) is given.
+ 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 [const char*,mrb_int] Receive two arguments; s! gives (NULL,0) for nil
+ z: String [const char*] NUL terminated string; z! gives NULL for nil
+ a: Array [const mrb_value*,mrb_int] Receive two arguments; a! gives (NULL,0) for nil
+ c: Class/Module [strcut RClass*]
+ f: Integer/Float [mrb_float]
+ i: Integer/Float [mrb_int]
+ b: boolean [mrb_bool]
+ n: String/Symbol [mrb_sym]
+ d: data [void*,mrb_data_type const] 2nd argument will be used to check data type so it won't be modified; when ! follows, the value may be nil
+ I: inline struct [void*]
+ &: block [mrb_value] &! raises exception if no block given
+ *: rest argument [const mrb_value*,mrb_int] The rest of the arguments as an array; *! avoid copy of the stack
+ |: optional Following arguments are optional
+ ?: optional given [mrb_bool] true if preceding argument (optional) is given
+ ':': keyword args [mrb_kwargs const] Get keyword arguments
*/
MRB_API mrb_int
mrb_get_args(mrb_state *mrb, const char *format, ...)
{
+ const char *fmt = format;
char c;
- int i = 0;
- mrb_value *sp = mrb->c->stack + 1;
+ mrb_int i = 0;
va_list ap;
- int argc = mrb->c->ci->argc;
+ mrb_int argc = mrb->c->ci->argc;
+ mrb_value *array_argv = mrb->c->ci->stack+1;
+ mrb_bool argv_on_stack = argc >= 0;
mrb_bool opt = FALSE;
+ mrb_bool opt_skip = TRUE;
mrb_bool given = TRUE;
+ mrb_value kdict;
+ mrb_bool reqkarg = FALSE;
+ int argc_min = 0, argc_max = 0;
+ if (!argv_on_stack) {
+ struct RArray *a = mrb_ary_ptr(*array_argv);
+ array_argv = ARY_PTR(a);
+ argc = ARY_LEN(a);
+ }
va_start(ap, format);
- if (argc < 0) {
- struct RArray *a = mrb_ary_ptr(mrb->c->stack[1]);
- argc = a->len;
- sp = a->ptr;
+#define ARGV array_argv
+
+ while ((c = *fmt++)) {
+ switch (c) {
+ case '|':
+ opt = TRUE;
+ break;
+ case '*':
+ opt_skip = FALSE;
+ argc_max = -1;
+ if (!reqkarg) reqkarg = strchr(fmt, ':') ? TRUE : FALSE;
+ goto check_exit;
+ case '!':
+ break;
+ case ':':
+ reqkarg = TRUE;
+ /* fall through */
+ case '&': case '?':
+ if (opt) opt_skip = FALSE;
+ break;
+ default:
+ if (!opt) argc_min++;
+ argc_max++;
+ break;
+ }
+ }
+
+ check_exit:
+ if (reqkarg && argc > argc_min && mrb_hash_p(kdict = ARGV[argc - 1])) {
+ mrb_hash_check_kdict(mrb, kdict);
+ argc --;
+ }
+ else {
+ kdict = mrb_nil_value();
}
+
+ opt = FALSE;
+ i = 0;
while ((c = *format++)) {
+ mrb_value *argv = ARGV;
+ mrb_bool altmode;
+
switch (c) {
- case '|': case '*': case '&': case '?':
+ case '|': case '*': case '&': case '?': case ':':
break;
default:
if (argc <= i) {
@@ -490,12 +979,20 @@ mrb_get_args(mrb_state *mrb, const char *format, ...)
given = FALSE;
}
else {
- mrb_raise(mrb, E_ARGUMENT_ERROR, "wrong number of arguments");
+ mrb_argnum_error(mrb, argc, argc_min, argc_max);
}
}
break;
}
+ if (*format == '!') {
+ format ++;
+ altmode = TRUE;
+ }
+ else {
+ altmode = FALSE;
+ }
+
switch (c) {
case 'o':
{
@@ -503,8 +1000,7 @@ mrb_get_args(mrb_state *mrb, const char *format, ...)
p = va_arg(ap, mrb_value*);
if (i < argc) {
- *p = *sp++;
- i++;
+ *p = argv[i++];
}
}
break;
@@ -516,18 +1012,27 @@ mrb_get_args(mrb_state *mrb, const char *format, ...)
if (i < argc) {
mrb_value ss;
- ss = *sp++;
- switch (mrb_type(ss)) {
- case MRB_TT_CLASS:
- case MRB_TT_MODULE:
- case MRB_TT_SCLASS:
- break;
- default:
- mrb_raisef(mrb, E_TYPE_ERROR, "%S is not class/module", ss);
- break;
+ ss = argv[i++];
+ if (!class_ptr_p(ss)) {
+ mrb_raisef(mrb, E_TYPE_ERROR, "%v is not class/module", ss);
}
*p = ss;
- i++;
+ }
+ }
+ break;
+ case 'c':
+ {
+ struct RClass **p;
+
+ p = va_arg(ap, struct RClass**);
+ if (i < argc) {
+ mrb_value ss;
+
+ ss = argv[i++];
+ if (!class_ptr_p(ss)) {
+ mrb_raisef(mrb, E_TYPE_ERROR, "%v is not class/module", ss);
+ }
+ *p = mrb_class_ptr(ss);
}
}
break;
@@ -537,8 +1042,10 @@ mrb_get_args(mrb_state *mrb, const char *format, ...)
p = va_arg(ap, mrb_value*);
if (i < argc) {
- *p = to_str(mrb, *sp++);
- i++;
+ *p = argv[i++];
+ if (!(altmode && mrb_nil_p(*p))) {
+ mrb_to_str(mrb, *p);
+ }
}
}
break;
@@ -548,8 +1055,10 @@ mrb_get_args(mrb_state *mrb, const char *format, ...)
p = va_arg(ap, mrb_value*);
if (i < argc) {
- *p = to_ary(mrb, *sp++);
- i++;
+ *p = argv[i++];
+ if (!(altmode && mrb_nil_p(*p))) {
+ *p = to_ary(mrb, *p);
+ }
}
}
break;
@@ -559,24 +1068,32 @@ mrb_get_args(mrb_state *mrb, const char *format, ...)
p = va_arg(ap, mrb_value*);
if (i < argc) {
- *p = to_hash(mrb, *sp++);
- i++;
+ *p = argv[i++];
+ if (!(altmode && mrb_nil_p(*p))) {
+ *p = to_hash(mrb, *p);
+ }
}
}
break;
case 's':
{
mrb_value ss;
- char **ps = 0;
+ const char **ps = 0;
mrb_int *pl = 0;
- ps = va_arg(ap, char**);
+ ps = va_arg(ap, const char**);
pl = va_arg(ap, mrb_int*);
if (i < argc) {
- ss = to_str(mrb, *sp++);
- *ps = RSTRING_PTR(ss);
- *pl = RSTRING_LEN(ss);
- i++;
+ ss = argv[i++];
+ if (altmode && mrb_nil_p(ss)) {
+ *ps = NULL;
+ *pl = 0;
+ }
+ else {
+ mrb_to_str(mrb, ss);
+ *ps = RSTRING_PTR(ss);
+ *pl = RSTRING_LEN(ss);
+ }
}
}
break;
@@ -587,9 +1104,14 @@ mrb_get_args(mrb_state *mrb, const char *format, ...)
ps = va_arg(ap, const char**);
if (i < argc) {
- ss = to_str(mrb, *sp++);
- *ps = mrb_string_value_cstr(mrb, &ss);
- i++;
+ ss = argv[i++];
+ if (altmode && mrb_nil_p(ss)) {
+ *ps = NULL;
+ }
+ else {
+ mrb_to_str(mrb, ss);
+ *ps = RSTRING_CSTR(mrb, ss);
+ }
}
}
break;
@@ -597,61 +1119,61 @@ mrb_get_args(mrb_state *mrb, const char *format, ...)
{
mrb_value aa;
struct RArray *a;
- mrb_value **pb;
+ const mrb_value **pb;
mrb_int *pl;
- pb = va_arg(ap, mrb_value**);
+ pb = va_arg(ap, const mrb_value**);
pl = va_arg(ap, mrb_int*);
if (i < argc) {
- aa = to_ary(mrb, *sp++);
- a = mrb_ary_ptr(aa);
- *pb = a->ptr;
- *pl = a->len;
- i++;
+ aa = argv[i++];
+ if (altmode && mrb_nil_p(aa)) {
+ *pb = 0;
+ *pl = 0;
+ }
+ else {
+ aa = to_ary(mrb, aa);
+ a = mrb_ary_ptr(aa);
+ *pb = ARY_PTR(a);
+ *pl = ARY_LEN(a);
+ }
+ }
+ }
+ break;
+ case 'I':
+ {
+ void* *p;
+ mrb_value ss;
+
+ p = va_arg(ap, void**);
+ if (i < argc) {
+ ss = argv[i++];
+ if (!mrb_istruct_p(ss))
+ {
+ mrb_raisef(mrb, E_TYPE_ERROR, "%v is not inline struct", ss);
+ }
+ *p = mrb_istruct_ptr(ss);
}
}
break;
+#ifndef MRB_NO_FLOAT
case 'f':
{
mrb_float *p;
p = va_arg(ap, mrb_float*);
if (i < argc) {
- *p = mrb_to_flo(mrb, *sp);
- sp++;
- i++;
+ *p = mrb_as_float(mrb, argv[i]); i++;
}
}
break;
+#endif
case 'i':
{
mrb_int *p;
p = va_arg(ap, mrb_int*);
if (i < argc) {
- switch (mrb_type(*sp)) {
- case MRB_TT_FIXNUM:
- *p = mrb_fixnum(*sp);
- break;
- case MRB_TT_FLOAT:
- {
- mrb_float f = mrb_float(*sp);
-
- if (!FIXABLE(f)) {
- mrb_raise(mrb, E_RANGE_ERROR, "float too big for int");
- }
- *p = (mrb_int)f;
- }
- break;
- case MRB_TT_STRING:
- mrb_raise(mrb, E_TYPE_ERROR, "no implicit conversion of String into Integer");
- break;
- default:
- *p = mrb_fixnum(mrb_Integer(mrb, *sp));
- break;
- }
- sp++;
- i++;
+ *p = mrb_as_int(mrb, argv[i]); i++;
}
}
break;
@@ -660,9 +1182,8 @@ mrb_get_args(mrb_state *mrb, const char *format, ...)
mrb_bool *boolp = va_arg(ap, mrb_bool*);
if (i < argc) {
- mrb_value b = *sp++;
+ mrb_value b = argv[i++];
*boolp = mrb_test(b);
- i++;
}
}
break;
@@ -674,9 +1195,8 @@ mrb_get_args(mrb_state *mrb, const char *format, ...)
if (i < argc) {
mrb_value ss;
- ss = *sp++;
+ ss = argv[i++];
*symp = to_sym(mrb, ss);
- i++;
}
}
break;
@@ -688,8 +1208,13 @@ mrb_get_args(mrb_state *mrb, const char *format, ...)
datap = va_arg(ap, void**);
type = va_arg(ap, struct mrb_data_type const*);
if (i < argc) {
- *datap = mrb_data_get_ptr(mrb, *sp++, type);
- ++i;
+ mrb_value dd = argv[i++];
+ if (altmode && mrb_nil_p(dd)) {
+ *datap = 0;
+ }
+ else {
+ *datap = mrb_data_get_ptr(mrb, dd, type);
+ }
}
}
break;
@@ -700,15 +1225,19 @@ mrb_get_args(mrb_state *mrb, const char *format, ...)
p = va_arg(ap, mrb_value*);
if (mrb->c->ci->argc < 0) {
- bp = mrb->c->stack + 2;
+ bp = mrb->c->ci->stack + 2;
}
else {
- bp = mrb->c->stack + mrb->c->ci->argc + 1;
+ bp = mrb->c->ci->stack + mrb->c->ci->argc + 1;
+ }
+ if (altmode && mrb_nil_p(*bp)) {
+ mrb_raise(mrb, E_ARGUMENT_ERROR, "no block given");
}
*p = *bp;
}
break;
case '|':
+ if (opt_skip && i == argc) goto finish;
opt = TRUE;
break;
case '?':
@@ -722,18 +1251,25 @@ mrb_get_args(mrb_state *mrb, const char *format, ...)
case '*':
{
- mrb_value **var;
+ const mrb_value **var;
mrb_int *pl;
+ mrb_bool nocopy = (altmode || !argv_on_stack) ? TRUE : FALSE;
- var = va_arg(ap, mrb_value**);
+ var = va_arg(ap, const mrb_value**);
pl = va_arg(ap, mrb_int*);
if (argc > i) {
*pl = argc-i;
if (*pl > 0) {
- *var = sp;
+ if (nocopy) {
+ *var = argv+i;
+ }
+ else {
+ mrb_value args = mrb_ary_new_from_values(mrb, *pl, argv+i);
+ RARRAY(args)->c = NULL;
+ *var = RARRAY_PTR(args);
+ }
}
i = argc;
- sp += *pl;
}
else {
*pl = 0;
@@ -741,14 +1277,75 @@ mrb_get_args(mrb_state *mrb, const char *format, ...)
}
}
break;
+
+ case ':':
+ {
+ mrb_value ksrc = mrb_hash_p(kdict) ? mrb_hash_dup(mrb, kdict) : mrb_hash_new(mrb);
+ const mrb_kwargs *kwargs = va_arg(ap, const mrb_kwargs*);
+ mrb_value *rest;
+
+ if (kwargs == NULL) {
+ rest = NULL;
+ }
+ else {
+ uint32_t kwnum = kwargs->num;
+ uint32_t required = kwargs->required;
+ const mrb_sym *kname = kwargs->table;
+ mrb_value *values = kwargs->values;
+ uint32_t j;
+ const uint32_t keyword_max = 40;
+
+ if (kwnum > keyword_max || required > kwnum) {
+ mrb_raise(mrb, E_ARGUMENT_ERROR, "keyword number is too large");
+ }
+
+ for (j = required; j > 0; j --, kname ++, values ++) {
+ mrb_value k = mrb_symbol_value(*kname);
+ if (!mrb_hash_key_p(mrb, ksrc, k)) {
+ mrb_raisef(mrb, E_ARGUMENT_ERROR, "missing keyword: %n", *kname);
+ }
+ *values = mrb_hash_delete_key(mrb, ksrc, k);
+ mrb_gc_protect(mrb, *values);
+ }
+
+ for (j = kwnum - required; j > 0; j --, kname ++, values ++) {
+ mrb_value k = mrb_symbol_value(*kname);
+ if (mrb_hash_key_p(mrb, ksrc, k)) {
+ *values = mrb_hash_delete_key(mrb, ksrc, k);
+ mrb_gc_protect(mrb, *values);
+ }
+ else {
+ *values = mrb_undef_value();
+ }
+ }
+
+ rest = kwargs->rest;
+ }
+
+ if (rest) {
+ *rest = ksrc;
+ }
+ else if (!mrb_hash_empty_p(mrb, ksrc)) {
+ ksrc = mrb_hash_keys(mrb, ksrc);
+ ksrc = RARRAY_PTR(ksrc)[0];
+ mrb_raisef(mrb, E_ARGUMENT_ERROR, "unknown keyword: %v", ksrc);
+ }
+ }
+ break;
+
default:
- mrb_raisef(mrb, E_ARGUMENT_ERROR, "invalid argument specifier %S", mrb_str_new(mrb, &c, 1));
+ mrb_raisef(mrb, E_ARGUMENT_ERROR, "invalid argument specifier %c", c);
break;
}
}
+
+#undef ARGV
+
if (!c && argc > i) {
- mrb_raise(mrb, E_ARGUMENT_ERROR, "wrong number of arguments");
+ mrb_argnum_error(mrb, argc, argc_min, argc_max);
}
+
+finish:
va_end(ap);
return i;
}
@@ -758,7 +1355,7 @@ boot_defclass(mrb_state *mrb, struct RClass *super)
{
struct RClass *c;
- c = (struct RClass*)mrb_obj_alloc(mrb, MRB_TT_CLASS, mrb->class_class);
+ c = MRB_OBJ_ALLOC(mrb, MRB_TT_CLASS, mrb->class_class);
if (super) {
c->super = super;
mrb_field_write_barrier(mrb, (struct RBasic*)c, (struct RBasic*)super);
@@ -766,80 +1363,194 @@ boot_defclass(mrb_state *mrb, struct RClass *super)
else {
c->super = mrb->object_class;
}
- c->mt = kh_init(mt, mrb);
+ c->mt = mt_new(mrb);
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;
+ if (!mod->mt) {
+ mod->mt = mt_new(mrb);
+ }
+}
+
+static struct RClass*
+include_class_new(mrb_state *mrb, struct RClass *m, struct RClass *super)
+{
+ struct RClass *ic = 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;
+ int original_seen = FALSE;
+ int superclass_seen = FALSE;
- if (c->mt && c->mt == m->mt) {
- mrb_raise(mrb, E_ARGUMENT_ERROR, "cyclic include detected");
- }
+ if (c == ins_pos) original_seen = TRUE;
+ if (m->flags & MRB_FL_CLASS_IS_PREPENDED)
+ goto skip;
+
+ if (klass_mt && klass_mt == m->mt)
+ return -1;
+
+ p = c->super;
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 (c == p) original_seen = TRUE;
+ if (p->tt == MRB_TT_ICLASS) {
+ if (p->mt == m->mt) {
+ if (!superclass_seen && original_seen) {
+ ins_pos = p; /* move insert point */
+ }
+ goto skip;
}
- goto skip;
+ } else if (p->tt == MRB_TT_CLASS) {
+ if (!search_super) break;
+ superclass_seen = TRUE;
}
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);
+ m->flags |= MRB_FL_CLASS_IS_INHERITED;
ins_pos->super = ic;
mrb_field_write_barrier(mrb, (struct RBasic*)ins_pos, (struct RBasic*)ic);
ins_pos = ic;
skip:
m = m->super;
}
+ mc_clear(mrb);
+ return 0;
+}
+
+static int
+fix_include_module(mrb_state *mrb, struct RBasic *obj, void *data)
+{
+ struct RClass **m = (struct RClass**)data;
+
+ if (obj->tt == MRB_TT_ICLASS && obj->c == m[0] && !MRB_FLAG_TEST(obj, MRB_FL_CLASS_IS_ORIGIN)) {
+ struct RClass *ic = (struct RClass*)obj;
+ include_module_at(mrb, ic, ic, m[1], 1);
+ }
+ return MRB_EACH_OBJ_OK;
+}
+
+MRB_API void
+mrb_include_module(mrb_state *mrb, struct RClass *c, struct RClass *m)
+{
+ mrb_check_frozen(mrb, c);
+ if (include_module_at(mrb, c, find_origin(c), m, 1) < 0) {
+ mrb_raise(mrb, E_ARGUMENT_ERROR, "cyclic include detected");
+ }
+ if (c->tt == MRB_TT_MODULE && (c->flags & MRB_FL_CLASS_IS_INHERITED)) {
+ struct RClass *data[2];
+ data[0] = c;
+ data[1] = m;
+ mrb_objspace_each_objects(mrb, fix_include_module, data);
+ }
+}
+
+static int
+fix_prepend_module(mrb_state *mrb, struct RBasic *obj, void *data)
+{
+ struct RClass **m = (struct RClass**)data;
+ struct RClass *c = (struct RClass*)obj;
+
+ if (c->tt == MRB_TT_CLASS || c->tt == MRB_TT_MODULE) {
+ struct RClass *p = c->super;
+ struct RClass *ins_pos = c;
+ while (p) {
+ if (c == m[0]) break;
+ if (p == m[0]->super->c) {
+ ins_pos = c;
+ }
+ if (p->tt == MRB_TT_CLASS) break;
+ if (p->c == m[0]) {
+ include_module_at(mrb, ins_pos, ins_pos, m[1], 0);
+ break;
+ }
+ c = p;
+ p = p->super;
+ }
+ }
+ return MRB_EACH_OBJ_OK;
+}
+
+MRB_API void
+mrb_prepend_module(mrb_state *mrb, struct RClass *c, struct RClass *m)
+{
+ struct RClass *origin;
+
+ mrb_check_frozen(mrb, c);
+ if (!(c->flags & MRB_FL_CLASS_IS_PREPENDED)) {
+ struct RClass *c0;
+
+ if (c->tt == MRB_TT_ICLASS) {
+ c0 = c->c;
+ }
+ else {
+ c0 = c;
+ }
+ origin = MRB_OBJ_ALLOC(mrb, MRB_TT_ICLASS, c0);
+ origin->flags |= MRB_FL_CLASS_IS_ORIGIN | MRB_FL_CLASS_IS_INHERITED;
+ origin->super = c->super;
+ c->super = origin;
+ origin->mt = c->mt;
+ c->mt = NULL;
+ origin->iv = c->iv;
+ mrb_field_write_barrier(mrb, (struct RBasic*)c, (struct RBasic*)origin);
+ c->flags |= MRB_FL_CLASS_IS_PREPENDED;
+ }
+ if (include_module_at(mrb, c, c, m, 0) < 0) {
+ mrb_raise(mrb, E_ARGUMENT_ERROR, "cyclic prepend detected");
+ }
+ if (c->tt == MRB_TT_MODULE &&
+ (c->flags & (MRB_FL_CLASS_IS_INHERITED|MRB_FL_CLASS_IS_PREPENDED))) {
+ struct RClass *data[2];
+ data[0] = c;
+ data[1] = m;
+ mrb_objspace_each_objects(mrb, fix_prepend_module, data);
+ }
}
static mrb_value
-mrb_mod_append_features(mrb_state *mrb, mrb_value mod)
+mrb_mod_prepend_features(mrb_state *mrb, mrb_value mod)
{
- mrb_value klass;
+ struct RClass *c;
mrb_check_type(mrb, mod, MRB_TT_MODULE);
- mrb_get_args(mrb, "C", &klass);
- mrb_include_module(mrb, mrb_class_ptr(klass), mrb_class_ptr(mod));
+ mrb_get_args(mrb, "c", &c);
+ mrb_prepend_module(mrb, c, mrb_class_ptr(mod));
return mod;
}
static mrb_value
-mrb_mod_include(mrb_state *mrb, mrb_value klass)
+mrb_mod_append_features(mrb_state *mrb, mrb_value mod)
{
- 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], "append_features", 1, klass);
- mrb_funcall(mrb, argv[argc], "included", 1, klass);
- }
+ struct RClass *c;
- return klass;
+ mrb_check_type(mrb, mod, MRB_TT_MODULE);
+ mrb_get_args(mrb, "c", &c);
+ mrb_include_module(mrb, c, mrb_class_ptr(mod));
+ return mod;
}
/* 15.2.2.4.28 */
@@ -884,15 +1595,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_FL_CLASS_IS_PREPENDED)) {
mrb_ary_push(mrb, result, mrb_obj_value(c));
}
c = c->super;
@@ -904,82 +1612,26 @@ mrb_mod_ancestors(mrb_state *mrb, mrb_value self)
static mrb_value
mrb_mod_extend_object(mrb_state *mrb, mrb_value mod)
{
- mrb_value obj;
+ mrb_value obj = mrb_get_arg1(mrb);
mrb_check_type(mrb, mod, MRB_TT_MODULE);
- mrb_get_args(mrb, "o", &obj);
mrb_include_module(mrb, mrb_class_ptr(mrb_singleton_class(mrb, obj)), mrb_class_ptr(mod));
return mod;
}
static mrb_value
-mrb_mod_included_modules(mrb_state *mrb, mrb_value self)
-{
- mrb_value result;
- struct RClass *c = mrb_class_ptr(self);
-
- result = mrb_ary_new(mrb);
- while (c) {
- if (c->tt == MRB_TT_ICLASS) {
- mrb_ary_push(mrb, result, mrb_obj_value(c->c));
- }
- c = c->super;
- }
-
- return result;
-}
-
-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;
}
-mrb_value mrb_class_instance_method_list(mrb_state*, mrb_bool, struct RClass*, int);
-
-/* 15.2.2.4.33 */
-/*
- * call-seq:
- * mod.instance_methods(include_super=true) -> array
- *
- * Returns an array containing the names of the public and protected instance
- * methods in the receiver. For a module, these are the public and protected methods;
- * for a class, they are the instance (not singleton) methods. With no
- * argument, or with an argument that is <code>false</code>, the
- * instance methods in <i>mod</i> are returned, otherwise the methods
- * in <i>mod</i> and <i>mod</i>'s superclasses are returned.
- *
- * module A
- * def method1() end
- * end
- * class B
- * def method2() end
- * end
- * class C < B
- * def method3() end
- * end
- *
- * A.instance_methods #=> [:method1]
- * B.instance_methods(false) #=> [:method2]
- * C.instance_methods(false) #=> [:method3]
- * C.instance_methods(true).length #=> 43
- */
-
-static mrb_value
-mrb_mod_instance_methods(mrb_state *mrb, mrb_value mod)
-{
- struct RClass *c = mrb_class_ptr(mod);
- mrb_bool recur = TRUE;
- mrb_get_args(mrb, "|b", &recur);
- return mrb_class_instance_method_list(mrb, recur, c, 0);
-}
-
/* implementation of module_eval/class_eval */
mrb_value mrb_mod_module_eval(mrb_state*, mrb_value);
@@ -989,35 +1641,44 @@ mrb_mod_dummy_visibility(mrb_state *mrb, mrb_value mod)
return mod;
}
-MRB_API mrb_value
-mrb_singleton_class(mrb_state *mrb, mrb_value v)
+/* returns mrb_class_ptr(mrb_singleton_class()) */
+/* except that it return NULL for immediate values */
+MRB_API struct RClass*
+mrb_singleton_class_ptr(mrb_state *mrb, mrb_value v)
{
struct RBasic *obj;
switch (mrb_type(v)) {
case MRB_TT_FALSE:
if (mrb_nil_p(v))
- return mrb_obj_value(mrb->nil_class);
- return mrb_obj_value(mrb->false_class);
+ return mrb->nil_class;
+ return mrb->false_class;
case MRB_TT_TRUE:
- return mrb_obj_value(mrb->true_class);
+ return mrb->true_class;
case MRB_TT_CPTR:
- return mrb_obj_value(mrb->object_class);
case MRB_TT_SYMBOL:
- case MRB_TT_FIXNUM:
+ case MRB_TT_INTEGER:
+#ifndef MRB_NO_FLOAT
case MRB_TT_FLOAT:
- mrb_raise(mrb, E_TYPE_ERROR, "can't define singleton");
- return mrb_nil_value(); /* not reached */
+#endif
+ return NULL;
default:
break;
}
obj = mrb_basic_ptr(v);
prepare_singleton_class(mrb, obj);
- if (mrb->c && mrb->c->ci && mrb->c->ci->target_class) {
- mrb_obj_iv_set(mrb, (struct RObject*)obj->c, mrb_intern_lit(mrb, "__outer__"),
- mrb_obj_value(mrb->c->ci->target_class));
+ return obj->c;
+}
+
+MRB_API mrb_value
+mrb_singleton_class(mrb_state *mrb, mrb_value v)
+{
+ struct RClass *c = mrb_singleton_class_ptr(mrb, v);
+
+ if (c == NULL) {
+ mrb_raise(mrb, E_TYPE_ERROR, "can't define singleton");
}
- return mrb_obj_value(obj->c);
+ return mrb_obj_value(c);
}
MRB_API void
@@ -1028,102 +1689,213 @@ mrb_define_singleton_method(mrb_state *mrb, struct RObject *o, const char *name,
}
MRB_API void
+mrb_define_singleton_method_id(mrb_state *mrb, struct RObject *o, mrb_sym name, mrb_func_t func, mrb_aspec aspec)
+{
+ prepare_singleton_class(mrb, (struct RBasic*)o);
+ mrb_define_method_id(mrb, o->c, name, func, aspec);
+}
+
+MRB_API void
mrb_define_class_method(mrb_state *mrb, struct RClass *c, const char *name, mrb_func_t func, mrb_aspec aspec)
{
mrb_define_singleton_method(mrb, (struct RObject*)c, name, func, aspec);
}
MRB_API void
+mrb_define_class_method_id(mrb_state *mrb, struct RClass *c, mrb_sym name, mrb_func_t func, mrb_aspec aspec)
+{
+ mrb_define_singleton_method_id(mrb, (struct RObject*)c, name, func, aspec);
+}
+
+MRB_API void
+mrb_define_module_function_id(mrb_state *mrb, struct RClass *c, mrb_sym name, mrb_func_t func, mrb_aspec aspec)
+{
+ mrb_define_class_method_id(mrb, c, name, func, aspec);
+ mrb_define_method_id(mrb, c, name, func, aspec);
+}
+
+MRB_API void
mrb_define_module_function(mrb_state *mrb, struct RClass *c, const char *name, mrb_func_t func, mrb_aspec aspec)
{
- mrb_define_class_method(mrb, c, name, func, aspec);
- mrb_define_method(mrb, c, name, func, aspec);
+ mrb_define_module_function_id(mrb, c, mrb_intern_cstr(mrb, name), func, aspec);
+}
+
+#ifndef MRB_NO_METHOD_CACHE
+static void
+mc_clear(mrb_state *mrb)
+{
+ memset(mrb->cache, 0, MRB_METHOD_CACHE_SIZE*sizeof(mrb->cache[0]));
}
-MRB_API struct RProc*
+void
+mrb_mc_clear_by_class(mrb_state *mrb, struct RClass *c)
+{
+ struct mrb_cache_entry *mc = mrb->cache;
+ int i;
+
+ if (c->flags & MRB_FL_CLASS_IS_INHERITED) {
+ mc_clear(mrb);
+ return;
+ }
+ for (i=0; i<MRB_METHOD_CACHE_SIZE; i++) {
+ if (mc[i].c == c) mc[i].c = 0;
+ }
+}
+#endif
+
+MRB_API mrb_method_t
mrb_method_search_vm(mrb_state *mrb, struct RClass **cp, mrb_sym mid)
{
- khiter_t k;
- struct RProc *m;
+ mrb_method_t m;
struct RClass *c = *cp;
+#ifndef MRB_NO_METHOD_CACHE
+ struct RClass *oc = c;
+ int h = kh_int_hash_func(mrb, ((intptr_t)oc) ^ mid) & (MRB_METHOD_CACHE_SIZE-1);
+ struct mrb_cache_entry *mc = &mrb->cache[h];
+
+ if (mc->c == c && mc->mid == mid) {
+ *cp = mc->c0;
+ return mc->m;
+ }
+#endif
while (c) {
- khash_t(mt) *h = c->mt;
+ mt_tbl *h = c->mt;
if (h) {
- k = kh_get(mt, mrb, h, mid);
- if (k != kh_end(h)) {
- m = kh_value(h, k);
- if (!m) break;
+ struct mt_elem *e = mt_get(mrb, h, mid);
+ if (e) {
+ if (e->ptr.proc == 0) break;
*cp = c;
+ if (e->func_p) {
+ MRB_METHOD_FROM_FUNC(m, e->ptr.func);
+ }
+ else {
+ MRB_METHOD_FROM_PROC(m, e->ptr.proc);
+ }
+ if (e->noarg_p) {
+ MRB_METHOD_NOARG_SET(m);
+ }
+#ifndef MRB_NO_METHOD_CACHE
+ mc->c = oc;
+ mc->c0 = c;
+ mc->mid = mid;
+ mc->m = m;
+#endif
return m;
}
}
c = c->super;
}
- return NULL; /* no method */
+ MRB_METHOD_FROM_PROC(m, NULL);
+ return m; /* no method */
}
-MRB_API struct RProc*
+MRB_API mrb_method_t
mrb_method_search(mrb_state *mrb, struct RClass* c, mrb_sym mid)
{
- struct RProc *m;
+ mrb_method_t m;
m = mrb_method_search_vm(mrb, &c, mid);
- if (!m) {
- mrb_value inspect = mrb_funcall(mrb, mrb_obj_value(c), "inspect", 0);
- if (RSTRING_LEN(inspect) > 64) {
- inspect = mrb_any_to_s(mrb, mrb_obj_value(c));
- }
- mrb_name_error(mrb, mid, "undefined method '%S' for class %S",
- mrb_sym2str(mrb, mid), inspect);
+ if (MRB_METHOD_UNDEF_P(m)) {
+ mrb_name_error(mrb, mid, "undefined method '%n' for class %C", mid, c);
}
return m;
}
+#define ONSTACK_ALLOC_MAX 32
+
+static mrb_sym
+prepare_name_common(mrb_state *mrb, mrb_sym sym, const char *prefix, const char *suffix)
+{
+ char onstack[ONSTACK_ALLOC_MAX];
+ mrb_int sym_len;
+ const char *sym_str = mrb_sym_name_len(mrb, sym, &sym_len);
+ size_t prefix_len = prefix ? strlen(prefix) : 0;
+ size_t suffix_len = suffix ? strlen(suffix) : 0;
+ size_t name_len = sym_len + prefix_len + suffix_len;
+ char *buf = name_len > sizeof(onstack) ? (char *)mrb_alloca(mrb, name_len) : onstack;
+ char *p = buf;
+
+ if (prefix_len > 0) {
+ memcpy(p, prefix, prefix_len);
+ p += prefix_len;
+ }
+
+ memcpy(p, sym_str, sym_len);
+ p += sym_len;
+
+ if (suffix_len > 0) {
+ memcpy(p, suffix, suffix_len);
+ p += suffix_len;
+ }
+
+ return mrb_intern(mrb, buf, name_len);
+}
+
static mrb_value
-attr_reader(mrb_state *mrb, mrb_value obj)
+prepare_ivar_name(mrb_state *mrb, mrb_sym sym)
{
- mrb_value name = mrb_proc_cfunc_env_get(mrb, 0);
- return mrb_iv_get(mrb, obj, to_sym(mrb, name));
+ sym = prepare_name_common(mrb, sym, "@", NULL);
+ mrb_iv_name_sym_check(mrb, sym);
+ return mrb_symbol_value(sym);
+}
+
+static mrb_sym
+prepare_writer_name(mrb_state *mrb, mrb_sym sym)
+{
+ return prepare_name_common(mrb, sym, NULL, "=");
}
static mrb_value
-mrb_mod_attr_reader(mrb_state *mrb, mrb_value mod)
+mod_attr_define(mrb_state *mrb, mrb_value mod, mrb_value (*accessor)(mrb_state *, mrb_value), mrb_sym (*access_name)(mrb_state *, mrb_sym))
{
struct RClass *c = mrb_class_ptr(mod);
- mrb_value *argv;
+ const mrb_value *argv;
mrb_int argc, i;
int ai;
mrb_get_args(mrb, "*", &argv, &argc);
ai = mrb_gc_arena_save(mrb);
for (i=0; i<argc; i++) {
- mrb_value name, str;
- mrb_sym method, sym;
+ mrb_value name;
+ mrb_sym method;
+ struct RProc *p;
+ mrb_method_t m;
method = to_sym(mrb, argv[i]);
- name = mrb_sym2str(mrb, method);
- str = mrb_str_buf_new(mrb, RSTRING_LEN(name)+1);
- mrb_str_cat_lit(mrb, str, "@");
- mrb_str_cat_str(mrb, str, name);
- sym = mrb_intern_str(mrb, str);
- mrb_iv_check(mrb, sym);
- name = mrb_symbol_value(sym);
- mrb_define_method_raw(mrb, c, method,
- mrb_proc_new_cfunc_with_env(mrb, attr_reader, 1, &name));
+ name = prepare_ivar_name(mrb, method);
+ if (access_name) {
+ method = access_name(mrb, method);
+ }
+
+ p = mrb_proc_new_cfunc_with_env(mrb, accessor, 1, &name);
+ MRB_METHOD_FROM_PROC(m, p);
+ mrb_define_method_raw(mrb, c, method, m);
mrb_gc_arena_restore(mrb, ai);
}
return mrb_nil_value();
}
static mrb_value
+attr_reader(mrb_state *mrb, mrb_value obj)
+{
+ mrb_value name = mrb_proc_cfunc_env_get(mrb, 0);
+ return mrb_iv_get(mrb, obj, to_sym(mrb, name));
+}
+
+static mrb_value
+mrb_mod_attr_reader(mrb_state *mrb, mrb_value mod)
+{
+ return mod_attr_define(mrb, mod, attr_reader, NULL);
+}
+
+static mrb_value
attr_writer(mrb_state *mrb, mrb_value obj)
{
mrb_value name = mrb_proc_cfunc_env_get(mrb, 0);
- mrb_value val;
+ mrb_value val = mrb_get_arg1(mrb);
- mrb_get_args(mrb, "o", &val);
mrb_iv_set(mrb, obj, to_sym(mrb, name), val);
return val;
}
@@ -1131,39 +1903,7 @@ attr_writer(mrb_state *mrb, mrb_value obj)
static mrb_value
mrb_mod_attr_writer(mrb_state *mrb, mrb_value mod)
{
- struct RClass *c = mrb_class_ptr(mod);
- mrb_value *argv;
- mrb_int argc, i;
- int ai;
-
- mrb_get_args(mrb, "*", &argv, &argc);
- ai = mrb_gc_arena_save(mrb);
- for (i=0; i<argc; i++) {
- mrb_value name, str, attr;
- mrb_sym method, sym;
-
- method = to_sym(mrb, argv[i]);
-
- /* prepare iv name (@name) */
- name = mrb_sym2str(mrb, method);
- str = mrb_str_buf_new(mrb, RSTRING_LEN(name)+1);
- mrb_str_cat_lit(mrb, str, "@");
- mrb_str_cat_str(mrb, str, name);
- sym = mrb_intern_str(mrb, str);
- mrb_iv_check(mrb, sym);
- attr = mrb_symbol_value(sym);
-
- /* prepare method name (name=) */
- str = mrb_str_buf_new(mrb, RSTRING_LEN(str));
- mrb_str_cat_str(mrb, str, name);
- mrb_str_cat_lit(mrb, str, "=");
- method = mrb_intern_str(mrb, str);
-
- mrb_define_method_raw(mrb, c, method,
- mrb_proc_new_cfunc_with_env(mrb, attr_writer, 1, &attr));
- mrb_gc_arena_restore(mrb, ai);
- }
- return mrb_nil_value();
+ return mod_attr_define(mrb, mod, attr_writer, prepare_writer_name);
}
static mrb_value
@@ -1177,6 +1917,9 @@ mrb_instance_alloc(mrb_state *mrb, mrb_value cv)
mrb_raise(mrb, E_TYPE_ERROR, "can't create instance of singleton class");
if (ttype == 0) ttype = MRB_TT_OBJECT;
+ if (ttype <= MRB_TT_CPTR) {
+ mrb_raisef(mrb, E_TYPE_ERROR, "can't create instance of %v", cv);
+ }
o = (struct RObject*)mrb_obj_alloc(mrb, ttype, c);
return mrb_obj_value(o);
}
@@ -1185,25 +1928,28 @@ mrb_instance_alloc(mrb_state *mrb, mrb_value cv)
* call-seq:
* class.new(args, ...) -> obj
*
- * Calls <code>allocate</code> to create a new object of
- * <i>class</i>'s class, then invokes that object's
- * <code>initialize</code> method, passing it <i>args</i>.
- * This is the method that ends up getting called whenever
- * an object is constructed using .new.
+ * Creates a new object of <i>class</i>'s class, then
+ * invokes that object's <code>initialize</code> method,
+ * passing it <i>args</i>. This is the method that ends
+ * up getting called whenever an object is constructed using
+ * `.new`.
*
*/
-MRB_API mrb_value
+mrb_value
mrb_instance_new(mrb_state *mrb, mrb_value cv)
{
mrb_value obj, blk;
- mrb_value *argv;
+ const mrb_value *argv;
mrb_int argc;
+ mrb_sym init;
- mrb_get_args(mrb, "*&", &argv, &argc, &blk);
+ mrb_get_args(mrb, "*!&", &argv, &argc, &blk);
obj = mrb_instance_alloc(mrb, cv);
- mrb_funcall_with_block(mrb, obj, mrb_intern_lit(mrb, "initialize"), argc, argv, blk);
-
+ init = MRB_SYM(initialize);
+ if (!mrb_func_basic_p(mrb, obj, init, mrb_bob_init)) {
+ mrb_funcall_with_block(mrb, obj, init, argc, argv, blk);
+ }
return obj;
}
@@ -1211,10 +1957,13 @@ MRB_API mrb_value
mrb_obj_new(mrb_state *mrb, struct RClass *c, mrb_int argc, const mrb_value *argv)
{
mrb_value obj;
+ mrb_sym mid;
obj = mrb_instance_alloc(mrb, mrb_obj_value(c));
- mrb_funcall_argv(mrb, obj, mrb_intern_lit(mrb, "initialize"), argc, argv);
-
+ mid = MRB_SYM(initialize);
+ if (!mrb_func_basic_p(mrb, obj, mid, mrb_bob_init)) {
+ mrb_funcall_argv(mrb, obj, mid, argc, argv);
+ }
return obj;
}
@@ -1236,13 +1985,20 @@ mrb_class_new_class(mrb_state *mrb, mrb_value cv)
mrb_int n;
mrb_value super, blk;
mrb_value new_class;
+ mrb_sym mid;
n = mrb_get_args(mrb, "|C&", &super, &blk);
if (n == 0) {
super = mrb_obj_value(mrb->object_class);
}
new_class = mrb_obj_value(mrb_class_new(mrb, mrb_class_ptr(super)));
- mrb_funcall_with_block(mrb, new_class, mrb_intern_lit(mrb, "initialize"), n, &super, blk);
+ mid = MRB_SYM(initialize);
+ if (mrb_func_basic_p(mrb, new_class, mid, mrb_class_initialize)) {
+ mrb_class_initialize(mrb, new_class);
+ }
+ else {
+ mrb_funcall_with_block(mrb, new_class, mid, n, &super, blk);
+ }
mrb_class_inherited(mrb, mrb_class_ptr(super), mrb_class_ptr(new_class));
return new_class;
}
@@ -1253,9 +2009,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);
@@ -1273,92 +2029,55 @@ mrb_bob_not(mrb_state *mrb, mrb_value cv)
return mrb_bool_value(!mrb_test(cv));
}
-/* 15.3.1.3.30 */
+/* 15.3.1.3.1 */
+/* 15.3.1.3.10 */
+/* 15.3.1.3.11 */
/*
* call-seq:
- * obj.method_missing(symbol [, *args] ) -> result
+ * obj == other -> true or false
+ * obj.equal?(other) -> true or false
+ * obj.eql?(other) -> true or false
*
- * Invoked by Ruby when <i>obj</i> is sent a message it cannot handle.
- * <i>symbol</i> is the symbol for the method called, and <i>args</i>
- * are any arguments that were passed to it. By default, the interpreter
- * raises an error when this method is called. However, it is possible
- * to override the method to provide more dynamic behavior.
- * If it is decided that a particular method should not be handled, then
- * <i>super</i> should be called, so that ancestors can pick up the
- * missing method.
- * The example below creates
- * a class <code>Roman</code>, which responds to methods with names
- * consisting of roman numerals, returning the corresponding integer
- * values.
+ * Equality---At the <code>Object</code> level, <code>==</code> returns
+ * <code>true</code> only if <i>obj</i> and <i>other</i> are the
+ * same object. Typically, this method is overridden in descendant
+ * classes to provide class-specific meaning.
*
- * class Roman
- * def romanToInt(str)
- * # ...
- * end
- * def method_missing(methId)
- * str = methId.id2name
- * romanToInt(str)
- * end
- * end
+ * Unlike <code>==</code>, the <code>equal?</code> method should never be
+ * overridden by subclasses: it is used to determine object identity
+ * (that is, <code>a.equal?(b)</code> iff <code>a</code> is the same
+ * object as <code>b</code>).
*
- * r = Roman.new
- * r.iv #=> 4
- * r.xxiii #=> 23
- * r.mm #=> 2000
+ * The <code>eql?</code> method returns <code>true</code> if
+ * <i>obj</i> and <i>anObject</i> have the same value. Used by
+ * <code>Hash</code> to test members for equality. For objects of
+ * class <code>Object</code>, <code>eql?</code> is synonymous with
+ * <code>==</code>. Subclasses normally continue this tradition, but
+ * there are exceptions. <code>Numeric</code> types, for example,
+ * perform type conversion across <code>==</code>, but not across
+ * <code>eql?</code>, so:
+ *
+ * 1 == 1.0 #=> true
+ * 1.eql? 1.0 #=> false
*/
-static mrb_value
-mrb_bob_missing(mrb_state *mrb, mrb_value mod)
+mrb_value
+mrb_obj_equal_m(mrb_state *mrb, mrb_value self)
{
- mrb_sym name;
- mrb_value *a;
- mrb_int alen;
- mrb_sym inspect;
- mrb_value repr;
-
- mrb_get_args(mrb, "n*", &name, &a, &alen);
-
- inspect = mrb_intern_lit(mrb, "inspect");
- if (mrb->c->ci > mrb->c->cibase && mrb->c->ci[-1].mid == inspect) {
- /* method missing in inspect; avoid recursion */
- repr = mrb_any_to_s(mrb, mod);
- }
- else if (mrb_respond_to(mrb, mod, inspect) && mrb->c->ci - mrb->c->cibase < 64) {
- repr = mrb_funcall_argv(mrb, mod, inspect, 0, 0);
- if (RSTRING_LEN(repr) > 64) {
- repr = mrb_any_to_s(mrb, mod);
- }
- }
- else {
- repr = mrb_any_to_s(mrb, mod);
- }
+ mrb_value arg = mrb_get_arg1(mrb);
- mrb_no_method_error(mrb, name, alen, a, "undefined method '%S' for %S", mrb_sym2str(mrb, name), repr);
- /* not reached */
- return mrb_nil_value();
+ return mrb_bool_value(mrb_obj_equal(mrb, self, arg));
}
MRB_API mrb_bool
mrb_obj_respond_to(mrb_state *mrb, struct RClass* c, mrb_sym mid)
{
- khiter_t k;
-
- while (c) {
- khash_t(mt) *h = c->mt;
+ mrb_method_t m;
- if (h) {
- k = kh_get(mt, mrb, h, mid);
- if (k != kh_end(h)) {
- if (kh_value(h, k)) {
- return TRUE; /* method exists */
- }
- else {
- return FALSE; /* undefined method */
- }
- }
- }
- c = c->super;
+ m = mrb_method_search_vm(mrb, &c, mid);
+ if (MRB_METHOD_UNDEF_P(m)) {
+ return FALSE;
}
- return FALSE; /* no method */
+ return TRUE;
}
MRB_API mrb_bool
@@ -1371,49 +2090,27 @@ MRB_API mrb_value
mrb_class_path(mrb_state *mrb, struct RClass *c)
{
mrb_value path;
- const char *name;
- mrb_sym classpath = mrb_intern_lit(mrb, "__classpath__");
+ mrb_sym nsym = MRB_SYM(__classname__);
- path = mrb_obj_iv_get(mrb, (struct RObject*)c, classpath);
+ path = mrb_obj_iv_get(mrb, (struct RObject*)c, nsym);
if (mrb_nil_p(path)) {
- struct RClass *outer = mrb_class_outer_module(mrb, c);
- mrb_sym sym = mrb_class_sym(mrb, c, outer);
- mrb_int len;
-
- if (sym == 0) {
- return mrb_nil_value();
- }
- else if (outer && outer != mrb->object_class) {
- mrb_value base = mrb_class_path(mrb, outer);
- path = mrb_str_buf_new(mrb, 0);
- if (mrb_nil_p(base)) {
- mrb_str_cat_lit(mrb, path, "#<Class:");
- mrb_str_concat(mrb, path, mrb_ptr_to_str(mrb, outer));
- mrb_str_cat_lit(mrb, path, ">");
- }
- else {
- mrb_str_concat(mrb, path, base);
- }
- mrb_str_cat_lit(mrb, path, "::");
- name = mrb_sym2name_len(mrb, sym, &len);
- mrb_str_cat(mrb, path, name, len);
- }
- else {
- name = mrb_sym2name_len(mrb, sym, &len);
- path = mrb_str_new(mrb, name, len);
- }
- mrb_obj_iv_set(mrb, (struct RObject*)c, classpath, path);
+ /* no name (yet) */
+ return mrb_class_find_path(mrb, c);
}
- return path;
+ else if (mrb_symbol_p(path)) {
+ /* toplevel class/module */
+ return mrb_sym_str(mrb, mrb_symbol(path));
+ }
+ return mrb_str_dup(mrb, path);
}
-MRB_API struct RClass *
+MRB_API struct RClass*
mrb_class_real(struct RClass* cl)
{
- if (cl == 0)
- return NULL;
+ if (cl == 0) return NULL;
while ((cl->tt == MRB_TT_SCLASS) || (cl->tt == MRB_TT_ICLASS)) {
cl = cl->super;
+ if (cl == 0) return NULL;
}
return cl;
}
@@ -1421,13 +2118,11 @@ mrb_class_real(struct RClass* cl)
MRB_API const char*
mrb_class_name(mrb_state *mrb, struct RClass* c)
{
- mrb_value path = mrb_class_path(mrb, c);
- if (mrb_nil_p(path)) {
- path = mrb_str_new_lit(mrb, "#<Class:");
- mrb_str_concat(mrb, path, mrb_ptr_to_str(mrb, c));
- mrb_str_cat_lit(mrb, path, ">");
- }
- return RSTRING_PTR(path);
+ mrb_value name;
+
+ if (c == NULL) return NULL;
+ name = class_name_str(mrb, c);
+ return RSTRING_PTR(name);
}
MRB_API const char*
@@ -1446,7 +2141,7 @@ static void
mrb_check_inheritable(mrb_state *mrb, struct RClass *super)
{
if (super->tt != MRB_TT_CLASS) {
- mrb_raisef(mrb, E_TYPE_ERROR, "superclass must be a Class (%S given)", mrb_obj_value(super));
+ mrb_raisef(mrb, E_TYPE_ERROR, "superclass must be a Class (%C given)", super);
}
if (super->tt == MRB_TT_SCLASS) {
mrb_raise(mrb, E_TYPE_ERROR, "can't make subclass of singleton class");
@@ -1485,9 +2180,8 @@ mrb_class_new(mrb_state *mrb, struct RClass *super)
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);
-
+ struct RClass *m = MRB_OBJ_ALLOC(mrb, MRB_TT_MODULE, mrb->module_class);
+ boot_initmod(mrb, m);
return m;
}
@@ -1501,7 +2195,7 @@ mrb_module_new(mrb_state *mrb)
* called with an explicit receiver, as <code>class</code> is also a
* reserved word in Ruby.
*
- * 1.class #=> Fixnum
+ * 1.class #=> Integer
* self.class #=> Object
*/
@@ -1514,13 +2208,35 @@ mrb_obj_class(mrb_state *mrb, mrb_value obj)
MRB_API void
mrb_alias_method(mrb_state *mrb, struct RClass *c, mrb_sym a, mrb_sym b)
{
- struct RProc *m = mrb_method_search(mrb, c, b);
+ if (a == b) return;
+ mrb_method_t m = mrb_method_search(mrb, c, b);
- mrb_define_method_vm(mrb, c, a, mrb_obj_value(m));
+ if (!MRB_METHOD_CFUNC_P(m)) {
+ struct RProc *p = MRB_METHOD_PROC(m);
+
+ if (MRB_PROC_ENV_P(p)) {
+ MRB_PROC_ENV(p)->mid = b;
+ }
+ else if (p->color != MRB_GC_RED) {
+ struct RClass *tc = MRB_PROC_TARGET_CLASS(p);
+ struct REnv *e = MRB_OBJ_ALLOC(mrb, MRB_TT_ENV, NULL);
+
+ e->mid = b;
+ if (tc) {
+ e->c = tc;
+ mrb_field_write_barrier(mrb, (struct RBasic*)e, (struct RBasic*)tc);
+ }
+ p->e.env = e;
+ p->flags |= MRB_PROC_ENVSET;
+ mrb_field_write_barrier(mrb, (struct RBasic*)p, (struct RBasic*)e);
+ }
+ }
+ mrb_define_method_raw(mrb, c, a, m);
}
/*!
* Defines an alias of a method.
+ * \param mrb the mruby state
* \param klass the class which the original method belongs to
* \param name1 a new name for the method
* \param name2 the original name of the method
@@ -1531,6 +2247,12 @@ mrb_define_alias(mrb_state *mrb, struct RClass *klass, const char *name1, const
mrb_alias_method(mrb, klass, mrb_intern_cstr(mrb, name1), mrb_intern_cstr(mrb, name2));
}
+MRB_API void
+mrb_define_alias_id(mrb_state *mrb, struct RClass *klass, mrb_sym a, mrb_sym b)
+{
+ mrb_alias_method(mrb, klass, a, b);
+}
+
/*
* call-seq:
* mod.to_s -> string
@@ -1540,60 +2262,28 @@ mrb_define_alias(mrb_state *mrb, struct RClass *klass, const char *name1, const
* show information on the thing we're attached to as well.
*/
-static mrb_value
+mrb_value
mrb_mod_to_s(mrb_state *mrb, mrb_value klass)
{
- mrb_value str;
-
- if (mrb_type(klass) == MRB_TT_SCLASS) {
- mrb_value v = mrb_iv_get(mrb, klass, mrb_intern_lit(mrb, "__attached__"));
+ if (mrb_sclass_p(klass)) {
+ mrb_value v = mrb_iv_get(mrb, klass, MRB_SYM(__attached__));
+ mrb_value str = mrb_str_new_lit(mrb, "#<Class:");
- str = mrb_str_new_lit(mrb, "#<Class:");
-
- switch (mrb_type(v)) {
- case MRB_TT_CLASS:
- case MRB_TT_MODULE:
- case MRB_TT_SCLASS:
- mrb_str_append(mrb, str, mrb_inspect(mrb, v));
- break;
- default:
- mrb_str_append(mrb, str, mrb_any_to_s(mrb, v));
- break;
+ if (class_ptr_p(v)) {
+ mrb_str_cat_str(mrb, str, mrb_inspect(mrb, v));
+ }
+ else {
+ mrb_str_cat_str(mrb, str, mrb_any_to_s(mrb, v));
}
return mrb_str_cat_lit(mrb, str, ">");
}
else {
- struct RClass *c;
- mrb_value path;
-
- str = mrb_str_buf_new(mrb, 32);
- c = mrb_class_ptr(klass);
- path = mrb_class_path(mrb, c);
-
- if (mrb_nil_p(path)) {
- switch (mrb_type(klass)) {
- case MRB_TT_CLASS:
- mrb_str_cat_lit(mrb, str, "#<Class:");
- break;
-
- case MRB_TT_MODULE:
- mrb_str_cat_lit(mrb, str, "#<Module:");
- break;
-
- default:
- /* Shouldn't be happened? */
- mrb_str_cat_lit(mrb, str, "#<??????:");
- break;
- }
- mrb_str_concat(mrb, str, mrb_ptr_to_str(mrb, c));
- return mrb_str_cat_lit(mrb, str, ">");
- }
- else {
- return path;
- }
+ return class_name_str(mrb, mrb_class_ptr(klass));
}
}
+void mrb_method_added(mrb_state *mrb, struct RClass *c, mrb_sym mid);
+
static mrb_value
mrb_mod_alias(mrb_state *mrb, mrb_value mod)
{
@@ -1602,21 +2292,26 @@ mrb_mod_alias(mrb_state *mrb, mrb_value mod)
mrb_get_args(mrb, "nn", &new_name, &old_name);
mrb_alias_method(mrb, c, new_name, old_name);
- return mrb_nil_value();
+ mrb_method_added(mrb, c, new_name);
+ return mod;
}
static void
undef_method(mrb_state *mrb, struct RClass *c, mrb_sym a)
{
- mrb_value m;
+ mrb_method_t m;
+ MRB_METHOD_FROM_PROC(m, NULL);
+ mrb_define_method_raw(mrb, c, a, m);
+}
+
+MRB_API void
+mrb_undef_method_id(mrb_state *mrb, struct RClass *c, mrb_sym a)
+{
if (!mrb_obj_respond_to(mrb, c, a)) {
- mrb_name_error(mrb, a, "undefined method '%S' for class '%S'", mrb_sym2str(mrb, a), mrb_obj_value(c));
- }
- else {
- SET_PROC_VALUE(m, 0);
- mrb_define_method_vm(mrb, c, a, m);
+ mrb_name_error(mrb, a, "undefined method '%n' for class '%C'", a, c);
}
+ undef_method(mrb, c, a);
}
MRB_API void
@@ -1626,187 +2321,155 @@ mrb_undef_method(mrb_state *mrb, struct RClass *c, const char *name)
}
MRB_API void
+mrb_undef_class_method_id(mrb_state *mrb, struct RClass *c, mrb_sym name)
+{
+ mrb_undef_method_id(mrb, mrb_class_ptr(mrb_singleton_class(mrb, mrb_obj_value(c))), name);
+}
+
+MRB_API void
mrb_undef_class_method(mrb_state *mrb, struct RClass *c, const char *name)
{
mrb_undef_method(mrb, mrb_class_ptr(mrb_singleton_class(mrb, mrb_obj_value(c))), name);
}
+MRB_API void
+mrb_remove_method(mrb_state *mrb, struct RClass *c, mrb_sym mid)
+{
+ mt_tbl *h;
+
+ MRB_CLASS_ORIGIN(c);
+ h = c->mt;
+
+ if (h && mt_del(mrb, h, mid)) return;
+ mrb_name_error(mrb, mid, "method '%n' not defined in %C", mid, c);
+}
+
static mrb_value
mrb_mod_undef(mrb_state *mrb, mrb_value mod)
{
struct RClass *c = mrb_class_ptr(mod);
mrb_int argc;
- mrb_value *argv;
+ const mrb_value *argv;
mrb_get_args(mrb, "*", &argv, &argc);
while (argc--) {
- undef_method(mrb, c, mrb_symbol(*argv));
+ mrb_undef_method_id(mrb, c, to_sym(mrb, *argv));
argv++;
}
return mrb_nil_value();
}
-static mrb_value
-mod_define_method(mrb_state *mrb, mrb_value self)
+static void
+check_const_name_sym(mrb_state *mrb, mrb_sym id)
{
- struct RClass *c = mrb_class_ptr(self);
- struct RProc *p;
- mrb_sym mid;
- mrb_value blk;
-
- mrb_get_args(mrb, "n&", &mid, &blk);
- if (mrb_nil_p(blk)) {
- mrb_raise(mrb, E_ARGUMENT_ERROR, "no block given");
+ mrb_int len;
+ const char *name = mrb_sym_name_len(mrb, id, &len);
+ if (!mrb_const_name_p(mrb, name, len)) {
+ mrb_name_error(mrb, id, "wrong constant name %n", id);
}
- p = (struct RProc*)mrb_obj_alloc(mrb, MRB_TT_PROC, mrb->proc_class);
- mrb_proc_copy(p, mrb_proc_ptr(blk));
- p->flags |= MRB_PROC_STRICT;
- mrb_define_method_raw(mrb, c, mid, p);
- return mrb_symbol_value(mid);
}
-static void
-check_cv_name_str(mrb_state *mrb, mrb_value str)
+static mrb_value
+mrb_mod_const_defined(mrb_state *mrb, mrb_value mod)
{
- const char *s = RSTRING_PTR(str);
- mrb_int len = RSTRING_LEN(str);
+ mrb_sym id;
+ mrb_bool inherit = TRUE;
- if (len < 3 || !(s[0] == '@' && s[1] == '@')) {
- mrb_name_error(mrb, mrb_intern_str(mrb, str), "`%S' is not allowed as a class variable name", str);
+ mrb_get_args(mrb, "n|b", &id, &inherit);
+ check_const_name_sym(mrb, id);
+ if (inherit) {
+ return mrb_bool_value(mrb_const_defined(mrb, mod, id));
}
+ return mrb_bool_value(mrb_const_defined_at(mrb, mod, id));
}
-static void
-check_cv_name_sym(mrb_state *mrb, mrb_sym id)
-{
- check_cv_name_str(mrb, mrb_sym2str(mrb, id));
-}
-
-/* 15.2.2.4.16 */
-/*
- * call-seq:
- * obj.class_variable_defined?(symbol) -> true or false
- *
- * Returns <code>true</code> if the given class variable is defined
- * in <i>obj</i>.
- *
- * class Fred
- * @@foo = 99
- * end
- * Fred.class_variable_defined?(:@@foo) #=> true
- * Fred.class_variable_defined?(:@@bar) #=> false
- */
-
static mrb_value
-mrb_mod_cvar_defined(mrb_state *mrb, mrb_value mod)
+mrb_const_get_sym(mrb_state *mrb, mrb_value mod, mrb_sym id)
{
- mrb_sym id;
-
- mrb_get_args(mrb, "n", &id);
- check_cv_name_sym(mrb, id);
- return mrb_bool_value(mrb_cv_defined(mrb, mod, id));
+ check_const_name_sym(mrb, id);
+ return mrb_const_get(mrb, mod, id);
}
-/* 15.2.2.4.17 */
-/*
- * call-seq:
- * mod.class_variable_get(symbol) -> obj
- *
- * Returns the value of the given class variable (or throws a
- * <code>NameError</code> exception). The <code>@@</code> part of the
- * variable name should be included for regular class variables
- *
- * class Fred
- * @@foo = 99
- * end
- * Fred.class_variable_get(:@@foo) #=> 99
- */
-
static mrb_value
-mrb_mod_cvar_get(mrb_state *mrb, mrb_value mod)
+mrb_mod_const_get(mrb_state *mrb, mrb_value mod)
{
+ mrb_value path = mrb_get_arg1(mrb);
mrb_sym id;
+ char *ptr;
+ mrb_int off, end, len;
+
+ if (mrb_symbol_p(path)) {
+ /* const get with symbol */
+ id = mrb_symbol(path);
+ return mrb_const_get_sym(mrb, mod, id);
+ }
+
+ /* const get with class path string */
+ path = mrb_ensure_string_type(mrb, path);
+ ptr = RSTRING_PTR(path);
+ len = RSTRING_LEN(path);
+ off = 0;
+
+ while (off < len) {
+ end = mrb_str_index_lit(mrb, path, "::", off);
+ end = (end == -1) ? len : end;
+ id = mrb_intern(mrb, ptr+off, end-off);
+ mod = mrb_const_get_sym(mrb, mod, id);
+ if (end == len)
+ off = end;
+ else {
+ off = end + 2;
+ if (off == len) { /* trailing "::" */
+ mrb_name_error(mrb, id, "wrong constant name '%v'", path);
+ }
+ }
+ }
- mrb_get_args(mrb, "n", &id);
- check_cv_name_sym(mrb, id);
- return mrb_cv_get(mrb, mod, id);
+ return mod;
}
-/* 15.2.2.4.18 */
-/*
- * call-seq:
- * obj.class_variable_set(symbol, obj) -> obj
- *
- * Sets the class variable names by <i>symbol</i> to
- * <i>object</i>.
- *
- * class Fred
- * @@foo = 99
- * def foo
- * @@foo
- * end
- * end
- * Fred.class_variable_set(:@@foo, 101) #=> 101
- * Fred.new.foo #=> 101
- */
-
static mrb_value
-mrb_mod_cvar_set(mrb_state *mrb, mrb_value mod)
+mrb_mod_const_set(mrb_state *mrb, mrb_value mod)
{
- mrb_value value;
mrb_sym id;
+ mrb_value value;
mrb_get_args(mrb, "no", &id, &value);
- check_cv_name_sym(mrb, id);
- mrb_cv_set(mrb, mod, id, value);
+ check_const_name_sym(mrb, id);
+ mrb_const_set(mrb, mod, id, value);
return value;
}
-/* 15.2.2.4.39 */
-/*
- * call-seq:
- * remove_class_variable(sym) -> obj
- *
- * Removes the definition of the <i>sym</i>, returning that
- * constant's value.
- *
- * class Dummy
- * @@var = 99
- * puts @@var
- * p class_variables
- * remove_class_variable(:@@var)
- * p class_variables
- * end
- *
- * <em>produces:</em>
- *
- * 99
- * [:@@var]
- * []
- */
-
static mrb_value
-mrb_mod_remove_cvar(mrb_state *mrb, mrb_value mod)
+mrb_mod_remove_const(mrb_state *mrb, mrb_value mod)
{
- mrb_value val;
mrb_sym id;
+ mrb_value val;
mrb_get_args(mrb, "n", &id);
- check_cv_name_sym(mrb, id);
-
+ check_const_name_sym(mrb, id);
val = mrb_iv_remove(mrb, mod, id);
- if (!mrb_undef_p(val)) return val;
-
- if (mrb_cv_defined(mrb, mod, id)) {
- mrb_name_error(mrb, id, "cannot remove %S for %S",
- mrb_sym2str(mrb, id), mod);
+ if (mrb_undef_p(val)) {
+ mrb_name_error(mrb, id, "constant %n not defined", id);
}
+ return val;
+}
- mrb_name_error(mrb, id, "class variable %S not defined for %S",
- mrb_sym2str(mrb, id), mod);
+static mrb_value
+mrb_mod_const_missing(mrb_state *mrb, mrb_value mod)
+{
+ mrb_sym sym;
- /* not reached */
- return mrb_nil_value();
+ mrb_get_args(mrb, "n", &sym);
+
+ if (mrb_class_real(mrb_class_ptr(mod)) != mrb->object_class) {
+ mrb_name_error(mrb, sym, "uninitialized constant %v::%n", mod, sym);
+ }
+ else {
+ mrb_name_error(mrb, sym, "uninitialized constant %n", sym);
+ }
+ /* not reached */
+ return mrb_nil_value();
}
/* 15.2.2.4.34 */
@@ -1845,197 +2508,367 @@ mrb_mod_method_defined(mrb_state *mrb, mrb_value mod)
return mrb_bool_value(mrb_obj_respond_to(mrb, mrb_class_ptr(mod), id));
}
-static void
-remove_method(mrb_state *mrb, mrb_value mod, mrb_sym mid)
+void
+mrb_method_added(mrb_state *mrb, struct RClass *c, mrb_sym mid)
{
- struct RClass *c = mrb_class_ptr(mod);
- khash_t(mt) *h = c->mt;
- khiter_t k;
+ mrb_sym added;
+ mrb_value recv = mrb_obj_value(c);
- if (h) {
- k = kh_get(mt, mrb, h, mid);
- if (k != kh_end(h)) {
- kh_del(mt, mrb, h, k);
- return;
- }
+ if (c->tt == MRB_TT_SCLASS) {
+ added = MRB_SYM(singleton_method_added);
+ recv = mrb_iv_get(mrb, recv, MRB_SYM(__attached__));
}
-
- mrb_name_error(mrb, mid, "method `%S' not defined in %S",
- mrb_sym2str(mrb, mid), mod);
+ else {
+ added = MRB_SYM(method_added);
+ }
+ mrb_funcall_id(mrb, recv, added, 1, mrb_symbol_value(mid));
}
-/* 15.2.2.4.41 */
-/*
- * call-seq:
- * remove_method(symbol) -> self
- *
- * Removes the method identified by _symbol_ from the current
- * class. For an example, see <code>Module.undef_method</code>.
- */
-
-static mrb_value
-mrb_mod_remove_method(mrb_state *mrb, mrb_value mod)
+mrb_value
+mrb_mod_define_method_m(mrb_state *mrb, struct RClass *c)
{
- mrb_int argc;
- mrb_value *argv;
+ struct RProc *p;
+ mrb_method_t m;
+ mrb_sym mid;
+ mrb_value proc = mrb_undef_value();
+ mrb_value blk;
- mrb_get_args(mrb, "*", &argv, &argc);
- while (argc--) {
- remove_method(mrb, mod, mrb_symbol(*argv));
- argv++;
+ mrb_get_args(mrb, "n|o&", &mid, &proc, &blk);
+ switch (mrb_type(proc)) {
+ case MRB_TT_PROC:
+ blk = proc;
+ break;
+ case MRB_TT_UNDEF:
+ /* ignored */
+ break;
+ default:
+ mrb_raisef(mrb, E_TYPE_ERROR, "wrong argument type %T (expected Proc)", proc);
+ break;
}
- return mod;
-}
-
-
-
-static void
-check_const_name_str(mrb_state *mrb, mrb_value str)
-{
- if (RSTRING_LEN(str) < 1 || !ISUPPER(*RSTRING_PTR(str))) {
- mrb_name_error(mrb, mrb_intern_str(mrb, str), "wrong constant name %S", str);
+ if (mrb_nil_p(blk)) {
+ mrb_raise(mrb, E_ARGUMENT_ERROR, "no block given");
}
+ p = MRB_OBJ_ALLOC(mrb, MRB_TT_PROC, mrb->proc_class);
+ mrb_proc_copy(p, mrb_proc_ptr(blk));
+ p->flags |= MRB_PROC_STRICT;
+ MRB_METHOD_FROM_PROC(m, p);
+ mrb_define_method_raw(mrb, c, mid, m);
+ mrb_method_added(mrb, c, mid);
+ return mrb_symbol_value(mid);
}
-static void
-check_const_name_sym(mrb_state *mrb, mrb_sym id)
+static mrb_value
+mod_define_method(mrb_state *mrb, mrb_value self)
{
- check_const_name_str(mrb, mrb_sym2str(mrb, id));
+ return mrb_mod_define_method_m(mrb, mrb_class_ptr(self));
}
static mrb_value
-const_defined(mrb_state *mrb, mrb_value mod, mrb_sym id, mrb_bool inherit)
+top_define_method(mrb_state *mrb, mrb_value self)
{
- if (inherit) {
- return mrb_bool_value(mrb_const_defined(mrb, mod, id));
- }
- return mrb_bool_value(mrb_const_defined_at(mrb, mod, id));
+ return mrb_mod_define_method_m(mrb, mrb->object_class);
}
static mrb_value
-mrb_mod_const_defined(mrb_state *mrb, mrb_value mod)
+mrb_mod_eqq(mrb_state *mrb, mrb_value mod)
{
- mrb_sym id;
- mrb_bool inherit = TRUE;
+ mrb_value obj = mrb_get_arg1(mrb);
+ mrb_bool eqq;
- mrb_get_args(mrb, "n|b", &id, &inherit);
- check_const_name_sym(mrb, id);
- return const_defined(mrb, mod, id, inherit);
+ eqq = mrb_obj_is_kind_of(mrb, obj, mrb_class_ptr(mod));
+
+ return mrb_bool_value(eqq);
}
static mrb_value
-mrb_mod_const_get(mrb_state *mrb, mrb_value mod)
+mrb_mod_dup(mrb_state *mrb, mrb_value self)
{
- mrb_sym id;
-
- mrb_get_args(mrb, "n", &id);
- check_const_name_sym(mrb, id);
- return mrb_const_get(mrb, mod, id);
+ mrb_value mod = mrb_obj_clone(mrb, self);
+ MRB_UNSET_FROZEN_FLAG(mrb_obj_ptr(mod));
+ return mod;
}
static mrb_value
-mrb_mod_const_set(mrb_state *mrb, mrb_value mod)
+mrb_mod_module_function(mrb_state *mrb, mrb_value mod)
{
- mrb_sym id;
- mrb_value value;
+ const mrb_value *argv;
+ mrb_int argc, i;
+ mrb_sym mid;
+ mrb_method_t m;
+ struct RClass *rclass;
+ int ai;
- mrb_get_args(mrb, "no", &id, &value);
- check_const_name_sym(mrb, id);
- mrb_const_set(mrb, mod, id, value);
- return value;
-}
+ mrb_check_type(mrb, mod, MRB_TT_MODULE);
-static mrb_value
-mrb_mod_remove_const(mrb_state *mrb, mrb_value mod)
-{
- mrb_sym id;
- mrb_value val;
+ mrb_get_args(mrb, "*", &argv, &argc);
+ if (argc == 0) {
+ /* set MODFUNC SCOPE if implemented */
+ return mod;
+ }
- mrb_get_args(mrb, "n", &id);
- check_const_name_sym(mrb, id);
- val = mrb_iv_remove(mrb, mod, id);
- if (mrb_undef_p(val)) {
- mrb_name_error(mrb, id, "constant %S not defined", mrb_sym2str(mrb, id));
+ /* set PRIVATE method visibility if implemented */
+ /* mrb_mod_dummy_visibility(mrb, mod); */
+
+ for (i=0; i<argc; i++) {
+ mrb_check_type(mrb, argv[i], MRB_TT_SYMBOL);
+
+ mid = mrb_symbol(argv[i]);
+ rclass = mrb_class_ptr(mod);
+ m = mrb_method_search(mrb, rclass, mid);
+
+ prepare_singleton_class(mrb, (struct RBasic*)rclass);
+ ai = mrb_gc_arena_save(mrb);
+ mrb_define_method_raw(mrb, rclass->c, mid, m);
+ mrb_gc_arena_restore(mrb, ai);
}
- return val;
+
+ return mod;
}
-static mrb_value
-mrb_mod_const_missing(mrb_state *mrb, mrb_value mod)
+static struct RClass*
+mrb_singleton_class_clone(mrb_state *mrb, mrb_value obj)
{
- mrb_sym sym;
+ struct RClass *klass = mrb_basic_ptr(obj)->c;
- mrb_get_args(mrb, "n", &sym);
+ if (klass->tt != MRB_TT_SCLASS)
+ return klass;
+ else {
+ /* copy singleton(unnamed) class */
+ struct RClass *clone = (struct RClass*)mrb_obj_alloc(mrb, klass->tt, mrb->class_class);
- if (mrb_class_real(mrb_class_ptr(mod)) != mrb->object_class) {
- mrb_name_error(mrb, sym, "uninitialized constant %S::%S",
- mod,
- mrb_sym2str(mrb, sym));
+ switch (mrb_type(obj)) {
+ case MRB_TT_CLASS:
+ case MRB_TT_SCLASS:
+ break;
+ default:
+ clone->c = mrb_singleton_class_clone(mrb, mrb_obj_value(klass));
+ break;
+ }
+ clone->super = klass->super;
+ if (klass->iv) {
+ mrb_iv_copy(mrb, mrb_obj_value(clone), mrb_obj_value(klass));
+ mrb_obj_iv_set(mrb, (struct RObject*)clone, MRB_SYM(__attached__), obj);
+ }
+ if (klass->mt) {
+ clone->mt = mt_copy(mrb, klass->mt);
+ }
+ else {
+ clone->mt = mt_new(mrb);
+ }
+ clone->tt = MRB_TT_SCLASS;
+ return clone;
+ }
+}
+
+static void
+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_FL_CLASS_IS_PREPENDED) {
+ struct RClass *c0 = sc->super;
+ struct RClass *c1 = dc;
+
+ /* copy prepended iclasses */
+ while (!(c0->flags & MRB_FL_CLASS_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_FL_CLASS_IS_ORIGIN;
+ }
+ if (sc->mt) {
+ dc->mt = mt_copy(mrb, sc->mt);
}
else {
- mrb_name_error(mrb, sym, "uninitialized constant %S",
- mrb_sym2str(mrb, sym));
+ dc->mt = mt_new(mrb);
}
- /* not reached */
- return mrb_nil_value();
+ dc->super = sc->super;
+ MRB_SET_INSTANCE_TT(dc, MRB_INSTANCE_TT(sc));
}
+/* 15.3.1.3.16 */
static mrb_value
-mrb_mod_s_constants(mrb_state *mrb, mrb_value mod)
+mrb_obj_init_copy(mrb_state *mrb, mrb_value self)
{
- mrb_raise(mrb, E_NOTIMP_ERROR, "Module.constants not implemented");
- return mrb_nil_value(); /* not reached */
+ mrb_value orig = mrb_get_arg1(mrb);
+
+ if (mrb_obj_equal(mrb, self, orig)) return self;
+ if ((mrb_type(self) != mrb_type(orig)) || (mrb_obj_class(mrb, self) != mrb_obj_class(mrb, orig))) {
+ mrb_raise(mrb, E_TYPE_ERROR, "initialize_copy should take same class object");
+ }
+ return self;
}
-static mrb_value
-mrb_mod_eqq(mrb_state *mrb, mrb_value mod)
+static void
+init_copy(mrb_state *mrb, mrb_value dest, mrb_value obj)
{
- mrb_value obj;
- mrb_bool eqq;
-
- mrb_get_args(mrb, "o", &obj);
- eqq = mrb_obj_is_kind_of(mrb, obj, mrb_class_ptr(mod));
+ switch (mrb_type(obj)) {
+ case MRB_TT_ICLASS:
+ copy_class(mrb, dest, obj);
+ return;
+ case MRB_TT_CLASS:
+ case MRB_TT_MODULE:
+ copy_class(mrb, dest, obj);
+ mrb_iv_copy(mrb, dest, obj);
+ mrb_iv_remove(mrb, dest, MRB_SYM(__classname__));
+ break;
+ case MRB_TT_OBJECT:
+ case MRB_TT_SCLASS:
+ case MRB_TT_HASH:
+ case MRB_TT_DATA:
+ case MRB_TT_EXCEPTION:
+ mrb_iv_copy(mrb, dest, obj);
+ break;
+ case MRB_TT_ISTRUCT:
+ mrb_istruct_copy(dest, obj);
+ break;
- return mrb_bool_value(eqq);
+ default:
+ break;
+ }
+ if (!mrb_func_basic_p(mrb, dest, MRB_SYM(initialize_copy), mrb_obj_init_copy)) {
+ mrb_funcall_id(mrb, dest, MRB_SYM(initialize_copy), 1, obj);
+ }
}
+/* 15.3.1.3.8 */
+/*
+ * call-seq:
+ * obj.clone -> an_object
+ *
+ * Produces a shallow copy of <i>obj</i>---the instance variables of
+ * <i>obj</i> are copied, but not the objects they reference. Copies
+ * the frozen state of <i>obj</i>. See also the discussion
+ * under <code>Object#dup</code>.
+ *
+ * class Klass
+ * attr_accessor :str
+ * end
+ * s1 = Klass.new #=> #<Klass:0x401b3a38>
+ * s1.str = "Hello" #=> "Hello"
+ * s2 = s1.clone #=> #<Klass:0x401b3998 @str="Hello">
+ * s2.str[1,4] = "i" #=> "i"
+ * s1.inspect #=> "#<Klass:0x401b3a38 @str=\"Hi\">"
+ * s2.inspect #=> "#<Klass:0x401b3998 @str=\"Hi\">"
+ *
+ * This method may have class-specific behavior. If so, that
+ * behavior will be documented under the #+initialize_copy+ method of
+ * the class.
+ *
+ * Some Class(True False Nil Symbol Integer Float) Object cannot clone.
+ */
MRB_API mrb_value
-mrb_mod_module_function(mrb_state *mrb, mrb_value mod)
+mrb_obj_clone(mrb_state *mrb, mrb_value self)
{
- mrb_value *argv;
- mrb_int argc, i;
- mrb_sym mid;
- struct RProc *method_rproc;
- struct RClass *rclass;
- int ai;
+ struct RObject *p;
+ mrb_value clone;
- mrb_check_type(mrb, mod, MRB_TT_MODULE);
-
- mrb_get_args(mrb, "*", &argv, &argc);
- if(argc == 0) {
- /* set MODFUNC SCOPE if implemented */
- return mod;
+ if (mrb_immediate_p(self)) {
+ return self;
}
+ if (mrb_sclass_p(self)) {
+ mrb_raise(mrb, E_TYPE_ERROR, "can't clone singleton class");
+ }
+ p = (struct RObject*)mrb_obj_alloc(mrb, mrb_type(self), mrb_obj_class(mrb, self));
+ p->c = mrb_singleton_class_clone(mrb, self);
+ mrb_field_write_barrier(mrb, (struct RBasic*)p, (struct RBasic*)p->c);
+ clone = mrb_obj_value(p);
+ init_copy(mrb, clone, self);
+ p->flags |= mrb_obj_ptr(self)->flags & MRB_FL_OBJ_IS_FROZEN;
- /* set PRIVATE method visibility if implemented */
- /* mrb_mod_dummy_visibility(mrb, mod); */
+ return clone;
+}
- for (i=0; i<argc; i++) {
- mrb_check_type(mrb, argv[i], MRB_TT_SYMBOL);
+/* 15.3.1.3.9 */
+/*
+ * call-seq:
+ * obj.dup -> an_object
+ *
+ * Produces a shallow copy of <i>obj</i>---the instance variables of
+ * <i>obj</i> are copied, but not the objects they reference.
+ * <code>dup</code> copies the frozen state of <i>obj</i>. See also
+ * the discussion under <code>Object#clone</code>. In general,
+ * <code>clone</code> and <code>dup</code> may have different semantics
+ * in descendant classes. While <code>clone</code> is used to duplicate
+ * an object, including its internal state, <code>dup</code> typically
+ * uses the class of the descendant object to create the new instance.
+ *
+ * This method may have class-specific behavior. If so, that
+ * behavior will be documented under the #+initialize_copy+ method of
+ * the class.
+ */
- mid = mrb_symbol(argv[i]);
- rclass = mrb_class_ptr(mod);
- method_rproc = mrb_method_search(mrb, rclass, mid);
+MRB_API mrb_value
+mrb_obj_dup(mrb_state *mrb, mrb_value obj)
+{
+ struct RBasic *p;
+ mrb_value dup;
- prepare_singleton_class(mrb, (struct RBasic*)rclass);
- ai = mrb_gc_arena_save(mrb);
- mrb_define_method_raw(mrb, rclass->c, mid, method_rproc);
- mrb_gc_arena_restore(mrb, ai);
+ if (mrb_immediate_p(obj)) {
+ return obj;
+ }
+ if (mrb_sclass_p(obj)) {
+ mrb_raise(mrb, E_TYPE_ERROR, "can't dup singleton class");
}
+ p = mrb_obj_alloc(mrb, mrb_type(obj), mrb_obj_class(mrb, obj));
+ dup = mrb_obj_value(p);
+ init_copy(mrb, dup, obj);
- return mod;
+ return dup;
+}
+
+/* implementation of __id__ */
+mrb_value mrb_obj_id_m(mrb_state *mrb, mrb_value self);
+/* implementation of instance_eval */
+mrb_value mrb_obj_instance_eval(mrb_state*, mrb_value);
+
+static mrb_value
+inspect_main(mrb_state *mrb, mrb_value mod)
+{
+ return mrb_str_new_lit(mrb, "main");
+}
+
+static const mrb_code new_iseq[] = {
+ OP_ENTER, 0x0, 0x10, 0x1, /* OP_ENTER 0:0:1:0:0:0:1 */
+ OP_LOADSELF, 0x3, /* OP_LOADSELF R3 */
+ OP_SEND, 0x3, 0x0, 0x0, /* OP_SEND R3 :allocate 0 */
+ OP_MOVE, 0x0, 0x3, /* OP_MOVE R0 R3 */
+ OP_MOVE, 0x4, 0x1, /* OP_MOVE R4 R1 */
+ OP_MOVE, 0x5, 0x2, /* OP_MOVE R5 R2 */
+ OP_SENDVB, 0x3, 0x1, /* OP_SENDVB R3 :initialize */
+ OP_RETURN, 0x0 /* OP_RETURN R0 */
+};
+
+MRB_PRESYM_DEFINE_VAR_AND_INITER(new_syms, 2, MRB_SYM(allocate), MRB_SYM(initialize))
+
+static const mrb_irep new_irep = {
+ 3, 6, 0, MRB_IREP_STATIC,
+ new_iseq, NULL, new_syms, NULL, NULL, NULL,
+ sizeof(new_iseq), 0, 2, 0, 0,
+};
+
+static const struct RProc new_proc = {
+ NULL, NULL, MRB_TT_PROC, MRB_GC_RED, MRB_FL_OBJ_IS_FROZEN | MRB_PROC_SCOPE | MRB_PROC_STRICT,
+ { &new_irep }, NULL, { NULL }
+};
+
+static void
+init_class_new(mrb_state *mrb, struct RClass *cls)
+{
+ mrb_method_t m;
+
+ MRB_PRESYM_INIT_SYMBOLS(mrb, new_syms);
+ MRB_METHOD_FROM_PROC(m, &new_proc);
+ mrb_define_method_raw(mrb, cls, MRB_SYM(new), m);
}
+/* implementation of #send method */
+mrb_value mrb_f_send(mrb_state *mrb, mrb_value self);
+
void
mrb_init_class(mrb_state *mrb)
{
@@ -2057,17 +2890,16 @@ mrb_init_class(mrb_state *mrb)
make_metaclass(mrb, cls);
/* name basic classes */
- mrb_define_const(mrb, bob, "BasicObject", mrb_obj_value(bob));
- mrb_define_const(mrb, obj, "BasicObject", mrb_obj_value(bob));
- mrb_define_const(mrb, obj, "Object", mrb_obj_value(obj));
- mrb_define_const(mrb, obj, "Module", mrb_obj_value(mod));
- mrb_define_const(mrb, obj, "Class", mrb_obj_value(cls));
+ mrb_define_const_id(mrb, bob, MRB_SYM(BasicObject), mrb_obj_value(bob));
+ mrb_define_const_id(mrb, obj, MRB_SYM(Object), mrb_obj_value(obj));
+ mrb_define_const_id(mrb, obj, MRB_SYM(Module), mrb_obj_value(mod));
+ mrb_define_const_id(mrb, obj, MRB_SYM(Class), mrb_obj_value(cls));
/* name each classes */
- name_class(mrb, bob, mrb_intern_lit(mrb, "BasicObject"));
- name_class(mrb, obj, mrb_intern_lit(mrb, "Object")); /* 15.2.1 */
- name_class(mrb, mod, mrb_intern_lit(mrb, "Module")); /* 15.2.2 */
- name_class(mrb, cls, mrb_intern_lit(mrb, "Class")); /* 15.2.3 */
+ mrb_class_name_class(mrb, NULL, bob, MRB_SYM(BasicObject));
+ mrb_class_name_class(mrb, NULL, obj, MRB_SYM(Object)); /* 15.2.1 */
+ mrb_class_name_class(mrb, NULL, mod, MRB_SYM(Module)); /* 15.2.2 */
+ mrb_class_name_class(mrb, NULL, cls, MRB_SYM(Class)); /* 15.2.3 */
mrb->proc_class = mrb_define_class(mrb, "Proc", mrb->object_class); /* 15.2.17 */
MRB_SET_INSTANCE_TT(mrb->proc_class, MRB_TT_PROC);
@@ -2075,36 +2907,36 @@ mrb_init_class(mrb_state *mrb)
MRB_SET_INSTANCE_TT(cls, MRB_TT_CLASS);
mrb_define_method(mrb, bob, "initialize", mrb_bob_init, MRB_ARGS_NONE());
mrb_define_method(mrb, bob, "!", mrb_bob_not, MRB_ARGS_NONE());
- mrb_define_method(mrb, bob, "method_missing", mrb_bob_missing, MRB_ARGS_ANY()); /* 15.3.1.3.30 */
-
- mrb_define_class_method(mrb, cls, "new", mrb_class_new_class, MRB_ARGS_OPT(1));
+ mrb_define_method(mrb, bob, "==", mrb_obj_equal_m, MRB_ARGS_REQ(1)); /* 15.3.1.3.1 */
+ mrb_define_method(mrb, bob, "__id__", mrb_obj_id_m, MRB_ARGS_NONE()); /* 15.3.1.3.4 */
+ mrb_define_method(mrb, bob, "__send__", mrb_f_send, MRB_ARGS_REQ(1)|MRB_ARGS_REST()|MRB_ARGS_BLOCK()); /* 15.3.1.3.5 */
+ mrb_define_method(mrb, bob, "equal?", mrb_obj_equal_m, MRB_ARGS_REQ(1)); /* 15.3.1.3.11 */
+ mrb_define_method(mrb, bob, "instance_eval", mrb_obj_instance_eval, MRB_ARGS_OPT(1)|MRB_ARGS_BLOCK()); /* 15.3.1.3.18 */
+ mrb_define_method(mrb, bob, "singleton_method_added", mrb_bob_init, MRB_ARGS_REQ(1));
+
+ mrb_define_class_method(mrb, cls, "new", mrb_class_new_class, MRB_ARGS_OPT(1)|MRB_ARGS_BLOCK());
+ mrb_define_method(mrb, cls, "allocate", mrb_instance_alloc, MRB_ARGS_NONE());
mrb_define_method(mrb, cls, "superclass", mrb_class_superclass, MRB_ARGS_NONE()); /* 15.2.3.3.4 */
- mrb_define_method(mrb, cls, "new", mrb_instance_new, MRB_ARGS_ANY()); /* 15.2.3.3.3 */
mrb_define_method(mrb, cls, "initialize", mrb_class_initialize, MRB_ARGS_OPT(1)); /* 15.2.3.3.1 */
mrb_define_method(mrb, cls, "inherited", mrb_bob_init, MRB_ARGS_REQ(1));
+ init_class_new(mrb, cls);
+
MRB_SET_INSTANCE_TT(mod, MRB_TT_MODULE);
- mrb_define_method(mrb, mod, "class_variable_defined?", mrb_mod_cvar_defined, MRB_ARGS_REQ(1)); /* 15.2.2.4.16 */
- mrb_define_method(mrb, mod, "class_variable_get", mrb_mod_cvar_get, MRB_ARGS_REQ(1)); /* 15.2.2.4.17 */
- 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, "include", mrb_mod_include, MRB_ARGS_ANY()); /* 15.2.2.4.27 */
+ 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_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 */
mrb_define_method(mrb, mod, "class_eval", mrb_mod_module_eval, MRB_ARGS_ANY()); /* 15.2.2.4.15 */
mrb_define_method(mrb, mod, "included", mrb_bob_init, MRB_ARGS_REQ(1)); /* 15.2.2.4.29 */
- mrb_define_method(mrb, mod, "included_modules", mrb_mod_included_modules, MRB_ARGS_NONE()); /* 15.2.2.4.30 */
mrb_define_method(mrb, mod, "initialize", mrb_mod_initialize, MRB_ARGS_NONE()); /* 15.2.2.4.31 */
- mrb_define_method(mrb, mod, "instance_methods", mrb_mod_instance_methods, MRB_ARGS_ANY()); /* 15.2.2.4.33 */
- mrb_define_method(mrb, mod, "method_defined?", mrb_mod_method_defined, MRB_ARGS_REQ(1)); /* 15.2.2.4.34 */
mrb_define_method(mrb, mod, "module_eval", mrb_mod_module_eval, MRB_ARGS_ANY()); /* 15.2.2.4.35 */
mrb_define_method(mrb, mod, "module_function", mrb_mod_module_function, MRB_ARGS_ANY());
mrb_define_method(mrb, mod, "private", mrb_mod_dummy_visibility, MRB_ARGS_ANY()); /* 15.2.2.4.36 */
mrb_define_method(mrb, mod, "protected", mrb_mod_dummy_visibility, MRB_ARGS_ANY()); /* 15.2.2.4.37 */
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, "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());
@@ -2115,14 +2947,21 @@ mrb_init_class(mrb_state *mrb)
mrb_define_method(mrb, mod, "const_defined?", mrb_mod_const_defined, MRB_ARGS_ARG(1,1)); /* 15.2.2.4.20 */
mrb_define_method(mrb, mod, "const_get", mrb_mod_const_get, MRB_ARGS_REQ(1)); /* 15.2.2.4.21 */
mrb_define_method(mrb, mod, "const_set", mrb_mod_const_set, MRB_ARGS_REQ(2)); /* 15.2.2.4.23 */
- mrb_define_method(mrb, mod, "constants", mrb_mod_constants, MRB_ARGS_OPT(1)); /* 15.2.2.4.24 */
mrb_define_method(mrb, mod, "remove_const", mrb_mod_remove_const, MRB_ARGS_REQ(1)); /* 15.2.2.4.40 */
mrb_define_method(mrb, mod, "const_missing", mrb_mod_const_missing, MRB_ARGS_REQ(1));
- mrb_define_method(mrb, mod, "define_method", mod_define_method, MRB_ARGS_REQ(1));
- mrb_define_method(mrb, mod, "class_variables", mrb_mod_class_variables, MRB_ARGS_NONE()); /* 15.2.2.4.19 */
- mrb_define_method(mrb, mod, "===", mrb_mod_eqq, MRB_ARGS_REQ(1));
- mrb_define_class_method(mrb, mod, "constants", mrb_mod_s_constants, MRB_ARGS_ANY()); /* 15.2.2.3.1 */
+ mrb_define_method(mrb, mod, "method_defined?", mrb_mod_method_defined, MRB_ARGS_REQ(1)); /* 15.2.2.4.34 */
+ mrb_define_method(mrb, mod, "define_method", mod_define_method, MRB_ARGS_ARG(1,1));
+ mrb_define_method(mrb, mod, "===", mrb_mod_eqq, MRB_ARGS_REQ(1)); /* 15.2.2.4.7 */
+ mrb_define_method(mrb, mod, "dup", mrb_mod_dup, MRB_ARGS_NONE());
+ mrb_define_method(mrb, bob, "method_added", mrb_bob_init, MRB_ARGS_REQ(1));
mrb_undef_method(mrb, cls, "append_features");
+ mrb_undef_method(mrb, cls, "prepend_features");
mrb_undef_method(mrb, cls, "extend_object");
+ mrb_undef_method(mrb, cls, "module_function");
+
+ mrb->top_self = MRB_OBJ_ALLOC(mrb, MRB_TT_OBJECT, mrb->object_class);
+ mrb_define_singleton_method(mrb, mrb->top_self, "inspect", inspect_main, MRB_ARGS_NONE());
+ mrb_define_singleton_method(mrb, mrb->top_self, "to_s", inspect_main, MRB_ARGS_NONE());
+ mrb_define_singleton_method(mrb, mrb->top_self, "define_method", top_define_method, MRB_ARGS_ARG(1,1));
}
diff --git a/src/codedump.c b/src/codedump.c
new file mode 100644
index 000000000..4d1d96171
--- /dev/null
+++ b/src/codedump.c
@@ -0,0 +1,595 @@
+#include <mruby.h>
+#include <mruby/irep.h>
+#include <mruby/debug.h>
+#include <mruby/opcode.h>
+#include <mruby/string.h>
+#include <mruby/proc.h>
+#include <mruby/dump.h>
+
+#ifndef MRB_NO_STDIO
+static void
+print_r(mrb_state *mrb, const mrb_irep *irep, size_t n)
+{
+ if (n == 0) return;
+ if (n >= irep->nlocals) return;
+ if (!irep->lv[n-1]) return;
+ printf(" R%d:%s", (int)n, mrb_sym_dump(mrb, irep->lv[n-1]));
+}
+
+static void
+print_lv_a(mrb_state *mrb, const mrb_irep *irep, uint16_t a)
+{
+ if (!irep->lv || a >= irep->nlocals || a == 0) {
+ printf("\n");
+ return;
+ }
+ printf("\t;");
+ print_r(mrb, irep, a);
+ printf("\n");
+}
+
+static void
+print_lv_ab(mrb_state *mrb, const mrb_irep *irep, uint16_t a, uint16_t b)
+{
+ if (!irep->lv || (a >= irep->nlocals && b >= irep->nlocals) || a+b == 0) {
+ printf("\n");
+ return;
+ }
+ printf("\t;");
+ if (a > 0) print_r(mrb, irep, a);
+ if (b > 0) print_r(mrb, irep, b);
+ printf("\n");
+}
+
+static void
+print_header(mrb_state *mrb, const mrb_irep *irep, uint32_t i)
+{
+ int32_t line;
+
+ line = mrb_debug_get_line(mrb, irep, i);
+ if (line < 0) {
+ printf(" ");
+ }
+ else {
+ printf("%5d ", line);
+ }
+
+ printf("%03d ", (int)i);
+}
+
+#define CASE(insn,ops) case insn: FETCH_ ## ops (); L_ ## insn
+
+static void
+codedump(mrb_state *mrb, const mrb_irep *irep)
+{
+ int ai;
+ const mrb_code *pc, *pcend;
+ mrb_code ins;
+ const char *file = NULL, *next_file;
+
+ if (!irep) return;
+ printf("irep %p nregs=%d nlocals=%d pools=%d syms=%d reps=%d iseq=%d\n", (void*)irep,
+ irep->nregs, irep->nlocals, (int)irep->plen, (int)irep->slen, (int)irep->rlen, (int)irep->ilen);
+
+ if (irep->lv) {
+ int i;
+
+ printf("local variable names:\n");
+ for (i = 1; i < irep->nlocals; ++i) {
+ char const *s = mrb_sym_dump(mrb, irep->lv[i - 1]);
+ printf(" R%d:%s\n", i, s ? s : "");
+ }
+ }
+
+ if (irep->clen > 0) {
+ int i = irep->clen;
+ const struct mrb_irep_catch_handler *e = mrb_irep_catch_handler_table(irep);
+
+ for (; i > 0; i --, e ++) {
+ uint32_t begin = mrb_irep_catch_handler_unpack(e->begin);
+ uint32_t end = mrb_irep_catch_handler_unpack(e->end);
+ uint32_t target = mrb_irep_catch_handler_unpack(e->target);
+ char buf[20];
+ const char *type;
+
+ switch (e->type) {
+ case MRB_CATCH_RESCUE:
+ type = "rescue";
+ break;
+ case MRB_CATCH_ENSURE:
+ type = "ensure";
+ break;
+ default:
+ buf[0] = '\0';
+ snprintf(buf, sizeof(buf), "0x%02x <unknown>", (int)e->type);
+ type = buf;
+ break;
+ }
+ printf("catch type: %-8s begin: %04" PRIu32 " end: %04" PRIu32 " target: %04" PRIu32 "\n", type, begin, end, target);
+ }
+ }
+
+ pc = irep->iseq;
+ pcend = pc + irep->ilen;
+ while (pc < pcend) {
+ ptrdiff_t i;
+ uint32_t a;
+ uint16_t b;
+ uint16_t c;
+
+ ai = mrb_gc_arena_save(mrb);
+
+ i = pc - irep->iseq;
+ next_file = mrb_debug_get_filename(mrb, irep, (uint32_t)i);
+ if (next_file && file != next_file) {
+ printf("file: %s\n", next_file);
+ file = next_file;
+ }
+ print_header(mrb, irep, (uint32_t)i);
+ ins = READ_B();
+ switch (ins) {
+ CASE(OP_NOP, Z):
+ printf("OP_NOP\n");
+ break;
+ CASE(OP_MOVE, BB):
+ printf("OP_MOVE\tR%d\tR%d\t", a, b);
+ print_lv_ab(mrb, irep, a, b);
+ break;
+
+ CASE(OP_LOADL, BB):
+ switch (irep->pool[b].tt) {
+#ifndef MRB_NO_FLOAT
+ case IREP_TT_FLOAT:
+ printf("OP_LOADL\tR%d\tL(%d)\t; %f", a, b, (double)irep->pool[b].u.f);
+ break;
+#endif
+ case IREP_TT_INT32:
+ printf("OP_LOADL\tR%d\tL(%d)\t; %" PRId32, a, b, irep->pool[b].u.i32);
+ break;
+#ifdef MRB_64BIT
+ case IREP_TT_INT64:
+ printf("OP_LOADL\tR%d\tL(%d)\t; %" PRId64, a, b, irep->pool[b].u.i64);
+ break;
+#endif
+ default:
+ printf("OP_LOADL\tR%d\tL(%d)\t", a, b);
+ break;
+ }
+ print_lv_a(mrb, irep, a);
+ break;
+ CASE(OP_LOADI, BB):
+ printf("OP_LOADI\tR%d\t%d\t", a, b);
+ print_lv_a(mrb, irep, a);
+ break;
+ CASE(OP_LOADINEG, BB):
+ printf("OP_LOADI\tR%d\t-%d\t", a, b);
+ print_lv_a(mrb, irep, a);
+ break;
+ CASE(OP_LOADI16, BS):
+ printf("OP_LOADI16\tR%d\t%d\t", a, (int)(int16_t)b);
+ print_lv_a(mrb, irep, a);
+ break;
+ CASE(OP_LOADI32, BSS):
+ printf("OP_LOADI32\tR%d\t%d\t", a, (int32_t)(((uint32_t)b<<16)+c));
+ print_lv_a(mrb, irep, a);
+ break;
+ CASE(OP_LOADI__1, B):
+ printf("OP_LOADI__1\tR%d\t\t", a);
+ print_lv_a(mrb, irep, a);
+ break;
+ CASE(OP_LOADI_0, B): goto L_LOADI;
+ CASE(OP_LOADI_1, B): goto L_LOADI;
+ CASE(OP_LOADI_2, B): goto L_LOADI;
+ CASE(OP_LOADI_3, B): goto L_LOADI;
+ CASE(OP_LOADI_4, B): goto L_LOADI;
+ CASE(OP_LOADI_5, B): goto L_LOADI;
+ CASE(OP_LOADI_6, B): goto L_LOADI;
+ CASE(OP_LOADI_7, B):
+ L_LOADI:
+ printf("OP_LOADI_%d\tR%d\t\t", ins-(int)OP_LOADI_0, a);
+ print_lv_a(mrb, irep, a);
+ break;
+ CASE(OP_LOADSYM, BB):
+ printf("OP_LOADSYM\tR%d\t:%s\t", a, mrb_sym_dump(mrb, irep->syms[b]));
+ print_lv_a(mrb, irep, a);
+ break;
+ CASE(OP_LOADNIL, B):
+ printf("OP_LOADNIL\tR%d\t\t", a);
+ print_lv_a(mrb, irep, a);
+ break;
+ CASE(OP_LOADSELF, B):
+ printf("OP_LOADSELF\tR%d\t\t", a);
+ print_lv_a(mrb, irep, a);
+ break;
+ CASE(OP_LOADT, B):
+ printf("OP_LOADT\tR%d\t\t", a);
+ print_lv_a(mrb, irep, a);
+ break;
+ CASE(OP_LOADF, B):
+ printf("OP_LOADF\tR%d\t\t", a);
+ print_lv_a(mrb, irep, a);
+ break;
+ CASE(OP_GETGV, BB):
+ printf("OP_GETGV\tR%d\t:%s", a, mrb_sym_dump(mrb, irep->syms[b]));
+ print_lv_a(mrb, irep, a);
+ break;
+ CASE(OP_SETGV, BB):
+ printf("OP_SETGV\t:%s\tR%d", mrb_sym_dump(mrb, irep->syms[b]), a);
+ print_lv_a(mrb, irep, a);
+ break;
+ CASE(OP_GETSV, BB):
+ printf("OP_GETSV\tR%d\t:%s", a, mrb_sym_dump(mrb, irep->syms[b]));
+ print_lv_a(mrb, irep, a);
+ break;
+ CASE(OP_SETSV, BB):
+ printf("OP_SETSV\t:%s\tR%d", mrb_sym_dump(mrb, irep->syms[b]), a);
+ print_lv_a(mrb, irep, a);
+ break;
+ CASE(OP_GETCONST, BB):
+ printf("OP_GETCONST\tR%d\t:%s", a, mrb_sym_dump(mrb, irep->syms[b]));
+ print_lv_a(mrb, irep, a);
+ break;
+ CASE(OP_SETCONST, BB):
+ printf("OP_SETCONST\t:%s\tR%d", mrb_sym_dump(mrb, irep->syms[b]), a);
+ print_lv_a(mrb, irep, a);
+ break;
+ CASE(OP_GETMCNST, BB):
+ printf("OP_GETMCNST\tR%d\tR%d::%s", a, a, mrb_sym_dump(mrb, irep->syms[b]));
+ print_lv_a(mrb, irep, a);
+ break;
+ CASE(OP_SETMCNST, BB):
+ printf("OP_SETMCNST\tR%d::%s\tR%d", a+1, mrb_sym_dump(mrb, irep->syms[b]), a);
+ print_lv_a(mrb, irep, a);
+ break;
+ CASE(OP_GETIV, BB):
+ printf("OP_GETIV\tR%d\t%s", a, mrb_sym_dump(mrb, irep->syms[b]));
+ print_lv_a(mrb, irep, a);
+ break;
+ CASE(OP_SETIV, BB):
+ printf("OP_SETIV\t%s\tR%d", mrb_sym_dump(mrb, irep->syms[b]), a);
+ print_lv_a(mrb, irep, a);
+ break;
+ CASE(OP_GETUPVAR, BBB):
+ printf("OP_GETUPVAR\tR%d\t%d\t%d", a, b, c);
+ print_lv_a(mrb, irep, a);
+ break;
+ CASE(OP_SETUPVAR, BBB):
+ printf("OP_SETUPVAR\tR%d\t%d\t%d", a, b, c);
+ print_lv_a(mrb, irep, a);
+ break;
+ CASE(OP_GETCV, BB):
+ printf("OP_GETCV\tR%d\t%s", a, mrb_sym_dump(mrb, irep->syms[b]));
+ print_lv_a(mrb, irep, a);
+ break;
+ CASE(OP_SETCV, BB):
+ printf("OP_SETCV\t%s\tR%d", mrb_sym_dump(mrb, irep->syms[b]), a);
+ print_lv_a(mrb, irep, a);
+ break;
+ CASE(OP_JMP, S):
+ i = pc - irep->iseq;
+ printf("OP_JMP\t\t%03d\n", (int)i+(int16_t)a);
+ break;
+ CASE(OP_JMPUW, S):
+ i = pc - irep->iseq;
+ printf("OP_JMPUW\t\t%03d\n", (int)i+(int16_t)a);
+ break;
+ CASE(OP_JMPIF, BS):
+ i = pc - irep->iseq;
+ printf("OP_JMPIF\tR%d\t%03d\t", a, (int)i+(int16_t)b);
+ print_lv_a(mrb, irep, a);
+ break;
+ CASE(OP_JMPNOT, BS):
+ i = pc - irep->iseq;
+ printf("OP_JMPNOT\tR%d\t%03d\t", a, (int)i+(int16_t)b);
+ print_lv_a(mrb, irep, a);
+ break;
+ CASE(OP_JMPNIL, BS):
+ i = pc - irep->iseq;
+ printf("OP_JMPNIL\tR%d\t%03d\t", a, (int)i+(int16_t)b);
+ print_lv_a(mrb, irep, a);
+ break;
+ CASE(OP_SENDV, BB):
+ printf("OP_SENDV\tR%d\t:%s\n", a, mrb_sym_dump(mrb, irep->syms[b]));
+ break;
+ CASE(OP_SENDVB, BB):
+ printf("OP_SENDVB\tR%d\t:%s\n", a, mrb_sym_dump(mrb, irep->syms[b]));
+ break;
+ CASE(OP_SEND, BBB):
+ printf("OP_SEND\tR%d\t:%s\t%d\n", a, mrb_sym_dump(mrb, irep->syms[b]), c);
+ break;
+ CASE(OP_SENDB, BBB):
+ printf("OP_SENDB\tR%d\t:%s\t%d\n", a, mrb_sym_dump(mrb, irep->syms[b]), c);
+ break;
+ CASE(OP_SENDVK, BB):
+ printf("OP_SENDVK\tR%d\t:%s\n", a, mrb_sym_dump(mrb, irep->syms[b]));
+ break;
+ CASE(OP_CALL, Z):
+ printf("OP_CALL\n");
+ break;
+ CASE(OP_SUPER, BB):
+ printf("OP_SUPER\tR%d\t%d\n", a, b);
+ break;
+ CASE(OP_ARGARY, BS):
+ printf("OP_ARGARY\tR%d\t%d:%d:%d:%d (%d)", a,
+ (b>>11)&0x3f,
+ (b>>10)&0x1,
+ (b>>5)&0x1f,
+ (b>>4)&0x1,
+ (b>>0)&0xf);
+ print_lv_a(mrb, irep, a);
+ break;
+ CASE(OP_ENTER, W):
+ printf("OP_ENTER\t%d:%d:%d:%d:%d:%d:%d\n",
+ MRB_ASPEC_REQ(a),
+ MRB_ASPEC_OPT(a),
+ MRB_ASPEC_REST(a),
+ MRB_ASPEC_POST(a),
+ MRB_ASPEC_KEY(a),
+ MRB_ASPEC_KDICT(a),
+ MRB_ASPEC_BLOCK(a));
+ break;
+ CASE(OP_KEY_P, BB):
+ printf("OP_KEY_P\tR%d\t:%s\t", a, mrb_sym_dump(mrb, irep->syms[b]));
+ print_lv_a(mrb, irep, a);
+ break;
+ CASE(OP_KEYEND, Z):
+ printf("OP_KEYEND\n");
+ break;
+ CASE(OP_KARG, BB):
+ printf("OP_KARG\tR%d\t:%s\t", a, mrb_sym_dump(mrb, irep->syms[b]));
+ print_lv_a(mrb, irep, a);
+ break;
+ CASE(OP_RETURN, B):
+ printf("OP_RETURN\tR%d\t\t", a);
+ print_lv_a(mrb, irep, a);
+ break;
+ CASE(OP_RETURN_BLK, B):
+ printf("OP_RETURN_BLK\tR%d\t\t", a);
+ print_lv_a(mrb, irep, a);
+ break;
+ CASE(OP_BREAK, B):
+ printf("OP_BREAK\tR%d\t\t", a);
+ print_lv_a(mrb, irep, a);
+ break;
+ CASE(OP_BLKPUSH, BS):
+ printf("OP_BLKPUSH\tR%d\t%d:%d:%d:%d (%d)", a,
+ (b>>11)&0x3f,
+ (b>>10)&0x1,
+ (b>>5)&0x1f,
+ (b>>4)&0x1,
+ (b>>0)&0xf);
+ print_lv_a(mrb, irep, a);
+ break;
+ CASE(OP_LAMBDA, BB):
+ printf("OP_LAMBDA\tR%d\tI(%d:%p)\n", a, b, (void*)irep->reps[b]);
+ break;
+ CASE(OP_BLOCK, BB):
+ printf("OP_BLOCK\tR%d\tI(%d:%p)\n", a, b, (void*)irep->reps[b]);
+ break;
+ CASE(OP_METHOD, BB):
+ printf("OP_METHOD\tR%d\tI(%d:%p)\n", a, b, (void*)irep->reps[b]);
+ break;
+ CASE(OP_RANGE_INC, B):
+ printf("OP_RANGE_INC\tR%d\n", a);
+ break;
+ CASE(OP_RANGE_EXC, B):
+ printf("OP_RANGE_EXC\tR%d\n", a);
+ break;
+ CASE(OP_DEF, BB):
+ printf("OP_DEF\tR%d\t:%s\n", a, mrb_sym_dump(mrb, irep->syms[b]));
+ break;
+ CASE(OP_UNDEF, B):
+ printf("OP_UNDEF\t:%s\n", mrb_sym_dump(mrb, irep->syms[a]));
+ break;
+ CASE(OP_ALIAS, BB):
+ printf("OP_ALIAS\t:%s\t%s\n", mrb_sym_dump(mrb, irep->syms[a]), mrb_sym_dump(mrb, irep->syms[b]));
+ break;
+ CASE(OP_ADD, B):
+ printf("OP_ADD\tR%d\tR%d\n", a, a+1);
+ break;
+ CASE(OP_ADDI, BB):
+ printf("OP_ADDI\tR%d\t%d\n", a, b);
+ break;
+ CASE(OP_SUB, B):
+ printf("OP_SUB\tR%d\tR%d\n", a, a+1);
+ break;
+ CASE(OP_SUBI, BB):
+ printf("OP_SUBI\tR%d\t%d\n", a, b);
+ break;
+ CASE(OP_MUL, B):
+ printf("OP_MUL\tR%d\tR%d\n", a, a+1);
+ break;
+ CASE(OP_DIV, B):
+ printf("OP_DIV\tR%d\tR%d\n", a, a+1);
+ break;
+ CASE(OP_LT, B):
+ printf("OP_LT\t\tR%d\tR%d\n", a, a+1);
+ break;
+ CASE(OP_LE, B):
+ printf("OP_LE\t\tR%d\tR%d\n", a, a+1);
+ break;
+ CASE(OP_GT, B):
+ printf("OP_GT\t\tR%d\tR%d\n", a, a+1);
+ break;
+ CASE(OP_GE, B):
+ printf("OP_GE\t\tR%d\tR%d\n", a, a+1);
+ break;
+ CASE(OP_EQ, B):
+ printf("OP_EQ\t\tR%d\tR%d\n", a, a+1);
+ break;
+ CASE(OP_ARRAY, BB):
+ printf("OP_ARRAY\tR%d\t%d\t", a, b);
+ print_lv_a(mrb, irep, a);
+ break;
+ CASE(OP_ARRAY2, BBB):
+ printf("OP_ARRAY\tR%d\tR%d\t%d\t", a, b, c);
+ print_lv_ab(mrb, irep, a, b);
+ break;
+ CASE(OP_ARYCAT, B):
+ printf("OP_ARYCAT\tR%d\t", a);
+ print_lv_a(mrb, irep, a);
+ break;
+ CASE(OP_ARYPUSH, B):
+ printf("OP_ARYPUSH\tR%d\t", a);
+ print_lv_a(mrb, irep, a);
+ break;
+ CASE(OP_ARYDUP, B):
+ printf("OP_ARYDUP\tR%d\t", a);
+ print_lv_a(mrb, irep, a);
+ break;
+ CASE(OP_AREF, BBB):
+ printf("OP_AREF\tR%d\tR%d\t%d", a, b, c);
+ print_lv_ab(mrb, irep, a, b);
+ break;
+ CASE(OP_ASET, BBB):
+ printf("OP_ASET\tR%d\tR%d\t%d", a, b, c);
+ print_lv_ab(mrb, irep, a, b);
+ break;
+ CASE(OP_APOST, BBB):
+ printf("OP_APOST\tR%d\t%d\t%d", a, b, c);
+ print_lv_a(mrb, irep, a);
+ break;
+ CASE(OP_INTERN, B):
+ printf("OP_INTERN\tR%d", a);
+ print_lv_a(mrb, irep, a);
+ break;
+ CASE(OP_STRING, BB):
+ if ((irep->pool[b].tt & IREP_TT_NFLAG) == 0) {
+ printf("OP_STRING\tR%d\tL(%d)\t; %s", a, b, irep->pool[b].u.str);
+ }
+ else {
+ printf("OP_STRING\tR%d\tL(%d)\t", a, b);
+ }
+ print_lv_a(mrb, irep, a);
+ break;
+ CASE(OP_STRCAT, B):
+ printf("OP_STRCAT\tR%d\t", a);
+ print_lv_a(mrb, irep, a);
+ break;
+ CASE(OP_HASH, BB):
+ printf("OP_HASH\tR%d\t%d\t", a, b);
+ print_lv_a(mrb, irep, a);
+ break;
+ CASE(OP_HASHADD, BB):
+ printf("OP_HASHADD\tR%d\t%d\t", a, b);
+ print_lv_a(mrb, irep, a);
+ break;
+ CASE(OP_HASHCAT, B):
+ printf("OP_HASHCAT\tR%d\t", a);
+ print_lv_a(mrb, irep, a);
+ break;
+
+ CASE(OP_OCLASS, B):
+ printf("OP_OCLASS\tR%d\t\t", a);
+ print_lv_a(mrb, irep, a);
+ break;
+ CASE(OP_CLASS, BB):
+ printf("OP_CLASS\tR%d\t:%s", a, mrb_sym_dump(mrb, irep->syms[b]));
+ print_lv_a(mrb, irep, a);
+ break;
+ CASE(OP_MODULE, BB):
+ printf("OP_MODULE\tR%d\t:%s", a, mrb_sym_dump(mrb, irep->syms[b]));
+ print_lv_a(mrb, irep, a);
+ break;
+ CASE(OP_EXEC, BB):
+ printf("OP_EXEC\tR%d\tI(%d:%p)", a, b, (void*)irep->reps[b]);
+ print_lv_a(mrb, irep, a);
+ break;
+ CASE(OP_SCLASS, B):
+ printf("OP_SCLASS\tR%d\t", a);
+ print_lv_a(mrb, irep, a);
+ break;
+ CASE(OP_TCLASS, B):
+ printf("OP_TCLASS\tR%d\t\t", a);
+ print_lv_a(mrb, irep, a);
+ break;
+ CASE(OP_ERR, B):
+ if ((irep->pool[a].tt & IREP_TT_NFLAG) == 0) {
+ printf("OP_ERR\t%s\n", irep->pool[a].u.str);
+ }
+ else {
+ printf("OP_ERR\tL(%d)\n", a);
+ }
+ break;
+ CASE(OP_EXCEPT, B):
+ printf("OP_EXCEPT\tR%d\t\t", a);
+ print_lv_a(mrb, irep, a);
+ break;
+ CASE(OP_RESCUE, BB):
+ printf("OP_RESCUE\tR%d\tR%d", a, b);
+ print_lv_ab(mrb, irep, a, b);
+ break;
+ CASE(OP_RAISEIF, B):
+ printf("OP_RAISEIF\tR%d\t\t", a);
+ print_lv_a(mrb, irep, a);
+ break;
+
+ CASE(OP_DEBUG, BBB):
+ printf("OP_DEBUG\t%d\t%d\t%d\n", a, b, c);
+ break;
+
+ CASE(OP_STOP, Z):
+ printf("OP_STOP\n");
+ break;
+
+ CASE(OP_EXT1, Z):
+ ins = READ_B();
+ printf("OP_EXT1\n");
+ print_header(mrb, irep, pc-irep->iseq-2);
+ switch (ins) {
+#define OPCODE(i,x) case OP_ ## i: FETCH_ ## x ## _1 (); goto L_OP_ ## i;
+#include "mruby/ops.h"
+#undef OPCODE
+ }
+ break;
+ CASE(OP_EXT2, Z):
+ ins = READ_B();
+ printf("OP_EXT2\n");
+ print_header(mrb, irep, pc-irep->iseq-2);
+ switch (ins) {
+#define OPCODE(i,x) case OP_ ## i: FETCH_ ## x ## _2 (); goto L_OP_ ## i;
+#include "mruby/ops.h"
+#undef OPCODE
+ }
+ break;
+ CASE(OP_EXT3, Z):
+ ins = READ_B();
+ printf("OP_EXT3\n");
+ print_header(mrb, irep, pc-irep->iseq-2);
+ switch (ins) {
+#define OPCODE(i,x) case OP_ ## i: FETCH_ ## x ## _3 (); goto L_OP_ ## i;
+#include "mruby/ops.h"
+#undef OPCODE
+ }
+ break;
+
+ default:
+ printf("OP_unknown (0x%x)\n", ins);
+ break;
+ }
+ mrb_gc_arena_restore(mrb, ai);
+ }
+ printf("\n");
+}
+
+static void
+codedump_recur(mrb_state *mrb, const mrb_irep *irep)
+{
+ int i;
+
+ codedump(mrb, irep);
+ if (irep->reps) {
+ for (i=0; i<irep->rlen; i++) {
+ codedump_recur(mrb, irep->reps[i]);
+ }
+ }
+}
+#endif
+
+void
+mrb_codedump_all(mrb_state *mrb, struct RProc *proc)
+{
+#ifndef MRB_NO_STDIO
+ codedump_recur(mrb, proc->body.irep);
+#endif
+}
diff --git a/src/codegen.c b/src/codegen.c
deleted file mode 100644
index a880244cc..000000000
--- a/src/codegen.c
+++ /dev/null
@@ -1,3156 +0,0 @@
-/*
-** codegen.c - mruby code generator
-**
-** See Copyright Notice in mruby.h
-*/
-
-#include <ctype.h>
-#include <limits.h>
-#include <stdlib.h>
-#include <string.h>
-#include "mruby.h"
-#include "mruby/compile.h"
-#include "mruby/proc.h"
-#include "mruby/numeric.h"
-#include "mruby/string.h"
-#include "mruby/debug.h"
-#include "node.h"
-#include "mruby/opcode.h"
-#include "mruby/re.h"
-#include "mrb_throw.h"
-
-typedef mrb_ast_node node;
-typedef struct mrb_parser_state parser_state;
-
-enum looptype {
- LOOP_NORMAL,
- LOOP_BLOCK,
- LOOP_FOR,
- LOOP_BEGIN,
- LOOP_RESCUE,
-};
-
-struct loopinfo {
- enum looptype type;
- int pc1, pc2, pc3, acc;
- int ensure_level;
- struct loopinfo *prev;
-};
-
-typedef struct scope {
- mrb_state *mrb;
- mrb_pool *mpool;
- struct mrb_jmpbuf jmp;
-
- struct scope *prev;
-
- node *lv;
-
- int sp;
- int pc;
- int lastlabel;
- int ainfo:15;
- mrb_bool mscope:1;
-
- struct loopinfo *loop;
- int ensure_level;
- char const *filename;
- uint16_t lineno;
-
- mrb_code *iseq;
- uint16_t *lines;
- int icapa;
-
- mrb_irep *irep;
- size_t pcapa;
- size_t scapa;
- size_t rcapa;
-
- uint16_t nlocals;
- uint16_t nregs;
- int ai;
-
- int debug_start_pos;
- uint16_t filename_index;
- parser_state* parser;
-} codegen_scope;
-
-static codegen_scope* scope_new(mrb_state *mrb, codegen_scope *prev, node *lv);
-static void scope_finish(codegen_scope *s);
-static struct loopinfo *loop_push(codegen_scope *s, enum looptype t);
-static void loop_break(codegen_scope *s, node *tree);
-static void loop_pop(codegen_scope *s, int val);
-
-static void gen_assignment(codegen_scope *s, node *tree, int sp, int val);
-static void gen_vmassignment(codegen_scope *s, node *tree, int rhs, int val);
-
-static void codegen(codegen_scope *s, node *tree, int val);
-static void raise_error(codegen_scope *s, const char *msg);
-
-static void
-codegen_error(codegen_scope *s, const char *message)
-{
- if (!s) return;
- while (s->prev) {
- codegen_scope *tmp = s->prev;
- mrb_pool_close(s->mpool);
- s = tmp;
- }
-#ifdef ENABLE_STDIO
- if (s->filename && s->lineno) {
- fprintf(stderr, "codegen error:%s:%d: %s\n", s->filename, s->lineno, message);
- }
- else {
- fprintf(stderr, "codegen error: %s\n", message);
- }
-#endif
- MRB_THROW(&s->jmp);
-}
-
-static void*
-codegen_palloc(codegen_scope *s, size_t len)
-{
- void *p = mrb_pool_alloc(s->mpool, len);
-
- if (!p) codegen_error(s, "pool memory allocation");
- return p;
-}
-
-static void*
-codegen_malloc(codegen_scope *s, size_t len)
-{
- void *p = mrb_malloc_simple(s->mrb, len);
-
- if (!p) codegen_error(s, "mrb_malloc");
- return p;
-}
-
-static void*
-codegen_realloc(codegen_scope *s, void *p, size_t len)
-{
- p = mrb_realloc_simple(s->mrb, p, len);
-
- if (!p && len > 0) codegen_error(s, "mrb_realloc");
- return p;
-}
-
-static int
-new_label(codegen_scope *s)
-{
- s->lastlabel = s->pc;
- return s->pc;
-}
-
-static inline int
-genop(codegen_scope *s, mrb_code i)
-{
- if (s->pc == s->icapa) {
- s->icapa *= 2;
- s->iseq = (mrb_code *)codegen_realloc(s, s->iseq, sizeof(mrb_code)*s->icapa);
- if (s->lines) {
- s->lines = (uint16_t*)codegen_realloc(s, s->lines, sizeof(short)*s->icapa);
- s->irep->lines = s->lines;
- }
- }
- s->iseq[s->pc] = i;
- if (s->lines) {
- s->lines[s->pc] = s->lineno;
- }
- return s->pc++;
-}
-
-#define NOVAL 0
-#define VAL 1
-
-static mrb_bool
-no_optimize(codegen_scope *s)
-{
- if (s && s->parser && s->parser->no_optimize)
- return TRUE;
- return FALSE;
-}
-
-static int
-genop_peep(codegen_scope *s, mrb_code i, int val)
-{
- /* peephole optimization */
- if (!no_optimize(s) && s->lastlabel != s->pc && s->pc > 0) {
- mrb_code i0 = s->iseq[s->pc-1];
- int c1 = GET_OPCODE(i);
- int c0 = GET_OPCODE(i0);
-
- switch (c1) {
- case OP_MOVE:
- if (GETARG_A(i) == GETARG_B(i)) {
- /* skip useless OP_MOVE */
- return 0;
- }
- if (val) break;
- switch (c0) {
- case OP_MOVE:
- if (GETARG_B(i) == GETARG_A(i0) && GETARG_A(i) == GETARG_B(i0)) {
- /* skip swapping OP_MOVE */
- return 0;
- }
- if (GETARG_B(i) == GETARG_A(i0) && GETARG_A(i0) >= s->nlocals) {
- s->pc--;
- return genop_peep(s, MKOP_AB(OP_MOVE, GETARG_A(i), GETARG_B(i0)), val);
- }
- break;
- case OP_LOADI:
- if (GETARG_B(i) == GETARG_A(i0) && GETARG_A(i0) >= s->nlocals) {
- s->iseq[s->pc-1] = MKOP_AsBx(OP_LOADI, GETARG_A(i), GETARG_sBx(i0));
- return 0;
- }
- break;
- case OP_ARRAY:
- case OP_HASH:
- case OP_RANGE:
- case OP_AREF:
- case OP_GETUPVAR:
- if (GETARG_B(i) == GETARG_A(i0) && GETARG_A(i0) >= s->nlocals) {
- s->iseq[s->pc-1] = MKOP_ABC(c0, GETARG_A(i), GETARG_B(i0), GETARG_C(i0));
- return 0;
- }
- break;
- case OP_LOADSYM:
- case OP_GETGLOBAL:
- case OP_GETIV:
- case OP_GETCV:
- case OP_GETCONST:
- case OP_GETSPECIAL:
- case OP_LOADL:
- case OP_STRING:
- if (GETARG_B(i) == GETARG_A(i0) && GETARG_A(i0) >= s->nlocals) {
- s->iseq[s->pc-1] = MKOP_ABx(c0, GETARG_A(i), GETARG_Bx(i0));
- return 0;
- }
- break;
- case OP_SCLASS:
- if (GETARG_B(i) == GETARG_A(i0) && GETARG_A(i0) >= s->nlocals) {
- s->iseq[s->pc-1] = MKOP_AB(c0, GETARG_A(i), GETARG_B(i0));
- return 0;
- }
- break;
- case OP_LOADNIL:
- case OP_LOADSELF:
- case OP_LOADT:
- case OP_LOADF:
- case OP_OCLASS:
- if (GETARG_B(i) == GETARG_A(i0) && GETARG_A(i0) >= s->nlocals) {
- s->iseq[s->pc-1] = MKOP_A(c0, GETARG_A(i));
- return 0;
- }
- break;
- default:
- break;
- }
- break;
- case OP_SETIV:
- case OP_SETCV:
- case OP_SETCONST:
- case OP_SETMCNST:
- case OP_SETGLOBAL:
- if (val) break;
- if (c0 == OP_MOVE) {
- if (GETARG_A(i) == GETARG_A(i0)) {
- s->iseq[s->pc-1] = MKOP_ABx(c1, GETARG_B(i0), GETARG_Bx(i));
- return 0;
- }
- }
- break;
- case OP_SETUPVAR:
- if (val) break;
- if (c0 == OP_MOVE) {
- if (GETARG_A(i) == GETARG_A(i0)) {
- s->iseq[s->pc-1] = MKOP_ABC(c1, GETARG_B(i0), GETARG_B(i), GETARG_C(i));
- return 0;
- }
- }
- break;
- case OP_EPOP:
- if (c0 == OP_EPOP) {
- s->iseq[s->pc-1] = MKOP_A(OP_EPOP, GETARG_A(i0)+GETARG_A(i));
- return 0;
- }
- break;
- case OP_POPERR:
- if (c0 == OP_POPERR) {
- s->iseq[s->pc-1] = MKOP_A(OP_POPERR, GETARG_A(i0)+GETARG_A(i));
- return 0;
- }
- break;
- case OP_RETURN:
- switch (c0) {
- case OP_RETURN:
- return 0;
- case OP_MOVE:
- if (GETARG_A(i0) >= s->nlocals) {
- s->iseq[s->pc-1] = MKOP_AB(OP_RETURN, GETARG_B(i0), OP_R_NORMAL);
- return 0;
- }
- break;
- case OP_SETIV:
- case OP_SETCV:
- case OP_SETCONST:
- case OP_SETMCNST:
- case OP_SETUPVAR:
- case OP_SETGLOBAL:
- s->pc--;
- genop_peep(s, i0, NOVAL);
- i0 = s->iseq[s->pc-1];
- return genop(s, MKOP_AB(OP_RETURN, GETARG_A(i0), OP_R_NORMAL));
-#if 0
- case OP_SEND:
- if (GETARG_B(i) == OP_R_NORMAL && GETARG_A(i) == GETARG_A(i0)) {
- s->iseq[s->pc-1] = MKOP_ABC(OP_TAILCALL, GETARG_A(i0), GETARG_B(i0), GETARG_C(i0));
- return;
- }
- break;
-#endif
- default:
- break;
- }
- break;
- case OP_ADD:
- case OP_SUB:
- if (c0 == OP_LOADI) {
- int c = GETARG_sBx(i0);
-
- if (c1 == OP_SUB) c = -c;
- if (c > 127 || c < -127) break;
- if (0 <= c)
- s->iseq[s->pc-1] = MKOP_ABC(OP_ADDI, GETARG_A(i), GETARG_B(i), c);
- else
- s->iseq[s->pc-1] = MKOP_ABC(OP_SUBI, GETARG_A(i), GETARG_B(i), -c);
- return 0;
- }
- case OP_STRCAT:
- if (c0 == OP_STRING) {
- mrb_value v = s->irep->pool[GETARG_Bx(i0)];
-
- if (mrb_string_p(v) && RSTRING_LEN(v) == 0) {
- s->pc--;
- return 0;
- }
- }
- break;
- case OP_JMPIF:
- case OP_JMPNOT:
- if (c0 == OP_MOVE && GETARG_A(i) == GETARG_A(i0)) {
- s->iseq[s->pc-1] = MKOP_AsBx(c1, GETARG_B(i0), GETARG_sBx(i));
- return s->pc-1;
- }
- break;
- default:
- break;
- }
- }
- return genop(s, i);
-}
-
-static void
-scope_error(codegen_scope *s)
-{
- exit(EXIT_FAILURE);
-}
-
-static inline void
-dispatch(codegen_scope *s, int pc)
-{
- int diff = s->pc - pc;
- mrb_code i = s->iseq[pc];
- int c = GET_OPCODE(i);
-
- s->lastlabel = s->pc;
- switch (c) {
- case OP_JMP:
- case OP_JMPIF:
- case OP_JMPNOT:
- case OP_ONERR:
- break;
- default:
-#ifdef ENABLE_STDIO
- fprintf(stderr, "bug: dispatch on non JMP op\n");
-#endif
- scope_error(s);
- break;
- }
- s->iseq[pc] = MKOP_AsBx(c, GETARG_A(i), diff);
-}
-
-static void
-dispatch_linked(codegen_scope *s, int pc)
-{
- mrb_code i;
- int pos;
-
- if (!pc) return;
- for (;;) {
- i = s->iseq[pc];
- pos = GETARG_sBx(i);
- dispatch(s, pc);
- if (!pos) break;
- pc = pos;
- }
-}
-
-#define nregs_update do {if (s->sp > s->nregs) s->nregs = s->sp;} while (0)
-static void
-push_(codegen_scope *s)
-{
- if (s->sp > 511) {
- codegen_error(s, "too complex expression");
- }
- s->sp++;
- nregs_update;
-}
-
-#define push() push_(s)
-#define pop_(s) ((s)->sp--)
-#define pop() pop_(s)
-#define pop_n(n) (s->sp-=(n))
-#define cursp() (s->sp)
-
-static inline int
-new_lit(codegen_scope *s, mrb_value val)
-{
- size_t i;
- mrb_value *pv;
-
- switch (mrb_type(val)) {
- case MRB_TT_STRING:
- for (i=0; i<s->irep->plen; i++) {
- mrb_int len;
- pv = &s->irep->pool[i];
-
- if (mrb_type(*pv) != MRB_TT_STRING) continue;
- if ((len = RSTRING_LEN(*pv)) != RSTRING_LEN(val)) continue;
- if (memcmp(RSTRING_PTR(*pv), RSTRING_PTR(val), len) == 0)
- return i;
- }
- break;
- case MRB_TT_FLOAT:
- for (i=0; i<s->irep->plen; i++) {
- pv = &s->irep->pool[i];
- if (mrb_type(*pv) != MRB_TT_FLOAT) continue;
- if (mrb_float(*pv) == mrb_float(val)) return i;
- }
- break;
- case MRB_TT_FIXNUM:
- for (i=0; i<s->irep->plen; i++) {
- pv = &s->irep->pool[i];
- if (!mrb_fixnum_p(*pv)) continue;
- if (mrb_fixnum(*pv) == mrb_fixnum(val)) return i;
- }
- break;
- default:
- /* should not happen */
- return 0;
- }
-
- if (s->irep->plen == s->pcapa) {
- s->pcapa *= 2;
- s->irep->pool = (mrb_value *)codegen_realloc(s, s->irep->pool, sizeof(mrb_value)*s->pcapa);
- }
-
- pv = &s->irep->pool[s->irep->plen];
- i = s->irep->plen++;
-
- switch (mrb_type(val)) {
- case MRB_TT_STRING:
- *pv = mrb_str_pool(s->mrb, val);
- break;
-
- case MRB_TT_FLOAT:
-#ifdef MRB_WORD_BOXING
- *pv = mrb_float_pool(s->mrb, mrb_float(val));
- break;
-#endif
- case MRB_TT_FIXNUM:
- *pv = val;
- break;
-
- default:
- /* should not happen */
- break;
- }
- return i;
-}
-
-static inline int
-new_msym(codegen_scope *s, mrb_sym sym)
-{
- size_t i, len;
-
- mrb_assert(s->irep);
-
- len = s->irep->slen;
- if (len > 256) len = 256;
- for (i=0; i<len; i++) {
- if (s->irep->syms[i] == sym) return i;
- if (s->irep->syms[i] == 0) break;
- }
- if (i == 256) {
- codegen_error(s, "too many symbols (max 256)");
- }
- s->irep->syms[i] = sym;
- if (i == s->irep->slen) s->irep->slen++;
- return i;
-}
-
-static inline int
-new_sym(codegen_scope *s, mrb_sym sym)
-{
- size_t i;
-
- for (i=0; i<s->irep->slen; i++) {
- if (s->irep->syms[i] == sym) return i;
- }
- if (s->irep->slen > 125 && s->irep->slen < 256) {
- s->irep->syms = (mrb_sym *)codegen_realloc(s, s->irep->syms, sizeof(mrb_sym)*65536);
- for (i = 0; i < 256 - s->irep->slen; i++) {
- static const mrb_sym mrb_sym_zero = { 0 };
- s->irep->syms[i + s->irep->slen] = mrb_sym_zero;
- }
- s->irep->slen = 256;
- }
- s->irep->syms[s->irep->slen] = sym;
- return s->irep->slen++;
-}
-
-static int
-node_len(node *tree)
-{
- int n = 0;
-
- while (tree) {
- n++;
- tree = tree->cdr;
- }
- return n;
-}
-
-#define sym(x) ((mrb_sym)(intptr_t)(x))
-#define lv_name(lv) sym((lv)->car)
-static int
-lv_idx(codegen_scope *s, mrb_sym id)
-{
- node *lv = s->lv;
- int n = 1;
-
- while (lv) {
- if (lv_name(lv) == id) return n;
- n++;
- lv = lv->cdr;
- }
- return 0;
-}
-
-static void
-for_body(codegen_scope *s, node *tree)
-{
- codegen_scope *prev = s;
- int idx;
- struct loopinfo *lp;
- node *n2;
- mrb_code c;
-
- /* generate receiver */
- codegen(s, tree->cdr->car, VAL);
- /* generate loop-block */
- s = scope_new(s->mrb, s, NULL);
- if (s == NULL) {
- raise_error(prev, "unexpected scope");
- }
-
- push(); /* push for a block parameter */
-
- lp = loop_push(s, LOOP_FOR);
- lp->pc1 = new_label(s);
-
- /* generate loop variable */
- n2 = tree->car;
- genop(s, MKOP_Ax(OP_ENTER, 0x40000));
- if (n2->car && !n2->car->cdr && !n2->cdr) {
- gen_assignment(s, n2->car->car, 1, NOVAL);
- }
- else {
- gen_vmassignment(s, n2, 1, VAL);
- }
- codegen(s, tree->cdr->cdr->car, VAL);
- pop();
- if (s->pc > 0) {
- c = s->iseq[s->pc-1];
- if (GET_OPCODE(c) != OP_RETURN || GETARG_B(c) != OP_R_NORMAL || s->pc == s->lastlabel)
- genop_peep(s, MKOP_AB(OP_RETURN, cursp(), OP_R_NORMAL), NOVAL);
- }
- loop_pop(s, NOVAL);
- scope_finish(s);
- s = prev;
- genop(s, MKOP_Abc(OP_LAMBDA, cursp(), s->irep->rlen-1, OP_L_BLOCK));
- pop();
- idx = new_msym(s, mrb_intern_lit(s->mrb, "each"));
- genop(s, MKOP_ABC(OP_SENDB, cursp(), idx, 0));
-}
-
-static int
-lambda_body(codegen_scope *s, node *tree, int blk)
-{
- mrb_code c;
- codegen_scope *parent = s;
- s = scope_new(s->mrb, s, tree->car);
- if (s == NULL) {
- raise_error(parent, "unexpected scope");
- }
-
- s->mscope = !blk;
-
- if (blk) {
- struct loopinfo *lp = loop_push(s, LOOP_BLOCK);
- lp->pc1 = new_label(s);
- }
- tree = tree->cdr;
- if (tree->car) {
- mrb_aspec a;
- int ma, oa, ra, pa, ka, kd, ba;
- int pos, i;
- node *n, *opt;
-
- ma = node_len(tree->car->car);
- n = tree->car->car;
- while (n) {
- n = n->cdr;
- }
- oa = node_len(tree->car->cdr->car);
- ra = tree->car->cdr->cdr->car ? 1 : 0;
- pa = node_len(tree->car->cdr->cdr->cdr->car);
- ka = kd = 0;
- ba = tree->car->cdr->cdr->cdr->cdr ? 1 : 0;
-
- a = ((mrb_aspec)(ma & 0x1f) << 18)
- | ((mrb_aspec)(oa & 0x1f) << 13)
- | ((ra & 1) << 12)
- | ((pa & 0x1f) << 7)
- | ((ka & 0x1f) << 2)
- | ((kd & 1)<< 1)
- | (ba & 1);
- s->ainfo = (((ma+oa) & 0x3f) << 6) /* (12bits = 6:1:5) */
- | ((ra & 1) << 5)
- | (pa & 0x1f);
- genop(s, MKOP_Ax(OP_ENTER, a));
- pos = new_label(s);
- for (i=0; i<oa; i++) {
- new_label(s);
- genop(s, MKOP_sBx(OP_JMP, 0));
- }
- if (oa > 0) {
- genop(s, MKOP_sBx(OP_JMP, 0));
- }
- opt = tree->car->cdr->car;
- i = 0;
- while (opt) {
- int idx;
-
- dispatch(s, pos+i);
- codegen(s, opt->car->cdr, VAL);
- idx = lv_idx(s, (mrb_sym)(intptr_t)opt->car->car);
- pop();
- genop_peep(s, MKOP_AB(OP_MOVE, idx, cursp()), NOVAL);
- i++;
- opt = opt->cdr;
- }
- if (oa > 0) {
- dispatch(s, pos+i);
- }
- }
- codegen(s, tree->cdr->car, VAL);
- pop();
- if (s->pc > 0) {
- c = s->iseq[s->pc-1];
- if (GET_OPCODE(c) != OP_RETURN || GETARG_B(c) != OP_R_NORMAL || s->pc == s->lastlabel) {
- if (s->nregs == 0) {
- genop(s, MKOP_A(OP_LOADNIL, 0));
- genop(s, MKOP_AB(OP_RETURN, 0, OP_R_NORMAL));
- }
- else {
- genop_peep(s, MKOP_AB(OP_RETURN, cursp(), OP_R_NORMAL), NOVAL);
- }
- }
- }
- if (blk) {
- loop_pop(s, NOVAL);
- }
- scope_finish(s);
- return parent->irep->rlen - 1;
-}
-
-static int
-scope_body(codegen_scope *s, node *tree, int val)
-{
- codegen_scope *scope = scope_new(s->mrb, s, tree->car);
- if (scope == NULL) {
- raise_error(s, "unexpected scope");
- }
-
- codegen(scope, tree->cdr, VAL);
- if (!s->iseq) {
- genop(scope, MKOP_A(OP_STOP, 0));
- }
- else if (!val) {
- genop(scope, MKOP_AB(OP_RETURN, 0, OP_R_NORMAL));
- }
- else {
- if (scope->nregs == 0) {
- genop(scope, MKOP_A(OP_LOADNIL, 0));
- genop(scope, MKOP_AB(OP_RETURN, 0, OP_R_NORMAL));
- }
- else {
- genop_peep(scope, MKOP_AB(OP_RETURN, scope->sp-1, OP_R_NORMAL), NOVAL);
- }
- }
- scope_finish(scope);
- if (!s->irep) {
- /* should not happen */
- return 0;
- }
- return s->irep->rlen - 1;
-}
-
-static mrb_bool
-nosplat(node *t)
-{
- while (t) {
- if ((intptr_t)t->car->car == NODE_SPLAT) return FALSE;
- t = t->cdr;
- }
- return TRUE;
-}
-
-static mrb_sym
-attrsym(codegen_scope *s, mrb_sym a)
-{
- const char *name;
- mrb_int len;
- char *name2;
-
- name = mrb_sym2name_len(s->mrb, a, &len);
- name2 = (char *)codegen_palloc(s,
- (size_t)len
- + 1 /* '=' */
- + 1 /* '\0' */
- );
- mrb_assert_int_fit(mrb_int, len, size_t, SIZE_MAX);
- memcpy(name2, name, (size_t)len);
- name2[len] = '=';
- name2[len+1] = '\0';
-
- return mrb_intern(s->mrb, name2, len+1);
-}
-
-static int
-gen_values(codegen_scope *s, node *t, int val)
-{
- int n = 0;
- int is_splat;
-
- while (t) {
- is_splat = (intptr_t)t->car->car == NODE_SPLAT; /* splat mode */
- if (n >= 127 || is_splat) {
- if (val) {
- pop_n(n);
- genop(s, MKOP_ABC(OP_ARRAY, cursp(), cursp(), n));
- push();
- codegen(s, t->car, VAL);
- pop(); pop();
- if (is_splat) {
- genop(s, MKOP_AB(OP_ARYCAT, cursp(), cursp()+1));
- }
- else {
- genop(s, MKOP_AB(OP_ARYPUSH, cursp(), cursp()+1));
- }
- t = t->cdr;
- while (t) {
- push();
- codegen(s, t->car, VAL);
- pop(); pop();
- if ((intptr_t)t->car->car == NODE_SPLAT) {
- genop(s, MKOP_AB(OP_ARYCAT, cursp(), cursp()+1));
- }
- else {
- genop(s, MKOP_AB(OP_ARYPUSH, cursp(), cursp()+1));
- }
- t = t->cdr;
- }
- }
- else {
- codegen(s, t->car->cdr, NOVAL);
- t = t->cdr;
- while (t) {
- codegen(s, t->car, NOVAL);
- t = t->cdr;
- }
- }
- return -1;
- }
- /* normal (no splat) mode */
- codegen(s, t->car, val);
- n++;
- t = t->cdr;
- }
- return n;
-}
-
-#define CALL_MAXARGS 127
-
-static void
-gen_call(codegen_scope *s, node *tree, mrb_sym name, int sp, int val)
-{
- mrb_sym sym = name ? name : sym(tree->cdr->car);
- int idx;
- int n = 0, noop = 0, sendv = 0, blk = 0;
-
- codegen(s, tree->car, VAL); /* receiver */
- idx = new_msym(s, sym);
- tree = tree->cdr->cdr->car;
- if (tree) {
- n = gen_values(s, tree->car, VAL);
- if (n < 0) {
- n = noop = sendv = 1;
- push();
- }
- }
- if (sp) {
- if (sendv) {
- pop();
- genop(s, MKOP_AB(OP_ARYPUSH, cursp(), sp));
- push();
- }
- else {
- genop(s, MKOP_AB(OP_MOVE, cursp(), sp));
- push();
- n++;
- }
- }
- if (tree && tree->cdr) {
- noop = 1;
- codegen(s, tree->cdr, VAL);
- pop();
- }
- else {
- blk = cursp();
- }
- push();pop();
- pop_n(n+1);
- {
- mrb_int symlen;
- const char *symname = mrb_sym2name_len(s->mrb, sym, &symlen);
-
- if (!noop && symlen == 1 && symname[0] == '+') {
- genop_peep(s, MKOP_ABC(OP_ADD, cursp(), idx, n), val);
- }
- else if (!noop && symlen == 1 && symname[0] == '-') {
- genop_peep(s, MKOP_ABC(OP_SUB, cursp(), idx, n), val);
- }
- else if (!noop && symlen == 1 && symname[0] == '*') {
- genop(s, MKOP_ABC(OP_MUL, cursp(), idx, n));
- }
- else if (!noop && symlen == 1 && symname[0] == '/') {
- genop(s, MKOP_ABC(OP_DIV, cursp(), idx, n));
- }
- else if (!noop && symlen == 1 && symname[0] == '<') {
- genop(s, MKOP_ABC(OP_LT, cursp(), idx, n));
- }
- else if (!noop && symlen == 2 && symname[0] == '<' && symname[1] == '=') {
- genop(s, MKOP_ABC(OP_LE, cursp(), idx, n));
- }
- else if (!noop && symlen == 1 && symname[0] == '>') {
- genop(s, MKOP_ABC(OP_GT, cursp(), idx, n));
- }
- else if (!noop && symlen == 2 && symname[0] == '>' && symname[1] == '=') {
- genop(s, MKOP_ABC(OP_GE, cursp(), idx, n));
- }
- else if (!noop && symlen == 2 && symname[0] == '=' && symname[1] == '=') {
- genop(s, MKOP_ABC(OP_EQ, cursp(), idx, n));
- }
- else {
- if (sendv) n = CALL_MAXARGS;
- if (blk > 0) { /* no block */
- genop(s, MKOP_ABC(OP_SEND, cursp(), idx, n));
- }
- else {
- genop(s, MKOP_ABC(OP_SENDB, cursp(), idx, n));
- }
- }
- }
- if (val) {
- push();
- }
-}
-
-static void
-gen_assignment(codegen_scope *s, node *tree, int sp, int val)
-{
- int idx;
- int type = (intptr_t)tree->car;
-
- tree = tree->cdr;
- switch ((intptr_t)type) {
- case NODE_GVAR:
- idx = new_sym(s, sym(tree));
- genop_peep(s, MKOP_ABx(OP_SETGLOBAL, sp, idx), val);
- break;
- case NODE_LVAR:
- idx = lv_idx(s, sym(tree));
- if (idx > 0) {
- if (idx != sp) {
- genop_peep(s, MKOP_AB(OP_MOVE, idx, sp), val);
- }
- break;
- }
- else { /* upvar */
- int lv = 0;
- codegen_scope *up = s->prev;
-
- while (up) {
- idx = lv_idx(up, sym(tree));
- if (idx > 0) {
- genop_peep(s, MKOP_ABC(OP_SETUPVAR, sp, idx, lv), val);
- break;
- }
- lv++;
- up = up->prev;
- }
- }
- break;
- case NODE_IVAR:
- idx = new_sym(s, sym(tree));
- genop_peep(s, MKOP_ABx(OP_SETIV, sp, idx), val);
- break;
- case NODE_CVAR:
- idx = new_sym(s, sym(tree));
- genop_peep(s, MKOP_ABx(OP_SETCV, sp, idx), val);
- break;
- case NODE_CONST:
- idx = new_sym(s, sym(tree));
- genop_peep(s, MKOP_ABx(OP_SETCONST, sp, idx), val);
- break;
- case NODE_COLON2:
- idx = new_sym(s, sym(tree->cdr));
- genop_peep(s, MKOP_AB(OP_MOVE, cursp(), sp), NOVAL);
- push();
- codegen(s, tree->car, VAL);
- pop_n(2);
- genop_peep(s, MKOP_ABx(OP_SETMCNST, cursp(), idx), val);
- break;
-
- case NODE_CALL:
- push();
- gen_call(s, tree, attrsym(s, sym(tree->cdr->car)), sp, NOVAL);
- pop();
- if (val) {
- genop_peep(s, MKOP_AB(OP_MOVE, cursp(), sp), val);
- }
- break;
-
- case NODE_MASGN:
- gen_vmassignment(s, tree->car, sp, val);
- break;
-
- push();
- gen_call(s, tree, attrsym(s, sym(tree->cdr->car)), sp, NOVAL);
- pop();
- if (val) {
- genop_peep(s, MKOP_AB(OP_MOVE, cursp(), sp), val);
- }
- break;
-
- default:
-#ifdef ENABLE_STDIO
- printf("unknown lhs %d\n", type);
-#endif
- break;
- }
- if (val) push();
-}
-
-static void
-gen_vmassignment(codegen_scope *s, node *tree, int rhs, int val)
-{
- int n = 0, post = 0;
- node *t, *p;
-
- if (tree->car) { /* pre */
- t = tree->car;
- n = 0;
- while (t) {
- genop(s, MKOP_ABC(OP_AREF, cursp(), rhs, n));
- gen_assignment(s, t->car, cursp(), NOVAL);
- n++;
- t = t->cdr;
- }
- }
- t = tree->cdr;
- if (t) {
- if (t->cdr) { /* post count */
- p = t->cdr->car;
- while (p) {
- post++;
- p = p->cdr;
- }
- }
- if (val) {
- genop(s, MKOP_AB(OP_MOVE, cursp(), rhs));
- push();
- }
- genop(s, MKOP_ABC(OP_APOST, cursp(), n, post));
- n = 1;
- if (t->car) { /* rest */
- gen_assignment(s, t->car, cursp(), NOVAL);
- }
- if (t->cdr && t->cdr->car) {
- t = t->cdr->car;
- while (t) {
- gen_assignment(s, t->car, cursp()+n, NOVAL);
- t = t->cdr;
- n++;
- }
- }
- }
- if (!val) {
- pop();
- }
-}
-
-static void
-gen_send_intern(codegen_scope *s)
-{
- pop();
- genop(s, MKOP_ABC(OP_SEND, cursp(), new_msym(s, mrb_intern_lit(s->mrb, "intern")), 0));
- push();
-}
-static void
-gen_literal_array(codegen_scope *s, node *tree, mrb_bool sym, int val)
-{
- if (val) {
- int i = 0, j = 0;
-
- while (tree) {
- switch ((intptr_t)tree->car->car) {
- case NODE_STR:
- if ((tree->cdr == NULL) && ((intptr_t)tree->car->cdr->cdr == 0))
- break;
- /* fall through */
- case NODE_BEGIN:
- codegen(s, tree->car, VAL);
- ++j;
- break;
-
- case NODE_LITERAL_DELIM:
- if (j > 0) {
- j = 0;
- ++i;
- if (sym)
- gen_send_intern(s);
- }
- break;
- }
- if (j >= 2) {
- pop(); pop();
- genop_peep(s, MKOP_AB(OP_STRCAT, cursp(), cursp()+1), VAL);
- push();
- j = 1;
- }
- tree = tree->cdr;
- }
- if (j > 0) {
- ++i;
- if (sym)
- gen_send_intern(s);
- }
- pop_n(i);
- genop(s, MKOP_ABC(OP_ARRAY, cursp(), cursp(), i));
- push();
- }
- else {
- while (tree) {
- switch ((intptr_t)tree->car->car) {
- case NODE_BEGIN: case NODE_BLOCK:
- codegen(s, tree->car, NOVAL);
- }
- tree = tree->cdr;
- }
- }
-}
-
-static void
-raise_error(codegen_scope *s, const char *msg)
-{
- int idx = new_lit(s, mrb_str_new_cstr(s->mrb, msg));
-
- genop(s, MKOP_ABx(OP_ERR, 1, idx));
-}
-
-static double
-readint_float(codegen_scope *s, const char *p, int base)
-{
- const char *e = p + strlen(p);
- double f = 0;
- int n;
-
- if (*p == '+') p++;
- while (p < e) {
- char c = *p;
- c = tolower((unsigned char)c);
- for (n=0; n<base; n++) {
- if (mrb_digitmap[n] == c) {
- f *= base;
- f += n;
- break;
- }
- }
- if (n == base) {
- codegen_error(s, "malformed readint input");
- }
- p++;
- }
- return f;
-}
-
-static mrb_int
-readint_mrb_int(codegen_scope *s, const char *p, int base, mrb_bool neg, mrb_bool *overflow)
-{
- const char *e = p + strlen(p);
- mrb_int result = 0;
- int n;
-
- mrb_assert(base >= 2 && base <= 36);
- if (*p == '+') p++;
- while (p < e) {
- char c = *p;
- c = tolower((unsigned char)c);
- for (n=0; n<base; n++) {
- if (mrb_digitmap[n] == c) {
- break;
- }
- }
- if (n == base) {
- codegen_error(s, "malformed readint input");
- }
-
- if (neg) {
- if ((MRB_INT_MIN + n)/base > result) {
- *overflow = TRUE;
- return 0;
- }
- result *= base;
- result -= n;
- }
- else {
- if ((MRB_INT_MAX - n)/base < result) {
- *overflow = TRUE;
- return 0;
- }
- result *= base;
- result += n;
- }
- p++;
- }
- *overflow = FALSE;
- return result;
-}
-
-static void
-codegen(codegen_scope *s, node *tree, int val)
-{
- int nt;
-
- if (!tree) return;
-
- if (s->irep && s->filename_index != tree->filename_index) {
- s->irep->filename = mrb_parser_get_filename(s->parser, s->filename_index);
- mrb_debug_info_append_file(s->mrb, s->irep, s->debug_start_pos, s->pc);
- s->debug_start_pos = s->pc;
- s->filename_index = tree->filename_index;
- s->filename = mrb_parser_get_filename(s->parser, tree->filename_index);
- }
-
- nt = (intptr_t)tree->car;
- s->lineno = tree->lineno;
- tree = tree->cdr;
- switch (nt) {
- case NODE_BEGIN:
- if (val && !tree) {
- genop(s, MKOP_A(OP_LOADNIL, cursp()));
- push();
- }
- while (tree) {
- codegen(s, tree->car, tree->cdr ? NOVAL : val);
- tree = tree->cdr;
- }
- break;
-
- case NODE_RESCUE:
- {
- int onerr, noexc, exend, pos1, pos2, tmp;
- struct loopinfo *lp;
-
- onerr = genop(s, MKOP_Bx(OP_ONERR, 0));
- lp = loop_push(s, LOOP_BEGIN);
- lp->pc1 = onerr;
- if (tree->car) {
- codegen(s, tree->car, val);
- if (val) pop();
- }
- lp->type = LOOP_RESCUE;
- noexc = genop(s, MKOP_Bx(OP_JMP, 0));
- dispatch(s, onerr);
- tree = tree->cdr;
- exend = 0;
- pos1 = 0;
- if (tree->car) {
- node *n2 = tree->car;
- int exc = cursp();
-
- genop(s, MKOP_A(OP_RESCUE, exc));
- push();
- while (n2) {
- node *n3 = n2->car;
- node *n4 = n3->car;
-
- if (pos1) dispatch(s, pos1);
- pos2 = 0;
- do {
- if (n4) {
- codegen(s, n4->car, VAL);
- }
- else {
- genop(s, MKOP_ABx(OP_GETCONST, cursp(), new_msym(s, mrb_intern_lit(s->mrb, "StandardError"))));
- push();
- }
- genop(s, MKOP_AB(OP_MOVE, cursp(), exc));
- pop();
- if (n4 && n4->car && (intptr_t)n4->car->car == NODE_SPLAT) {
- genop(s, MKOP_ABC(OP_SEND, cursp(), new_msym(s, mrb_intern_lit(s->mrb, "__case_eqq")), 1));
- }
- else {
- genop(s, MKOP_ABC(OP_SEND, cursp(), new_msym(s, mrb_intern_lit(s->mrb, "===")), 1));
- }
- tmp = genop(s, MKOP_AsBx(OP_JMPIF, cursp(), pos2));
- pos2 = tmp;
- if (n4) {
- n4 = n4->cdr;
- }
- } while (n4);
- pos1 = genop(s, MKOP_sBx(OP_JMP, 0));
- dispatch_linked(s, pos2);
-
- pop();
- if (n3->cdr->car) {
- gen_assignment(s, n3->cdr->car, exc, NOVAL);
- }
- if (n3->cdr->cdr->car) {
- codegen(s, n3->cdr->cdr->car, val);
- if (val) pop();
- }
- tmp = genop(s, MKOP_sBx(OP_JMP, exend));
- exend = tmp;
- n2 = n2->cdr;
- push();
- }
- if (pos1) {
- dispatch(s, pos1);
- genop(s, MKOP_A(OP_RAISE, exc));
- }
- }
- pop();
- tree = tree->cdr;
- dispatch(s, noexc);
- genop(s, MKOP_A(OP_POPERR, 1));
- if (tree->car) {
- codegen(s, tree->car, val);
- }
- else if (val) {
- push();
- }
- dispatch_linked(s, exend);
- loop_pop(s, NOVAL);
- }
- break;
-
- case NODE_ENSURE:
- {
- int idx;
- int epush = s->pc;
-
- genop(s, MKOP_Bx(OP_EPUSH, 0));
- s->ensure_level++;
- codegen(s, tree->car, val);
- idx = scope_body(s, tree->cdr, NOVAL);
- s->iseq[epush] = MKOP_Bx(OP_EPUSH, idx);
- s->ensure_level--;
- genop_peep(s, MKOP_A(OP_EPOP, 1), NOVAL);
- }
- break;
-
- case NODE_LAMBDA:
- {
- int idx = lambda_body(s, tree, 1);
-
- genop(s, MKOP_Abc(OP_LAMBDA, cursp(), idx, OP_L_LAMBDA));
- push();
- }
- break;
-
- case NODE_BLOCK:
- {
- int idx = lambda_body(s, tree, 1);
-
- genop(s, MKOP_Abc(OP_LAMBDA, cursp(), idx, OP_L_BLOCK));
- push();
- }
- break;
-
- case NODE_IF:
- {
- int pos1, pos2;
- node *e = tree->cdr->cdr->car;
-
- codegen(s, tree->car, VAL);
- pop();
- pos1 = genop_peep(s, MKOP_AsBx(OP_JMPNOT, cursp(), 0), NOVAL);
-
- codegen(s, tree->cdr->car, val);
- if (val && !(tree->cdr->car)) {
- genop(s, MKOP_A(OP_LOADNIL, cursp()));
- push();
- }
- if (e) {
- if (val) pop();
- pos2 = genop(s, MKOP_sBx(OP_JMP, 0));
- dispatch(s, pos1);
- codegen(s, e, val);
- dispatch(s, pos2);
- }
- else {
- if (val) {
- pop();
- pos2 = genop(s, MKOP_sBx(OP_JMP, 0));
- dispatch(s, pos1);
- genop(s, MKOP_A(OP_LOADNIL, cursp()));
- dispatch(s, pos2);
- push();
- }
- else {
- dispatch(s, pos1);
- }
- }
- }
- break;
-
- case NODE_AND:
- {
- int pos;
-
- codegen(s, tree->car, VAL);
- pop();
- pos = genop(s, MKOP_AsBx(OP_JMPNOT, cursp(), 0));
- codegen(s, tree->cdr, val);
- dispatch(s, pos);
- }
- break;
-
- case NODE_OR:
- {
- int pos;
-
- codegen(s, tree->car, VAL);
- pop();
- pos = genop(s, MKOP_AsBx(OP_JMPIF, cursp(), 0));
- codegen(s, tree->cdr, val);
- dispatch(s, pos);
- }
- break;
-
- case NODE_WHILE:
- {
- struct loopinfo *lp = loop_push(s, LOOP_NORMAL);
-
- lp->pc1 = genop(s, MKOP_sBx(OP_JMP, 0));
- lp->pc2 = new_label(s);
- codegen(s, tree->cdr, NOVAL);
- dispatch(s, lp->pc1);
- codegen(s, tree->car, VAL);
- pop();
- genop(s, MKOP_AsBx(OP_JMPIF, cursp(), lp->pc2 - s->pc));
-
- loop_pop(s, val);
- }
- break;
-
- case NODE_UNTIL:
- {
- struct loopinfo *lp = loop_push(s, LOOP_NORMAL);
-
- lp->pc1 = genop(s, MKOP_sBx(OP_JMP, 0));
- lp->pc2 = new_label(s);
- codegen(s, tree->cdr, NOVAL);
- dispatch(s, lp->pc1);
- codegen(s, tree->car, VAL);
- pop();
- genop(s, MKOP_AsBx(OP_JMPNOT, cursp(), lp->pc2 - s->pc));
-
- loop_pop(s, val);
- }
- break;
-
- case NODE_FOR:
- for_body(s, tree);
- if (val) push();
- break;
-
- case NODE_CASE:
- {
- int head = 0;
- int pos1, pos2, pos3, tmp;
- node *n;
-
- pos3 = 0;
- if (tree->car) {
- head = cursp();
- codegen(s, tree->car, VAL);
- }
- tree = tree->cdr;
- while (tree) {
- n = tree->car->car;
- pos1 = pos2 = 0;
- while (n) {
- codegen(s, n->car, VAL);
- if (head) {
- genop(s, MKOP_AB(OP_MOVE, cursp(), head));
- pop();
- if ((intptr_t)n->car->car == NODE_SPLAT) {
- genop(s, MKOP_ABC(OP_SEND, cursp(), new_msym(s, mrb_intern_lit(s->mrb, "__case_eqq")), 1));
- }
- else {
- genop(s, MKOP_ABC(OP_SEND, cursp(), new_msym(s, mrb_intern_lit(s->mrb, "===")), 1));
- }
- }
- else {
- pop();
- }
- tmp = genop(s, MKOP_AsBx(OP_JMPIF, cursp(), pos2));
- pos2 = tmp;
- n = n->cdr;
- }
- if (tree->car->car) {
- pos1 = genop(s, MKOP_sBx(OP_JMP, 0));
- dispatch_linked(s, pos2);
- }
- codegen(s, tree->car->cdr, val);
- if (val) pop();
- tmp = genop(s, MKOP_sBx(OP_JMP, pos3));
- pos3 = tmp;
- if (pos1) dispatch(s, pos1);
- tree = tree->cdr;
- }
- if (val) {
- int pos = cursp();
- genop(s, MKOP_A(OP_LOADNIL, cursp()));
- if (pos3) dispatch_linked(s, pos3);
- pop();
- genop(s, MKOP_AB(OP_MOVE, cursp(), pos));
- push();
- }
- else if (pos3) {
- dispatch_linked(s, pos3);
- }
- }
- break;
-
- case NODE_SCOPE:
- scope_body(s, tree, NOVAL);
- break;
-
- case NODE_FCALL:
- case NODE_CALL:
- gen_call(s, tree, 0, 0, val);
- break;
-
- case NODE_DOT2:
- codegen(s, tree->car, val);
- codegen(s, tree->cdr, val);
- if (val) {
- pop(); pop();
- genop(s, MKOP_ABC(OP_RANGE, cursp(), cursp(), FALSE));
- push();
- }
- break;
-
- case NODE_DOT3:
- codegen(s, tree->car, val);
- codegen(s, tree->cdr, val);
- if (val) {
- pop(); pop();
- genop(s, MKOP_ABC(OP_RANGE, cursp(), cursp(), TRUE));
- push();
- }
- break;
-
- case NODE_COLON2:
- {
- int sym = new_sym(s, sym(tree->cdr));
-
- codegen(s, tree->car, VAL);
- pop();
- genop(s, MKOP_ABx(OP_GETMCNST, cursp(), sym));
- if (val) push();
- }
- break;
-
- case NODE_COLON3:
- {
- int sym = new_sym(s, sym(tree));
-
- genop(s, MKOP_A(OP_OCLASS, cursp()));
- genop(s, MKOP_ABx(OP_GETMCNST, cursp(), sym));
- if (val) push();
- }
- break;
-
- case NODE_ARRAY:
- {
- int n;
-
- n = gen_values(s, tree, val);
- if (n >= 0) {
- if (val) {
- pop_n(n);
- genop(s, MKOP_ABC(OP_ARRAY, cursp(), cursp(), n));
- push();
- }
- }
- else if (val) {
- push();
- }
- }
- break;
-
- case NODE_HASH:
- {
- int len = 0;
- mrb_bool update = FALSE;
-
- while (tree) {
- codegen(s, tree->car->car, val);
- codegen(s, tree->car->cdr, val);
- len++;
- tree = tree->cdr;
- if (val && len == 126) {
- pop_n(len*2);
- genop(s, MKOP_ABC(OP_HASH, cursp(), cursp(), len));
- if (update) {
- pop();
- genop(s, MKOP_ABC(OP_SEND, cursp(), new_msym(s, mrb_intern_lit(s->mrb, "__update")), 1));
- }
- push();
- update = TRUE;
- len = 0;
- }
- }
- if (val) {
- pop_n(len*2);
- genop(s, MKOP_ABC(OP_HASH, cursp(), cursp(), len));
- if (update) {
- pop();
- genop(s, MKOP_ABC(OP_SEND, cursp(), new_msym(s, mrb_intern_lit(s->mrb, "__update")), 1));
- }
- push();
- }
- }
- break;
-
- case NODE_SPLAT:
- codegen(s, tree, VAL);
- break;
-
- case NODE_ASGN:
- codegen(s, tree->cdr, VAL);
- pop();
- gen_assignment(s, tree->car, cursp(), val);
- break;
-
- case NODE_MASGN:
- {
- int len = 0, n = 0, post = 0;
- node *t = tree->cdr, *p;
- int rhs = cursp();
-
- if ((intptr_t)t->car == NODE_ARRAY && nosplat(t->cdr)) {
- /* fixed rhs */
- t = t->cdr;
- while (t) {
- codegen(s, t->car, VAL);
- len++;
- t = t->cdr;
- }
- tree = tree->car;
- if (tree->car) { /* pre */
- t = tree->car;
- n = 0;
- while (t) {
- gen_assignment(s, t->car, rhs+n, NOVAL);
- n++;
- t = t->cdr;
- }
- }
- t = tree->cdr;
- if (t) {
- if (t->cdr) { /* post count */
- p = t->cdr->car;
- while (p) {
- post++;
- p = p->cdr;
- }
- }
- if (t->car) { /* rest (len - pre - post) */
- int rn = len - post - n;
-
- genop(s, MKOP_ABC(OP_ARRAY, cursp(), rhs+n, rn));
- gen_assignment(s, t->car, cursp(), NOVAL);
- n += rn;
- }
- if (t->cdr && t->cdr->car) {
- t = t->cdr->car;
- while (n<len) {
- gen_assignment(s, t->car, rhs+n, NOVAL);
- t = t->cdr;
- n++;
- }
- }
- }
- pop_n(len);
- if (val) {
- genop(s, MKOP_ABC(OP_ARRAY, rhs, rhs, len));
- push();
- }
- }
- else {
- /* variable rhs */
- codegen(s, t, VAL);
- gen_vmassignment(s, tree->car, rhs, val);
- }
- }
- break;
-
- case NODE_OP_ASGN:
- {
- mrb_sym sym = sym(tree->cdr->car);
- mrb_int len;
- const char *name = mrb_sym2name_len(s->mrb, sym, &len);
- int idx;
-
- codegen(s, tree->car, VAL);
- if (len == 2 &&
- ((name[0] == '|' && name[1] == '|') ||
- (name[0] == '&' && name[1] == '&'))) {
- int pos;
-
- pop();
- pos = genop_peep(s, MKOP_AsBx(name[0] == '|' ? OP_JMPIF : OP_JMPNOT, cursp(), 0), NOVAL);
- codegen(s, tree->cdr->cdr->car, VAL);
- pop();
- gen_assignment(s, tree->car, cursp(), val);
- dispatch(s, pos);
- break;
- }
- codegen(s, tree->cdr->cdr->car, VAL);
- push(); pop();
- pop(); pop();
-
- idx = new_msym(s, sym);
- if (len == 1 && name[0] == '+') {
- genop_peep(s, MKOP_ABC(OP_ADD, cursp(), idx, 1), val);
- }
- else if (len == 1 && name[0] == '-') {
- genop_peep(s, MKOP_ABC(OP_SUB, cursp(), idx, 1), val);
- }
- else if (len == 1 && name[0] == '*') {
- genop(s, MKOP_ABC(OP_MUL, cursp(), idx, 1));
- }
- else if (len == 1 && name[0] == '/') {
- genop(s, MKOP_ABC(OP_DIV, cursp(), idx, 1));
- }
- else if (len == 1 && name[0] == '<') {
- genop(s, MKOP_ABC(OP_LT, cursp(), idx, 1));
- }
- else if (len == 2 && name[0] == '<' && name[1] == '=') {
- genop(s, MKOP_ABC(OP_LE, cursp(), idx, 1));
- }
- else if (len == 1 && name[0] == '>') {
- genop(s, MKOP_ABC(OP_GT, cursp(), idx, 1));
- }
- else if (len == 2 && name[0] == '>' && name[1] == '=') {
- genop(s, MKOP_ABC(OP_GE, cursp(), idx, 1));
- }
- else {
- genop(s, MKOP_ABC(OP_SEND, cursp(), idx, 1));
- }
- }
- gen_assignment(s, tree->car, cursp(), val);
- break;
-
- case NODE_SUPER:
- {
- int n = 0, noop = 0, sendv = 0;
-
- push(); /* room for receiver */
- if (tree) {
- node *args = tree->car;
- if (args) {
- n = gen_values(s, args, VAL);
- if (n < 0) {
- n = noop = sendv = 1;
- push();
- }
- }
- }
- if (tree && tree->cdr) {
- codegen(s, tree->cdr, VAL);
- pop();
- }
- else {
- genop(s, MKOP_A(OP_LOADNIL, cursp()));
- push(); pop();
- }
- pop_n(n+1);
- if (sendv) n = CALL_MAXARGS;
- genop(s, MKOP_ABC(OP_SUPER, cursp(), 0, n));
- if (val) push();
- }
- break;
-
- case NODE_ZSUPER:
- {
- codegen_scope *s2 = s;
- int lv = 0, ainfo = 0;
-
- push(); /* room for receiver */
- while (!s2->mscope) {
- lv++;
- s2 = s2->prev;
- if (!s2) break;
- }
- if (s2) ainfo = s2->ainfo;
- genop(s, MKOP_ABx(OP_ARGARY, cursp(), (ainfo<<4)|(lv & 0xf)));
- push(); push(); pop(); /* ARGARY pushes two values */
- if (tree && tree->cdr) {
- codegen(s, tree->cdr, VAL);
- pop();
- }
- pop(); pop();
- genop(s, MKOP_ABC(OP_SUPER, cursp(), 0, CALL_MAXARGS));
- if (val) push();
- }
- break;
-
- case NODE_RETURN:
- if (tree) {
- codegen(s, tree, VAL);
- pop();
- }
- else {
- genop(s, MKOP_A(OP_LOADNIL, cursp()));
- }
- if (s->loop) {
- genop(s, MKOP_AB(OP_RETURN, cursp(), OP_R_RETURN));
- }
- else {
- genop_peep(s, MKOP_AB(OP_RETURN, cursp(), OP_R_NORMAL), NOVAL);
- }
- if (val) push();
- break;
-
- case NODE_YIELD:
- {
- codegen_scope *s2 = s;
- int lv = 0, ainfo = 0;
- int n = 0, sendv = 0;
-
- while (!s2->mscope) {
- lv++;
- s2 = s2->prev;
- if (!s2) break;
- }
- if (s2) ainfo = s2->ainfo;
- genop(s, MKOP_ABx(OP_BLKPUSH, cursp(), (ainfo<<4)|(lv & 0xf)));
- push();
- if (tree) {
- n = gen_values(s, tree, VAL);
- if (n < 0) {
- n = sendv = 1;
- push();
- }
- }
- pop_n(n+1);
- if (sendv) n = CALL_MAXARGS;
- genop(s, MKOP_ABC(OP_SEND, cursp(), new_msym(s, mrb_intern_lit(s->mrb, "call")), n));
- if (val) push();
- }
- break;
-
- case NODE_BREAK:
- loop_break(s, tree);
- if (val) push();
- break;
-
- case NODE_NEXT:
- if (!s->loop) {
- raise_error(s, "unexpected next");
- }
- else if (s->loop->type == LOOP_NORMAL) {
- if (s->ensure_level > s->loop->ensure_level) {
- genop_peep(s, MKOP_A(OP_EPOP, s->ensure_level - s->loop->ensure_level), NOVAL);
- }
- codegen(s, tree, NOVAL);
- genop(s, MKOP_sBx(OP_JMP, s->loop->pc1 - s->pc));
- }
- else {
- if (tree) {
- codegen(s, tree, VAL);
- pop();
- }
- else {
- genop(s, MKOP_A(OP_LOADNIL, cursp()));
- }
- genop_peep(s, MKOP_AB(OP_RETURN, cursp(), OP_R_NORMAL), NOVAL);
- }
- if (val) push();
- break;
-
- case NODE_REDO:
- if (!s->loop) {
- raise_error(s, "unexpected redo");
- }
- else {
- if (s->ensure_level > s->loop->ensure_level) {
- genop_peep(s, MKOP_A(OP_EPOP, s->ensure_level - s->loop->ensure_level), NOVAL);
- }
- genop(s, MKOP_sBx(OP_JMP, s->loop->pc2 - s->pc));
- }
- break;
-
- case NODE_RETRY:
- {
- const char *msg = "unexpected retry";
-
- if (!s->loop) {
- raise_error(s, msg);
- }
- else {
- struct loopinfo *lp = s->loop;
- int n = 0;
-
- while (lp && lp->type != LOOP_RESCUE) {
- if (lp->type == LOOP_BEGIN) {
- n++;
- }
- lp = lp->prev;
- }
- if (!lp) {
- raise_error(s, msg);
- }
- else {
- if (n > 0) {
- while (n--) {
- genop_peep(s, MKOP_A(OP_POPERR, 1), NOVAL);
- }
- }
- if (s->ensure_level > lp->ensure_level) {
- genop_peep(s, MKOP_A(OP_EPOP, s->ensure_level - lp->ensure_level), NOVAL);
- }
- genop(s, MKOP_sBx(OP_JMP, lp->pc1 - s->pc));
- }
- }
- }
- break;
-
- case NODE_LVAR:
- if (val) {
- int idx = lv_idx(s, sym(tree));
-
- if (idx > 0) {
- genop_peep(s, MKOP_AB(OP_MOVE, cursp(), idx), NOVAL);
- }
- else {
- int lv = 0;
- codegen_scope *up = s->prev;
-
- while (up) {
- idx = lv_idx(up, sym(tree));
- if (idx > 0) {
- genop(s, MKOP_ABC(OP_GETUPVAR, cursp(), idx, lv));
- break;
- }
- lv++;
- up = up->prev;
- }
- }
- push();
- }
- break;
-
- case NODE_GVAR:
- {
- int sym = new_sym(s, sym(tree));
-
- genop(s, MKOP_ABx(OP_GETGLOBAL, cursp(), sym));
- push();
- }
- break;
-
- case NODE_IVAR:
- {
- int sym = new_sym(s, sym(tree));
-
- genop(s, MKOP_ABx(OP_GETIV, cursp(), sym));
- push();
- }
- break;
-
- case NODE_CVAR:
- {
- int sym = new_sym(s, sym(tree));
-
- genop(s, MKOP_ABx(OP_GETCV, cursp(), sym));
- push();
- }
- break;
-
- case NODE_CONST:
- {
- int sym = new_sym(s, sym(tree));
-
- genop(s, MKOP_ABx(OP_GETCONST, cursp(), sym));
- push();
- }
- break;
-
- case NODE_DEFINED:
- codegen(s, tree, VAL);
- break;
-
- case NODE_BACK_REF:
- {
- char buf[2] = { '$' };
- mrb_value str;
- int sym;
-
- buf[1] = (char)(intptr_t)tree;
- str = mrb_str_new(s->mrb, buf, 2);
- sym = new_sym(s, mrb_intern_str(s->mrb, str));
- genop(s, MKOP_ABx(OP_GETGLOBAL, cursp(), sym));
- push();
- }
- break;
-
- case NODE_NTH_REF:
- {
- int sym;
- mrb_state *mrb = s->mrb;
- mrb_value fix = mrb_fixnum_value((intptr_t)tree);
- mrb_value str = mrb_str_buf_new(mrb, 4);
-
- mrb_str_cat_lit(mrb, str, "$");
- mrb_str_cat_str(mrb, str, mrb_fixnum_to_str(mrb, fix, 10));
- sym = new_sym(s, mrb_intern_str(mrb, str));
- genop(s, MKOP_ABx(OP_GETGLOBAL, cursp(), sym));
- push();
- }
- break;
-
- case NODE_ARG:
- /* should not happen */
- break;
-
- case NODE_BLOCK_ARG:
- codegen(s, tree, VAL);
- break;
-
- case NODE_INT:
- if (val) {
- char *p = (char*)tree->car;
- int base = (intptr_t)tree->cdr->car;
- mrb_int i;
- mrb_code co;
- mrb_bool overflow;
-
- i = readint_mrb_int(s, p, base, FALSE, &overflow);
- if (overflow) {
- double f = readint_float(s, p, base);
- int off = new_lit(s, mrb_float_value(s->mrb, f));
-
- genop(s, MKOP_ABx(OP_LOADL, cursp(), off));
- }
- else {
- if (i < MAXARG_sBx && i > -MAXARG_sBx) {
- co = MKOP_AsBx(OP_LOADI, cursp(), i);
- }
- else {
- int off = new_lit(s, mrb_fixnum_value(i));
- co = MKOP_ABx(OP_LOADL, cursp(), off);
- }
- genop(s, co);
- }
- push();
- }
- break;
-
- case NODE_FLOAT:
- if (val) {
- char *p = (char*)tree;
- mrb_float f = str_to_mrb_float(p);
- int off = new_lit(s, mrb_float_value(s->mrb, f));
-
- genop(s, MKOP_ABx(OP_LOADL, cursp(), off));
- push();
- }
- break;
-
- case NODE_NEGATE:
- {
- nt = (intptr_t)tree->car;
- tree = tree->cdr;
- switch (nt) {
- case NODE_FLOAT:
- {
- char *p = (char*)tree;
- mrb_float f = str_to_mrb_float(p);
- int off = new_lit(s, mrb_float_value(s->mrb, -f));
-
- genop(s, MKOP_ABx(OP_LOADL, cursp(), off));
- push();
- }
- break;
-
- case NODE_INT:
- {
- char *p = (char*)tree->car;
- int base = (intptr_t)tree->cdr->car;
- mrb_int i;
- mrb_code co;
- mrb_bool overflow;
-
- i = readint_mrb_int(s, p, base, TRUE, &overflow);
- if (overflow) {
- double f = readint_float(s, p, base);
- int off = new_lit(s, mrb_float_value(s->mrb, -f));
-
- genop(s, MKOP_ABx(OP_LOADL, cursp(), off));
- }
- else {
- if (i < MAXARG_sBx && i > -MAXARG_sBx) {
- co = MKOP_AsBx(OP_LOADI, cursp(), i);
- }
- else {
- int off = new_lit(s, mrb_fixnum_value(i));
- co = MKOP_ABx(OP_LOADL, cursp(), off);
- }
- genop(s, co);
- }
- push();
- }
- break;
-
- default:
- {
- int sym = new_msym(s, mrb_intern_lit(s->mrb, "-"));
-
- genop(s, MKOP_ABx(OP_LOADI, cursp(), 0));
- push();
- codegen(s, tree, VAL);
- pop(); pop();
- genop(s, MKOP_ABC(OP_SUB, cursp(), sym, 2));
- }
- break;
- }
- }
- break;
-
- case NODE_STR:
- if (val) {
- char *p = (char*)tree->car;
- size_t len = (intptr_t)tree->cdr;
- int ai = mrb_gc_arena_save(s->mrb);
- int off = new_lit(s, mrb_str_new(s->mrb, p, len));
-
- mrb_gc_arena_restore(s->mrb, ai);
- genop(s, MKOP_ABx(OP_STRING, cursp(), off));
- push();
- }
- break;
-
- case NODE_HEREDOC:
- tree = ((struct mrb_parser_heredoc_info *)tree)->doc;
- /* fall through */
- case NODE_DSTR:
- if (val) {
- node *n = tree;
-
- codegen(s, n->car, VAL);
- n = n->cdr;
- while (n) {
- codegen(s, n->car, VAL);
- pop(); pop();
- genop_peep(s, MKOP_AB(OP_STRCAT, cursp(), cursp()+1), VAL);
- push();
- n = n->cdr;
- }
- }
- else {
- node *n = tree;
-
- while (n) {
- if ((intptr_t)n->car->car != NODE_STR) {
- codegen(s, n->car, NOVAL);
- }
- n = n->cdr;
- }
- }
- break;
-
- case NODE_WORDS:
- gen_literal_array(s, tree, FALSE, val);
- break;
-
- case NODE_SYMBOLS:
- gen_literal_array(s, tree, TRUE, val);
- break;
-
- case NODE_DXSTR:
- {
- node *n;
- int ai = mrb_gc_arena_save(s->mrb);
- int sym = new_sym(s, mrb_intern_lit(s->mrb, "Kernel"));
-
- if (val == NOVAL) { push(); }
- genop(s, MKOP_A(OP_OCLASS, cursp()));
- genop(s, MKOP_ABx(OP_GETMCNST, cursp(), sym));
- push();
- codegen(s, tree->car, VAL);
- n = tree->cdr;
- while (n) {
- if ((intptr_t)n->car->car == NODE_XSTR) {
- n->car->car = (struct mrb_ast_node*)(intptr_t)NODE_STR;
- mrb_assert(!n->cdr); /* must be the end */
- }
- codegen(s, n->car, VAL);
- pop(); pop();
- genop_peep(s, MKOP_AB(OP_STRCAT, cursp(), cursp()+1), VAL);
- push();
- n = n->cdr;
- }
- pop();
- pop();
- sym = new_sym(s, mrb_intern_lit(s->mrb, "`"));
- genop(s, MKOP_ABC(OP_SEND, cursp(), sym, 1));
- if (val == NOVAL) { pop(); }
- else { push(); }
- mrb_gc_arena_restore(s->mrb, ai);
- }
- break;
-
- case NODE_XSTR:
- {
- char *p = (char*)tree->car;
- size_t len = (intptr_t)tree->cdr;
- int ai = mrb_gc_arena_save(s->mrb);
- int sym = new_sym(s, mrb_intern_lit(s->mrb, "Kernel"));
- int off = new_lit(s, mrb_str_new(s->mrb, p, len));
-
- if (val == NOVAL) { push(); }
- genop(s, MKOP_A(OP_OCLASS, cursp()));
- genop(s, MKOP_ABx(OP_GETMCNST, cursp(), sym));
- push();
- genop(s, MKOP_ABx(OP_STRING, cursp(), off));
- pop();
- sym = new_sym(s, mrb_intern_lit(s->mrb, "`"));
- genop(s, MKOP_ABC(OP_SEND, cursp(), sym, 1));
- if (val == NOVAL) { pop(); }
- else { push(); }
- mrb_gc_arena_restore(s->mrb, ai);
- }
- break;
-
- case NODE_REGX:
- if (val) {
- char *p1 = (char*)tree->car;
- char *p2 = (char*)tree->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));
- int argc = 1;
-
- genop(s, MKOP_A(OP_OCLASS, cursp()));
- genop(s, MKOP_ABx(OP_GETMCNST, cursp(), sym));
- push();
- genop(s, MKOP_ABx(OP_STRING, cursp(), off));
- if (p2) {
- push();
- off = new_lit(s, mrb_str_new_cstr(s->mrb, p2));
- genop(s, MKOP_ABx(OP_STRING, cursp(), off));
- argc++;
- pop();
- }
- pop();
- sym = new_sym(s, mrb_intern_lit(s->mrb, "compile"));
- genop(s, MKOP_ABC(OP_SEND, cursp(), sym, argc));
- mrb_gc_arena_restore(s->mrb, ai);
- push();
- }
- break;
-
- case NODE_DREGX:
- if (val) {
- node *n = tree->car;
- int ai = mrb_gc_arena_save(s->mrb);
- int sym = new_sym(s, mrb_intern_lit(s->mrb, REGEXP_CLASS));
- int argc = 1;
- int off;
- char *p;
-
- genop(s, MKOP_A(OP_OCLASS, cursp()));
- genop(s, MKOP_ABx(OP_GETMCNST, cursp(), sym));
- push();
- codegen(s, n->car, VAL);
- n = n->cdr;
- while (n) {
- codegen(s, n->car, VAL);
- pop(); pop();
- genop_peep(s, MKOP_AB(OP_STRCAT, cursp(), cursp()+1), VAL);
- push();
- n = n->cdr;
- }
- n = tree->cdr->cdr;
- if (n->car) {
- p = (char*)n->car;
- off = new_lit(s, mrb_str_new_cstr(s->mrb, p));
- codegen(s, tree->car, VAL);
- genop(s, MKOP_ABx(OP_STRING, cursp(), off));
- pop();
- genop_peep(s, MKOP_AB(OP_STRCAT, cursp(), cursp()+1), VAL);
- }
- if (n->cdr) {
- char *p2 = (char*)n->cdr;
-
- push();
- off = new_lit(s, mrb_str_new_cstr(s->mrb, p2));
- genop(s, MKOP_ABx(OP_STRING, cursp(), off));
- argc++;
- pop();
- }
- pop();
- sym = new_sym(s, mrb_intern_lit(s->mrb, "compile"));
- genop(s, MKOP_ABC(OP_SEND, cursp(), sym, argc));
- mrb_gc_arena_restore(s->mrb, ai);
- push();
- }
- else {
- node *n = tree->car;
-
- while (n) {
- if ((intptr_t)n->car->car != NODE_STR) {
- codegen(s, n->car, NOVAL);
- }
- n = n->cdr;
- }
- }
- break;
-
- case NODE_SYM:
- if (val) {
- int sym = new_sym(s, sym(tree));
-
- genop(s, MKOP_ABx(OP_LOADSYM, cursp(), sym));
- push();
- }
- break;
-
- case NODE_DSYM:
- codegen(s, tree, val);
- if (val) {
- gen_send_intern(s);
- }
- break;
-
- case NODE_SELF:
- if (val) {
- genop(s, MKOP_A(OP_LOADSELF, cursp()));
- push();
- }
- break;
-
- case NODE_NIL:
- if (val) {
- genop(s, MKOP_A(OP_LOADNIL, cursp()));
- push();
- }
- break;
-
- case NODE_TRUE:
- if (val) {
- genop(s, MKOP_A(OP_LOADT, cursp()));
- push();
- }
- break;
-
- case NODE_FALSE:
- if (val) {
- genop(s, MKOP_A(OP_LOADF, cursp()));
- push();
- }
- break;
-
- case NODE_ALIAS:
- {
- int a = new_msym(s, sym(tree->car));
- int b = new_msym(s, sym(tree->cdr));
- int c = new_msym(s, mrb_intern_lit(s->mrb, "alias_method"));
-
- genop(s, MKOP_A(OP_TCLASS, cursp()));
- push();
- genop(s, MKOP_ABx(OP_LOADSYM, cursp(), a));
- push();
- genop(s, MKOP_ABx(OP_LOADSYM, cursp(), b));
- push();
- genop(s, MKOP_A(OP_LOADNIL, cursp()));
- pop_n(3);
- genop(s, MKOP_ABC(OP_SEND, cursp(), c, 2));
- if (val) {
- push();
- }
- }
- break;
-
- case NODE_UNDEF:
- {
- int undef = new_msym(s, mrb_intern_lit(s->mrb, "undef_method"));
- int num = 0;
- node *t = tree;
-
- genop(s, MKOP_A(OP_TCLASS, cursp()));
- push();
- while (t) {
- int symbol = new_msym(s, sym(t->car));
- genop(s, MKOP_ABx(OP_LOADSYM, cursp(), symbol));
- push();
- t = t->cdr;
- num++;
- }
- pop_n(num + 1);
- genop(s, MKOP_ABC(OP_SEND, cursp(), undef, num));
- if (val) {
- push();
- }
- }
- break;
-
- case NODE_CLASS:
- {
- int idx;
-
- if (tree->car->car == (node*)0) {
- genop(s, MKOP_A(OP_LOADNIL, cursp()));
- push();
- }
- else if (tree->car->car == (node*)1) {
- genop(s, MKOP_A(OP_OCLASS, cursp()));
- push();
- }
- else {
- codegen(s, tree->car->car, VAL);
- }
- if (tree->cdr->car) {
- codegen(s, tree->cdr->car, VAL);
- }
- else {
- genop(s, MKOP_A(OP_LOADNIL, cursp()));
- push();
- }
- pop(); pop();
- idx = new_msym(s, sym(tree->car->cdr));
- genop(s, MKOP_AB(OP_CLASS, cursp(), idx));
- idx = scope_body(s, tree->cdr->cdr->car, val);
- genop(s, MKOP_ABx(OP_EXEC, cursp(), idx));
- if (val) {
- push();
- }
- }
- break;
-
- case NODE_MODULE:
- {
- int idx;
-
- if (tree->car->car == (node*)0) {
- genop(s, MKOP_A(OP_LOADNIL, cursp()));
- push();
- }
- else if (tree->car->car == (node*)1) {
- genop(s, MKOP_A(OP_OCLASS, cursp()));
- push();
- }
- else {
- codegen(s, tree->car->car, VAL);
- }
- pop();
- idx = new_msym(s, sym(tree->car->cdr));
- genop(s, MKOP_AB(OP_MODULE, cursp(), idx));
- idx = scope_body(s, tree->cdr->car, val);
- genop(s, MKOP_ABx(OP_EXEC, cursp(), idx));
- if (val) {
- push();
- }
- }
- break;
-
- case NODE_SCLASS:
- {
- int idx;
-
- codegen(s, tree->car, VAL);
- pop();
- genop(s, MKOP_AB(OP_SCLASS, cursp(), cursp()));
- idx = scope_body(s, tree->cdr->car, val);
- genop(s, MKOP_ABx(OP_EXEC, cursp(), idx));
- if (val) {
- push();
- }
- }
- break;
-
- case NODE_DEF:
- {
- int sym = new_msym(s, sym(tree->car));
- int idx = lambda_body(s, tree->cdr, 0);
-
- genop(s, MKOP_A(OP_TCLASS, cursp()));
- push();
- genop(s, MKOP_Abc(OP_LAMBDA, cursp(), idx, OP_L_METHOD));
- push(); pop();
- pop();
- genop(s, MKOP_AB(OP_METHOD, cursp(), sym));
- if (val) {
- genop(s, MKOP_ABx(OP_LOADSYM, cursp(), sym));
- push();
- }
- }
- break;
-
- case NODE_SDEF:
- {
- node *recv = tree->car;
- int sym = new_msym(s, sym(tree->cdr->car));
- int idx = lambda_body(s, tree->cdr->cdr, 0);
-
- codegen(s, recv, VAL);
- pop();
- genop(s, MKOP_AB(OP_SCLASS, cursp(), cursp()));
- push();
- genop(s, MKOP_Abc(OP_LAMBDA, cursp(), idx, OP_L_METHOD));
- pop();
- genop(s, MKOP_AB(OP_METHOD, cursp(), sym));
- if (val) {
- genop(s, MKOP_ABx(OP_LOADSYM, cursp(), sym));
- push();
- }
- }
- break;
-
- case NODE_POSTEXE:
- codegen(s, tree, NOVAL);
- break;
-
- default:
- break;
- }
-}
-
-static void
-scope_add_irep(codegen_scope *s, mrb_irep *irep)
-{
- if (s->irep == NULL) {
- s->irep = irep;
- return;
- }
- if (s->irep->rlen == s->rcapa) {
- s->rcapa *= 2;
- s->irep->reps = (mrb_irep**)codegen_realloc(s, s->irep->reps, sizeof(mrb_irep*)*s->rcapa);
- }
- s->irep->reps[s->irep->rlen] = irep;
- s->irep->rlen++;
-}
-
-static codegen_scope*
-scope_new(mrb_state *mrb, codegen_scope *prev, node *lv)
-{
- static const codegen_scope codegen_scope_zero = { 0 };
- mrb_pool *pool = mrb_pool_open(mrb);
- codegen_scope *p = (codegen_scope *)mrb_pool_alloc(pool, sizeof(codegen_scope));
-
- if (!p) return NULL;
- *p = codegen_scope_zero;
- p->mrb = mrb;
- p->mpool = pool;
- if (!prev) return p;
- p->prev = prev;
- p->ainfo = -1;
- p->mscope = 0;
-
- p->irep = mrb_add_irep(mrb);
- scope_add_irep(prev, p->irep);
-
- p->rcapa = 8;
- p->irep->reps = (mrb_irep**)mrb_malloc(mrb, sizeof(mrb_irep*)*p->rcapa);
-
- p->icapa = 1024;
- p->iseq = (mrb_code*)mrb_malloc(mrb, sizeof(mrb_code)*p->icapa);
- p->irep->iseq = p->iseq;
-
- p->pcapa = 32;
- p->irep->pool = (mrb_value*)mrb_malloc(mrb, sizeof(mrb_value)*p->pcapa);
- p->irep->plen = 0;
-
- p->scapa = 256;
- p->irep->syms = (mrb_sym*)mrb_malloc(mrb, sizeof(mrb_sym)*p->scapa);
- p->irep->slen = 0;
-
- p->lv = lv;
- p->sp += node_len(lv)+1; /* add self */
- p->nlocals = p->sp;
- if (lv) {
- node *n = lv;
- size_t i = 0;
-
- p->irep->lv = (struct mrb_locals*)mrb_malloc(mrb, sizeof(struct mrb_locals) * (p->nlocals - 1));
- for (i=0, n=lv; n; i++,n=n->cdr) {
- p->irep->lv[i].name = lv_name(n);
- if (lv_name(n)) {
- p->irep->lv[i].r = lv_idx(p, lv_name(n));
- }
- else {
- p->irep->lv[i].r = 0;
- }
- }
- mrb_assert(i + 1 == p->nlocals);
- }
- p->ai = mrb_gc_arena_save(mrb);
-
- p->filename = prev->filename;
- if (p->filename) {
- p->lines = (uint16_t*)mrb_malloc(mrb, sizeof(short)*p->icapa);
- }
- p->lineno = prev->lineno;
-
- /* debug setting */
- p->debug_start_pos = 0;
- if (p->filename) {
- mrb_debug_info_alloc(mrb, p->irep);
- p->irep->filename = p->filename;
- p->irep->lines = p->lines;
- }
- else {
- p->irep->debug_info = NULL;
- }
- p->parser = prev->parser;
- p->filename_index = prev->filename_index;
-
- return p;
-}
-
-static void
-scope_finish(codegen_scope *s)
-{
- mrb_state *mrb = s->mrb;
- mrb_irep *irep = s->irep;
- size_t fname_len;
- char *fname;
-
- irep->flags = 0;
- if (s->iseq) {
- irep->iseq = (mrb_code *)codegen_realloc(s, s->iseq, sizeof(mrb_code)*s->pc);
- irep->ilen = s->pc;
- if (s->lines) {
- irep->lines = (uint16_t *)codegen_realloc(s, s->lines, sizeof(uint16_t)*s->pc);
- }
- else {
- irep->lines = 0;
- }
- }
- irep->pool = (mrb_value*)codegen_realloc(s, irep->pool, sizeof(mrb_value)*irep->plen);
- irep->syms = (mrb_sym*)codegen_realloc(s, irep->syms, sizeof(mrb_sym)*irep->slen);
- irep->reps = (mrb_irep**)codegen_realloc(s, irep->reps, sizeof(mrb_irep*)*irep->rlen);
- if (s->filename) {
- s->irep->filename = mrb_parser_get_filename(s->parser, s->filename_index);
- mrb_debug_info_append_file(mrb, s->irep, s->debug_start_pos, s->pc);
-
- fname_len = strlen(s->filename);
- fname = (char*)codegen_malloc(s, fname_len + 1);
- memcpy(fname, s->filename, fname_len);
- fname[fname_len] = '\0';
- irep->filename = fname;
- }
-
- irep->nlocals = s->nlocals;
- irep->nregs = s->nregs;
-
- mrb_gc_arena_restore(mrb, s->ai);
- mrb_pool_close(s->mpool);
-}
-
-static struct loopinfo*
-loop_push(codegen_scope *s, enum looptype t)
-{
- struct loopinfo *p = (struct loopinfo *)codegen_palloc(s, sizeof(struct loopinfo));
-
- p->type = t;
- p->pc1 = p->pc2 = p->pc3 = 0;
- p->prev = s->loop;
- p->ensure_level = s->ensure_level;
- p->acc = cursp();
- s->loop = p;
-
- return p;
-}
-
-static void
-loop_break(codegen_scope *s, node *tree)
-{
- if (!s->loop) {
- codegen(s, tree, NOVAL);
- raise_error(s, "unexpected break");
- }
- else {
- struct loopinfo *loop;
-
- if (tree) {
- codegen(s, tree, VAL);
- pop();
- }
-
- loop = s->loop;
- while (loop->type == LOOP_BEGIN) {
- genop_peep(s, MKOP_A(OP_POPERR, 1), NOVAL);
- loop = loop->prev;
- }
- while (loop->type == LOOP_RESCUE) {
- loop = loop->prev;
- }
- if (loop->type == LOOP_NORMAL) {
- int tmp;
-
- if (s->ensure_level > s->loop->ensure_level) {
- genop_peep(s, MKOP_A(OP_EPOP, s->ensure_level - s->loop->ensure_level), NOVAL);
- }
- if (tree) {
- genop_peep(s, MKOP_AB(OP_MOVE, loop->acc, cursp()), NOVAL);
- }
- tmp = genop(s, MKOP_sBx(OP_JMP, loop->pc3));
- loop->pc3 = tmp;
- }
- else {
- genop(s, MKOP_AB(OP_RETURN, cursp(), OP_R_BREAK));
- }
- }
-}
-
-static void
-loop_pop(codegen_scope *s, int val)
-{
- if (val) {
- genop(s, MKOP_A(OP_LOADNIL, cursp()));
- }
- dispatch_linked(s, s->loop->pc3);
- s->loop = s->loop->prev;
- if (val) push();
-}
-
-#ifdef ENABLE_STDIO
-static int
-print_r(mrb_state *mrb, mrb_irep *irep, size_t n, int pre)
-{
- size_t i;
-
- if (n == 0) return 0;
-
- for (i=0; i+1<irep->nlocals; i++) {
- if (irep->lv[i].r == n) {
- mrb_sym sym = irep->lv[i].name;
- if (pre) printf(" ");
- printf("R%d:%s", (int)n, mrb_sym2name(mrb, sym));
- return 1;
- }
- }
- return 0;
-}
-
-#define RA 1
-#define RB 2
-#define RAB 3
-
-static void
-print_lv(mrb_state *mrb, mrb_irep *irep, mrb_code c, int r)
-{
- int pre = 0;
-
- if (!irep->lv
- || ((!(r & RA) || GETARG_A(c) >= irep->nlocals)
- && (!(r & RB) || GETARG_B(c) >= irep->nlocals))) {
- printf("\n");
- return;
- }
- printf("\t; ");
- if (r & RA) {
- pre = print_r(mrb, irep, GETARG_A(c), 0);
- }
- if (r & RB) {
- print_r(mrb, irep, GETARG_B(c), pre);
- }
- printf("\n");
-}
-#endif
-
-static void
-codedump(mrb_state *mrb, mrb_irep *irep)
-{
-#ifdef ENABLE_STDIO
- int i;
- int ai;
- mrb_code c;
- const char *file = NULL, *next_file;
- int32_t line;
-
- if (!irep) return;
- printf("irep %p nregs=%d nlocals=%d pools=%d syms=%d reps=%d\n", (void*)irep,
- irep->nregs, irep->nlocals, (int)irep->plen, (int)irep->slen, (int)irep->rlen);
-
- for (i = 0; i < (int)irep->ilen; i++) {
- ai = mrb_gc_arena_save(mrb);
-
- next_file = mrb_debug_get_filename(irep, i);
- if (next_file && file != next_file) {
- printf("file: %s\n", next_file);
- file = next_file;
- }
- line = mrb_debug_get_line(irep, i);
- if (line < 0) {
- printf(" ");
- }
- else {
- printf("%5d ", line);
- }
-
- printf("%03d ", i);
- c = irep->iseq[i];
- switch (GET_OPCODE(c)) {
- case OP_NOP:
- printf("OP_NOP\n");
- break;
- case OP_MOVE:
- printf("OP_MOVE\tR%d\tR%d\t", GETARG_A(c), GETARG_B(c));
- print_lv(mrb, irep, c, RAB);
- break;
- case OP_LOADL:
- {
- mrb_value v = irep->pool[GETARG_Bx(c)];
- mrb_value s = mrb_inspect(mrb, v);
- printf("OP_LOADL\tR%d\tL(%d)\t; %s", GETARG_A(c), GETARG_Bx(c), RSTRING_PTR(s));
- }
- print_lv(mrb, irep, c, RA);
- break;
- case OP_LOADI:
- printf("OP_LOADI\tR%d\t%d\t", GETARG_A(c), GETARG_sBx(c));
- print_lv(mrb, irep, c, RA);
- break;
- case OP_LOADSYM:
- printf("OP_LOADSYM\tR%d\t:%s", GETARG_A(c),
- mrb_sym2name(mrb, irep->syms[GETARG_Bx(c)]));
- print_lv(mrb, irep, c, RA);
- break;
- case OP_LOADNIL:
- printf("OP_LOADNIL\tR%d\t\t", GETARG_A(c));
- print_lv(mrb, irep, c, RA);
- break;
- case OP_LOADSELF:
- printf("OP_LOADSELF\tR%d\t\t", GETARG_A(c));
- print_lv(mrb, irep, c, RA);
- break;
- case OP_LOADT:
- printf("OP_LOADT\tR%d\t\t", GETARG_A(c));
- print_lv(mrb, irep, c, RA);
- break;
- case OP_LOADF:
- printf("OP_LOADF\tR%d\t\t", GETARG_A(c));
- print_lv(mrb, irep, c, RA);
- break;
- case OP_GETGLOBAL:
- printf("OP_GETGLOBAL\tR%d\t:%s", GETARG_A(c),
- mrb_sym2name(mrb, irep->syms[GETARG_Bx(c)]));
- print_lv(mrb, irep, c, RA);
- break;
- case OP_SETGLOBAL:
- printf("OP_SETGLOBAL\t:%s\tR%d\t",
- mrb_sym2name(mrb, irep->syms[GETARG_Bx(c)]),
- GETARG_A(c));
- print_lv(mrb, irep, c, RA);
- break;
- case OP_GETCONST:
- printf("OP_GETCONST\tR%d\t:%s", GETARG_A(c),
- mrb_sym2name(mrb, irep->syms[GETARG_Bx(c)]));
- print_lv(mrb, irep, c, RA);
- break;
- case OP_SETCONST:
- printf("OP_SETCONST\t:%s\tR%d\t",
- mrb_sym2name(mrb, irep->syms[GETARG_Bx(c)]),
- GETARG_A(c));
- print_lv(mrb, irep, c, RA);
- break;
- case OP_GETMCNST:
- printf("OP_GETMCNST\tR%d\tR%d::%s", GETARG_A(c), GETARG_A(c),
- mrb_sym2name(mrb, irep->syms[GETARG_Bx(c)]));
- print_lv(mrb, irep, c, RAB);
- break;
- case OP_SETMCNST:
- printf("OP_SETMCNST\tR%d::%s\tR%d", GETARG_A(c)+1,
- mrb_sym2name(mrb, irep->syms[GETARG_Bx(c)]),
- GETARG_A(c));
- print_lv(mrb, irep, c, RA);
- break;
- case OP_GETIV:
- printf("OP_GETIV\tR%d\t%s", GETARG_A(c),
- mrb_sym2name(mrb, irep->syms[GETARG_Bx(c)]));
- print_lv(mrb, irep, c, RA);
- break;
- case OP_SETIV:
- printf("OP_SETIV\t%s\tR%d",
- mrb_sym2name(mrb, irep->syms[GETARG_Bx(c)]),
- GETARG_A(c));
- print_lv(mrb, irep, c, RA);
- break;
- case OP_GETUPVAR:
- printf("OP_GETUPVAR\tR%d\t%d\t%d",
- GETARG_A(c), GETARG_B(c), GETARG_C(c));
- print_lv(mrb, irep, c, RA);
- break;
- case OP_SETUPVAR:
- printf("OP_SETUPVAR\tR%d\t%d\t%d",
- GETARG_A(c), GETARG_B(c), GETARG_C(c));
- print_lv(mrb, irep, c, RA);
- break;
- case OP_GETCV:
- printf("OP_GETCV\tR%d\t%s", GETARG_A(c),
- mrb_sym2name(mrb, irep->syms[GETARG_Bx(c)]));
- print_lv(mrb, irep, c, RA);
- break;
- case OP_SETCV:
- printf("OP_SETCV\t%s\tR%d",
- mrb_sym2name(mrb, irep->syms[GETARG_Bx(c)]),
- GETARG_A(c));
- print_lv(mrb, irep, c, RA);
- break;
- case OP_JMP:
- printf("OP_JMP\t\t%03d\n", i+GETARG_sBx(c));
- break;
- case OP_JMPIF:
- printf("OP_JMPIF\tR%d\t%03d\n", GETARG_A(c), i+GETARG_sBx(c));
- break;
- case OP_JMPNOT:
- printf("OP_JMPNOT\tR%d\t%03d\n", GETARG_A(c), i+GETARG_sBx(c));
- break;
- case OP_SEND:
- printf("OP_SEND\tR%d\t:%s\t%d\n", GETARG_A(c),
- mrb_sym2name(mrb, irep->syms[GETARG_B(c)]),
- GETARG_C(c));
- break;
- case OP_SENDB:
- printf("OP_SENDB\tR%d\t:%s\t%d\n", GETARG_A(c),
- mrb_sym2name(mrb, irep->syms[GETARG_B(c)]),
- GETARG_C(c));
- break;
- case OP_TAILCALL:
- printf("OP_TAILCALL\tR%d\t:%s\t%d\n", GETARG_A(c),
- mrb_sym2name(mrb, irep->syms[GETARG_B(c)]),
- GETARG_C(c));
- break;
- case OP_SUPER:
- printf("OP_SUPER\tR%d\t%d\n", GETARG_A(c),
- GETARG_C(c));
- break;
- case OP_ARGARY:
- printf("OP_ARGARY\tR%d\t%d:%d:%d:%d", GETARG_A(c),
- (GETARG_Bx(c)>>10)&0x3f,
- (GETARG_Bx(c)>>9)&0x1,
- (GETARG_Bx(c)>>4)&0x1f,
- (GETARG_Bx(c)>>0)&0xf);
- print_lv(mrb, irep, c, RA);
- break;
-
- case OP_ENTER:
- printf("OP_ENTER\t%d:%d:%d:%d:%d:%d:%d\n",
- (GETARG_Ax(c)>>18)&0x1f,
- (GETARG_Ax(c)>>13)&0x1f,
- (GETARG_Ax(c)>>12)&0x1,
- (GETARG_Ax(c)>>7)&0x1f,
- (GETARG_Ax(c)>>2)&0x1f,
- (GETARG_Ax(c)>>1)&0x1,
- GETARG_Ax(c) & 0x1);
- break;
- case OP_RETURN:
- printf("OP_RETURN\tR%d", GETARG_A(c));
- switch (GETARG_B(c)) {
- case OP_R_NORMAL:
- case OP_R_RETURN:
- printf("\treturn"); break;
- case OP_R_BREAK:
- printf("\tbreak"); break;
- default:
- printf("\tbroken"); break;
- break;
- }
- print_lv(mrb, irep, c, RA);
- break;
- case OP_BLKPUSH:
- printf("OP_BLKPUSH\tR%d\t%d:%d:%d:%d", GETARG_A(c),
- (GETARG_Bx(c)>>10)&0x3f,
- (GETARG_Bx(c)>>9)&0x1,
- (GETARG_Bx(c)>>4)&0x1f,
- (GETARG_Bx(c)>>0)&0xf);
- print_lv(mrb, irep, c, RA);
- break;
-
- case OP_LAMBDA:
- printf("OP_LAMBDA\tR%d\tI(%+d)\t%d", GETARG_A(c), GETARG_b(c)+1, GETARG_c(c));
- print_lv(mrb, irep, c, RA);
- break;
- case OP_RANGE:
- printf("OP_RANGE\tR%d\tR%d\t%d", GETARG_A(c), GETARG_B(c), GETARG_C(c));
- print_lv(mrb, irep, c, RAB);
- break;
- case OP_METHOD:
- printf("OP_METHOD\tR%d\t:%s", GETARG_A(c),
- mrb_sym2name(mrb, irep->syms[GETARG_B(c)]));
- print_lv(mrb, irep, c, RA);
- break;
-
- case OP_ADD:
- printf("OP_ADD\tR%d\t:%s\t%d\n", GETARG_A(c),
- mrb_sym2name(mrb, irep->syms[GETARG_B(c)]),
- GETARG_C(c));
- break;
- case OP_ADDI:
- printf("OP_ADDI\tR%d\t:%s\t%d\n", GETARG_A(c),
- mrb_sym2name(mrb, irep->syms[GETARG_B(c)]),
- GETARG_C(c));
- break;
- case OP_SUB:
- printf("OP_SUB\tR%d\t:%s\t%d\n", GETARG_A(c),
- mrb_sym2name(mrb, irep->syms[GETARG_B(c)]),
- GETARG_C(c));
- break;
- case OP_SUBI:
- printf("OP_SUBI\tR%d\t:%s\t%d\n", GETARG_A(c),
- mrb_sym2name(mrb, irep->syms[GETARG_B(c)]),
- GETARG_C(c));
- break;
- case OP_MUL:
- printf("OP_MUL\tR%d\t:%s\t%d\n", GETARG_A(c),
- mrb_sym2name(mrb, irep->syms[GETARG_B(c)]),
- GETARG_C(c));
- break;
- case OP_DIV:
- printf("OP_DIV\tR%d\t:%s\t%d\n", GETARG_A(c),
- mrb_sym2name(mrb, irep->syms[GETARG_B(c)]),
- GETARG_C(c));
- break;
- case OP_LT:
- printf("OP_LT\tR%d\t:%s\t%d\n", GETARG_A(c),
- mrb_sym2name(mrb, irep->syms[GETARG_B(c)]),
- GETARG_C(c));
- break;
- case OP_LE:
- printf("OP_LE\tR%d\t:%s\t%d\n", GETARG_A(c),
- mrb_sym2name(mrb, irep->syms[GETARG_B(c)]),
- GETARG_C(c));
- break;
- case OP_GT:
- printf("OP_GT\tR%d\t:%s\t%d\n", GETARG_A(c),
- mrb_sym2name(mrb, irep->syms[GETARG_B(c)]),
- GETARG_C(c));
- break;
- case OP_GE:
- printf("OP_GE\tR%d\t:%s\t%d\n", GETARG_A(c),
- mrb_sym2name(mrb, irep->syms[GETARG_B(c)]),
- GETARG_C(c));
- break;
- case OP_EQ:
- printf("OP_EQ\tR%d\t:%s\t%d\n", GETARG_A(c),
- mrb_sym2name(mrb, irep->syms[GETARG_B(c)]),
- GETARG_C(c));
- break;
-
- case OP_STOP:
- printf("OP_STOP\n");
- break;
-
- case OP_ARRAY:
- printf("OP_ARRAY\tR%d\tR%d\t%d", GETARG_A(c), GETARG_B(c), GETARG_C(c));
- print_lv(mrb, irep, c, RAB);
- break;
- case OP_ARYCAT:
- printf("OP_ARYCAT\tR%d\tR%d\t", GETARG_A(c), GETARG_B(c));
- print_lv(mrb, irep, c, RAB);
- break;
- case OP_ARYPUSH:
- printf("OP_ARYPUSH\tR%d\tR%d\t", GETARG_A(c), GETARG_B(c));
- print_lv(mrb, irep, c, RAB);
- break;
- case OP_AREF:
- printf("OP_AREF\tR%d\tR%d\t%d", GETARG_A(c), GETARG_B(c), GETARG_C(c));
- print_lv(mrb, irep, c, RAB);
- break;
- case OP_APOST:
- printf("OP_APOST\tR%d\t%d\t%d", GETARG_A(c), GETARG_B(c), GETARG_C(c));
- print_lv(mrb, irep, c, RA);
- break;
- case OP_STRING:
- {
- mrb_value v = irep->pool[GETARG_Bx(c)];
- mrb_value s = mrb_str_dump(mrb, mrb_str_new(mrb, RSTRING_PTR(v), RSTRING_LEN(v)));
- printf("OP_STRING\tR%d\tL(%d)\t; %s", GETARG_A(c), GETARG_Bx(c), RSTRING_PTR(s));
- }
- print_lv(mrb, irep, c, RA);
- break;
- case OP_STRCAT:
- printf("OP_STRCAT\tR%d\tR%d\t", GETARG_A(c), GETARG_B(c));
- print_lv(mrb, irep, c, RAB);
- break;
- case OP_HASH:
- printf("OP_HASH\tR%d\tR%d\t%d", GETARG_A(c), GETARG_B(c), GETARG_C(c));
- print_lv(mrb, irep, c, RAB);
- break;
-
- case OP_OCLASS:
- printf("OP_OCLASS\tR%d\t\t", GETARG_A(c));
- print_lv(mrb, irep, c, RA);
- break;
- case OP_CLASS:
- printf("OP_CLASS\tR%d\t:%s", GETARG_A(c),
- mrb_sym2name(mrb, irep->syms[GETARG_B(c)]));
- print_lv(mrb, irep, c, RA);
- break;
- case OP_MODULE:
- printf("OP_MODULE\tR%d\t:%s", GETARG_A(c),
- mrb_sym2name(mrb, irep->syms[GETARG_B(c)]));
- print_lv(mrb, irep, c, RA);
- break;
- case OP_EXEC:
- printf("OP_EXEC\tR%d\tI(%+d)", GETARG_A(c), GETARG_Bx(c)+1);
- print_lv(mrb, irep, c, RA);
- break;
- case OP_SCLASS:
- printf("OP_SCLASS\tR%d\tR%d\t", GETARG_A(c), GETARG_B(c));
- print_lv(mrb, irep, c, RAB);
- break;
- case OP_TCLASS:
- printf("OP_TCLASS\tR%d\t\t", GETARG_A(c));
- print_lv(mrb, irep, c, RA);
- break;
- case OP_ERR:
- {
- mrb_value v = irep->pool[GETARG_Bx(c)];
- mrb_value s = mrb_str_dump(mrb, mrb_str_new(mrb, RSTRING_PTR(v), RSTRING_LEN(v)));
- printf("OP_ERR\t%s\n", RSTRING_PTR(s));
- }
- break;
- case OP_EPUSH:
- printf("OP_EPUSH\t:I(%+d)\n", GETARG_Bx(c)+1);
- break;
- case OP_ONERR:
- printf("OP_ONERR\t%03d\n", i+GETARG_sBx(c));
- break;
- case OP_RESCUE:
- printf("OP_RESCUE\tR%d\t\t", GETARG_A(c));
- print_lv(mrb, irep, c, RA);
- break;
- case OP_RAISE:
- printf("OP_RAISE\tR%d\t\t", GETARG_A(c));
- print_lv(mrb, irep, c, RA);
- break;
- case OP_POPERR:
- printf("OP_POPERR\t%d\t\t", GETARG_A(c));
- print_lv(mrb, irep, c, RA);
- break;
- case OP_EPOP:
- printf("OP_EPOP\t%d\n", GETARG_A(c));
- break;
-
- default:
- printf("OP_unknown %d\t%d\t%d\t%d\n", GET_OPCODE(c),
- GETARG_A(c), GETARG_B(c), GETARG_C(c));
- break;
- }
- mrb_gc_arena_restore(mrb, ai);
- }
- printf("\n");
-#endif
-}
-
-static void
-codedump_recur(mrb_state *mrb, mrb_irep *irep)
-{
- size_t i;
-
- codedump(mrb, irep);
- for (i=0; i<irep->rlen; i++) {
- codedump_recur(mrb, irep->reps[i]);
- }
-}
-
-void
-mrb_codedump_all(mrb_state *mrb, struct RProc *proc)
-{
- codedump_recur(mrb, proc->body.irep);
-}
-
-MRB_API struct RProc*
-mrb_generate_code(mrb_state *mrb, parser_state *p)
-{
- codegen_scope *scope = scope_new(mrb, 0, 0);
- struct RProc *proc;
-
- if (!scope) {
- return NULL;
- }
- scope->mrb = mrb;
- scope->parser = p;
- scope->filename = p->filename;
- scope->filename_index = p->current_filename_index;
-
- MRB_TRY(&scope->jmp) {
- /* prepare irep */
- codegen(scope, p->tree, NOVAL);
- proc = mrb_proc_new(mrb, scope->irep);
- mrb_irep_decref(mrb, scope->irep);
- mrb_pool_close(scope->mpool);
- return proc;
- }
- MRB_CATCH(&scope->jmp) {
- if (scope->filename == scope->irep->filename) {
- scope->irep->filename = NULL;
- }
- mrb_irep_decref(mrb, scope->irep);
- mrb_pool_close(scope->mpool);
- return NULL;
- }
- MRB_END_EXC(&scope->jmp);
-}
diff --git a/src/compar.c b/src/compar.c
index 0186b942f..0032fc859 100644
--- a/src/compar.c
+++ b/src/compar.c
@@ -4,7 +4,7 @@
** See Copyright Notice in mruby.h
*/
-#include "mruby.h"
+#include <mruby.h>
void
mrb_init_comparable(mrb_state *mrb)
diff --git a/src/crc.c b/src/crc.c
deleted file mode 100644
index 290b2ca0e..000000000
--- a/src/crc.c
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
-** crc.c - calculate CRC
-**
-** See Copyright Notice in mruby.h
-*/
-
-#include <limits.h>
-#include <stdint.h>
-#include <stddef.h>
-
-/* Calculate CRC (CRC-16-CCITT)
-**
-** 0000_0000_0000_0000_0000_0000_0000_0000
-** ^|------- CRC -------|- work --|
-** carry
-*/
-#define CRC_16_CCITT 0x11021ul /* x^16+x^12+x^5+1 */
-#define CRC_XOR_PATTERN (CRC_16_CCITT << 8)
-#define CRC_CARRY_BIT (0x01000000)
-
-uint16_t
-calc_crc_16_ccitt(const uint8_t *src, size_t nbytes, uint16_t crc)
-{
- size_t ibyte;
- uint32_t ibit;
- uint32_t crcwk = crc << 8;
-
- for (ibyte = 0; ibyte < nbytes; ibyte++) {
- crcwk |= *src++;
- for (ibit = 0; ibit < CHAR_BIT; ibit++) {
- crcwk <<= 1;
- if (crcwk & CRC_CARRY_BIT) {
- crcwk ^= CRC_XOR_PATTERN;
- }
- }
- }
- return (uint16_t)(crcwk >> 8);
-}
-
diff --git a/src/debug.c b/src/debug.c
index 4ac692086..c03c91cf5 100644
--- a/src/debug.c
+++ b/src/debug.c
@@ -1,9 +1,9 @@
#include <string.h>
-#include "mruby.h"
-#include "mruby/irep.h"
-#include "mruby/debug.h"
+#include <mruby.h>
+#include <mruby/irep.h>
+#include <mruby/debug.h>
-static mrb_irep_debug_info_file *
+static mrb_irep_debug_info_file*
get_file(mrb_irep_debug_info *info, uint32_t pc)
{
mrb_irep_debug_info_file **ret;
@@ -19,7 +19,8 @@ get_file(mrb_irep_debug_info *info, uint32_t pc)
if (!(pc < (*it)->start_pos)) {
ret = it + 1;
count -= step + 1;
- } else { count = step; }
+ }
+ else { count = step; }
}
--ret;
@@ -50,25 +51,25 @@ select_line_type(const uint16_t *lines, size_t lines_len)
}
MRB_API char const*
-mrb_debug_get_filename(mrb_irep *irep, uint32_t pc)
+mrb_debug_get_filename(mrb_state *mrb, const mrb_irep *irep, uint32_t pc)
{
if (irep && pc < irep->ilen) {
mrb_irep_debug_info_file* f = NULL;
- if (!irep->debug_info) { return irep->filename; }
+ if (!irep->debug_info) return NULL;
else if ((f = get_file(irep->debug_info, pc))) {
- return f->filename;
+ return mrb_sym_name_len(mrb, f->filename_sym, NULL);
}
}
return NULL;
}
MRB_API int32_t
-mrb_debug_get_line(mrb_irep *irep, uint32_t pc)
+mrb_debug_get_line(mrb_state *mrb, const mrb_irep *irep, uint32_t pc)
{
if (irep && pc < irep->ilen) {
mrb_irep_debug_info_file* f = NULL;
if (!irep->debug_info) {
- return irep->lines? irep->lines[pc] : -1;
+ return -1;
}
else if ((f = get_file(irep->debug_info, pc))) {
switch (f->line_type) {
@@ -86,7 +87,8 @@ mrb_debug_get_line(mrb_irep *irep, uint32_t pc)
if (!(pc < it->start_pos)) {
ret = it + 1;
count -= step + 1;
- } else { count = step; }
+ }
+ else { count = step; }
}
--ret;
@@ -106,7 +108,7 @@ mrb_debug_get_line(mrb_irep *irep, uint32_t pc)
return -1;
}
-MRB_API mrb_irep_debug_info *
+MRB_API mrb_irep_debug_info*
mrb_debug_info_alloc(mrb_state *mrb, mrb_irep *irep)
{
static const mrb_irep_debug_info initial = { 0, 0, NULL };
@@ -119,83 +121,80 @@ mrb_debug_info_alloc(mrb_state *mrb, mrb_irep *irep)
return ret;
}
-MRB_API mrb_irep_debug_info_file *
-mrb_debug_info_append_file(mrb_state *mrb, mrb_irep *irep,
+MRB_API mrb_irep_debug_info_file*
+mrb_debug_info_append_file(mrb_state *mrb, mrb_irep_debug_info *d,
+ const char *filename, uint16_t *lines,
uint32_t start_pos, uint32_t end_pos)
{
- mrb_irep_debug_info *info;
- mrb_irep_debug_info_file *ret;
+ mrb_irep_debug_info_file *f;
uint32_t file_pc_count;
size_t fn_len;
- mrb_int len;
uint32_t i;
- if (!irep->debug_info) { return NULL; }
-
- mrb_assert(irep->filename);
- mrb_assert(irep->lines);
+ if (!d) return NULL;
+ if (start_pos == end_pos) return NULL;
- info = irep->debug_info;
+ mrb_assert(filename);
+ mrb_assert(lines);
- if (info->flen > 0 && strcmp(irep->filename, info->files[info->flen - 1]->filename) == 0) {
- return NULL;
+ if (d->flen > 0) {
+ const char *fn = mrb_sym_name_len(mrb, d->files[d->flen - 1]->filename_sym, NULL);
+ if (strcmp(filename, fn) == 0)
+ return NULL;
}
- ret = (mrb_irep_debug_info_file *)mrb_malloc(mrb, sizeof(*ret));
- info->files =
- (mrb_irep_debug_info_file**)(
- info->files
- ? mrb_realloc(mrb, info->files, sizeof(mrb_irep_debug_info_file*) * (info->flen + 1))
+ f = (mrb_irep_debug_info_file*)mrb_malloc(mrb, sizeof(*f));
+ d->files = (mrb_irep_debug_info_file**)(
+ d->files
+ ? mrb_realloc(mrb, d->files, sizeof(mrb_irep_debug_info_file*) * (d->flen + 1))
: mrb_malloc(mrb, sizeof(mrb_irep_debug_info_file*)));
- info->files[info->flen++] = ret;
+ d->files[d->flen++] = f;
file_pc_count = end_pos - start_pos;
- ret->start_pos = start_pos;
- info->pc_count = end_pos;
+ f->start_pos = start_pos;
+ d->pc_count = end_pos;
- fn_len = strlen(irep->filename);
- ret->filename_sym = mrb_intern(mrb, irep->filename, fn_len);
- len = 0;
- ret->filename = mrb_sym2name_len(mrb, ret->filename_sym, &len);
+ fn_len = strlen(filename);
+ f->filename_sym = mrb_intern(mrb, filename, fn_len);
- ret->line_type = select_line_type(irep->lines + start_pos, end_pos - start_pos);
- ret->lines.ptr = NULL;
+ f->line_type = select_line_type(lines + start_pos, end_pos - start_pos);
+ f->lines.ptr = NULL;
- switch (ret->line_type) {
+ switch (f->line_type) {
case mrb_debug_line_ary:
- ret->line_entry_count = file_pc_count;
- ret->lines.ary = (uint16_t*)mrb_malloc(mrb, sizeof(uint16_t) * file_pc_count);
+ f->line_entry_count = file_pc_count;
+ f->lines.ary = (uint16_t*)mrb_malloc(mrb, sizeof(uint16_t) * file_pc_count);
for (i = 0; i < file_pc_count; ++i) {
- ret->lines.ary[i] = irep->lines[start_pos + i];
+ f->lines.ary[i] = lines[start_pos + i];
}
break;
case mrb_debug_line_flat_map: {
uint16_t prev_line = 0;
mrb_irep_debug_info_line m;
- ret->lines.flat_map = (mrb_irep_debug_info_line*)mrb_malloc(mrb, sizeof(mrb_irep_debug_info_line) * 1);
- ret->line_entry_count = 0;
+ f->lines.flat_map = (mrb_irep_debug_info_line*)mrb_malloc(mrb, sizeof(mrb_irep_debug_info_line) * 1);
+ f->line_entry_count = 0;
for (i = 0; i < file_pc_count; ++i) {
- if (irep->lines[start_pos + i] == prev_line) { continue; }
+ if (lines[start_pos + i] == prev_line) { continue; }
- ret->lines.flat_map = (mrb_irep_debug_info_line*)mrb_realloc(
- mrb, ret->lines.flat_map,
- sizeof(mrb_irep_debug_info_line) * (ret->line_entry_count + 1));
+ f->lines.flat_map = (mrb_irep_debug_info_line*)mrb_realloc(
+ mrb, f->lines.flat_map,
+ sizeof(mrb_irep_debug_info_line) * (f->line_entry_count + 1));
m.start_pos = start_pos + i;
- m.line = irep->lines[start_pos + i];
- ret->lines.flat_map[ret->line_entry_count] = m;
+ m.line = lines[start_pos + i];
+ f->lines.flat_map[f->line_entry_count] = m;
/* update */
- ++ret->line_entry_count;
- prev_line = irep->lines[start_pos + i];
+ ++f->line_entry_count;
+ prev_line = lines[start_pos + i];
}
} break;
default: mrb_assert(0); break;
}
- return ret;
+ return f;
}
MRB_API void
@@ -205,11 +204,14 @@ mrb_debug_info_free(mrb_state *mrb, mrb_irep_debug_info *d)
if (!d) { return; }
- for (i = 0; i < d->flen; ++i) {
- mrb_assert(d->files[i]);
- mrb_free(mrb, d->files[i]->lines.ptr);
- mrb_free(mrb, d->files[i]);
+ if (d->files) {
+ for (i = 0; i < d->flen; ++i) {
+ if (d->files[i]) {
+ mrb_free(mrb, d->files[i]->lines.ptr);
+ mrb_free(mrb, d->files[i]);
+ }
+ }
+ mrb_free(mrb, d->files);
}
- mrb_free(mrb, d->files);
mrb_free(mrb, d);
}
diff --git a/src/dump.c b/src/dump.c
index af12ed605..0b4200795 100644
--- a/src/dump.c
+++ b/src/dump.c
@@ -1,41 +1,28 @@
/*
-** dump.c - mruby binary dumper (mrbc binary format)
+** cdump.c - mruby binary dumper (in C)
**
** See Copyright Notice in mruby.h
*/
-#include <ctype.h>
#include <string.h>
#include <limits.h>
-#include "mruby/dump.h"
-#include "mruby/string.h"
-#include "mruby/irep.h"
-#include "mruby/numeric.h"
-#include "mruby/debug.h"
-
-#define FLAG_BYTEORDER_NATIVE 2
-#define FLAG_BYTEORDER_NONATIVE 0
-
-#ifdef ENABLE_STDIO
+#include <math.h>
+#include <mruby/dump.h>
+#include <mruby/string.h>
+#include <mruby/irep.h>
+#include <mruby/debug.h>
+
+#ifndef MRB_NO_FLOAT
+#include <mruby/endian.h>
+#endif
-static size_t get_irep_record_size_1(mrb_state *mrb, mrb_irep *irep);
+static size_t get_irep_record_size_1(mrb_state *mrb, const mrb_irep *irep);
#if UINT32_MAX > SIZE_MAX
# error This code cannot be built on your environment.
#endif
static size_t
-write_padding(uint8_t *buf)
-{
- const size_t align = MRB_DUMP_ALIGNMENT;
- size_t pad_len = -(intptr_t)buf & (align-1);
- if (pad_len > 0) {
- memset(buf, 0, pad_len);
- }
- return pad_len;
-}
-
-static size_t
get_irep_header_size(mrb_state *mrb)
{
size_t size = 0;
@@ -47,11 +34,11 @@ get_irep_header_size(mrb_state *mrb)
}
static ptrdiff_t
-write_irep_header(mrb_state *mrb, mrb_irep *irep, uint8_t *buf)
+write_irep_header(mrb_state *mrb, const mrb_irep *irep, uint8_t *buf)
{
uint8_t *cur = buf;
- cur += uint32_to_bin(get_irep_record_size_1(mrb, irep), cur); /* record size */
+ cur += uint32_to_bin((uint32_t)get_irep_record_size_1(mrb, irep), cur); /* record size */
cur += uint16_to_bin((uint16_t)irep->nlocals, cur); /* number of local variable */
cur += uint16_to_bin((uint16_t)irep->nregs, cur); /* number of register variable */
cur += uint16_to_bin((uint16_t)irep->rlen, cur); /* number of child irep */
@@ -59,83 +46,112 @@ write_irep_header(mrb_state *mrb, mrb_irep *irep, uint8_t *buf)
return cur - buf;
}
-
static size_t
-get_iseq_block_size(mrb_state *mrb, mrb_irep *irep)
+get_iseq_block_size(mrb_state *mrb, const mrb_irep *irep)
{
size_t size = 0;
- size += sizeof(uint32_t); /* ilen */
- size += sizeof(uint32_t); /* max padding */
- size += sizeof(uint32_t) * irep->ilen; /* iseq(n) */
+ size += sizeof(uint16_t); /* clen */
+ size += sizeof(uint16_t); /* ilen */
+ size += irep->ilen * sizeof(mrb_code); /* iseq(n) */
+ size += irep->clen * sizeof(struct mrb_irep_catch_handler);
return size;
}
static ptrdiff_t
-write_iseq_block(mrb_state *mrb, mrb_irep *irep, uint8_t *buf, uint8_t flags)
+write_iseq_block(mrb_state *mrb, const mrb_irep *irep, uint8_t *buf, uint8_t flags)
{
uint8_t *cur = buf;
- uint32_t iseq_no;
+ size_t seqlen = irep->ilen * sizeof(mrb_code) +
+ irep->clen * sizeof(struct mrb_irep_catch_handler);
+
+ cur += uint16_to_bin(irep->clen, cur); /* number of catch handlers */
+ cur += uint16_to_bin(irep->ilen, cur); /* number of opcode */
+ memcpy(cur, irep->iseq, seqlen);
+ cur += seqlen;
- cur += uint32_to_bin(irep->ilen, cur); /* number of opcode */
- cur += write_padding(cur);
- if (flags & FLAG_BYTEORDER_NATIVE) {
- memcpy(cur, irep->iseq, irep->ilen * sizeof(mrb_code));
- cur += irep->ilen * sizeof(mrb_code);
+ return cur - buf;
+}
+
+#ifndef MRB_NO_FLOAT
+static void
+dump_float(mrb_state *mrb, uint8_t *buf, mrb_float f)
+{
+ /* dump IEEE754 binary in little endian */
+ union {
+ double f;
+ char s[sizeof(double)];
+ } u = {.f = (double)f};
+
+ if (littleendian) {
+ memcpy(buf, u.s, sizeof(double));
}
else {
- for (iseq_no = 0; iseq_no < irep->ilen; iseq_no++) {
- cur += uint32_to_bin(irep->iseq[iseq_no], cur); /* opcode */
+ size_t i;
+
+ for (i=0; i<sizeof(double); i++) {
+ buf[i] = u.s[sizeof(double)-i-1];
}
}
-
- return cur - buf;
}
-
+#endif
static size_t
-get_pool_block_size(mrb_state *mrb, mrb_irep *irep)
+get_pool_block_size(mrb_state *mrb, const mrb_irep *irep)
{
+ int pool_no;
size_t size = 0;
- size_t pool_no;
- mrb_value str;
- char buf[32];
- size += sizeof(uint32_t); /* plen */
- size += irep->plen * (sizeof(uint8_t) + sizeof(uint16_t)); /* len(n) */
+ size += sizeof(uint16_t); /* plen */
+ size += irep->plen * sizeof(uint8_t); /* len(n) */
for (pool_no = 0; pool_no < irep->plen; pool_no++) {
int ai = mrb_gc_arena_save(mrb);
- switch (mrb_type(irep->pool[pool_no])) {
- case MRB_TT_FIXNUM:
- str = mrb_fixnum_to_str(mrb, irep->pool[pool_no], 10);
+ switch (irep->pool[pool_no].tt) {
+ case IREP_TT_INT64:
+#ifdef MRB_64BIT
{
- mrb_int len = RSTRING_LEN(str);
- mrb_assert_int_fit(mrb_int, len, size_t, SIZE_MAX);
- size += (size_t)len;
+ int64_t i = irep->pool[pool_no].u.i64;
+
+ if (i < INT32_MIN || INT32_MAX < i)
+ size += 8;
+ else
+ size += 4;
}
break;
+#else
+ /* fall through */
+#endif
+ case IREP_TT_INT32:
+ size += 4; /* 32bits = 4bytes */
+ break;
- case MRB_TT_FLOAT:
+ case IREP_TT_BIGINT:
{
- int len;
- len = mrb_float_to_str(buf, mrb_float(irep->pool[pool_no]));
+ mrb_int len = irep->pool[pool_no].u.str[0];
mrb_assert_int_fit(mrb_int, len, size_t, SIZE_MAX);
- size += (size_t)len;
+ size += sizeof(uint8_t);
+ size += (size_t)len+2;
}
break;
- case MRB_TT_STRING:
+ case IREP_TT_FLOAT:
+#ifndef MRB_NO_FLOAT
{
- mrb_int len = RSTRING_LEN(irep->pool[pool_no]);
- mrb_assert_int_fit(mrb_int, len, size_t, SIZE_MAX);
- size += (size_t)len;
+ size += sizeof(double);
}
+#endif
break;
- default:
+ default: /* packed IREP_TT_STRING */
+ {
+ mrb_int len = irep->pool[pool_no].tt >> 2; /* unpack length */
+ mrb_assert_int_fit(mrb_int, len, size_t, SIZE_MAX);
+ size += sizeof(uint16_t);
+ size += (size_t)len+1;
+ }
break;
}
mrb_gc_arena_restore(mrb, ai);
@@ -145,84 +161,89 @@ get_pool_block_size(mrb_state *mrb, mrb_irep *irep)
}
static ptrdiff_t
-write_pool_block(mrb_state *mrb, mrb_irep *irep, uint8_t *buf)
+write_pool_block(mrb_state *mrb, const mrb_irep *irep, uint8_t *buf)
{
- size_t pool_no;
+ int pool_no;
uint8_t *cur = buf;
- uint16_t len;
- mrb_value str;
- const char *char_ptr;
- char char_buf[30];
+ mrb_int len;
+ const char *ptr;
- cur += uint32_to_bin(irep->plen, cur); /* number of pool */
+ cur += uint16_to_bin(irep->plen, cur); /* number of pool */
for (pool_no = 0; pool_no < irep->plen; pool_no++) {
int ai = mrb_gc_arena_save(mrb);
- switch (mrb_type(irep->pool[pool_no])) {
- case MRB_TT_FIXNUM:
- cur += uint8_to_bin(IREP_TT_FIXNUM, cur); /* data type */
- str = mrb_fixnum_to_str(mrb, irep->pool[pool_no], 10);
- char_ptr = RSTRING_PTR(str);
+ switch (irep->pool[pool_no].tt) {
+#ifdef MRB_64BIT
+ case IREP_TT_INT64:
{
- mrb_int tlen;
-
- tlen = RSTRING_LEN(str);
- mrb_assert_int_fit(mrb_int, tlen, uint16_t, UINT16_MAX);
- len = (uint16_t)tlen;
+ int64_t i = irep->pool[pool_no].u.i64;
+ if (i < INT32_MIN || INT32_MAX < i) {
+ cur += uint8_to_bin(IREP_TT_INT64, cur); /* data type */
+ cur += uint32_to_bin((uint32_t)((i>>32) & 0xffffffff), cur); /* i64 hi */
+ cur += uint32_to_bin((uint32_t)((i ) & 0xffffffff), cur); /* i64 lo */
+ }
+ else {
+ cur += uint8_to_bin(IREP_TT_INT32, cur); /* data type */
+ cur += uint32_to_bin(irep->pool[pool_no].u.i32, cur); /* i32 */
+ }
}
break;
+#endif
+ case IREP_TT_INT32:
+ cur += uint8_to_bin(IREP_TT_INT32, cur); /* data type */
+ cur += uint32_to_bin(irep->pool[pool_no].u.i32, cur); /* i32 */
+ break;
- case MRB_TT_FLOAT:
- cur += uint8_to_bin(IREP_TT_FLOAT, cur); /* data type */
- {
- int tlen;
- tlen = mrb_float_to_str(char_buf, mrb_float(irep->pool[pool_no]));
- mrb_assert_int_fit(int, tlen, uint16_t, UINT16_MAX);
- len = (uint16_t)tlen;
- }
- char_ptr = &char_buf[0];
+ case IREP_TT_BIGINT:
+ cur += uint8_to_bin(IREP_TT_BIGINT, cur); /* data type */
+ len = irep->pool[pool_no].u.str[0];
+ memcpy(cur, irep->pool[pool_no].u.str, (size_t)len+2);
+ cur += len+2;
+ *cur++ = '\0';
break;
- case MRB_TT_STRING:
- cur += uint8_to_bin(IREP_TT_STRING, cur); /* data type */
- char_ptr = RSTRING_PTR(irep->pool[pool_no]);
+ case IREP_TT_FLOAT:
+ cur += uint8_to_bin(IREP_TT_FLOAT, cur); /* data type */
+#ifndef MRB_NO_FLOAT
{
- mrb_int tlen;
-
- tlen = RSTRING_LEN(irep->pool[pool_no]);
- mrb_assert_int_fit(mrb_int, tlen, uint16_t, UINT16_MAX);
- len = (uint16_t)tlen;
+ dump_float(mrb, cur,irep->pool[pool_no].u.f);
+ cur += sizeof(double);
}
+#else
+ cur += uint16_to_bin(0, cur); /* zero length */
+#endif
break;
- default:
- continue;
+ default: /* string */
+ cur += uint8_to_bin(IREP_TT_STR, cur); /* data type */
+ ptr = irep->pool[pool_no].u.str;
+ len = irep->pool[pool_no].tt>>2;
+ mrb_assert_int_fit(mrb_int, len, uint16_t, UINT16_MAX);
+ cur += uint16_to_bin((uint16_t)len, cur); /* data length */
+ memcpy(cur, ptr, (size_t)len);
+ cur += len;
+ *cur++ = '\0';
+ break;
}
-
- cur += uint16_to_bin(len, cur); /* data length */
- memcpy(cur, char_ptr, (size_t)len);
- cur += len;
-
mrb_gc_arena_restore(mrb, ai);
}
return cur - buf;
}
-
static size_t
-get_syms_block_size(mrb_state *mrb, mrb_irep *irep)
+get_syms_block_size(mrb_state *mrb, const mrb_irep *irep)
{
size_t size = 0;
- uint32_t sym_no;
+ int sym_no;
mrb_int len;
- size += sizeof(uint32_t); /* slen */
+ size += sizeof(uint16_t); /* slen */
for (sym_no = 0; sym_no < irep->slen; sym_no++) {
size += sizeof(uint16_t); /* snl(n) */
if (irep->syms[sym_no] != 0) {
- mrb_sym2name_len(mrb, irep->syms[sym_no], &len);
+ mrb_sym_name_len(mrb, irep->syms[sym_no], &len);
size += len + 1; /* sn(n) + null char */
}
}
@@ -231,19 +252,19 @@ get_syms_block_size(mrb_state *mrb, mrb_irep *irep)
}
static ptrdiff_t
-write_syms_block(mrb_state *mrb, mrb_irep *irep, uint8_t *buf)
+write_syms_block(mrb_state *mrb, const mrb_irep *irep, uint8_t *buf)
{
- uint32_t sym_no;
+ int sym_no;
uint8_t *cur = buf;
const char *name;
- cur += uint32_to_bin(irep->slen, cur); /* number of symbol */
+ cur += uint16_to_bin(irep->slen, cur); /* number of symbol */
for (sym_no = 0; sym_no < irep->slen; sym_no++) {
if (irep->syms[sym_no] != 0) {
mrb_int len;
- name = mrb_sym2name_len(mrb, irep->syms[sym_no], &len);
+ name = mrb_sym_name_len(mrb, irep->syms[sym_no], &len);
mrb_assert_int_fit(mrb_int, len, uint16_t, UINT16_MAX);
cur += uint16_to_bin((uint16_t)len, cur); /* length of symbol name */
@@ -260,7 +281,7 @@ write_syms_block(mrb_state *mrb, mrb_irep *irep, uint8_t *buf)
}
static size_t
-get_irep_record_size_1(mrb_state *mrb, mrb_irep *irep)
+get_irep_record_size_1(mrb_state *mrb, const mrb_irep *irep)
{
size_t size = 0;
@@ -272,10 +293,10 @@ get_irep_record_size_1(mrb_state *mrb, mrb_irep *irep)
}
static size_t
-get_irep_record_size(mrb_state *mrb, mrb_irep *irep)
+get_irep_record_size(mrb_state *mrb, const mrb_irep *irep)
{
size_t size = 0;
- size_t irep_no;
+ int irep_no;
size = get_irep_record_size_1(mrb, irep);
for (irep_no = 0; irep_no < irep->rlen; irep_no++) {
@@ -285,20 +306,15 @@ get_irep_record_size(mrb_state *mrb, mrb_irep *irep)
}
static int
-write_irep_record(mrb_state *mrb, mrb_irep *irep, uint8_t *bin, size_t *irep_record_size, uint8_t flags)
+write_irep_record(mrb_state *mrb, const mrb_irep *irep, uint8_t *bin, size_t *irep_record_size, uint8_t flags)
{
- uint32_t i;
+ int i;
uint8_t *src = bin;
if (irep == NULL) {
return MRB_DUMP_INVALID_IREP;
}
- *irep_record_size = get_irep_record_size_1(mrb, irep);
- if (*irep_record_size == 0) {
- return MRB_DUMP_GENERAL_FAILURE;
- }
-
bin += write_irep_header(mrb, irep, bin);
bin += write_iseq_block(mrb, irep, bin, flags);
bin += write_pool_block(mrb, irep, bin);
@@ -323,7 +339,7 @@ write_footer(mrb_state *mrb, uint8_t *bin)
{
struct rite_binary_footer footer;
- memcpy(footer.section_identify, RITE_BINARY_EOF, sizeof(footer.section_identify));
+ memcpy(footer.section_ident, RITE_BINARY_EOF, sizeof(footer.section_ident));
uint32_to_bin(sizeof(struct rite_binary_footer), footer.section_size);
memcpy(bin, &footer, sizeof(struct rite_binary_footer));
@@ -336,7 +352,7 @@ write_section_irep_header(mrb_state *mrb, size_t section_size, uint8_t *bin)
{
struct rite_section_irep_header *header = (struct rite_section_irep_header*)bin;
- memcpy(header->section_identify, RITE_SECTION_IREP_IDENTIFIER, sizeof(header->section_identify));
+ memcpy(header->section_ident, RITE_SECTION_IREP_IDENT, sizeof(header->section_ident));
mrb_assert_int_fit(size_t, section_size, uint32_t, UINT32_MAX);
uint32_to_bin((uint32_t)section_size, header->section_size);
@@ -346,7 +362,7 @@ write_section_irep_header(mrb_state *mrb, size_t section_size, uint8_t *bin)
}
static int
-write_section_irep(mrb_state *mrb, mrb_irep *irep, uint8_t *bin, size_t *len_p, uint8_t flags)
+write_section_irep(mrb_state *mrb, const mrb_irep *irep, uint8_t *bin, size_t *len_p, uint8_t flags)
{
int result;
size_t rsize = 0;
@@ -362,129 +378,19 @@ write_section_irep(mrb_state *mrb, mrb_irep *irep, uint8_t *bin, size_t *len_p,
if (result != MRB_DUMP_OK) {
return result;
}
+ mrb_assert(rsize == get_irep_record_size(mrb, irep));
*len_p = cur - bin + rsize;
write_section_irep_header(mrb, *len_p, bin);
return MRB_DUMP_OK;
}
-static int
-write_section_lineno_header(mrb_state *mrb, size_t section_size, uint8_t *bin)
-{
- struct rite_section_lineno_header *header = (struct rite_section_lineno_header*)bin;
-
- memcpy(header->section_identify, RITE_SECTION_LINENO_IDENTIFIER, sizeof(header->section_identify));
- uint32_to_bin((uint32_t)section_size, header->section_size);
-
- return MRB_DUMP_OK;
-}
-
static size_t
-get_lineno_record_size(mrb_state *mrb, mrb_irep *irep)
-{
- size_t size = 0;
-
- size += sizeof(uint32_t); /* record size */
- size += sizeof(uint16_t); /* filename size */
- if (irep->filename) {
- size += strlen(irep->filename); /* filename */
- }
- size += sizeof(uint32_t); /* niseq */
- if (irep->lines) {
- size += sizeof(uint16_t) * irep->ilen; /* lineno */
- }
-
- return size;
-}
-
-static size_t
-write_lineno_record_1(mrb_state *mrb, mrb_irep *irep, uint8_t* bin)
-{
- uint8_t *cur = bin;
- size_t iseq_no;
- size_t filename_len;
- ptrdiff_t diff;
-
- cur += sizeof(uint32_t); /* record size */
-
- if (irep->filename) {
- filename_len = strlen(irep->filename);
- } else {
- filename_len = 0;
- }
- mrb_assert_int_fit(size_t, filename_len, uint16_t, UINT16_MAX);
- cur += uint16_to_bin((uint16_t)filename_len, cur); /* filename size */
-
- if (filename_len) {
- memcpy(cur, irep->filename, filename_len);
- cur += filename_len; /* filename */
- }
-
- if (irep->lines) {
- mrb_assert_int_fit(size_t, irep->ilen, uint32_t, UINT32_MAX);
- cur += uint32_to_bin((uint32_t)(irep->ilen), cur); /* niseq */
- for (iseq_no = 0; iseq_no < irep->ilen; iseq_no++) {
- cur += uint16_to_bin(irep->lines[iseq_no], cur); /* opcode */
- }
- }
- else {
- cur += uint32_to_bin(0, cur); /* niseq */
- }
-
- diff = cur - bin;
- mrb_assert_int_fit(ptrdiff_t, diff, uint32_t, UINT32_MAX);
-
- uint32_to_bin((uint32_t)diff, bin); /* record size */
-
- mrb_assert_int_fit(ptrdiff_t, diff, size_t, SIZE_MAX);
- return (size_t)diff;
-}
-
-static size_t
-write_lineno_record(mrb_state *mrb, mrb_irep *irep, uint8_t* bin)
-{
- size_t i;
- size_t rlen, size = 0;
-
- rlen = write_lineno_record_1(mrb, irep, bin);
- bin += rlen;
- size += rlen;
- for (i=0; i<irep->rlen; i++) {
- rlen = write_lineno_record(mrb, irep, bin);
- bin += rlen;
- size += rlen;
- }
- return size;
-}
-
-static int
-write_section_lineno(mrb_state *mrb, mrb_irep *irep, uint8_t *bin)
-{
- size_t section_size = 0;
- size_t rlen = 0; /* size of irep record */
- uint8_t *cur = bin;
-
- if (mrb == NULL || bin == NULL) {
- return MRB_DUMP_INVALID_ARGUMENT;
- }
-
- cur += sizeof(struct rite_section_lineno_header);
- section_size += sizeof(struct rite_section_lineno_header);
-
- rlen = write_lineno_record(mrb, irep, cur);
- section_size += rlen;
-
- write_section_lineno_header(mrb, section_size, bin);
-
- return MRB_DUMP_OK;
-}
-
-static size_t
-get_debug_record_size(mrb_state *mrb, mrb_irep *irep)
+get_debug_record_size(mrb_state *mrb, const mrb_irep *irep)
{
size_t ret = 0;
uint16_t f_idx;
- size_t i;
+ int i;
ret += sizeof(uint32_t); /* record size */
ret += sizeof(uint16_t); /* file count */
@@ -529,11 +435,12 @@ find_filename_index(const mrb_sym *ary, int ary_len, mrb_sym s)
}
static size_t
-get_filename_table_size(mrb_state *mrb, mrb_irep *irep, mrb_sym **fp, uint16_t *lp)
+get_filename_table_size(mrb_state *mrb, const mrb_irep *irep, mrb_sym **fp, uint16_t *lp)
{
mrb_sym *filenames = *fp;
- size_t i, size = 0;
- mrb_irep_debug_info *di = irep->debug_info;
+ size_t size = 0;
+ const mrb_irep_debug_info *di = irep->debug_info;
+ int i;
mrb_assert(lp);
for (i = 0; i < di->flen; ++i) {
@@ -548,7 +455,7 @@ get_filename_table_size(mrb_state *mrb, mrb_irep *irep, mrb_sym **fp, uint16_t *
filenames[*lp - 1] = file->filename_sym;
/* filename */
- mrb_sym2name_len(mrb, file->filename_sym, &filename_len);
+ mrb_sym_name_len(mrb, file->filename_sym, &filename_len);
size += sizeof(uint16_t) + (size_t)filename_len;
}
}
@@ -559,7 +466,7 @@ get_filename_table_size(mrb_state *mrb, mrb_irep *irep, mrb_sym **fp, uint16_t *
}
static size_t
-write_debug_record_1(mrb_state *mrb, mrb_irep *irep, uint8_t *bin, mrb_sym const* filenames, uint16_t filenames_len)
+write_debug_record_1(mrb_state *mrb, const mrb_irep *irep, uint8_t *bin, mrb_sym const* filenames, uint16_t filenames_len)
{
uint8_t *cur;
uint16_t f_idx;
@@ -606,17 +513,17 @@ write_debug_record_1(mrb_state *mrb, mrb_irep *irep, uint8_t *bin, mrb_sym const
ret = cur - bin;
mrb_assert_int_fit(ptrdiff_t, ret, uint32_t, UINT32_MAX);
- uint32_to_bin(ret, bin);
+ uint32_to_bin((uint32_t)ret, bin);
mrb_assert_int_fit(ptrdiff_t, ret, size_t, SIZE_MAX);
return (size_t)ret;
}
static size_t
-write_debug_record(mrb_state *mrb, mrb_irep *irep, uint8_t *bin, mrb_sym const* filenames, uint16_t filenames_len)
+write_debug_record(mrb_state *mrb, const mrb_irep *irep, uint8_t *bin, mrb_sym const* filenames, uint16_t filenames_len)
{
size_t size, len;
- size_t irep_no;
+ int irep_no;
size = len = write_debug_record_1(mrb, irep, bin, filenames, filenames_len);
bin += len;
@@ -631,7 +538,7 @@ write_debug_record(mrb_state *mrb, mrb_irep *irep, uint8_t *bin, mrb_sym const*
}
static int
-write_section_debug(mrb_state *mrb, mrb_irep *irep, uint8_t *cur, mrb_sym const *filenames, uint16_t filenames_len)
+write_section_debug(mrb_state *mrb, const mrb_irep *irep, uint8_t *cur, mrb_sym const *filenames, uint16_t filenames_len)
{
size_t section_size = 0;
const uint8_t *bin = cur;
@@ -652,9 +559,9 @@ write_section_debug(mrb_state *mrb, mrb_irep *irep, uint8_t *cur, mrb_sym const
cur += uint16_to_bin(filenames_len, cur);
section_size += sizeof(uint16_t);
for (i = 0; i < filenames_len; ++i) {
- sym = mrb_sym2name_len(mrb, filenames[i], &sym_len);
+ sym = mrb_sym_name_len(mrb, filenames[i], &sym_len);
mrb_assert(sym);
- cur += uint16_to_bin(sym_len, cur);
+ cur += uint16_to_bin((uint16_t)sym_len, cur);
memcpy(cur, sym, sym_len);
cur += sym_len;
section_size += sizeof(uint16_t) + sym_len;
@@ -664,9 +571,9 @@ write_section_debug(mrb_state *mrb, mrb_irep *irep, uint8_t *cur, mrb_sym const
dlen = write_debug_record(mrb, irep, cur, filenames, filenames_len);
section_size += dlen;
- memcpy(header->section_identify, RITE_SECTION_DEBUG_IDENTIFIER, sizeof(header->section_identify));
+ memcpy(header->section_ident, RITE_SECTION_DEBUG_IDENT, sizeof(header->section_ident));
mrb_assert(section_size <= INT32_MAX);
- uint32_to_bin(section_size, header->section_size);
+ uint32_to_bin((uint32_t)section_size, header->section_size);
return MRB_DUMP_OK;
}
@@ -674,14 +581,14 @@ write_section_debug(mrb_state *mrb, mrb_irep *irep, uint8_t *cur, mrb_sym const
static void
create_lv_sym_table(mrb_state *mrb, const mrb_irep *irep, mrb_sym **syms, uint32_t *syms_len)
{
- size_t i;
+ int i;
if (*syms == NULL) {
*syms = (mrb_sym*)mrb_malloc(mrb, sizeof(mrb_sym) * 1);
}
for (i = 0; i + 1 < irep->nlocals; ++i) {
- mrb_sym const name = irep->lv[i].name;
+ mrb_sym const name = irep->lv[i];
if (name == 0) continue;
if (find_filename_index(*syms, *syms_len, name) != -1) continue;
@@ -706,8 +613,8 @@ write_lv_sym_table(mrb_state *mrb, uint8_t **start, mrb_sym const *syms, uint32_
cur += uint32_to_bin(syms_len, cur);
for (i = 0; i < syms_len; ++i) {
- str = mrb_sym2name_len(mrb, syms[i], &str_len);
- cur += uint16_to_bin(str_len, cur);
+ str = mrb_sym_name_len(mrb, syms[i], &str_len);
+ cur += uint16_to_bin((uint16_t)str_len, cur);
memcpy(cur, str, str_len);
cur += str_len;
}
@@ -721,19 +628,17 @@ static int
write_lv_record(mrb_state *mrb, const mrb_irep *irep, uint8_t **start, mrb_sym const *syms, uint32_t syms_len)
{
uint8_t *cur = *start;
- size_t i;
+ int i;
for (i = 0; i + 1 < irep->nlocals; ++i) {
- if (irep->lv[i].name == 0) {
+ if (irep->lv[i] == 0) {
cur += uint16_to_bin(RITE_LV_NULL_MARK, cur);
- cur += uint16_to_bin(0, cur);
}
else {
- int const sym_idx = find_filename_index(syms, syms_len, irep->lv[i].name);
+ int const sym_idx = find_filename_index(syms, syms_len, irep->lv[i]);
mrb_assert(sym_idx != -1); /* local variable name must be in syms */
cur += uint16_to_bin(sym_idx, cur);
- cur += uint16_to_bin(irep->lv[i].r, cur);
}
}
@@ -747,11 +652,12 @@ write_lv_record(mrb_state *mrb, const mrb_irep *irep, uint8_t **start, mrb_sym c
}
static size_t
-get_lv_record_size(mrb_state *mrb, mrb_irep *irep)
+get_lv_record_size(mrb_state *mrb, const mrb_irep *irep)
{
- size_t ret = 0, i;
+ size_t ret = 0;
+ int i;
- ret += (sizeof(uint16_t) + sizeof(uint16_t)) * (irep->nlocals - 1);
+ ret += sizeof(uint16_t) * (irep->nlocals - 1);
for (i = 0; i < irep->rlen; ++i) {
ret += get_lv_record_size(mrb, irep->reps[i]);
@@ -761,7 +667,7 @@ get_lv_record_size(mrb_state *mrb, mrb_irep *irep)
}
static size_t
-get_lv_section_size(mrb_state *mrb, mrb_irep *irep, mrb_sym const *syms, uint32_t syms_len)
+get_lv_section_size(mrb_state *mrb, const mrb_irep *irep, mrb_sym const *syms, uint32_t syms_len)
{
size_t ret = 0, i;
@@ -769,7 +675,7 @@ get_lv_section_size(mrb_state *mrb, mrb_irep *irep, mrb_sym const *syms, uint32_
ret += sizeof(uint16_t) * syms_len; /* symbol name lengths */
for (i = 0; i < syms_len; ++i) {
mrb_int str_len;
- mrb_sym2name_len(mrb, syms[i], &str_len);
+ mrb_sym_name_len(mrb, syms[i], &str_len);
ret += str_len;
}
@@ -779,7 +685,7 @@ get_lv_section_size(mrb_state *mrb, mrb_irep *irep, mrb_sym const *syms, uint32_
}
static int
-write_section_lv(mrb_state *mrb, mrb_irep *irep, uint8_t *start, mrb_sym const *syms, uint32_t const syms_len)
+write_section_lv(mrb_state *mrb, const mrb_irep *irep, uint8_t *start, mrb_sym const *syms, uint32_t const syms_len)
{
uint8_t *cur = start;
struct rite_section_lv_header *header;
@@ -803,11 +709,11 @@ write_section_lv(mrb_state *mrb, mrb_irep *irep, uint8_t *start, mrb_sym const *
goto lv_section_exit;
}
- memcpy(header->section_identify, RITE_SECTION_LV_IDENTIFIER, sizeof(header->section_identify));
+ memcpy(header->section_ident, RITE_SECTION_LV_IDENT, sizeof(header->section_ident));
diff = cur - start;
mrb_assert_int_fit(ptrdiff_t, diff, size_t, SIZE_MAX);
- uint32_to_bin(diff, header->section_size);
+ uint32_to_bin((uint32_t)diff, header->section_size);
lv_section_exit:
return result;
@@ -817,70 +723,53 @@ static int
write_rite_binary_header(mrb_state *mrb, size_t binary_size, uint8_t *bin, uint8_t flags)
{
struct rite_binary_header *header = (struct rite_binary_header *)bin;
- uint16_t crc;
- uint32_t offset;
- if (flags & FLAG_BYTEORDER_NATIVE) {
- uint32_t ident = 0;
- size_t i;
-
- for(i=0; i<sizeof(ident); i++) {
- ident<<=8;
- ident|=RITE_BINARY_IDENTIFIER[i];
- }
- memcpy(header->binary_identify, (char*)&ident, sizeof(uint32_t));
- }
- else {
- memcpy(header->binary_identify, RITE_BINARY_IDENTIFIER, sizeof(header->binary_identify));
- }
- memcpy(header->binary_version, RITE_BINARY_FORMAT_VER, sizeof(header->binary_version));
+ memcpy(header->binary_ident, RITE_BINARY_IDENT, sizeof(header->binary_ident));
+ memcpy(header->major_version, RITE_BINARY_MAJOR_VER, sizeof(header->major_version));
+ memcpy(header->minor_version, RITE_BINARY_MINOR_VER, sizeof(header->minor_version));
memcpy(header->compiler_name, RITE_COMPILER_NAME, sizeof(header->compiler_name));
memcpy(header->compiler_version, RITE_COMPILER_VERSION, sizeof(header->compiler_version));
mrb_assert(binary_size <= UINT32_MAX);
uint32_to_bin((uint32_t)binary_size, header->binary_size);
- offset = (&(header->binary_crc[0]) - bin) + sizeof(uint16_t);
- crc = calc_crc_16_ccitt(bin + offset, binary_size - offset, 0);
- uint16_to_bin(crc, header->binary_crc);
-
return MRB_DUMP_OK;
}
static mrb_bool
-is_debug_info_defined(mrb_irep *irep)
+debug_info_defined_p(const mrb_irep *irep)
{
- size_t i;
+ int i;
if (!irep->debug_info) return FALSE;
for (i=0; i<irep->rlen; i++) {
- if (!is_debug_info_defined(irep->reps[i])) return FALSE;
+ if (!debug_info_defined_p(irep->reps[i])) return FALSE;
}
return TRUE;
}
static mrb_bool
-is_lv_defined(mrb_irep *irep)
+lv_defined_p(const mrb_irep *irep)
{
- size_t i;
+ int i;
if (irep->lv) { return TRUE; }
for (i = 0; i < irep->rlen; ++i) {
- if (is_lv_defined(irep->reps[i])) { return TRUE; }
+ if (lv_defined_p(irep->reps[i])) { return TRUE; }
}
return FALSE;
}
static int
-dump_irep(mrb_state *mrb, mrb_irep *irep, int debug_info, uint8_t **bin, size_t *bin_size, uint8_t flags)
+dump_irep(mrb_state *mrb, const mrb_irep *irep, uint8_t flags, uint8_t **bin, size_t *bin_size)
{
int result = MRB_DUMP_GENERAL_FAILURE;
size_t malloc_size;
size_t section_irep_size;
size_t section_lineno_size = 0, section_lv_size = 0;
uint8_t *cur = NULL;
- mrb_bool const debug_info_defined = is_debug_info_defined(irep), lv_defined = is_lv_defined(irep);
+ mrb_bool const debug_info_defined = debug_info_defined_p(irep), lv_defined = lv_defined_p(irep);
mrb_sym *lv_syms = NULL; uint32_t lv_syms_len = 0;
mrb_sym *filenames = NULL; uint16_t filenames_len = 0;
@@ -893,7 +782,7 @@ dump_irep(mrb_state *mrb, mrb_irep *irep, int debug_info, uint8_t **bin, size_t
section_irep_size += get_irep_record_size(mrb, irep);
/* DEBUG section size */
- if (debug_info) {
+ if (flags & MRB_DUMP_DEBUG_INFO) {
if (debug_info_defined) {
section_lineno_size += sizeof(struct rite_section_debug_header);
/* filename table */
@@ -905,10 +794,6 @@ dump_irep(mrb_state *mrb, mrb_irep *irep, int debug_info, uint8_t **bin, size_t
section_lineno_size += get_debug_record_size(mrb, irep);
}
- else {
- section_lineno_size += sizeof(struct rite_section_lineno_header);
- section_lineno_size += get_lineno_record_size(mrb, irep);
- }
}
if (lv_defined) {
@@ -933,15 +818,12 @@ dump_irep(mrb_state *mrb, mrb_irep *irep, int debug_info, uint8_t **bin, size_t
sizeof(struct rite_binary_footer);
/* write DEBUG section */
- if (debug_info) {
+ if (flags & MRB_DUMP_DEBUG_INFO) {
if (debug_info_defined) {
result = write_section_debug(mrb, irep, cur, filenames, filenames_len);
- }
- else {
- result = write_section_lineno(mrb, irep, cur);
- }
- if (result != MRB_DUMP_OK) {
- goto error_exit;
+ if (result != MRB_DUMP_OK) {
+ goto error_exit;
+ }
}
cur += section_lineno_size;
}
@@ -962,23 +844,21 @@ error_exit:
mrb_free(mrb, *bin);
*bin = NULL;
}
- if (lv_syms) {
- mrb_free(mrb, lv_syms);
- }
- if (filenames) {
- mrb_free(mrb, filenames);
- }
+ mrb_free(mrb, lv_syms);
+ mrb_free(mrb, filenames);
return result;
}
int
-mrb_dump_irep(mrb_state *mrb, mrb_irep *irep, int debug_info, uint8_t **bin, size_t *bin_size)
+mrb_dump_irep(mrb_state *mrb, const mrb_irep *irep, uint8_t flags, uint8_t **bin, size_t *bin_size)
{
- return dump_irep(mrb, irep, debug_info, bin, bin_size, FLAG_BYTEORDER_NONATIVE);
+ return dump_irep(mrb, irep, flags, bin, bin_size);
}
+#ifndef MRB_NO_STDIO
+
int
-mrb_dump_irep_binary(mrb_state *mrb, mrb_irep *irep, int debug_info, FILE* fp)
+mrb_dump_irep_binary(mrb_state *mrb, const mrb_irep *irep, uint8_t flags, FILE* fp)
{
uint8_t *bin = NULL;
size_t bin_size = 0;
@@ -988,7 +868,7 @@ mrb_dump_irep_binary(mrb_state *mrb, mrb_irep *irep, int debug_info, FILE* fp)
return MRB_DUMP_INVALID_ARGUMENT;
}
- result = dump_irep(mrb, irep, debug_info, &bin, &bin_size, FLAG_BYTEORDER_NONATIVE);
+ result = dump_irep(mrb, irep, flags, &bin, &bin_size);
if (result == MRB_DUMP_OK) {
if (fwrite(bin, sizeof(bin[0]), bin_size, fp) != bin_size) {
result = MRB_DUMP_WRITE_FAULT;
@@ -999,48 +879,30 @@ mrb_dump_irep_binary(mrb_state *mrb, mrb_irep *irep, int debug_info, FILE* fp)
return result;
}
-static mrb_bool
-is_valid_c_symbol_name(const char *name)
-{
- const char *c = NULL;
-
- if (name == NULL || name[0] == '\0') return FALSE;
- if (!ISALPHA(name[0]) && name[0] != '_') return FALSE;
-
- c = &name[1];
- for (; *c != '\0'; ++c) {
- if (!ISALNUM(*c) && *c != '_') return FALSE;
- }
-
- return TRUE;
-}
-
int
-mrb_dump_irep_cfunc(mrb_state *mrb, mrb_irep *irep, int debug_info, FILE *fp, const char *initname)
+mrb_dump_irep_cfunc(mrb_state *mrb, const mrb_irep *irep, uint8_t flags, FILE *fp, const char *initname)
{
uint8_t *bin = NULL;
size_t bin_size = 0, bin_idx = 0;
int result;
- if (fp == NULL || initname == NULL || !is_valid_c_symbol_name(initname)) {
+ if (fp == NULL || initname == NULL || initname[0] == '\0') {
return MRB_DUMP_INVALID_ARGUMENT;
}
-
- result = dump_irep(mrb, irep, debug_info, &bin, &bin_size, FLAG_BYTEORDER_NATIVE);
+ result = dump_irep(mrb, irep, flags, &bin, &bin_size);
if (result == MRB_DUMP_OK) {
if (fprintf(fp, "#include <stdint.h>\n") < 0) { /* for uint8_t under at least Darwin */
mrb_free(mrb, bin);
return MRB_DUMP_WRITE_FAULT;
}
if (fprintf(fp,
- "const uint8_t\n"
- "#if defined __GNUC__\n"
- "__attribute__((aligned(%u)))\n"
- "#elif defined _MSC_VER\n"
- "__declspec(align(%u))\n"
- "#endif\n"
- "%s[] = {",
- (uint16_t)MRB_DUMP_ALIGNMENT, (uint16_t)MRB_DUMP_ALIGNMENT, initname) < 0) {
+ "%s\n"
+ "const uint8_t %s[] = {",
+ (flags & MRB_DUMP_STATIC) ? "static"
+ : "#ifdef __cplusplus\n"
+ "extern\n"
+ "#endif",
+ initname) < 0) {
mrb_free(mrb, bin);
return MRB_DUMP_WRITE_FAULT;
}
@@ -1066,4 +928,4 @@ mrb_dump_irep_cfunc(mrb_state *mrb, mrb_irep *irep, int debug_info, FILE *fp, co
return result;
}
-#endif /* ENABLE_STDIO */
+#endif /* MRB_NO_STDIO */
diff --git a/src/enum.c b/src/enum.c
index 3def9e860..b95956715 100644
--- a/src/enum.c
+++ b/src/enum.c
@@ -4,11 +4,27 @@
** See Copyright Notice in mruby.h
*/
-#include "mruby.h"
+#include <mruby.h>
+#include <mruby/proc.h>
+
+/* internal method `__update_hash(oldhash, index, itemhash)` */
+static mrb_value
+enum_update_hash(mrb_state *mrb, mrb_value self)
+{
+ mrb_int hash;
+ mrb_int index;
+ mrb_int hv;
+
+ mrb_get_args(mrb, "iii", &hash, &index, &hv);
+ hash ^= ((uint32_t)hv << (index % 16));
+
+ return mrb_int_value(mrb, hash);
+}
void
mrb_init_enumerable(mrb_state *mrb)
{
- mrb_define_module(mrb, "Enumerable"); /* 15.3.2 */
+ struct RClass *enumerable;
+ enumerable = mrb_define_module(mrb, "Enumerable"); /* 15.3.2 */
+ mrb_define_module_function(mrb, enumerable, "__update_hash", enum_update_hash, MRB_ARGS_REQ(3));
}
-
diff --git a/src/error.c b/src/error.c
index 0a1a97a0b..7953deea7 100644
--- a/src/error.c
+++ b/src/error.c
@@ -7,19 +7,19 @@
#include <errno.h>
#include <stdarg.h>
#include <stdlib.h>
-#include "mruby.h"
-#include "mruby/array.h"
-#include "mruby/irep.h"
-#include "mruby/proc.h"
-#include "mruby/string.h"
-#include "mruby/variable.h"
-#include "mruby/debug.h"
-#include "mruby/error.h"
-#include "mruby/class.h"
-#include "mrb_throw.h"
+#include <mruby.h>
+#include <mruby/array.h>
+#include <mruby/irep.h>
+#include <mruby/proc.h>
+#include <mruby/string.h>
+#include <mruby/variable.h>
+#include <mruby/error.h>
+#include <mruby/class.h>
+#include <mruby/throw.h>
+#include <mruby/presym.h>
MRB_API mrb_value
-mrb_exc_new(mrb_state *mrb, struct RClass *c, const char *ptr, long len)
+mrb_exc_new(mrb_state *mrb, struct RClass *c, const char *ptr, size_t len)
{
mrb_value arg = mrb_str_new(mrb, ptr, len);
return mrb_obj_new(mrb, c, 1, &arg);
@@ -28,7 +28,7 @@ mrb_exc_new(mrb_state *mrb, struct RClass *c, const char *ptr, long len)
MRB_API mrb_value
mrb_exc_new_str(mrb_state *mrb, struct RClass* c, mrb_value str)
{
- str = mrb_str_to_str(mrb, str);
+ mrb_to_str(mrb, str);
return mrb_obj_new(mrb, c, 1, &str);
}
@@ -46,7 +46,7 @@ exc_initialize(mrb_state *mrb, mrb_value exc)
mrb_value mesg;
if (mrb_get_args(mrb, "|o", &mesg) == 1) {
- mrb_iv_set(mrb, exc, mrb_intern_lit(mrb, "mesg"), mesg);
+ mrb_iv_set(mrb, exc, MRB_SYM(mesg), mesg);
}
return exc;
}
@@ -60,7 +60,7 @@ exc_initialize(mrb_state *mrb, mrb_value exc)
* With no argument, or if the argument is the same as the receiver,
* return the receiver. Otherwise, create a new
* exception object of the same class as the receiver, but with a
- * message equal to <code>string.to_str</code>.
+ * message equal to <code>string</code>.
*
*/
@@ -69,13 +69,13 @@ exc_exception(mrb_state *mrb, mrb_value self)
{
mrb_value exc;
mrb_value a;
- int argc;
+ mrb_int argc;
argc = mrb_get_args(mrb, "|o", &a);
if (argc == 0) return self;
if (mrb_obj_equal(mrb, self, a)) return self;
exc = mrb_obj_clone(mrb, self);
- mrb_iv_set(mrb, exc, mrb_intern_lit(mrb, "mesg"), a);
+ mrb_iv_set(mrb, exc, MRB_SYM(mesg), a);
return exc;
}
@@ -91,7 +91,7 @@ exc_exception(mrb_state *mrb, mrb_value self)
static mrb_value
exc_to_s(mrb_state *mrb, mrb_value exc)
{
- mrb_value mesg = mrb_attr_get(mrb, exc, mrb_intern_lit(mrb, "mesg"));
+ mrb_value mesg = mrb_attr_get(mrb, exc, MRB_SYM(mesg));
struct RObject *p;
if (!mrb_string_p(mesg)) {
@@ -109,15 +109,13 @@ exc_to_s(mrb_state *mrb, mrb_value exc)
* exception.message -> string
*
* Returns the result of invoking <code>exception.to_s</code>.
- * Normally this returns the exception's message or name. By
- * supplying a to_str method, exceptions are agreeing to
- * be used where Strings are expected.
+ * Normally this returns the exception's message or name.
*/
static mrb_value
exc_message(mrb_state *mrb, mrb_value exc)
{
- return mrb_funcall(mrb, exc, "to_s", 0);
+ return mrb_funcall_id(mrb, exc, MRB_SYM(to_s), 0);
}
/*
@@ -130,139 +128,250 @@ exc_message(mrb_state *mrb, mrb_value exc)
* returns message and class name.
*/
-static mrb_value
-exc_inspect(mrb_state *mrb, mrb_value exc)
+mrb_value
+mrb_exc_inspect(mrb_state *mrb, mrb_value exc)
{
- mrb_value str, mesg, file, line;
- mrb_bool append_mesg;
+ mrb_value mesg = mrb_attr_get(mrb, exc, MRB_SYM(mesg));
+ mrb_value cname = mrb_mod_to_s(mrb, mrb_obj_value(mrb_obj_class(mrb, exc)));
+ mesg = mrb_obj_as_string(mrb, mesg);
+ return RSTRING_LEN(mesg) == 0 ? cname : mrb_format(mrb, "%v (%v)", mesg, cname);
+}
- mesg = mrb_attr_get(mrb, exc, mrb_intern_lit(mrb, "mesg"));
- file = mrb_attr_get(mrb, exc, mrb_intern_lit(mrb, "file"));
- line = mrb_attr_get(mrb, exc, mrb_intern_lit(mrb, "line"));
+void mrb_keep_backtrace(mrb_state *mrb, mrb_value exc);
- append_mesg = !mrb_nil_p(mesg);
- if (append_mesg) {
- mesg = mrb_obj_as_string(mrb, mesg);
- append_mesg = RSTRING_LEN(mesg) > 0;
+static void
+set_backtrace(mrb_state *mrb, mrb_value exc, mrb_value backtrace)
+{
+ if (!mrb_array_p(backtrace)) {
+ type_err:
+ mrb_raise(mrb, E_TYPE_ERROR, "backtrace must be Array of String");
}
+ else {
+ const mrb_value *p = RARRAY_PTR(backtrace);
+ const mrb_value *pend = p + RARRAY_LEN(backtrace);
- if (!mrb_nil_p(file) && !mrb_nil_p(line)) {
- str = mrb_str_dup(mrb, file);
- mrb_str_cat_lit(mrb, str, ":");
- mrb_str_append(mrb, str, line);
- mrb_str_cat_lit(mrb, str, ": ");
- if (append_mesg) {
- mrb_str_append(mrb, str, mesg);
- mrb_str_cat_lit(mrb, str, " (");
- }
- mrb_str_cat_cstr(mrb, str, mrb_obj_classname(mrb, exc));
- if (append_mesg) {
- mrb_str_cat_lit(mrb, str, ")");
+ while (p < pend) {
+ if (!mrb_string_p(*p)) goto type_err;
+ p++;
}
}
+ mrb_iv_set(mrb, exc, MRB_SYM(backtrace), backtrace);
+}
+
+static mrb_value
+exc_set_backtrace(mrb_state *mrb, mrb_value exc)
+{
+ mrb_value backtrace = mrb_get_arg1(mrb);
+
+ set_backtrace(mrb, exc, backtrace);
+ return backtrace;
+}
+
+void
+mrb_exc_set(mrb_state *mrb, mrb_value exc)
+{
+ if (mrb_nil_p(exc)) {
+ mrb->exc = 0;
+ }
else {
- const char *cname = mrb_obj_classname(mrb, exc);
- str = mrb_str_new_cstr(mrb, cname);
- mrb_str_cat_lit(mrb, str, ": ");
- if (append_mesg) {
- mrb_str_append(mrb, str, mesg);
+ mrb->exc = mrb_obj_ptr(exc);
+ if (mrb->gc.arena_idx > 0 &&
+ (struct RBasic*)mrb->exc == mrb->gc.arena[mrb->gc.arena_idx-1]) {
+ mrb->gc.arena_idx--;
}
- else {
- mrb_str_cat_cstr(mrb, str, cname);
+ if (!mrb->gc.out_of_memory && !mrb_frozen_p(mrb->exc)) {
+ mrb_keep_backtrace(mrb, exc);
}
}
- return str;
}
-
-static void
-exc_debug_info(mrb_state *mrb, struct RObject *exc)
+static mrb_noreturn void
+exc_throw(mrb_state *mrb, mrb_value exc)
{
- mrb_callinfo *ci = mrb->c->ci;
- mrb_code *pc = ci->pc;
-
- mrb_obj_iv_set(mrb, exc, mrb_intern_lit(mrb, "ciidx"), mrb_fixnum_value((mrb_int)(ci - mrb->c->cibase)));
- while (ci >= mrb->c->cibase) {
- mrb_code *err = ci->err;
-
- if (!err && pc) err = pc - 1;
- if (err && ci->proc && !MRB_PROC_CFUNC_P(ci->proc)) {
- mrb_irep *irep = ci->proc->body.irep;
-
- int32_t const line = mrb_debug_get_line(irep, (uint32_t)(err - irep->iseq));
- char const* file = mrb_debug_get_filename(irep, (uint32_t)(err - irep->iseq));
- if (line != -1 && file) {
- mrb_obj_iv_set(mrb, exc, mrb_intern_lit(mrb, "file"), mrb_str_new_cstr(mrb, file));
- mrb_obj_iv_set(mrb, exc, mrb_intern_lit(mrb, "line"), mrb_fixnum_value(line));
- return;
- }
- }
- pc = ci->pc;
- ci--;
+ if (!mrb->jmp) {
+ mrb_p(mrb, exc);
+ abort();
}
+ MRB_THROW(mrb->jmp);
}
MRB_API mrb_noreturn void
mrb_exc_raise(mrb_state *mrb, mrb_value exc)
{
- mrb->exc = mrb_obj_ptr(exc);
- if (!mrb->out_of_memory) {
- exc_debug_info(mrb, mrb->exc);
+ if (mrb_break_p(exc)) {
+ mrb->exc = mrb_obj_ptr(exc);
}
- if (!mrb->jmp) {
- mrb_p(mrb, exc);
- abort();
+ else {
+ if (!mrb_obj_is_kind_of(mrb, exc, mrb->eException_class)) {
+ mrb_raise(mrb, E_TYPE_ERROR, "exception object expected");
+ }
+ mrb_exc_set(mrb, exc);
}
- MRB_THROW(mrb->jmp);
+ exc_throw(mrb, exc);
}
MRB_API mrb_noreturn void
mrb_raise(mrb_state *mrb, struct RClass *c, const char *msg)
{
- mrb_value mesg;
- mesg = mrb_str_new_cstr(mrb, msg);
- mrb_exc_raise(mrb, mrb_exc_new_str(mrb, c, mesg));
+ mrb_exc_raise(mrb, mrb_exc_new_str(mrb, c, mrb_str_new_cstr(mrb, msg)));
}
+/*
+ * <code>vsprintf</code> like formatting.
+ *
+ * The syntax of a format sequence is as follows.
+ *
+ * %[modifier]specifier
+ *
+ * The modifiers are:
+ *
+ * ----------+------------------------------------------------------------
+ * Modifier | Meaning
+ * ----------+------------------------------------------------------------
+ * ! | Convert to string by corresponding `inspect` instead of
+ * | corresponding `to_s`.
+ * ----------+------------------------------------------------------------
+ *
+ * The specifiers are:
+ *
+ * ----------+----------------+--------------------------------------------
+ * Specifier | Argument Type | Note
+ * ----------+----------------+--------------------------------------------
+ * c | char |
+ * d | int |
+ * f | mrb_float |
+ * i | mrb_int |
+ * l | char*, size_t | Arguments are string and length.
+ * n | mrb_sym |
+ * s | char* | Argument is NUL terminated string.
+ * t | mrb_value | Convert to type (class) of object.
+ * v,S | mrb_value |
+ * C | struct RClass* |
+ * T | mrb_value | Convert to real type (class) of object.
+ * Y | mrb_value | Same as `!v` if argument is `true`, `false`
+ * | | or `nil`, otherwise same as `T`.
+ * % | - | Convert to percent sign itself (no argument
+ * | | taken).
+ * ----------+----------------+--------------------------------------------
+ */
MRB_API mrb_value
mrb_vformat(mrb_state *mrb, const char *format, va_list ap)
{
- const char *p = format;
- const char *b = p;
- ptrdiff_t size;
- mrb_value ary = mrb_ary_new_capa(mrb, 4);
+ const char *chars, *p = format, *b = format, *e;
+ char ch;
+ size_t len;
+ mrb_int i;
+ struct RClass *cls;
+ mrb_bool inspect = FALSE;
+ mrb_value result = mrb_str_new_capa(mrb, 128), obj, str;
+ int ai = mrb_gc_arena_save(mrb);
while (*p) {
const char c = *p++;
-
+ e = p;
if (c == '%') {
- if (*p == 'S') {
- size = p - b - 1;
- mrb_ary_push(mrb, ary, mrb_str_new(mrb, b, size));
- mrb_ary_push(mrb, ary, va_arg(ap, mrb_value));
- b = p + 1;
+ if (*p == '!') {
+ inspect = TRUE;
+ ++p;
+ }
+ if (!*p) break;
+ switch (*p) {
+ case 'c':
+ ch = (char)va_arg(ap, int);
+ chars = &ch;
+ len = 1;
+ goto L_cat;
+ case 'd': case 'i':
+#if MRB_INT_MAX < INT_MAX
+ i = (mrb_int)va_arg(ap, int);
+#else
+ i = *p == 'd' ? (mrb_int)va_arg(ap, int) : va_arg(ap, mrb_int);
+#endif
+ obj = mrb_fixnum_value(i);
+ goto L_cat_obj;
+#ifndef MRB_NO_FLOAT
+ case 'f':
+ obj = mrb_float_value(mrb, (mrb_float)va_arg(ap, double));
+ goto L_cat_obj;
+#endif
+ case 'l':
+ chars = va_arg(ap, char*);
+ len = va_arg(ap, size_t);
+ L_cat:
+ if (inspect) {
+ obj = mrb_str_new(mrb, chars, len);
+ goto L_cat_obj;
+ }
+ L_cat_plain:
+ mrb_str_cat(mrb, result, b, e - b - 1);
+ mrb_str_cat(mrb, result, chars, len);
+ b = ++p;
+ mrb_gc_arena_restore(mrb, ai);
+ break;
+ case 'n':
+#if UINT32_MAX < INT_MAX
+ obj = mrb_symbol_value((mrb_sym)va_arg(ap, int));
+#else
+ obj = mrb_symbol_value(va_arg(ap, mrb_sym));
+#endif
+ goto L_cat_obj;
+ case 's':
+ chars = va_arg(ap, char*);
+ len = strlen(chars);
+ goto L_cat;
+ case 't':
+ cls = mrb_class(mrb, va_arg(ap, mrb_value));
+ goto L_cat_class;
+ case 'v': case 'S':
+ obj = va_arg(ap, mrb_value);
+ L_cat_obj:
+ str = (inspect ? mrb_inspect : mrb_obj_as_string)(mrb, obj);
+ if (mrb_type(str) != MRB_TT_STRING) {
+ chars = "void (no string conversion)";
+ len = strlen(chars);
+ }
+ else {
+ chars = RSTRING_PTR(str);
+ len = RSTRING_LEN(str);
+ }
+ goto L_cat_plain;
+ case 'C':
+ cls = va_arg(ap, struct RClass*);
+ L_cat_class:
+ obj = mrb_obj_value(cls);
+ goto L_cat_obj;
+ case 'T':
+ obj = va_arg(ap, mrb_value);
+ L_cat_real_class_of:
+ cls = mrb_obj_class(mrb, obj);
+ goto L_cat_class;
+ case 'Y':
+ obj = va_arg(ap, mrb_value);
+ if (!mrb_test(obj) || mrb_true_p(obj)) {
+ inspect = TRUE;
+ goto L_cat_obj;
+ }
+ else {
+ goto L_cat_real_class_of;
+ }
+ case '%':
+ L_cat_current:
+ chars = p;
+ len = 1;
+ goto L_cat_plain;
+ default:
+ mrb_raisef(mrb, E_ARGUMENT_ERROR, "malformed format string - %%%c", *p);
}
}
else if (c == '\\') {
- if (*p) {
- size = p - b - 1;
- mrb_ary_push(mrb, ary, mrb_str_new(mrb, b, size));
- mrb_ary_push(mrb, ary, mrb_str_new(mrb, p, 1));
- b = ++p;
- }
- else {
- break;
- }
+ if (!*p) break;
+ goto L_cat_current;
+
}
}
- if (b == format) {
- return mrb_str_new_cstr(mrb, format);
- }
- else {
- size = p - b;
- mrb_ary_push(mrb, ary, mrb_str_new(mrb, b, size));
- return mrb_ary_join(mrb, ary, mrb_str_new(mrb, NULL, 0));
- }
+
+ mrb_str_cat(mrb, result, b, p - b);
+ return result;
}
MRB_API mrb_value
@@ -278,38 +387,47 @@ mrb_format(mrb_state *mrb, const char *format, ...)
return str;
}
+static mrb_noreturn void
+raise_va(mrb_state *mrb, struct RClass *c, const char *fmt, va_list ap, int argc, mrb_value *argv)
+{
+ mrb_value mesg;
+
+ mesg = mrb_vformat(mrb, fmt, ap);
+ if (argv == NULL) {
+ argv = &mesg;
+ }
+ else {
+ argv[0] = mesg;
+ }
+ mrb_exc_raise(mrb, mrb_obj_new(mrb, c, argc+1, argv));
+}
+
MRB_API mrb_noreturn void
mrb_raisef(mrb_state *mrb, struct RClass *c, const char *fmt, ...)
{
va_list args;
- mrb_value mesg;
va_start(args, fmt);
- mesg = mrb_vformat(mrb, fmt, args);
+ raise_va(mrb, c, fmt, args, 0, NULL);
va_end(args);
- mrb_exc_raise(mrb, mrb_exc_new_str(mrb, c, mesg));
}
MRB_API mrb_noreturn void
mrb_name_error(mrb_state *mrb, mrb_sym id, const char *fmt, ...)
{
- mrb_value exc;
mrb_value argv[2];
va_list args;
va_start(args, fmt);
- argv[0] = mrb_vformat(mrb, fmt, args);
- va_end(args);
-
argv[1] = mrb_symbol_value(id);
- exc = mrb_obj_new(mrb, E_NAME_ERROR, 2, argv);
- mrb_exc_raise(mrb, exc);
+ raise_va(mrb, E_NAME_ERROR, fmt, args, 1, argv);
+ va_end(args);
}
MRB_API void
mrb_warn(mrb_state *mrb, const char *fmt, ...)
{
-#ifdef ENABLE_STDIO
+#ifndef MRB_NO_STDIO
va_list ap;
mrb_value str;
@@ -317,6 +435,7 @@ mrb_warn(mrb_state *mrb, const char *fmt, ...)
str = mrb_vformat(mrb, fmt, ap);
fputs("warning: ", stderr);
fwrite(RSTRING_PTR(str), RSTRING_LEN(str), 1, stderr);
+ putc('\n', stderr);
va_end(ap);
#endif
}
@@ -324,7 +443,7 @@ mrb_warn(mrb_state *mrb, const char *fmt, ...)
MRB_API mrb_noreturn void
mrb_bug(mrb_state *mrb, const char *fmt, ...)
{
-#ifdef ENABLE_STDIO
+#ifndef MRB_NO_STDIO
va_list ap;
mrb_value str;
@@ -337,14 +456,8 @@ mrb_bug(mrb_state *mrb, const char *fmt, ...)
exit(EXIT_FAILURE);
}
-static void
-set_backtrace(mrb_state *mrb, mrb_value info, mrb_value bt)
-{
- mrb_funcall(mrb, info, "set_backtrace", 1, bt);
-}
-
-static mrb_value
-make_exception(mrb_state *mrb, int argc, const mrb_value *argv, mrb_bool isstr)
+MRB_API mrb_value
+mrb_make_exception(mrb_state *mrb, mrb_int argc, const mrb_value *argv)
{
mrb_value mesg;
int n;
@@ -356,12 +469,9 @@ make_exception(mrb_state *mrb, int argc, const mrb_value *argv, mrb_bool isstr)
case 1:
if (mrb_nil_p(argv[0]))
break;
- if (isstr) {
- mesg = mrb_check_string_type(mrb, argv[0]);
- if (!mrb_nil_p(mesg)) {
- mesg = mrb_exc_new_str(mrb, E_RUNTIME_ERROR, mesg);
- break;
- }
+ if (mrb_string_p(argv[0])) {
+ mesg = mrb_exc_new_str(mrb, E_RUNTIME_ERROR, argv[0]);
+ break;
}
n = 0;
goto exception_call;
@@ -371,7 +481,7 @@ make_exception(mrb_state *mrb, int argc, const mrb_value *argv, mrb_bool isstr)
n = 1;
exception_call:
{
- mrb_sym exc = mrb_intern_lit(mrb, "exception");
+ mrb_sym exc = MRB_SYM(exception);
if (mrb_respond_to(mrb, argv[0], exc)) {
mesg = mrb_funcall_argv(mrb, argv[0], exc, n, argv+1);
}
@@ -383,25 +493,19 @@ exception_call:
break;
default:
- mrb_raisef(mrb, E_ARGUMENT_ERROR, "wrong number of arguments (%S for 0..3)", mrb_fixnum_value(argc));
+ mrb_argnum_error(mrb, argc, 0, 3);
break;
}
if (argc > 0) {
if (!mrb_obj_is_kind_of(mrb, mesg, mrb->eException_class))
- mrb_raise(mrb, E_TYPE_ERROR, "exception object expected");
+ mrb_raise(mrb, mrb->eException_class, "exception object expected");
if (argc > 2)
- set_backtrace(mrb, mesg, argv[2]);
+ set_backtrace(mrb, mesg, argv[2]);
}
return mesg;
}
-MRB_API mrb_value
-mrb_make_exception(mrb_state *mrb, int argc, const mrb_value *argv)
-{
- return make_exception(mrb, argc, argv, TRUE);
-}
-
MRB_API void
mrb_sys_fail(mrb_state *mrb, const char *mesg)
{
@@ -424,38 +528,134 @@ mrb_sys_fail(mrb_state *mrb, const char *mesg)
}
MRB_API mrb_noreturn void
-mrb_no_method_error(mrb_state *mrb, mrb_sym id, mrb_int argc, const mrb_value *argv, char const* fmt, ...)
+mrb_no_method_error(mrb_state *mrb, mrb_sym id, mrb_value args, char const* fmt, ...)
{
mrb_value exc;
+ mrb_value argv[3];
va_list ap;
va_start(ap, fmt);
- exc = mrb_funcall(mrb, mrb_obj_value(E_NOMETHOD_ERROR), "new", 3,
- mrb_vformat(mrb, fmt, ap), mrb_symbol_value(id),
- mrb_ary_new_from_values(mrb, argc, argv));
+ argv[0] = mrb_vformat(mrb, fmt, ap);
+ argv[1] = mrb_symbol_value(id);
+ argv[2] = args;
va_end(ap);
+ exc = mrb_obj_new(mrb, E_NOMETHOD_ERROR, 3, argv);
mrb_exc_raise(mrb, exc);
}
+MRB_API mrb_noreturn void
+mrb_frozen_error(mrb_state *mrb, void *frozen_obj)
+{
+ mrb_raisef(mrb, E_FROZEN_ERROR, "can't modify frozen %t", mrb_obj_value(frozen_obj));
+}
+
+MRB_API mrb_noreturn void
+mrb_argnum_error(mrb_state *mrb, mrb_int argc, int min, int max)
+{
+#define FMT(exp) "wrong number of arguments (given %i, expected " exp ")"
+ if (min == max)
+ mrb_raisef(mrb, E_ARGUMENT_ERROR, FMT("%d"), argc, min);
+ else if (max < 0)
+ mrb_raisef(mrb, E_ARGUMENT_ERROR, FMT("%d+"), argc, min);
+ else
+ mrb_raisef(mrb, E_ARGUMENT_ERROR, FMT("%d..%d"), argc, min, max);
+#undef FMT
+}
+
+void mrb_core_init_printabort(void);
+
+int
+mrb_core_init_protect(mrb_state *mrb, void (*body)(mrb_state *, void *), void *opaque)
+{
+ struct mrb_jmpbuf *prev_jmp = mrb->jmp;
+ struct mrb_jmpbuf c_jmp;
+ int err = 1;
+
+ MRB_TRY(&c_jmp) {
+ mrb->jmp = &c_jmp;
+ body(mrb, opaque);
+ err = 0;
+ } MRB_CATCH(&c_jmp) {
+ if (mrb->exc) {
+ mrb_p(mrb, mrb_obj_value(mrb->exc));
+ mrb->exc = NULL;
+ }
+ else {
+ mrb_core_init_printabort();
+ }
+ } MRB_END_EXC(&c_jmp);
+
+ mrb->jmp = prev_jmp;
+
+ return err;
+}
+
+mrb_noreturn void
+mrb_core_init_abort(mrb_state *mrb)
+{
+ mrb->exc = NULL;
+ exc_throw(mrb, mrb_nil_value());
+}
+
+void
+mrb_protect_atexit(mrb_state *mrb)
+{
+ if (mrb->atexit_stack_len > 0) {
+ struct mrb_jmpbuf *prev_jmp = mrb->jmp;
+ struct mrb_jmpbuf c_jmp;
+ for (int i = mrb->atexit_stack_len; i > 0; --i) {
+ MRB_TRY(&c_jmp) {
+ mrb->jmp = &c_jmp;
+ mrb->atexit_stack[i - 1](mrb);
+ mrb->jmp = prev_jmp;
+ } MRB_CATCH(&c_jmp) {
+ /* ignore atexit errors */
+ } MRB_END_EXC(&c_jmp);
+ }
+#ifndef MRB_FIXED_STATE_ATEXIT_STACK
+ mrb_free(mrb, mrb->atexit_stack);
+#endif
+ mrb->jmp = prev_jmp;
+ }
+}
+
+mrb_noreturn void
+mrb_raise_nomemory(mrb_state *mrb)
+{
+ if (mrb->nomem_err) {
+ mrb_exc_raise(mrb, mrb_obj_value(mrb->nomem_err));
+ }
+ else {
+ mrb_core_init_abort(mrb);
+ }
+}
+
void
mrb_init_exception(mrb_state *mrb)
{
- struct RClass *exception, *runtime_error, *script_error;
+ struct RClass *exception, *script_error, *stack_error, *nomem_error;
mrb->eException_class = exception = mrb_define_class(mrb, "Exception", mrb->object_class); /* 15.2.22 */
MRB_SET_INSTANCE_TT(exception, MRB_TT_EXCEPTION);
- mrb_define_class_method(mrb, exception, "exception", mrb_instance_new, MRB_ARGS_ANY());
- mrb_define_method(mrb, exception, "exception", exc_exception, MRB_ARGS_ANY());
- mrb_define_method(mrb, exception, "initialize", exc_initialize, MRB_ARGS_ANY());
+ mrb_define_class_method(mrb, exception, "exception", mrb_instance_new, MRB_ARGS_OPT(1));
+ mrb_define_method(mrb, exception, "exception", exc_exception, MRB_ARGS_OPT(1));
+ mrb_define_method(mrb, exception, "initialize", exc_initialize, MRB_ARGS_OPT(1));
mrb_define_method(mrb, exception, "to_s", exc_to_s, MRB_ARGS_NONE());
mrb_define_method(mrb, exception, "message", exc_message, MRB_ARGS_NONE());
- mrb_define_method(mrb, exception, "inspect", exc_inspect, MRB_ARGS_NONE());
+ mrb_define_method(mrb, exception, "inspect", mrb_exc_inspect, MRB_ARGS_NONE());
mrb_define_method(mrb, exception, "backtrace", mrb_exc_backtrace, MRB_ARGS_NONE());
+ mrb_define_method(mrb, exception, "set_backtrace", exc_set_backtrace, MRB_ARGS_REQ(1));
mrb->eStandardError_class = mrb_define_class(mrb, "StandardError", mrb->eException_class); /* 15.2.23 */
- runtime_error = mrb_define_class(mrb, "RuntimeError", mrb->eStandardError_class); /* 15.2.28 */
- mrb->nomem_err = mrb_obj_ptr(mrb_exc_new_str(mrb, runtime_error, mrb_str_new_lit(mrb, "Out of memory")));
+ mrb_define_class(mrb, "RuntimeError", mrb->eStandardError_class); /* 15.2.28 */
script_error = mrb_define_class(mrb, "ScriptError", mrb->eException_class); /* 15.2.37 */
mrb_define_class(mrb, "SyntaxError", script_error); /* 15.2.38 */
- mrb_define_class(mrb, "SystemStackError", exception);
+ stack_error = mrb_define_class(mrb, "SystemStackError", exception);
+ mrb->stack_err = mrb_obj_ptr(mrb_exc_new_lit(mrb, stack_error, "stack level too deep"));
+
+ nomem_error = mrb_define_class(mrb, "NoMemoryError", exception);
+ mrb->nomem_err = mrb_obj_ptr(mrb_exc_new_lit(mrb, nomem_error, "Out of memory"));
+#ifdef MRB_GC_FIXED_ARENA
+ mrb->arena_err = mrb_obj_ptr(mrb_exc_new_lit(mrb, nomem_error, "arena overflow error"));
+#endif
}
diff --git a/src/error.h b/src/error.h
index 0e0dacf63..eb755ec7f 100644
--- a/src/error.h
+++ b/src/error.h
@@ -1,3 +1,3 @@
/* this header file is to be removed soon.
added for compatibility purpose (1.0.0) */
-#include "mruby/error.h"
+#include <mruby/error.h>
diff --git a/src/etc.c b/src/etc.c
index 635052b67..a5c48bbe3 100644
--- a/src/etc.c
+++ b/src/etc.c
@@ -1,22 +1,21 @@
/*
-** etc.c -
+** etc.c
**
** See Copyright Notice in mruby.h
*/
-#include "mruby.h"
-#include "mruby/string.h"
-#include "mruby/data.h"
-#include "mruby/class.h"
-#include "mruby/re.h"
-#include "mruby/irep.h"
+#include <mruby.h>
+#include <mruby/string.h>
+#include <mruby/data.h>
+#include <mruby/class.h>
+#include <mruby/numeric.h>
MRB_API struct RData*
mrb_data_object_alloc(mrb_state *mrb, struct RClass *klass, void *ptr, const mrb_data_type *type)
{
struct RData *data;
- data = (struct RData*)mrb_obj_alloc(mrb, MRB_TT_DATA, klass);
+ data = MRB_OBJ_ALLOC(mrb, MRB_TT_DATA, klass);
data->data = ptr;
data->type = type;
@@ -26,21 +25,19 @@ mrb_data_object_alloc(mrb_state *mrb, struct RClass *klass, void *ptr, const mrb
MRB_API void
mrb_data_check_type(mrb_state *mrb, mrb_value obj, const mrb_data_type *type)
{
- if (mrb_immediate_p(obj) || (mrb_type(obj) != MRB_TT_DATA)) {
+ if (!mrb_data_p(obj)) {
mrb_check_type(mrb, obj, MRB_TT_DATA);
}
if (DATA_TYPE(obj) != type) {
const mrb_data_type *t2 = DATA_TYPE(obj);
if (t2) {
- mrb_raisef(mrb, E_TYPE_ERROR, "wrong argument type %S (expected %S)",
- mrb_str_new_cstr(mrb, t2->struct_name), mrb_str_new_cstr(mrb, type->struct_name));
+ mrb_raisef(mrb, E_TYPE_ERROR, "wrong argument type %s (expected %s)",
+ t2->struct_name, type->struct_name);
}
else {
- struct RClass *c = mrb_class(mrb, obj);
-
- mrb_raisef(mrb, E_TYPE_ERROR, "uninitialized %S (expected %S)",
- mrb_obj_value(c), mrb_str_new_cstr(mrb, type->struct_name));
+ mrb_raisef(mrb, E_TYPE_ERROR, "uninitialized %t (expected %s)",
+ obj, type->struct_name);
}
}
}
@@ -48,7 +45,7 @@ mrb_data_check_type(mrb_state *mrb, mrb_value obj, const mrb_data_type *type)
MRB_API void*
mrb_data_check_get_ptr(mrb_state *mrb, mrb_value obj, const mrb_data_type *type)
{
- if (mrb_immediate_p(obj) || (mrb_type(obj) != MRB_TT_DATA)) {
+ if (!mrb_data_p(obj)) {
return NULL;
}
if (DATA_TYPE(obj) != type) {
@@ -67,33 +64,16 @@ mrb_data_get_ptr(mrb_state *mrb, mrb_value obj, const mrb_data_type *type)
MRB_API mrb_sym
mrb_obj_to_sym(mrb_state *mrb, mrb_value name)
{
- mrb_value tmp;
- mrb_sym id;
-
- switch (mrb_type(name)) {
- default:
- tmp = mrb_check_string_type(mrb, name);
- if (mrb_nil_p(tmp)) {
- tmp = mrb_inspect(mrb, name);
- mrb_raisef(mrb, E_TYPE_ERROR, "%S is not a symbol", tmp);
- }
- name = tmp;
- /* fall through */
- case MRB_TT_STRING:
- name = mrb_str_intern(mrb, name);
- /* fall through */
- case MRB_TT_SYMBOL:
- id = mrb_symbol(name);
- }
- return id;
+ if (mrb_symbol_p(name)) return mrb_symbol(name);
+ if (mrb_string_p(name)) return mrb_intern_str(mrb, name);
+ mrb_raisef(mrb, E_TYPE_ERROR, "%!v is not a symbol nor a string", name);
+ return 0; /* not reached */
}
-MRB_API mrb_int
-mrb_float_id(mrb_float f)
+static mrb_int
+make_num_id(const char *p, size_t len)
{
- const char *p = (const char*)&f;
- int len = sizeof(f);
- mrb_int id = 0;
+ uint32_t id = 0;
while (len--) {
id = id*65599 + *p;
@@ -101,8 +81,24 @@ mrb_float_id(mrb_float f)
}
id = id + (id>>5);
- return id;
+ return (mrb_int)id;
+}
+
+MRB_API mrb_int
+mrb_int_id(mrb_int n)
+{
+ return make_num_id((const char*)&n, sizeof(n));
+}
+
+#ifndef MRB_NO_FLOAT
+MRB_API mrb_int
+mrb_float_id(mrb_float f)
+{
+ /* normalize -0.0 to 0.0 */
+ if (f == 0) f = 0.0;
+ return make_num_id((const char*)&f, sizeof(f));
}
+#endif
MRB_API mrb_int
mrb_obj_id(mrb_value obj)
@@ -118,16 +114,19 @@ mrb_obj_id(mrb_value obj)
return MakeID(0); /* not define */
case MRB_TT_FALSE:
if (mrb_nil_p(obj))
- return MakeID(1);
- return MakeID(0);
+ return MakeID(4);
+ else
+ return MakeID(0);
case MRB_TT_TRUE:
- return MakeID(1);
+ return MakeID(2);
case MRB_TT_SYMBOL:
return MakeID(mrb_symbol(obj));
- case MRB_TT_FIXNUM:
- return MakeID2(mrb_float_id((mrb_float)mrb_fixnum(obj)), MRB_TT_FLOAT);
+ case MRB_TT_INTEGER:
+ return MakeID(mrb_int_id(mrb_integer(obj)));
+#ifndef MRB_NO_FLOAT
case MRB_TT_FLOAT:
return MakeID(mrb_float_id(mrb_float(obj)));
+#endif
case MRB_TT_STRING:
case MRB_TT_OBJECT:
case MRB_TT_CLASS:
@@ -139,50 +138,60 @@ mrb_obj_id(mrb_value obj)
case MRB_TT_HASH:
case MRB_TT_RANGE:
case MRB_TT_EXCEPTION:
- case MRB_TT_FILE:
case MRB_TT_DATA:
+ case MRB_TT_ISTRUCT:
default:
return MakeID(mrb_ptr(obj));
}
}
+#if defined(MRB_NAN_BOXING) && defined(MRB_64BIT)
+#define mrb_xxx_boxing_cptr_value mrb_nan_boxing_cptr_value
+#endif
+
#ifdef MRB_WORD_BOXING
+#define mrb_xxx_boxing_cptr_value mrb_word_boxing_cptr_value
+
+#ifndef MRB_NO_FLOAT
MRB_API mrb_value
mrb_word_boxing_float_value(mrb_state *mrb, mrb_float f)
{
- mrb_value v;
+ union mrb_value_ v;
- v.value.p = mrb_obj_alloc(mrb, MRB_TT_FLOAT, mrb->float_class);
- v.value.fp->f = f;
- return v;
+ v.p = mrb_obj_alloc(mrb, MRB_TT_FLOAT, mrb->float_class);
+ v.fp->f = f;
+ MRB_SET_FROZEN_FLAG(v.bp);
+ return v.value;
}
+#endif /* MRB_NO_FLOAT */
MRB_API mrb_value
-mrb_word_boxing_float_pool(mrb_state *mrb, mrb_float f)
+mrb_word_boxing_int_value(mrb_state *mrb, mrb_int n)
{
- struct RFloat *nf = (struct RFloat *)mrb_malloc(mrb, sizeof(struct RFloat));
- nf->tt = MRB_TT_FLOAT;
- nf->c = mrb->float_class;
- nf->f = f;
- return mrb_obj_value(nf);
+ if (FIXABLE(n)) return mrb_fixnum_value(n);
+ else {
+ union mrb_value_ v;
+
+ v.p = mrb_obj_alloc(mrb, MRB_TT_INTEGER, mrb->integer_class);
+ v.ip->i = n;
+ MRB_SET_FROZEN_FLAG(v.ip);
+ return v.value;
+ }
}
+#endif /* MRB_WORD_BOXING */
+#if defined(MRB_WORD_BOXING) || (defined(MRB_NAN_BOXING) && defined(MRB_64BIT))
MRB_API mrb_value
-mrb_word_boxing_cptr_value(mrb_state *mrb, void *p)
+mrb_xxx_boxing_cptr_value(mrb_state *mrb, void *p)
{
mrb_value v;
+ struct RCptr *cptr = MRB_OBJ_ALLOC(mrb, MRB_TT_CPTR, mrb->object_class);
- v.value.p = mrb_obj_alloc(mrb, MRB_TT_CPTR, mrb->object_class);
- v.value.vp->p = p;
+ SET_OBJ_VALUE(v, cptr);
+ cptr->p = p;
return v;
}
-#endif /* MRB_WORD_BOXING */
-
-MRB_API mrb_bool
-mrb_regexp_p(mrb_state *mrb, mrb_value v)
-{
- return mrb_class_defined(mrb, REGEXP_CLASS) && mrb_obj_is_kind_of(mrb, v, mrb_class_get(mrb, REGEXP_CLASS));
-}
+#endif
#if defined _MSC_VER && _MSC_VER < 1900
diff --git a/src/ext/.gitkeep b/src/ext/.gitkeep
deleted file mode 100644
index e69de29bb..000000000
--- a/src/ext/.gitkeep
+++ /dev/null
diff --git a/src/fmt_fp.c b/src/fmt_fp.c
new file mode 100644
index 000000000..43daf2307
--- /dev/null
+++ b/src/fmt_fp.c
@@ -0,0 +1,364 @@
+#include <mruby.h>
+#include <string.h>
+#include <stdlib.h>
+
+#ifndef MRB_NO_FLOAT
+/***********************************************************************
+
+ Routine for converting a single-precision
+ floating point number into a string.
+
+ The code in this function was inspired from Fred Bayer's pdouble.c.
+ Since pdouble.c was released as Public Domain, I'm releasing this
+ code as public domain as well.
+
+ Dave Hylands
+
+ The original code can be found in https://github.com/dhylands/format-float
+***********************************************************************/
+
+/***********************************************************************
+
+ I modified the routine for mruby:
+
+ * support `double`
+ * support `#` (alt_form) modifier
+
+ My modifications in this file are also placed in the public domain.
+
+ Matz (Yukihiro Matsumoto)
+
+***********************************************************************/
+
+#include <math.h>
+
+#ifdef MRB_USE_FLOAT32
+
+// 1 sign bit, 8 exponent bits, and 23 mantissa bits.
+// exponent values 0 and 255 are reserved, exponent can be 1 to 254.
+// exponent is stored with a bias of 127.
+// The min and max floats are on the order of 1x10^37 and 1x10^-37
+
+#define FLT_DECEXP 32
+#define FLT_ROUND_TO_ONE 0.9999995F
+#define FLT_MIN_BUF_SIZE 6 // -9e+99
+
+#else
+
+// 1 sign bit, 11 exponent bits, and 52 mantissa bits.
+
+#define FLT_DECEXP 256
+#define FLT_ROUND_TO_ONE 0.999999999995
+#define FLT_MIN_BUF_SIZE 7 // -9e+199
+
+#endif /* MRB_USE_FLOAT32 */
+
+static const mrb_float g_pos_pow[] = {
+#ifndef MRB_USE_FLOAT32
+ 1e256, 1e128, 1e64,
+#endif
+ 1e32, 1e16, 1e8, 1e4, 1e2, 1e1
+};
+static const mrb_float g_neg_pow[] = {
+#ifndef MRB_USE_FLOAT32
+ 1e-256, 1e-128, 1e-64,
+#endif
+ 1e-32, 1e-16, 1e-8, 1e-4, 1e-2, 1e-1
+};
+
+/*
+ * mrb_format_float(mrb_float f, char *buf, size_t buf_size, char fmt, int prec, char sign)
+ *
+ * fmt: should be one of 'e', 'E', 'f', 'F', 'g', or 'G'. (|0x80 for '#')
+ * prec: is the precision (as specified in printf)
+ * sign: should be '\0', '+', or ' ' ('\0' is the normal one - only print
+ * a sign if ```f``` is negative. Anything else is printed as the
+ * sign character for positive numbers.
+ */
+
+int
+mrb_format_float(mrb_float f, char *buf, size_t buf_size, char fmt, int prec, char sign) {
+ char *s = buf;
+ int buf_remaining = buf_size - 1;
+ int alt_form = 0;
+
+ if ((uint8_t)fmt & 0x80) {
+ fmt &= 0x7f; /* turn off alt_form flag */
+ alt_form = 1;
+ }
+ if (buf_size <= FLT_MIN_BUF_SIZE) {
+ // Smallest exp notion is -9e+99 (-9e+199) which is 6 (7) chars plus terminating
+ // null.
+
+ if (buf_size >= 2) {
+ *s++ = '?';
+ }
+ if (buf_size >= 1) {
+ *s++ = '\0';
+ }
+ return buf_size >= 2;
+ }
+ if (signbit(f)) {
+ *s++ = '-';
+ f = -f;
+ } else if (sign) {
+ *s++ = sign;
+ }
+ buf_remaining -= (s - buf); // Adjust for sign
+
+ {
+ char uc = fmt & 0x20;
+ if (isinf(f)) {
+ *s++ = 'I' ^ uc;
+ *s++ = 'N' ^ uc;
+ *s++ = 'F' ^ uc;
+ goto ret;
+ } else if (isnan(f)) {
+ *s++ = 'N' ^ uc;
+ *s++ = 'A' ^ uc;
+ *s++ = 'N' ^ uc;
+ ret:
+ *s = '\0';
+ return s - buf;
+ }
+ }
+
+ if (prec < 0) {
+ prec = 6;
+ }
+ char e_char = 'E' | (fmt & 0x20); // e_char will match case of fmt
+ fmt |= 0x20; // Force fmt to be lowercase
+ char org_fmt = fmt;
+ if (fmt == 'g' && prec == 0) {
+ prec = 1;
+ }
+ int e, e1;
+ int dec = 0;
+ char e_sign = '\0';
+ int num_digits = 0;
+ const mrb_float *pos_pow = g_pos_pow;
+ const mrb_float *neg_pow = g_neg_pow;
+
+ if (f == 0.0) {
+ e = 0;
+ if (fmt == 'e') {
+ e_sign = '+';
+ } else if (fmt == 'f') {
+ num_digits = prec + 1;
+ }
+ } else if (f < 1.0) { // f < 1.0
+ char first_dig = '0';
+ if (f >= FLT_ROUND_TO_ONE) {
+ first_dig = '1';
+ }
+
+ // Build negative exponent
+ for (e = 0, e1 = FLT_DECEXP; e1; e1 >>= 1, pos_pow++, neg_pow++) {
+ if (*neg_pow > f) {
+ e += e1;
+ f *= *pos_pow;
+ }
+ }
+ char e_sign_char = '-';
+ if (f < 1.0) {
+ if (f >= FLT_ROUND_TO_ONE) {
+ f = 1.0;
+ if (e == 0) {
+ e_sign_char = '+';
+ }
+ } else {
+ e++;
+ f *= 10.0;
+ }
+ }
+
+ // If the user specified 'g' format, and e is <= 4, then we'll switch
+ // to the fixed format ('f')
+
+ if (fmt == 'f' || (fmt == 'g' && e <= 4)) {
+ fmt = 'f';
+ dec = -1;
+ *s++ = first_dig;
+
+ if (org_fmt == 'g') {
+ prec += (e - 1);
+ }
+ // truncate precision to prevent buffer overflow
+ if (prec + 2 > buf_remaining) {
+ prec = buf_remaining - 2;
+ }
+ num_digits = prec;
+ if (num_digits || alt_form) {
+ *s++ = '.';
+ while (--e && num_digits) {
+ *s++ = '0';
+ num_digits--;
+ }
+ }
+ } else {
+ // For e & g formats, we'll be printing the exponent, so set the
+ // sign.
+ e_sign = e_sign_char;
+ dec = 0;
+
+ if (prec > (buf_remaining - FLT_MIN_BUF_SIZE)) {
+ prec = buf_remaining - FLT_MIN_BUF_SIZE;
+ if (fmt == 'g') {
+ prec++;
+ }
+ }
+ }
+ } else {
+ // Build positive exponent
+ for (e = 0, e1 = FLT_DECEXP; e1; e1 >>= 1, pos_pow++, neg_pow++) {
+ if (*pos_pow <= f) {
+ e += e1;
+ f *= *neg_pow;
+ }
+ }
+
+ // If the user specified fixed format (fmt == 'f') and e makes the
+ // number too big to fit into the available buffer, then we'll
+ // switch to the 'e' format.
+
+ if (fmt == 'f') {
+ if (e >= buf_remaining) {
+ fmt = 'e';
+ } else if ((e + prec + 2) > buf_remaining) {
+ prec = buf_remaining - e - 2;
+ if (prec < 0) {
+ // This means no decimal point, so we can add one back
+ // for the decimal.
+ prec++;
+ }
+ }
+ }
+ if (fmt == 'e' && prec > (buf_remaining - 6)) {
+ prec = buf_remaining - 6;
+ }
+ // If the user specified 'g' format, and e is < prec, then we'll switch
+ // to the fixed format.
+
+ if (fmt == 'g' && e < prec) {
+ fmt = 'f';
+ prec -= (e + 1);
+ }
+ if (fmt == 'f') {
+ dec = e;
+ num_digits = prec + e + 1;
+ } else {
+ e_sign = '+';
+ }
+ }
+ if (prec < 0) {
+ // This can happen when the prec is trimmed to prevent buffer overflow
+ prec = 0;
+ }
+
+ // We now have f as a floating point number between >= 1 and < 10
+ // (or equal to zero), and e contains the absolute value of the power of
+ // 10 exponent. and (dec + 1) == the number of dgits before the decimal.
+
+ // For e, prec is # digits after the decimal
+ // For f, prec is # digits after the decimal
+ // For g, prec is the max number of significant digits
+ //
+ // For e & g there will be a single digit before the decimal
+ // for f there will be e digits before the decimal
+
+ if (fmt == 'e') {
+ num_digits = prec + 1;
+ } else if (fmt == 'g') {
+ if (prec == 0) {
+ prec = 1;
+ }
+ num_digits = prec;
+ }
+
+ // Print the digits of the mantissa
+ for (int i = 0; i < num_digits; ++i, --dec) {
+ int8_t d = f;
+ *s++ = '0' + d;
+ if (dec == 0 && (prec > 0 || alt_form)) {
+ *s++ = '.';
+ }
+ f -= (mrb_float)d;
+ f *= 10.0;
+ }
+
+ // Round
+ if (f >= 5.0) {
+ char *rs = s;
+ rs--;
+ while (1) {
+ if (*rs == '.') {
+ rs--;
+ continue;
+ }
+ if (*rs < '0' || *rs > '9') {
+ // + or -
+ rs++; // So we sit on the digit to the right of the sign
+ break;
+ }
+ if (*rs < '9') {
+ (*rs)++;
+ break;
+ }
+ *rs = '0';
+ if (rs == buf) {
+ break;
+ }
+ rs--;
+ }
+ if (*rs == '0') {
+ // We need to insert a 1
+ if (rs[1] == '.' && fmt != 'f') {
+ // We're going to round 9.99 to 10.00
+ // Move the decimal point
+ rs[0] = '.';
+ rs[1] = '0';
+ if (e_sign == '-') {
+ e--;
+ } else {
+ e++;
+ }
+ }
+ s++;
+ char *ss = s;
+ while (ss > rs) {
+ *ss = ss[-1];
+ ss--;
+ }
+ *rs = '1';
+ if (f < 1.0 && fmt == 'f') {
+ // We rounded up to 1.0
+ prec--;
+ }
+ }
+ }
+
+ if (org_fmt == 'g' && prec > 0 && !alt_form) {
+ // Remove trailing zeros and a trailing decimal point
+ while (s[-1] == '0') {
+ s--;
+ }
+ if (s[-1] == '.') {
+ s--;
+ }
+ }
+ // Append the exponent
+ if (e_sign) {
+ *s++ = e_char;
+ *s++ = e_sign;
+ if (e >= 100) {
+ *s++ = '0' + (e / 100);
+ e %= 100;
+ }
+ *s++ = '0' + (e / 10);
+ *s++ = '0' + (e % 10);
+ }
+ *s = '\0';
+
+ return s - buf;
+}
+#endif
diff --git a/src/gc.c b/src/gc.c
index 8bd8243f1..bcd0b29b6 100644
--- a/src/gc.c
+++ b/src/gc.c
@@ -6,17 +6,23 @@
#include <string.h>
#include <stdlib.h>
-#include "mruby.h"
-#include "mruby/array.h"
-#include "mruby/class.h"
-#include "mruby/data.h"
-#include "mruby/hash.h"
-#include "mruby/proc.h"
-#include "mruby/range.h"
-#include "mruby/string.h"
-#include "mruby/variable.h"
-#include "mruby/gc.h"
-#include "mruby/error.h"
+#ifdef MRB_USE_MALLOC_TRIM
+#include <malloc.h>
+#endif
+#include <mruby.h>
+#include <mruby/array.h>
+#include <mruby/class.h>
+#include <mruby/data.h>
+#include <mruby/istruct.h>
+#include <mruby/hash.h>
+#include <mruby/proc.h>
+#include <mruby/range.h>
+#include <mruby/string.h>
+#include <mruby/variable.h>
+#include <mruby/gc.h>
+#include <mruby/error.h>
+#include <mruby/throw.h>
+#include <mruby/presym.h>
/*
= Tri-color Incremental Garbage Collection
@@ -33,9 +39,14 @@
* Gray - Marked, But the child objects are unmarked.
* Black - Marked, the child objects are also marked.
+ Extra color
+
+ * Red - Static (ROM object) no need to be collected.
+ - All child objects should be Red as well.
+
== Two White Types
- There're two white color types in a flip-flop fashion: White-A and White-B,
+ There are two white color types in a flip-flop fashion: White-A and White-B,
which respectively represent the Current White color (the newly allocated
objects in the current GC cycle) and the Sweep Target White color (the
dead objects to be swept).
@@ -65,7 +76,7 @@
mruby implementer and C extension library writer must insert a write
barrier when updating a reference from a field of an object.
- When updating a reference from a field of object A to object B,
+ When updating a reference from a field of object A to object B,
two different types of write barrier are available:
* mrb_field_write_barrier - target B object for a mark.
@@ -97,8 +108,14 @@ struct free_obj {
struct RBasic *next;
};
+struct RVALUE_initializer {
+ MRB_OBJECT_HEADER;
+ char padding[sizeof(void*) * 4 - sizeof(uint32_t)];
+};
+
typedef struct {
union {
+ struct RVALUE_initializer init; /* must be first member to ensure initialization */
struct free_obj free;
struct RBasic basic;
struct RObject object;
@@ -108,10 +125,16 @@ typedef struct {
struct RHash hash;
struct RRange range;
struct RData data;
+ struct RIStruct istruct;
struct RProc proc;
+ struct REnv env;
+ struct RFiber fiber;
struct RException exc;
+ struct RBreak brk;
#ifdef MRB_WORD_BOXING
+#ifndef MRB_NO_FLOAT
struct RFloat floatv;
+#endif
struct RCptr cptr;
#endif
} as;
@@ -136,8 +159,8 @@ gettimeofday_time(void)
#define GC_INVOKE_TIME_REPORT(with) do {\
fprintf(stderr, "%s\n", with);\
fprintf(stderr, "gc_invoke: %19.3f\n", gettimeofday_time() - program_invoke_time);\
- fprintf(stderr, "is_generational: %d\n", is_generational(mrb));\
- fprintf(stderr, "is_major_gc: %d\n", is_major_gc(mrb));\
+ fprintf(stderr, "is_generational: %d\n", is_generational(gc));\
+ fprintf(stderr, "is_major_gc: %d\n", is_major_gc(gc));\
} while(0)
#define GC_TIME_START do {\
@@ -147,10 +170,10 @@ gettimeofday_time(void)
#define GC_TIME_STOP_AND_REPORT do {\
gc_time = gettimeofday_time() - gc_time;\
gc_total_time += gc_time;\
- fprintf(stderr, "gc_state: %d\n", mrb->gc_state);\
- fprintf(stderr, "live: %zu\n", mrb->live);\
- fprintf(stderr, "majorgc_old_threshold: %zu\n", mrb->majorgc_old_threshold);\
- fprintf(stderr, "gc_threshold: %zu\n", mrb->gc_threshold);\
+ fprintf(stderr, "gc_state: %d\n", gc->state);\
+ fprintf(stderr, "live: %zu\n", gc->live);\
+ fprintf(stderr, "majorgc_old_threshold: %zu\n", gc->majorgc_old_threshold);\
+ fprintf(stderr, "gc_threshold: %zu\n", gc->threshold);\
fprintf(stderr, "gc_time: %30.20f\n", gc_time);\
fprintf(stderr, "gc_total_time: %30.20f\n\n", gc_total_time);\
} while(0)
@@ -166,8 +189,37 @@ gettimeofday_time(void)
#define DEBUG(x)
#endif
+#ifndef MRB_HEAP_PAGE_SIZE
+#define MRB_HEAP_PAGE_SIZE 1024
+#endif
+
#define GC_STEP_SIZE 1024
+/* white: 001 or 010, black: 100, gray: 000 */
+#define GC_GRAY 0
+#define GC_WHITE_A 1
+#define GC_WHITE_B (1 << 1)
+#define GC_BLACK (1 << 2)
+#define GC_RED MRB_GC_RED
+#define GC_WHITES (GC_WHITE_A | GC_WHITE_B)
+#define GC_COLOR_MASK 7
+mrb_static_assert1(MRB_GC_RED <= GC_COLOR_MASK);
+
+#define paint_gray(o) ((o)->color = GC_GRAY)
+#define paint_black(o) ((o)->color = GC_BLACK)
+#define paint_white(o) ((o)->color = GC_WHITES)
+#define paint_partial_white(s, o) ((o)->color = (s)->current_white_part)
+#define is_gray(o) ((o)->color == GC_GRAY)
+#define is_white(o) ((o)->color & GC_WHITES)
+#define is_black(o) ((o)->color == GC_BLACK)
+#define is_red(o) ((o)->color == GC_RED)
+#define flip_white_part(s) ((s)->current_white_part = other_white_part(s))
+#define other_white_part(s) ((s)->current_white_part ^ GC_WHITES)
+#define is_dead(s, o) (((o)->color & other_white_part(s) & GC_WHITES) || (o)->tt == MRB_TT_FREE)
+
+#define objects(p) ((RVALUE *)p->objects)
+
+mrb_noreturn void mrb_raise_nomemory(mrb_state *mrb);
MRB_API void*
mrb_realloc_simple(mrb_state *mrb, void *p, size_t len)
@@ -175,7 +227,7 @@ mrb_realloc_simple(mrb_state *mrb, void *p, size_t len)
void *p2;
p2 = (mrb->allocf)(mrb, p, len, mrb->allocf_ud);
- if (!p2 && len > 0 && mrb->heaps) {
+ if (!p2 && len > 0 && mrb->gc.heaps) {
mrb_full_gc(mrb);
p2 = (mrb->allocf)(mrb, p, len, mrb->allocf_ud);
}
@@ -183,24 +235,19 @@ mrb_realloc_simple(mrb_state *mrb, void *p, size_t len)
return p2;
}
-
MRB_API void*
mrb_realloc(mrb_state *mrb, void *p, size_t len)
{
void *p2;
p2 = mrb_realloc_simple(mrb, p, len);
- if (!p2 && len) {
- if (mrb->out_of_memory) {
- /* mrb_panic(mrb); */
- }
- else {
- mrb->out_of_memory = TRUE;
- mrb_exc_raise(mrb, mrb_obj_value(mrb->nomem_err));
- }
+ if (len == 0) return p2;
+ if (p2 == NULL) {
+ mrb->gc.out_of_memory = TRUE;
+ mrb_raise_nomemory(mrb);
}
else {
- mrb->out_of_memory = FALSE;
+ mrb->gc.out_of_memory = FALSE;
}
return p2;
@@ -244,101 +291,127 @@ mrb_free(mrb_state *mrb, void *p)
(mrb->allocf)(mrb, p, 0, mrb->allocf_ud);
}
-#ifndef MRB_HEAP_PAGE_SIZE
-#define MRB_HEAP_PAGE_SIZE 1024
-#endif
+MRB_API void*
+mrb_alloca(mrb_state *mrb, size_t size)
+{
+ struct RString *s;
+ s = MRB_OBJ_ALLOC(mrb, MRB_TT_STRING, mrb->string_class);
+ return s->as.heap.ptr = (char*)mrb_malloc(mrb, size);
+}
-struct heap_page {
- struct RBasic *freelist;
- struct heap_page *prev;
- struct heap_page *next;
- struct heap_page *free_next;
- struct heap_page *free_prev;
- mrb_bool old:1;
- RVALUE objects[MRB_HEAP_PAGE_SIZE];
-};
+static mrb_bool
+heap_p(mrb_gc *gc, struct RBasic *object)
+{
+ mrb_heap_page* page;
+
+ page = gc->heaps;
+ while (page) {
+ RVALUE *p;
+
+ p = objects(page);
+ if (&p[0].as.basic <= object && object <= &p[MRB_HEAP_PAGE_SIZE].as.basic) {
+ return TRUE;
+ }
+ page = page->next;
+ }
+ return FALSE;
+}
+
+MRB_API mrb_bool
+mrb_object_dead_p(mrb_state *mrb, struct RBasic *object) {
+ mrb_gc *gc = &mrb->gc;
+ if (!heap_p(gc, object)) return TRUE;
+ return is_dead(gc, object);
+}
static void
-link_heap_page(mrb_state *mrb, struct heap_page *page)
+link_heap_page(mrb_gc *gc, mrb_heap_page *page)
{
- page->next = mrb->heaps;
- if (mrb->heaps)
- mrb->heaps->prev = page;
- mrb->heaps = page;
+ page->next = gc->heaps;
+ if (gc->heaps)
+ gc->heaps->prev = page;
+ gc->heaps = page;
}
static void
-unlink_heap_page(mrb_state *mrb, struct heap_page *page)
+unlink_heap_page(mrb_gc *gc, mrb_heap_page *page)
{
if (page->prev)
page->prev->next = page->next;
if (page->next)
page->next->prev = page->prev;
- if (mrb->heaps == page)
- mrb->heaps = page->next;
+ if (gc->heaps == page)
+ gc->heaps = page->next;
page->prev = NULL;
page->next = NULL;
}
static void
-link_free_heap_page(mrb_state *mrb, struct heap_page *page)
+link_free_heap_page(mrb_gc *gc, mrb_heap_page *page)
{
- page->free_next = mrb->free_heaps;
- if (mrb->free_heaps) {
- mrb->free_heaps->free_prev = page;
+ page->free_next = gc->free_heaps;
+ if (gc->free_heaps) {
+ gc->free_heaps->free_prev = page;
}
- mrb->free_heaps = page;
+ gc->free_heaps = page;
}
static void
-unlink_free_heap_page(mrb_state *mrb, struct heap_page *page)
+unlink_free_heap_page(mrb_gc *gc, mrb_heap_page *page)
{
if (page->free_prev)
page->free_prev->free_next = page->free_next;
if (page->free_next)
page->free_next->free_prev = page->free_prev;
- if (mrb->free_heaps == page)
- mrb->free_heaps = page->free_next;
+ if (gc->free_heaps == page)
+ gc->free_heaps = page->free_next;
page->free_prev = NULL;
page->free_next = NULL;
}
static void
-add_heap(mrb_state *mrb)
+add_heap(mrb_state *mrb, mrb_gc *gc)
{
- struct heap_page *page = (struct heap_page *)mrb_calloc(mrb, 1, sizeof(struct heap_page));
+ mrb_heap_page *page = (mrb_heap_page *)mrb_calloc(mrb, 1, sizeof(mrb_heap_page) + MRB_HEAP_PAGE_SIZE * sizeof(RVALUE));
RVALUE *p, *e;
struct RBasic *prev = NULL;
- for (p = page->objects, e=p+MRB_HEAP_PAGE_SIZE; p<e; p++) {
+ for (p = objects(page), e=p+MRB_HEAP_PAGE_SIZE; p<e; p++) {
p->as.free.tt = MRB_TT_FREE;
p->as.free.next = prev;
prev = &p->as.basic;
}
page->freelist = prev;
- link_heap_page(mrb, page);
- link_free_heap_page(mrb, page);
+ link_heap_page(gc, page);
+ link_free_heap_page(gc, page);
}
#define DEFAULT_GC_INTERVAL_RATIO 200
#define DEFAULT_GC_STEP_RATIO 200
-#define DEFAULT_MAJOR_GC_INC_RATIO 200
-#define is_generational(mrb) ((mrb)->is_generational_gc_mode)
-#define is_major_gc(mrb) (is_generational(mrb) && (mrb)->gc_full)
-#define is_minor_gc(mrb) (is_generational(mrb) && !(mrb)->gc_full)
+#define MAJOR_GC_INC_RATIO 120
+#define MAJOR_GC_TOOMANY 10000
+#define is_generational(gc) ((gc)->generational)
+#define is_major_gc(gc) (is_generational(gc) && (gc)->full)
+#define is_minor_gc(gc) (is_generational(gc) && !(gc)->full)
void
-mrb_init_heap(mrb_state *mrb)
+mrb_gc_init(mrb_state *mrb, mrb_gc *gc)
{
- mrb->heaps = NULL;
- mrb->free_heaps = NULL;
- add_heap(mrb);
- mrb->gc_interval_ratio = DEFAULT_GC_INTERVAL_RATIO;
- mrb->gc_step_ratio = DEFAULT_GC_STEP_RATIO;
+#ifndef MRB_GC_FIXED_ARENA
+ gc->arena = (struct RBasic**)mrb_malloc(mrb, sizeof(struct RBasic*)*MRB_GC_ARENA_SIZE);
+ gc->arena_capa = MRB_GC_ARENA_SIZE;
+#endif
+
+ gc->current_white_part = GC_WHITE_A;
+ gc->heaps = NULL;
+ gc->free_heaps = NULL;
+ add_heap(mrb, gc);
+ gc->interval_ratio = DEFAULT_GC_INTERVAL_RATIO;
+ gc->step_ratio = DEFAULT_GC_STEP_RATIO;
#ifndef MRB_GC_TURN_OFF_GENERATIONAL
- mrb->is_generational_gc_mode = TRUE;
- mrb->gc_full = TRUE;
+ gc->generational = TRUE;
+ gc->full = TRUE;
#endif
#ifdef GC_PROFILE
@@ -346,85 +419,178 @@ mrb_init_heap(mrb_state *mrb)
#endif
}
-static void obj_free(mrb_state *mrb, struct RBasic *obj);
+static void obj_free(mrb_state *mrb, struct RBasic *obj, int end);
-void
-mrb_free_heap(mrb_state *mrb)
+static void
+free_heap(mrb_state *mrb, mrb_gc *gc)
{
- struct heap_page *page = mrb->heaps;
- struct heap_page *tmp;
+ mrb_heap_page *page = gc->heaps;
+ mrb_heap_page *tmp;
RVALUE *p, *e;
while (page) {
tmp = page;
page = page->next;
- for (p = tmp->objects, e=p+MRB_HEAP_PAGE_SIZE; p<e; p++) {
+ for (p = objects(tmp), e=p+MRB_HEAP_PAGE_SIZE; p<e; p++) {
if (p->as.free.tt != MRB_TT_FREE)
- obj_free(mrb, &p->as.basic);
+ obj_free(mrb, &p->as.basic, TRUE);
}
mrb_free(mrb, tmp);
}
}
+void
+mrb_gc_destroy(mrb_state *mrb, mrb_gc *gc)
+{
+ free_heap(mrb, gc);
+#ifndef MRB_GC_FIXED_ARENA
+ mrb_free(mrb, gc->arena);
+#endif
+}
+
static void
-gc_protect(mrb_state *mrb, struct RBasic *p)
+gc_protect(mrb_state *mrb, mrb_gc *gc, struct RBasic *p)
{
#ifdef MRB_GC_FIXED_ARENA
- if (mrb->arena_idx >= MRB_GC_ARENA_SIZE) {
+ if (gc->arena_idx >= MRB_GC_ARENA_SIZE) {
/* arena overflow error */
- mrb->arena_idx = MRB_GC_ARENA_SIZE - 4; /* force room in arena */
- mrb_raise(mrb, E_RUNTIME_ERROR, "arena overflow error");
+ gc->arena_idx = MRB_GC_ARENA_SIZE - 4; /* force room in arena */
+ mrb_exc_raise(mrb, mrb_obj_value(mrb->arena_err));
}
#else
- if (mrb->arena_idx >= mrb->arena_capa) {
+ if (gc->arena_idx >= gc->arena_capa) {
/* extend arena */
- mrb->arena_capa = (int)(mrb->arena_capa * 1.5);
- mrb->arena = (struct RBasic**)mrb_realloc(mrb, mrb->arena, sizeof(struct RBasic*)*mrb->arena_capa);
+ gc->arena_capa = (int)(gc->arena_capa * 3 / 2);
+ gc->arena = (struct RBasic**)mrb_realloc(mrb, gc->arena, sizeof(struct RBasic*)*gc->arena_capa);
}
#endif
- mrb->arena[mrb->arena_idx++] = p;
+ gc->arena[gc->arena_idx++] = p;
}
+/* mrb_gc_protect() leaves the object in the arena */
MRB_API void
mrb_gc_protect(mrb_state *mrb, mrb_value obj)
{
if (mrb_immediate_p(obj)) return;
- gc_protect(mrb, mrb_basic_ptr(obj));
+ gc_protect(mrb, &mrb->gc, mrb_basic_ptr(obj));
+}
+
+#define GC_ROOT_SYM MRB_SYM(_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 object 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_value table;
+
+ if (mrb_immediate_p(obj)) return;
+ root = GC_ROOT_SYM;
+ table = mrb_gv_get(mrb, root);
+ if (mrb_nil_p(table) || !mrb_array_p(table)) {
+ 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_value table;
+ struct RArray *a;
+ mrb_int i;
+
+ if (mrb_immediate_p(obj)) return;
+ root = GC_ROOT_SYM;
+ table = mrb_gv_get(mrb, root);
+ if (mrb_nil_p(table)) return;
+ if (!mrb_array_p(table)) {
+ mrb_gv_set(mrb, root, mrb_nil_value());
+ return;
+ }
+ a = mrb_ary_ptr(table);
+ mrb_ary_modify(mrb, a);
+ for (i = 0; i < ARY_LEN(a); i++) {
+ if (mrb_ptr(ARY_PTR(a)[i]) == mrb_ptr(obj)) {
+ mrb_int len = ARY_LEN(a)-1;
+ mrb_value *ptr = ARY_PTR(a);
+
+ ARY_SET_LEN(a, len);
+ memmove(&ptr[i], &ptr[i + 1], (len - i) * sizeof(mrb_value));
+ break;
+ }
+ }
}
MRB_API struct RBasic*
mrb_obj_alloc(mrb_state *mrb, enum mrb_vtype ttype, struct RClass *cls)
{
struct RBasic *p;
- static const RVALUE RVALUE_zero = { { { MRB_TT_FALSE } } };
+ static const RVALUE RVALUE_zero = { { { NULL, NULL, MRB_TT_FALSE } } };
+ mrb_gc *gc = &mrb->gc;
+
+ if (cls) {
+ enum mrb_vtype tt;
+
+ switch (cls->tt) {
+ case MRB_TT_CLASS:
+ case MRB_TT_SCLASS:
+ case MRB_TT_MODULE:
+ case MRB_TT_ENV:
+ break;
+ default:
+ mrb_raise(mrb, E_TYPE_ERROR, "allocation failure");
+ }
+ tt = MRB_INSTANCE_TT(cls);
+ if (tt != MRB_TT_FALSE &&
+ ttype != MRB_TT_SCLASS &&
+ ttype != MRB_TT_ICLASS &&
+ ttype != MRB_TT_ENV &&
+ ttype != tt) {
+ mrb_raisef(mrb, E_TYPE_ERROR, "allocation failure of %C", cls);
+ }
+ }
+ if (ttype <= MRB_TT_FREE) {
+ mrb_raisef(mrb, E_TYPE_ERROR, "allocation failure of %C (type %d)", cls, (int)ttype);
+ }
#ifdef MRB_GC_STRESS
mrb_full_gc(mrb);
#endif
- if (mrb->gc_threshold < mrb->live) {
+ if (gc->threshold < gc->live) {
mrb_incremental_gc(mrb);
}
- if (mrb->free_heaps == NULL) {
- add_heap(mrb);
+ if (gc->free_heaps == NULL) {
+ add_heap(mrb, gc);
}
- p = mrb->free_heaps->freelist;
- mrb->free_heaps->freelist = ((struct free_obj*)p)->next;
- if (mrb->free_heaps->freelist == NULL) {
- unlink_free_heap_page(mrb, mrb->free_heaps);
+ p = gc->free_heaps->freelist;
+ gc->free_heaps->freelist = ((struct free_obj*)p)->next;
+ if (gc->free_heaps->freelist == NULL) {
+ unlink_free_heap_page(gc, gc->free_heaps);
}
- mrb->live++;
- gc_protect(mrb, p);
+ gc->live++;
+ gc_protect(mrb, gc, p);
*(RVALUE *)p = RVALUE_zero;
p->tt = ttype;
p->c = cls;
- paint_partial_white(mrb, p);
+ paint_partial_white(gc, p);
return p;
}
static inline void
-add_gray_list(mrb_state *mrb, struct RBasic *obj)
+add_gray_list(mrb_state *mrb, mrb_gc *gc, struct RBasic *obj)
{
#ifdef MRB_GC_STRESS
if (obj->tt > MRB_TT_MAXDEFINE) {
@@ -432,8 +598,30 @@ add_gray_list(mrb_state *mrb, struct RBasic *obj)
}
#endif
paint_gray(obj);
- obj->gcnext = mrb->gray_list;
- mrb->gray_list = obj;
+ obj->gcnext = gc->gray_list;
+ gc->gray_list = obj;
+}
+
+static mrb_int
+ci_nregs(mrb_callinfo *ci)
+{
+ const struct RProc *p = ci->proc;
+ mrb_int n = 0;
+
+ if (!p) {
+ if (ci->argc < 0) return 3;
+ return ci->argc+2;
+ }
+ if (!MRB_PROC_CFUNC_P(p) && p->body.irep) {
+ n = p->body.irep->nregs;
+ }
+ if (ci->argc < 0) {
+ if (n < 3) n = 3; /* self + args + blk */
+ }
+ if (ci->argc > n) {
+ n = ci->argc + 2; /* self + blk */
+ }
+ return n;
}
static void
@@ -441,64 +629,71 @@ mark_context_stack(mrb_state *mrb, struct mrb_context *c)
{
size_t i;
size_t e;
+ mrb_value nil;
- e = c->stack - c->stbase;
- if (c->ci) e += c->ci->nregs;
+ if (c->stbase == NULL) return;
+ if (c->ci) {
+ e = (c->ci->stack ? c->ci->stack - c->stbase : 0);
+ e += ci_nregs(c->ci);
+ }
+ else {
+ e = 0;
+ }
if (c->stbase + e > c->stend) e = c->stend - c->stbase;
for (i=0; i<e; i++) {
mrb_value v = c->stbase[i];
if (!mrb_immediate_p(v)) {
- if (mrb_basic_ptr(v)->tt == MRB_TT_FREE) {
- c->stbase[i] = mrb_nil_value();
- }
- else {
- mrb_gc_mark(mrb, mrb_basic_ptr(v));
- }
+ mrb_gc_mark(mrb, mrb_basic_ptr(v));
}
}
+ e = c->stend - c->stbase;
+ nil = mrb_nil_value();
+ for (; i<e; i++) {
+ c->stbase[i] = nil;
+ }
}
static void
mark_context(mrb_state *mrb, struct mrb_context *c)
{
- int i, e = 0;
mrb_callinfo *ci;
- /* mark stack */
- mark_context_stack(mrb, c);
+ start:
+ if (c->status == MRB_FIBER_TERMINATED) return;
/* mark VM stack */
+ mark_context_stack(mrb, c);
+
+ /* mark call stack */
if (c->cibase) {
for (ci = c->cibase; ci <= c->ci; ci++) {
- if (ci->eidx > e) {
- e = ci->eidx;
- }
- mrb_gc_mark(mrb, (struct RBasic*)ci->env);
mrb_gc_mark(mrb, (struct RBasic*)ci->proc);
- mrb_gc_mark(mrb, (struct RBasic*)ci->target_class);
+ mrb_gc_mark(mrb, (struct RBasic*)ci->u.target_class);
}
}
- /* mark ensure stack */
- for (i=0; i<e; i++) {
- mrb_gc_mark(mrb, (struct RBasic*)c->ensure[i]);
- }
/* mark fibers */
- if (c->prev && c->prev->fib) {
- mrb_gc_mark(mrb, (struct RBasic*)c->prev->fib);
+ mrb_gc_mark(mrb, (struct RBasic*)c->fib);
+ if (c->prev) {
+ c = c->prev;
+ goto start;
}
}
static void
-gc_mark_children(mrb_state *mrb, struct RBasic *obj)
+gc_mark_children(mrb_state *mrb, mrb_gc *gc, struct RBasic *obj)
{
mrb_assert(is_gray(obj));
paint_black(obj);
- mrb->gray_list = obj->gcnext;
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_FL_CLASS_IS_ORIGIN))
+ mrb_gc_mark_mt(mrb, c);
+ mrb_gc_mark(mrb, (struct RBasic*)((struct RClass*)obj)->super);
+ }
break;
case MRB_TT_CLASS:
@@ -522,22 +717,22 @@ gc_mark_children(mrb_state *mrb, struct RBasic *obj)
{
struct RProc *p = (struct RProc*)obj;
- mrb_gc_mark(mrb, (struct RBasic*)p->env);
- mrb_gc_mark(mrb, (struct RBasic*)p->target_class);
+ mrb_gc_mark(mrb, (struct RBasic*)p->upper);
+ mrb_gc_mark(mrb, (struct RBasic*)p->e.env);
}
break;
case MRB_TT_ENV:
{
struct REnv *e = (struct REnv*)obj;
+ mrb_int i, len;
- if (!MRB_ENV_STACK_SHARED_P(e)) {
- mrb_int i, len;
-
- len = MRB_ENV_STACK_LEN(e);
- for (i=0; i<len; i++) {
- mrb_gc_mark_value(mrb, e->stack[i]);
- }
+ if (MRB_ENV_ONSTACK_P(e) && e->cxt && e->cxt->fib) {
+ mrb_gc_mark(mrb, (struct RBasic*)e->cxt->fib);
+ }
+ len = MRB_ENV_LEN(e);
+ for (i=0; i<len; i++) {
+ mrb_gc_mark_value(mrb, e->stack[i]);
}
}
break;
@@ -553,10 +748,11 @@ gc_mark_children(mrb_state *mrb, struct RBasic *obj)
case MRB_TT_ARRAY:
{
struct RArray *a = (struct RArray*)obj;
- size_t i, e;
+ size_t i, e=ARY_LEN(a);
+ mrb_value *p = ARY_PTR(a);
- for (i=0,e=a->len; i<e; i++) {
- mrb_gc_mark_value(mrb, a->ptr[i]);
+ for (i=0; i<e; i++) {
+ mrb_gc_mark_value(mrb, p[i]);
}
}
break;
@@ -567,16 +763,21 @@ gc_mark_children(mrb_state *mrb, struct RBasic *obj)
break;
case MRB_TT_STRING:
+ if (RSTR_FSHARED_P(obj)) {
+ struct RString *s = (struct RString*)obj;
+ mrb_gc_mark(mrb, (struct RBasic*)s->as.heap.aux.fshared);
+ }
break;
case MRB_TT_RANGE:
- {
- struct RRange *r = (struct RRange*)obj;
+ mrb_gc_mark_range(mrb, (struct RRange*)obj);
+ break;
- if (r->edges) {
- mrb_gc_mark_value(mrb, r->edges->beg);
- mrb_gc_mark_value(mrb, r->edges->end);
- }
+ case MRB_TT_BREAK:
+ {
+ struct RBreak *brk = (struct RBreak*)obj;
+ mrb_gc_mark(mrb, (struct RBasic*)mrb_break_proc_get(brk));
+ mrb_gc_mark_value(mrb, mrb_break_value_get(brk));
}
break;
@@ -590,30 +791,20 @@ mrb_gc_mark(mrb_state *mrb, struct RBasic *obj)
{
if (obj == 0) return;
if (!is_white(obj)) return;
+ if (is_red(obj)) return;
mrb_assert((obj)->tt != MRB_TT_FREE);
- add_gray_list(mrb, obj);
+ add_gray_list(mrb, &mrb->gc, obj);
}
static void
-obj_free(mrb_state *mrb, struct RBasic *obj)
+obj_free(mrb_state *mrb, struct RBasic *obj, int end)
{
- DEBUG(printf("obj_free(%p,tt=%d)\n",obj,obj->tt));
+ DEBUG(fprintf(stderr, "obj_free(%p,tt=%d)\n",obj,obj->tt));
switch (obj->tt) {
- /* immediate - no mark */
- case MRB_TT_TRUE:
- case MRB_TT_FIXNUM:
- case MRB_TT_SYMBOL:
- /* cannot happen */
- return;
-
- case MRB_TT_FLOAT:
-#ifdef MRB_WORD_BOXING
+ case MRB_TT_OBJECT:
+ mrb_gc_free_iv(mrb, (struct RObject*)obj);
break;
-#else
- return;
-#endif
- case MRB_TT_OBJECT:
case MRB_TT_EXCEPTION:
mrb_gc_free_iv(mrb, (struct RObject*)obj);
break;
@@ -623,16 +814,26 @@ obj_free(mrb_state *mrb, struct RBasic *obj)
case MRB_TT_SCLASS:
mrb_gc_free_mt(mrb, (struct RClass*)obj);
mrb_gc_free_iv(mrb, (struct RObject*)obj);
+ if (!end)
+ mrb_mc_clear_by_class(mrb, (struct RClass*)obj);
+ break;
+ case MRB_TT_ICLASS:
+ if (MRB_FLAG_TEST(obj, MRB_FL_CLASS_IS_ORIGIN))
+ mrb_gc_free_mt(mrb, (struct RClass*)obj);
+ if (!end)
+ mrb_mc_clear_by_class(mrb, (struct RClass*)obj);
break;
-
case MRB_TT_ENV:
{
struct REnv *e = (struct REnv*)obj;
- if (!MRB_ENV_STACK_SHARED_P(e)) {
- mrb_free(mrb, e->stack);
+ if (MRB_ENV_ONSTACK_P(e)) {
+ /* cannot be freed */
e->stack = NULL;
+ break;
}
+ mrb_free(mrb, e->stack);
+ e->stack = NULL;
}
break;
@@ -640,16 +841,30 @@ obj_free(mrb_state *mrb, struct RBasic *obj)
{
struct mrb_context *c = ((struct RFiber*)obj)->cxt;
- if (c != mrb->root_c)
+ if (c && c != mrb->root_c) {
+ if (!end && c->status != MRB_FIBER_TERMINATED) {
+ mrb_callinfo *ci = c->ci;
+ mrb_callinfo *ce = c->cibase;
+
+ while (ce <= ci) {
+ struct REnv *e = ci->u.env;
+ if (e && !mrb_object_dead_p(mrb, (struct RBasic*)e) &&
+ e->tt == MRB_TT_ENV && MRB_ENV_ONSTACK_P(e)) {
+ mrb_env_unshare(mrb, e);
+ }
+ ci--;
+ }
+ }
mrb_free_context(mrb, c);
+ }
}
break;
case MRB_TT_ARRAY:
if (ARY_SHARED_P(obj))
- mrb_ary_decref(mrb, ((struct RArray*)obj)->aux.shared);
- else
- mrb_free(mrb, ((struct RArray*)obj)->ptr);
+ mrb_ary_decref(mrb, ((struct RArray*)obj)->as.heap.aux.shared);
+ else if (!ARY_EMBED_P(obj))
+ mrb_free(mrb, ((struct RArray*)obj)->as.heap.ptr);
break;
case MRB_TT_HASH:
@@ -666,13 +881,17 @@ obj_free(mrb_state *mrb, struct RBasic *obj)
struct RProc *p = (struct RProc*)obj;
if (!MRB_PROC_CFUNC_P(p) && p->body.irep) {
- mrb_irep_decref(mrb, p->body.irep);
+ mrb_irep *irep = (mrb_irep*)p->body.irep;
+ if (end) {
+ mrb_irep_cutref(mrb, irep);
+ }
+ mrb_irep_decref(mrb, irep);
}
}
break;
case MRB_TT_RANGE:
- mrb_free(mrb, ((struct RRange*)obj)->edges);
+ mrb_gc_free_range(mrb, ((struct RRange*)obj));
break;
case MRB_TT_DATA:
@@ -685,6 +904,24 @@ obj_free(mrb_state *mrb, struct RBasic *obj)
}
break;
+#if defined(MRB_USE_RATIONAL) && defined(MRB_INT64) && defined(MRB_32BIT)
+ case MRB_TT_RATIONAL:
+ {
+ struct RData *o = (struct RData*)obj;
+ mrb_free(mrb, o->iv);
+ }
+ break;
+#endif
+
+#if defined(MRB_USE_COMPLEX) && defined(MRB_32BIT) && !defined(MRB_USE_FLOAT32)
+ case MRB_TT_COMPLEX:
+ {
+ struct RData *o = (struct RData*)obj;
+ mrb_free(mrb, o->iv);
+ }
+ break;
+#endif
+
default:
break;
}
@@ -692,45 +929,68 @@ obj_free(mrb_state *mrb, struct RBasic *obj)
}
static void
-root_scan_phase(mrb_state *mrb)
+root_scan_phase(mrb_state *mrb, mrb_gc *gc)
{
- size_t i, e;
+ int i, e;
- if (!is_minor_gc(mrb)) {
- mrb->gray_list = NULL;
- mrb->atomic_gray_list = NULL;
+ if (!is_minor_gc(gc)) {
+ gc->gray_list = NULL;
+ gc->atomic_gray_list = NULL;
}
mrb_gc_mark_gv(mrb);
/* mark arena */
- for (i=0,e=mrb->arena_idx; i<e; i++) {
- mrb_gc_mark(mrb, mrb->arena[i]);
+ for (i=0,e=gc->arena_idx; i<e; i++) {
+ mrb_gc_mark(mrb, gc->arena[i]);
}
/* mark class hierarchy */
mrb_gc_mark(mrb, (struct RBasic*)mrb->object_class);
+
+ /* mark built-in classes */
+ mrb_gc_mark(mrb, (struct RBasic*)mrb->class_class);
+ mrb_gc_mark(mrb, (struct RBasic*)mrb->module_class);
+ mrb_gc_mark(mrb, (struct RBasic*)mrb->proc_class);
+ mrb_gc_mark(mrb, (struct RBasic*)mrb->string_class);
+ mrb_gc_mark(mrb, (struct RBasic*)mrb->array_class);
+ mrb_gc_mark(mrb, (struct RBasic*)mrb->hash_class);
+ mrb_gc_mark(mrb, (struct RBasic*)mrb->range_class);
+
+#ifndef MRB_NO_FLOAT
+ mrb_gc_mark(mrb, (struct RBasic*)mrb->float_class);
+#endif
+ mrb_gc_mark(mrb, (struct RBasic*)mrb->integer_class);
+ mrb_gc_mark(mrb, (struct RBasic*)mrb->true_class);
+ mrb_gc_mark(mrb, (struct RBasic*)mrb->false_class);
+ mrb_gc_mark(mrb, (struct RBasic*)mrb->nil_class);
+ mrb_gc_mark(mrb, (struct RBasic*)mrb->symbol_class);
+ mrb_gc_mark(mrb, (struct RBasic*)mrb->kernel_module);
+
+ mrb_gc_mark(mrb, (struct RBasic*)mrb->eException_class);
+ mrb_gc_mark(mrb, (struct RBasic*)mrb->eStandardError_class);
+
/* mark top_self */
mrb_gc_mark(mrb, (struct RBasic*)mrb->top_self);
/* mark exception */
mrb_gc_mark(mrb, (struct RBasic*)mrb->exc);
/* mark pre-allocated exception */
mrb_gc_mark(mrb, (struct RBasic*)mrb->nomem_err);
+ mrb_gc_mark(mrb, (struct RBasic*)mrb->stack_err);
+#ifdef MRB_GC_FIXED_ARENA
+ mrb_gc_mark(mrb, (struct RBasic*)mrb->arena_err);
+#endif
- mark_context(mrb, mrb->root_c);
- if (mrb->root_c->fib) {
- mrb_gc_mark(mrb, (struct RBasic*)mrb->root_c->fib);
- }
+ mark_context(mrb, mrb->c);
if (mrb->root_c != mrb->c) {
- mark_context(mrb, mrb->c);
+ mark_context(mrb, mrb->root_c);
}
}
+/* rough estimation of number of GC marks (non recursive) */
static size_t
-gc_gray_mark(mrb_state *mrb, struct RBasic *obj)
+gc_gray_counts(mrb_state *mrb, mrb_gc *gc, struct RBasic *obj)
{
size_t children = 0;
- gc_mark_children(mrb, obj);
-
switch (obj->tt) {
case MRB_TT_ICLASS:
children++;
@@ -755,7 +1015,7 @@ gc_gray_mark(mrb_state *mrb, struct RBasic *obj)
break;
case MRB_TT_ENV:
- children += (int)obj->flags;
+ children += MRB_ENV_LEN(obj);
break;
case MRB_TT_FIBER:
@@ -764,16 +1024,17 @@ gc_gray_mark(mrb_state *mrb, struct RBasic *obj)
size_t i;
mrb_callinfo *ci;
- if (!c) break;
+ if (!c || c->status == MRB_FIBER_TERMINATED) break;
+
/* mark stack */
- i = c->stack - c->stbase;
- if (c->ci) i += c->ci->nregs;
+ i = c->ci->stack - c->stbase;
+
+ if (c->ci) {
+ i += ci_nregs(c->ci);
+ }
if (c->stbase + i > c->stend) i = c->stend - c->stbase;
children += i;
- /* mark ensure stack */
- children += (c->ci) ? c->ci->eidx : 0;
-
/* mark closure */
if (c->cibase) {
for (i=0, ci = c->cibase; ci <= c->ci; i++, ci++)
@@ -786,7 +1047,7 @@ gc_gray_mark(mrb_state *mrb, struct RBasic *obj)
case MRB_TT_ARRAY:
{
struct RArray *a = (struct RArray*)obj;
- children += a->len;
+ children += ARY_LEN(a);
}
break;
@@ -797,6 +1058,7 @@ gc_gray_mark(mrb_state *mrb, struct RBasic *obj)
case MRB_TT_PROC:
case MRB_TT_RANGE:
+ case MRB_TT_BREAK:
children+=2;
break;
@@ -808,133 +1070,151 @@ gc_gray_mark(mrb_state *mrb, struct RBasic *obj)
static void
-gc_mark_gray_list(mrb_state *mrb) {
- while (mrb->gray_list) {
- if (is_gray(mrb->gray_list))
- gc_mark_children(mrb, mrb->gray_list);
- else
- mrb->gray_list = mrb->gray_list->gcnext;
+gc_mark_gray_list(mrb_state *mrb, mrb_gc *gc) {
+ while (gc->gray_list) {
+ struct RBasic *obj = gc->gray_list;
+ gc->gray_list = obj->gcnext;
+ gc_mark_children(mrb, gc, obj);
}
}
static size_t
-incremental_marking_phase(mrb_state *mrb, size_t limit)
+incremental_marking_phase(mrb_state *mrb, mrb_gc *gc, size_t limit)
{
size_t tried_marks = 0;
- while (mrb->gray_list && tried_marks < limit) {
- tried_marks += gc_gray_mark(mrb, mrb->gray_list);
+ while (gc->gray_list && tried_marks < limit) {
+ struct RBasic *obj = gc->gray_list;
+ gc->gray_list = obj->gcnext;
+ gc_mark_children(mrb, gc, obj);
+ tried_marks += gc_gray_counts(mrb, gc, obj);
}
return tried_marks;
}
static void
-final_marking_phase(mrb_state *mrb)
+final_marking_phase(mrb_state *mrb, mrb_gc *gc)
{
- mark_context_stack(mrb, mrb->root_c);
- gc_mark_gray_list(mrb);
- mrb_assert(mrb->gray_list == NULL);
- mrb->gray_list = mrb->atomic_gray_list;
- mrb->atomic_gray_list = NULL;
- gc_mark_gray_list(mrb);
- mrb_assert(mrb->gray_list == NULL);
+ int i, e;
+
+ /* mark arena */
+ for (i=0,e=gc->arena_idx; i<e; i++) {
+ mrb_gc_mark(mrb, gc->arena[i]);
+ }
+ mrb_gc_mark_gv(mrb);
+ mark_context(mrb, mrb->c);
+ if (mrb->c != mrb->root_c) {
+ mark_context(mrb, mrb->root_c);
+ }
+ mrb_gc_mark(mrb, (struct RBasic*)mrb->exc);
+ gc_mark_gray_list(mrb, gc);
+ mrb_assert(gc->gray_list == NULL);
+ gc->gray_list = gc->atomic_gray_list;
+ gc->atomic_gray_list = NULL;
+ gc_mark_gray_list(mrb, gc);
+ mrb_assert(gc->gray_list == NULL);
}
static void
-prepare_incremental_sweep(mrb_state *mrb)
+prepare_incremental_sweep(mrb_state *mrb, mrb_gc *gc)
{
- mrb->gc_state = GC_STATE_SWEEP;
- mrb->sweeps = mrb->heaps;
- mrb->gc_live_after_mark = mrb->live;
+ gc->state = MRB_GC_STATE_SWEEP;
+ gc->sweeps = gc->heaps;
+ gc->live_after_mark = gc->live;
}
static size_t
-incremental_sweep_phase(mrb_state *mrb, size_t limit)
+incremental_sweep_phase(mrb_state *mrb, mrb_gc *gc, size_t limit)
{
- struct heap_page *page = mrb->sweeps;
+ mrb_heap_page *page = gc->sweeps;
size_t tried_sweep = 0;
while (page && (tried_sweep < limit)) {
- RVALUE *p = page->objects;
+ RVALUE *p = objects(page);
RVALUE *e = p + MRB_HEAP_PAGE_SIZE;
size_t freed = 0;
mrb_bool dead_slot = TRUE;
mrb_bool full = (page->freelist == NULL);
- if (is_minor_gc(mrb) && page->old) {
+ if (is_minor_gc(gc) && page->old) {
/* skip a slot which doesn't contain any young object */
p = e;
dead_slot = FALSE;
}
while (p<e) {
- if (is_dead(mrb, &p->as.basic)) {
+ if (is_dead(gc, &p->as.basic)) {
if (p->as.basic.tt != MRB_TT_FREE) {
- obj_free(mrb, &p->as.basic);
- p->as.free.next = page->freelist;
- page->freelist = (struct RBasic*)p;
- freed++;
+ obj_free(mrb, &p->as.basic, FALSE);
+ if (p->as.basic.tt == MRB_TT_FREE) {
+ p->as.free.next = page->freelist;
+ page->freelist = (struct RBasic*)p;
+ freed++;
+ }
+ else {
+ dead_slot = FALSE;
+ }
}
}
else {
- if (!is_generational(mrb))
- paint_partial_white(mrb, &p->as.basic); /* next gc target */
- dead_slot = 0;
+ if (!is_generational(gc))
+ paint_partial_white(gc, &p->as.basic); /* next gc target */
+ dead_slot = FALSE;
}
p++;
}
/* free dead slot */
if (dead_slot && freed < MRB_HEAP_PAGE_SIZE) {
- struct heap_page *next = page->next;
+ mrb_heap_page *next = page->next;
- unlink_heap_page(mrb, page);
- unlink_free_heap_page(mrb, page);
+ unlink_heap_page(gc, page);
+ unlink_free_heap_page(gc, page);
mrb_free(mrb, page);
page = next;
}
else {
if (full && freed > 0) {
- link_free_heap_page(mrb, page);
+ link_free_heap_page(gc, page);
}
- if (page->freelist == NULL && is_minor_gc(mrb))
+ if (page->freelist == NULL && is_minor_gc(gc))
page->old = TRUE;
else
page->old = FALSE;
page = page->next;
}
tried_sweep += MRB_HEAP_PAGE_SIZE;
- mrb->live -= freed;
- mrb->gc_live_after_mark -= freed;
+ gc->live -= freed;
+ gc->live_after_mark -= freed;
}
- mrb->sweeps = page;
+ gc->sweeps = page;
return tried_sweep;
}
static size_t
-incremental_gc(mrb_state *mrb, size_t limit)
+incremental_gc(mrb_state *mrb, mrb_gc *gc, size_t limit)
{
- switch (mrb->gc_state) {
- case GC_STATE_ROOT:
- root_scan_phase(mrb);
- mrb->gc_state = GC_STATE_MARK;
- flip_white_part(mrb);
+ switch (gc->state) {
+ case MRB_GC_STATE_ROOT:
+ root_scan_phase(mrb, gc);
+ gc->state = MRB_GC_STATE_MARK;
+ flip_white_part(gc);
return 0;
- case GC_STATE_MARK:
- if (mrb->gray_list) {
- return incremental_marking_phase(mrb, limit);
+ case MRB_GC_STATE_MARK:
+ if (gc->gray_list) {
+ return incremental_marking_phase(mrb, gc, limit);
}
else {
- final_marking_phase(mrb);
- prepare_incremental_sweep(mrb);
+ final_marking_phase(mrb, gc);
+ prepare_incremental_sweep(mrb, gc);
return 0;
}
- case GC_STATE_SWEEP: {
+ case MRB_GC_STATE_SWEEP: {
size_t tried_sweep = 0;
- tried_sweep = incremental_sweep_phase(mrb, limit);
+ tried_sweep = incremental_sweep_phase(mrb, gc, limit);
if (tried_sweep == 0)
- mrb->gc_state = GC_STATE_ROOT;
+ gc->state = MRB_GC_STATE_ROOT;
return tried_sweep;
}
default:
@@ -945,79 +1225,90 @@ incremental_gc(mrb_state *mrb, size_t limit)
}
static void
-incremental_gc_until(mrb_state *mrb, enum gc_state to_state)
+incremental_gc_until(mrb_state *mrb, mrb_gc *gc, mrb_gc_state to_state)
{
do {
- incremental_gc(mrb, SIZE_MAX);
- } while (mrb->gc_state != to_state);
+ incremental_gc(mrb, gc, SIZE_MAX);
+ } while (gc->state != to_state);
}
static void
-incremental_gc_step(mrb_state *mrb)
+incremental_gc_step(mrb_state *mrb, mrb_gc *gc)
{
size_t limit = 0, result = 0;
- limit = (GC_STEP_SIZE/100) * mrb->gc_step_ratio;
+ limit = (GC_STEP_SIZE/100) * gc->step_ratio;
while (result < limit) {
- result += incremental_gc(mrb, limit);
- if (mrb->gc_state == GC_STATE_ROOT)
+ result += incremental_gc(mrb, gc, limit);
+ if (gc->state == MRB_GC_STATE_ROOT)
break;
}
- mrb->gc_threshold = mrb->live + GC_STEP_SIZE;
+ gc->threshold = gc->live + GC_STEP_SIZE;
}
static void
-clear_all_old(mrb_state *mrb)
+clear_all_old(mrb_state *mrb, mrb_gc *gc)
{
- mrb_bool origin_mode = mrb->is_generational_gc_mode;
+ mrb_bool origin_mode = gc->generational;
- mrb_assert(is_generational(mrb));
- if (is_major_gc(mrb)) {
+ mrb_assert(is_generational(gc));
+ if (is_major_gc(gc)) {
/* finish the half baked GC */
- incremental_gc_until(mrb, GC_STATE_ROOT);
+ incremental_gc_until(mrb, gc, MRB_GC_STATE_ROOT);
}
/* Sweep the dead objects, then reset all the live objects
* (including all the old objects, of course) to white. */
- mrb->is_generational_gc_mode = FALSE;
- prepare_incremental_sweep(mrb);
- incremental_gc_until(mrb, GC_STATE_ROOT);
- mrb->is_generational_gc_mode = origin_mode;
+ gc->generational = FALSE;
+ prepare_incremental_sweep(mrb, gc);
+ incremental_gc_until(mrb, gc, MRB_GC_STATE_ROOT);
+ gc->generational = origin_mode;
/* The gray objects have already been painted as white */
- mrb->atomic_gray_list = mrb->gray_list = NULL;
+ gc->atomic_gray_list = gc->gray_list = NULL;
}
MRB_API void
mrb_incremental_gc(mrb_state *mrb)
{
- if (mrb->gc_disabled) return;
+ mrb_gc *gc = &mrb->gc;
+
+ if (gc->disabled || gc->iterating) return;
GC_INVOKE_TIME_REPORT("mrb_incremental_gc()");
GC_TIME_START;
- if (is_minor_gc(mrb)) {
- incremental_gc_until(mrb, GC_STATE_ROOT);
+ if (is_minor_gc(gc)) {
+ incremental_gc_until(mrb, gc, MRB_GC_STATE_ROOT);
}
else {
- incremental_gc_step(mrb);
+ incremental_gc_step(mrb, gc);
}
- if (mrb->gc_state == GC_STATE_ROOT) {
- mrb_assert(mrb->live >= mrb->gc_live_after_mark);
- mrb->gc_threshold = (mrb->gc_live_after_mark/100) * mrb->gc_interval_ratio;
- if (mrb->gc_threshold < GC_STEP_SIZE) {
- mrb->gc_threshold = GC_STEP_SIZE;
+ if (gc->state == MRB_GC_STATE_ROOT) {
+ mrb_assert(gc->live >= gc->live_after_mark);
+ gc->threshold = (gc->live_after_mark/100) * gc->interval_ratio;
+ if (gc->threshold < GC_STEP_SIZE) {
+ gc->threshold = GC_STEP_SIZE;
}
- if (is_major_gc(mrb)) {
- mrb->majorgc_old_threshold = mrb->gc_live_after_mark/100 * DEFAULT_MAJOR_GC_INC_RATIO;
- mrb->gc_full = FALSE;
+ if (is_major_gc(gc)) {
+ size_t threshold = gc->live_after_mark/100 * MAJOR_GC_INC_RATIO;
+
+ gc->full = FALSE;
+ if (threshold < MAJOR_GC_TOOMANY) {
+ gc->majorgc_old_threshold = threshold;
+ }
+ else {
+ /* too many objects allocated during incremental GC, */
+ /* instead of increasing threshold, invoke full GC. */
+ mrb_full_gc(mrb);
+ }
}
- else if (is_minor_gc(mrb)) {
- if (mrb->live > mrb->majorgc_old_threshold) {
- clear_all_old(mrb);
- mrb->gc_full = TRUE;
+ else if (is_minor_gc(gc)) {
+ if (gc->live > gc->majorgc_old_threshold) {
+ clear_all_old(mrb, gc);
+ gc->full = TRUE;
}
}
}
@@ -1029,28 +1320,35 @@ mrb_incremental_gc(mrb_state *mrb)
MRB_API void
mrb_full_gc(mrb_state *mrb)
{
- if (mrb->gc_disabled) return;
+ mrb_gc *gc = &mrb->gc;
+
+ if (!mrb->c) return;
+ if (gc->disabled || gc->iterating) return;
+
GC_INVOKE_TIME_REPORT("mrb_full_gc()");
GC_TIME_START;
- if (is_generational(mrb)) {
+ if (is_generational(gc)) {
/* clear all the old objects back to young */
- clear_all_old(mrb);
- mrb->gc_full = TRUE;
+ clear_all_old(mrb, gc);
+ gc->full = TRUE;
}
- else if (mrb->gc_state != GC_STATE_ROOT) {
+ else if (gc->state != MRB_GC_STATE_ROOT) {
/* finish half baked GC cycle */
- incremental_gc_until(mrb, GC_STATE_ROOT);
+ incremental_gc_until(mrb, gc, MRB_GC_STATE_ROOT);
}
- incremental_gc_until(mrb, GC_STATE_ROOT);
- mrb->gc_threshold = (mrb->gc_live_after_mark/100) * mrb->gc_interval_ratio;
+ incremental_gc_until(mrb, gc, MRB_GC_STATE_ROOT);
+ gc->threshold = (gc->live_after_mark/100) * gc->interval_ratio;
- if (is_generational(mrb)) {
- mrb->majorgc_old_threshold = mrb->gc_live_after_mark/100 * DEFAULT_MAJOR_GC_INC_RATIO;
- mrb->gc_full = FALSE;
+ if (is_generational(gc)) {
+ gc->majorgc_old_threshold = gc->live_after_mark/100 * MAJOR_GC_INC_RATIO;
+ gc->full = FALSE;
}
+#ifdef MRB_USE_MALLOC_TRIM
+ malloc_trim(0);
+#endif
GC_TIME_STOP_AND_REPORT;
}
@@ -1060,32 +1358,6 @@ mrb_garbage_collect(mrb_state *mrb)
mrb_full_gc(mrb);
}
-MRB_API int
-mrb_gc_arena_save(mrb_state *mrb)
-{
- return mrb->arena_idx;
-}
-
-MRB_API void
-mrb_gc_arena_restore(mrb_state *mrb, int idx)
-{
-#ifndef MRB_GC_FIXED_ARENA
- int capa = mrb->arena_capa;
-
- if (idx < capa / 2) {
- capa = (int)(capa * 0.66);
- if (capa < MRB_GC_ARENA_SIZE) {
- capa = MRB_GC_ARENA_SIZE;
- }
- if (capa != mrb->arena_capa) {
- mrb->arena = (struct RBasic**)mrb_realloc(mrb, mrb->arena, sizeof(struct RBasic*)*capa);
- mrb->arena_capa = capa;
- }
- }
-#endif
- mrb->arena_idx = idx;
-}
-
/*
* Field write barrier
* Paint obj(Black) -> value(White) to obj(Black) -> value(Gray).
@@ -1094,18 +1366,20 @@ mrb_gc_arena_restore(mrb_state *mrb, int idx)
MRB_API void
mrb_field_write_barrier(mrb_state *mrb, struct RBasic *obj, struct RBasic *value)
{
+ mrb_gc *gc = &mrb->gc;
+
if (!is_black(obj)) return;
if (!is_white(value)) return;
- mrb_assert(!is_dead(mrb, value) && !is_dead(mrb, obj));
- mrb_assert(is_generational(mrb) || mrb->gc_state != GC_STATE_ROOT);
+ mrb_assert(gc->state == MRB_GC_STATE_MARK || (!is_dead(gc, value) && !is_dead(gc, obj)));
+ mrb_assert(is_generational(gc) || gc->state != MRB_GC_STATE_ROOT);
- if (is_generational(mrb) || mrb->gc_state == GC_STATE_MARK) {
- add_gray_list(mrb, value);
+ if (is_generational(gc) || gc->state == MRB_GC_STATE_MARK) {
+ add_gray_list(mrb, gc, value);
}
else {
- mrb_assert(mrb->gc_state == GC_STATE_SWEEP);
- paint_partial_white(mrb, obj); /* for never write barriers */
+ mrb_assert(gc->state == MRB_GC_STATE_SWEEP);
+ paint_partial_white(gc, obj); /* for never write barriers */
}
}
@@ -1121,13 +1395,15 @@ mrb_field_write_barrier(mrb_state *mrb, struct RBasic *obj, struct RBasic *value
MRB_API void
mrb_write_barrier(mrb_state *mrb, struct RBasic *obj)
{
+ mrb_gc *gc = &mrb->gc;
+
if (!is_black(obj)) return;
- mrb_assert(!is_dead(mrb, obj));
- mrb_assert(is_generational(mrb) || mrb->gc_state != GC_STATE_ROOT);
+ mrb_assert(!is_dead(gc, obj));
+ mrb_assert(is_generational(gc) || gc->state != MRB_GC_STATE_ROOT);
paint_gray(obj);
- obj->gcnext = mrb->atomic_gray_list;
- mrb->atomic_gray_list = obj;
+ obj->gcnext = gc->atomic_gray_list;
+ gc->atomic_gray_list = obj;
}
/*
@@ -1161,9 +1437,9 @@ gc_start(mrb_state *mrb, mrb_value obj)
static mrb_value
gc_enable(mrb_state *mrb, mrb_value obj)
{
- mrb_bool old = mrb->gc_disabled;
+ mrb_bool old = mrb->gc.disabled;
- mrb->gc_disabled = FALSE;
+ mrb->gc.disabled = FALSE;
return mrb_bool_value(old);
}
@@ -1183,9 +1459,9 @@ gc_enable(mrb_state *mrb, mrb_value obj)
static mrb_value
gc_disable(mrb_state *mrb, mrb_value obj)
{
- mrb_bool old = mrb->gc_disabled;
+ mrb_bool old = mrb->gc.disabled;
- mrb->gc_disabled = TRUE;
+ mrb->gc.disabled = TRUE;
return mrb_bool_value(old);
}
@@ -1201,7 +1477,7 @@ gc_disable(mrb_state *mrb, mrb_value obj)
static mrb_value
gc_interval_ratio_get(mrb_state *mrb, mrb_value obj)
{
- return mrb_fixnum_value(mrb->gc_interval_ratio);
+ return mrb_fixnum_value(mrb->gc.interval_ratio);
}
/*
@@ -1219,7 +1495,7 @@ gc_interval_ratio_set(mrb_state *mrb, mrb_value obj)
mrb_int ratio;
mrb_get_args(mrb, "i", &ratio);
- mrb->gc_interval_ratio = ratio;
+ mrb->gc.interval_ratio = (int)ratio;
return mrb_nil_value();
}
@@ -1234,7 +1510,7 @@ gc_interval_ratio_set(mrb_state *mrb, mrb_value obj)
static mrb_value
gc_step_ratio_get(mrb_state *mrb, mrb_value obj)
{
- return mrb_fixnum_value(mrb->gc_step_ratio);
+ return mrb_fixnum_value(mrb->gc.step_ratio);
}
/*
@@ -1252,24 +1528,28 @@ gc_step_ratio_set(mrb_state *mrb, mrb_value obj)
mrb_int ratio;
mrb_get_args(mrb, "i", &ratio);
- mrb->gc_step_ratio = ratio;
+ mrb->gc.step_ratio = (int)ratio;
return mrb_nil_value();
}
static void
-change_gen_gc_mode(mrb_state *mrb, mrb_bool enable)
+change_gen_gc_mode(mrb_state *mrb, mrb_gc *gc, mrb_bool enable)
{
- if (is_generational(mrb) && !enable) {
- clear_all_old(mrb);
- mrb_assert(mrb->gc_state == GC_STATE_ROOT);
- mrb->gc_full = FALSE;
+ if (gc->disabled || gc->iterating) {
+ mrb_raise(mrb, E_RUNTIME_ERROR, "generational mode changed when GC disabled");
+ return;
}
- else if (!is_generational(mrb) && enable) {
- incremental_gc_until(mrb, GC_STATE_ROOT);
- mrb->majorgc_old_threshold = mrb->gc_live_after_mark/100 * DEFAULT_MAJOR_GC_INC_RATIO;
- mrb->gc_full = FALSE;
+ if (is_generational(gc) && !enable) {
+ clear_all_old(mrb, gc);
+ mrb_assert(gc->state == MRB_GC_STATE_ROOT);
+ gc->full = FALSE;
}
- mrb->is_generational_gc_mode = enable;
+ else if (!is_generational(gc) && enable) {
+ incremental_gc_until(mrb, gc, MRB_GC_STATE_ROOT);
+ gc->majorgc_old_threshold = gc->live_after_mark/100 * MAJOR_GC_INC_RATIO;
+ gc->full = FALSE;
+ }
+ gc->generational = enable;
}
/*
@@ -1283,7 +1563,7 @@ change_gen_gc_mode(mrb_state *mrb, mrb_bool enable)
static mrb_value
gc_generational_mode_get(mrb_state *mrb, mrb_value self)
{
- return mrb_bool_value(mrb->is_generational_gc_mode);
+ return mrb_bool_value(mrb->gc.generational);
}
/*
@@ -1300,41 +1580,74 @@ gc_generational_mode_set(mrb_state *mrb, mrb_value self)
mrb_bool enable;
mrb_get_args(mrb, "b", &enable);
- if (mrb->is_generational_gc_mode != enable)
- change_gen_gc_mode(mrb, enable);
+ if (mrb->gc.generational != enable)
+ change_gen_gc_mode(mrb, &mrb->gc, enable);
return mrb_bool_value(enable);
}
-void
-mrb_objspace_each_objects(mrb_state *mrb, mrb_each_object_callback *callback, void *data)
+
+static void
+gc_each_objects(mrb_state *mrb, mrb_gc *gc, mrb_each_object_callback *callback, void *data)
{
- struct heap_page* page = mrb->heaps;
+ mrb_heap_page* page;
+ page = gc->heaps;
while (page != NULL) {
- RVALUE *p, *pend;
+ RVALUE *p;
+ int i;
- p = page->objects;
- pend = p + MRB_HEAP_PAGE_SIZE;
- for (;p < pend; p++) {
- (*callback)(mrb, &p->as.basic, data);
+ p = objects(page);
+ for (i=0; i < MRB_HEAP_PAGE_SIZE; i++) {
+ if ((*callback)(mrb, &p[i].as.basic, data) == MRB_EACH_OBJ_BREAK)
+ return;
}
-
page = page->next;
}
}
-#ifdef GC_TEST
-#ifdef GC_DEBUG
-static mrb_value gc_test(mrb_state *, mrb_value);
-#endif
-#endif
+void
+mrb_objspace_each_objects(mrb_state *mrb, mrb_each_object_callback *callback, void *data)
+{
+ mrb_bool iterating = mrb->gc.iterating;
+
+ mrb_full_gc(mrb);
+ mrb->gc.iterating = TRUE;
+ if (iterating) {
+ gc_each_objects(mrb, &mrb->gc, callback, data);
+ }
+ else {
+ struct mrb_jmpbuf *prev_jmp = mrb->jmp;
+ struct mrb_jmpbuf c_jmp;
+
+ MRB_TRY(&c_jmp) {
+ mrb->jmp = &c_jmp;
+ gc_each_objects(mrb, &mrb->gc, callback, data);
+ mrb->jmp = prev_jmp;
+ mrb->gc.iterating = iterating;
+ } MRB_CATCH(&c_jmp) {
+ mrb->gc.iterating = iterating;
+ mrb->jmp = prev_jmp;
+ MRB_THROW(prev_jmp);
+ } MRB_END_EXC(&c_jmp);
+ }
+}
+
+size_t
+mrb_objspace_page_slot_size(void)
+{
+ return sizeof(RVALUE);
+}
+
void
mrb_init_gc(mrb_state *mrb)
{
struct RClass *gc;
+ mrb_static_assert(sizeof(RVALUE) <= sizeof(void*) * 6,
+ "RVALUE size must be within 6 words");
+
gc = mrb_define_module(mrb, "GC");
mrb_define_class_method(mrb, gc, "start", gc_start, MRB_ARGS_NONE());
@@ -1346,272 +1659,4 @@ mrb_init_gc(mrb_state *mrb)
mrb_define_class_method(mrb, gc, "step_ratio=", gc_step_ratio_set, MRB_ARGS_REQ(1));
mrb_define_class_method(mrb, gc, "generational_mode=", gc_generational_mode_set, MRB_ARGS_REQ(1));
mrb_define_class_method(mrb, gc, "generational_mode", gc_generational_mode_get, MRB_ARGS_NONE());
-#ifdef GC_TEST
-#ifdef GC_DEBUG
- mrb_define_class_method(mrb, gc, "test", gc_test, MRB_ARGS_NONE());
-#endif
-#endif
-}
-
-#ifdef GC_TEST
-#ifdef GC_DEBUG
-void
-test_mrb_field_write_barrier(void)
-{
- mrb_state *mrb = mrb_open();
- struct RBasic *obj, *value;
-
- puts("test_mrb_field_write_barrier");
- mrb->is_generational_gc_mode = FALSE;
- obj = mrb_basic_ptr(mrb_ary_new(mrb));
- value = mrb_basic_ptr(mrb_str_new_lit(mrb, "value"));
- paint_black(obj);
- paint_partial_white(mrb, value);
-
-
- puts(" in GC_STATE_MARK");
- mrb->gc_state = GC_STATE_MARK;
- mrb_field_write_barrier(mrb, obj, value);
-
- mrb_assert(is_gray(value));
-
-
- puts(" in GC_STATE_SWEEP");
- paint_partial_white(mrb, value);
- mrb->gc_state = GC_STATE_SWEEP;
- mrb_field_write_barrier(mrb, obj, value);
-
- mrb_assert(obj->color & mrb->current_white_part);
- mrb_assert(value->color & mrb->current_white_part);
-
-
- puts(" fail with black");
- mrb->gc_state = GC_STATE_MARK;
- paint_white(obj);
- paint_partial_white(mrb, value);
- mrb_field_write_barrier(mrb, obj, value);
-
- mrb_assert(obj->color & mrb->current_white_part);
-
-
- puts(" fail with gray");
- mrb->gc_state = GC_STATE_MARK;
- paint_black(obj);
- paint_gray(value);
- mrb_field_write_barrier(mrb, obj, value);
-
- mrb_assert(is_gray(value));
-
-
- {
- puts("test_mrb_field_write_barrier_value");
- obj = mrb_basic_ptr(mrb_ary_new(mrb));
- mrb_value value = mrb_str_new_lit(mrb, "value");
- paint_black(obj);
- paint_partial_white(mrb, mrb_basic_ptr(value));
-
- mrb->gc_state = GC_STATE_MARK;
- mrb_field_write_barrier_value(mrb, obj, value);
-
- mrb_assert(is_gray(mrb_basic_ptr(value)));
- }
-
- mrb_close(mrb);
-}
-
-void
-test_mrb_write_barrier(void)
-{
- mrb_state *mrb = mrb_open();
- struct RBasic *obj;
-
- puts("test_mrb_write_barrier");
- obj = mrb_basic_ptr(mrb_ary_new(mrb));
- paint_black(obj);
-
- puts(" in GC_STATE_MARK");
- mrb->gc_state = GC_STATE_MARK;
- mrb_write_barrier(mrb, obj);
-
- mrb_assert(is_gray(obj));
- mrb_assert(mrb->atomic_gray_list == obj);
-
-
- puts(" fail with gray");
- paint_gray(obj);
- mrb_write_barrier(mrb, obj);
-
- mrb_assert(is_gray(obj));
-
- mrb_close(mrb);
-}
-
-void
-test_add_gray_list(void)
-{
- mrb_state *mrb = mrb_open();
- struct RBasic *obj1, *obj2;
-
- puts("test_add_gray_list");
- change_gen_gc_mode(mrb, FALSE);
- mrb_assert(mrb->gray_list == NULL);
- obj1 = mrb_basic_ptr(mrb_str_new_lit(mrb, "test"));
- add_gray_list(mrb, obj1);
- mrb_assert(mrb->gray_list == obj1);
- mrb_assert(is_gray(obj1));
-
- obj2 = mrb_basic_ptr(mrb_str_new_lit(mrb, "test"));
- add_gray_list(mrb, obj2);
- mrb_assert(mrb->gray_list == obj2);
- mrb_assert(mrb->gray_list->gcnext == obj1);
- mrb_assert(is_gray(obj2));
-
- mrb_close(mrb);
-}
-
-void
-test_gc_gray_mark(void)
-{
- mrb_state *mrb = mrb_open();
- mrb_value obj_v, value_v;
- struct RBasic *obj;
- size_t gray_num = 0;
-
- puts("test_gc_gray_mark");
-
- puts(" in MRB_TT_CLASS");
- obj = (struct RBasic*)mrb->object_class;
- paint_gray(obj);
- gray_num = gc_gray_mark(mrb, obj);
- mrb_assert(is_black(obj));
- mrb_assert(gray_num > 1);
-
- puts(" in MRB_TT_ARRAY");
- obj_v = mrb_ary_new(mrb);
- value_v = mrb_str_new_lit(mrb, "test");
- paint_gray(mrb_basic_ptr(obj_v));
- paint_partial_white(mrb, mrb_basic_ptr(value_v));
- mrb_ary_push(mrb, obj_v, value_v);
- gray_num = gc_gray_mark(mrb, mrb_basic_ptr(obj_v));
- mrb_assert(is_black(mrb_basic_ptr(obj_v)));
- mrb_assert(is_gray(mrb_basic_ptr(value_v)));
- mrb_assert(gray_num == 1);
-
- mrb_close(mrb);
-}
-
-void
-test_incremental_gc(void)
-{
- mrb_state *mrb = mrb_open();
- size_t max = ~0, live = 0, total = 0, freed = 0;
- RVALUE *free;
- struct heap_page *page;
-
- puts("test_incremental_gc");
- change_gen_gc_mode(mrb, FALSE);
-
- puts(" in mrb_full_gc");
- mrb_full_gc(mrb);
-
- mrb_assert(mrb->gc_state == GC_STATE_ROOT);
- puts(" in GC_STATE_ROOT");
- incremental_gc(mrb, max);
- mrb_assert(mrb->gc_state == GC_STATE_MARK);
- puts(" in GC_STATE_MARK");
- incremental_gc_until(mrb, GC_STATE_SWEEP);
- mrb_assert(mrb->gc_state == GC_STATE_SWEEP);
-
- puts(" in GC_STATE_SWEEP");
- page = mrb->heaps;
- while (page) {
- RVALUE *p = page->objects;
- RVALUE *e = p + MRB_HEAP_PAGE_SIZE;
- while (p<e) {
- if (is_black(&p->as.basic)) {
- live++;
- }
- if (is_gray(&p->as.basic) && !is_dead(mrb, &p->as.basic)) {
- printf("%p\n", &p->as.basic);
- }
- p++;
- }
- page = page->next;
- total += MRB_HEAP_PAGE_SIZE;
- }
-
- mrb_assert(mrb->gray_list == NULL);
-
- incremental_gc(mrb, max);
- mrb_assert(mrb->gc_state == GC_STATE_SWEEP);
-
- incremental_gc(mrb, max);
- mrb_assert(mrb->gc_state == GC_STATE_ROOT);
-
- free = (RVALUE*)mrb->heaps->freelist;
- while (free) {
- freed++;
- free = (RVALUE*)free->as.free.next;
- }
-
- mrb_assert(mrb->live == live);
- mrb_assert(mrb->live == total-freed);
-
- puts("test_incremental_gc(gen)");
- incremental_gc_until(mrb, GC_STATE_SWEEP);
- change_gen_gc_mode(mrb, TRUE);
-
- mrb_assert(mrb->gc_full == FALSE);
- mrb_assert(mrb->gc_state == GC_STATE_ROOT);
-
- puts(" in minor");
- mrb_assert(is_minor_gc(mrb));
- mrb_assert(mrb->majorgc_old_threshold > 0);
- mrb->majorgc_old_threshold = 0;
- mrb_incremental_gc(mrb);
- mrb_assert(mrb->gc_full == TRUE);
- mrb_assert(mrb->gc_state == GC_STATE_ROOT);
-
- puts(" in major");
- mrb_assert(is_major_gc(mrb));
- do {
- mrb_incremental_gc(mrb);
- } while (mrb->gc_state != GC_STATE_ROOT);
- mrb_assert(mrb->gc_full == FALSE);
-
- mrb_close(mrb);
-}
-
-void
-test_incremental_sweep_phase(void)
-{
- mrb_state *mrb = mrb_open();
-
- puts("test_incremental_sweep_phase");
-
- add_heap(mrb);
- mrb->sweeps = mrb->heaps;
-
- mrb_assert(mrb->heaps->next->next == NULL);
- mrb_assert(mrb->free_heaps->next->next == NULL);
- incremental_sweep_phase(mrb, MRB_HEAP_PAGE_SIZE*3);
-
- mrb_assert(mrb->heaps->next == NULL);
- mrb_assert(mrb->heaps == mrb->free_heaps);
-
- mrb_close(mrb);
-}
-
-static mrb_value
-gc_test(mrb_state *mrb, mrb_value self)
-{
- test_mrb_field_write_barrier();
- test_mrb_write_barrier();
- test_add_gray_list();
- test_gc_gray_mark();
- test_incremental_gc();
- test_incremental_sweep_phase();
- return mrb_nil_value();
}
-#endif /* GC_DEBUG */
-#endif /* GC_TEST */
diff --git a/src/hash.c b/src/hash.c
index 0bda2b48b..2704be0c4 100644
--- a/src/hash.c
+++ b/src/hash.c
@@ -4,276 +4,1248 @@
** See Copyright Notice in mruby.h
*/
-#include "mruby.h"
-#include "mruby/array.h"
-#include "mruby/class.h"
-#include "mruby/hash.h"
-#include "mruby/khash.h"
-#include "mruby/string.h"
-#include "mruby/variable.h"
-
-/* a function to get hash value of a float number */
-mrb_int mrb_float_id(mrb_float f);
-
-static inline khint_t
-mrb_hash_ht_hash_func(mrb_state *mrb, mrb_value key)
-{
- enum mrb_vtype t = mrb_type(key);
- mrb_value hv;
- const char *p;
- mrb_int i, len;
- khint_t h;
-
- switch (t) {
- case MRB_TT_STRING:
- p = RSTRING_PTR(key);
- len = RSTRING_LEN(key);
- h = 0;
- for (i=0; i<len; i++) {
- h = (h << 5) - h + *p++;
- }
- return h;
+#include <string.h>
+#include <mruby.h>
+#include <mruby/array.h>
+#include <mruby/class.h>
+#include <mruby/hash.h>
+#include <mruby/string.h>
+#include <mruby/variable.h>
+#include <mruby/presym.h>
- case MRB_TT_SYMBOL:
- h = (khint_t)mrb_symbol(key);
- return kh_int_hash_func(mrb, h);
+/*
+ * === Glossary
+ *
+ * [EA]
+ * Entry Array. Store `Hash' entries in insertion order.
+ *
+ * [AR]
+ * Array Table Implementation. The structure of `Hash` that doesn't have a
+ * hash table and linearly searches EA. It is used when `Hash` size <= 16.
+ *
+ * [IB]
+ * Index Buckets. The buckets of hash table, where the bucket value is EA
+ * index. The index is represented by variable length bits according to
+ * the capacity.
+ *
+ * [HT]
+ * Hash Table Implementation. The structure of `Hash` that has IB and is
+ * searched by hash table algorithm. It is used when `Hash` size > 16.
+ * Collision resolution strategy is open addressing method.
+ *
+ * [size]
+ * The number of `Hash` entries (value of `Hash#size`).
+ *
+ * [slot]
+ * The generic term for EA or IB elements.
+ *
+ * [active]
+ * The state in which a slot is recognized as a `Hash` entry.
+ *
+ * [deleted]
+ * The state in which a slot is marked as deleted.
+ *
+ * [used]
+ * The state in which a slot is active or deleted.
+ *
+ * [empty]
+ * The state in which a slot is not used. Capacity is equal to the sum of
+ * the number of used slots and the number of empty slots.
+ */
- case MRB_TT_FIXNUM:
- h = (khint_t)mrb_float_id((mrb_float)mrb_fixnum(key));
- return kh_int_hash_func(mrb, h);
+#define EA_N_RESERVED_INDICES 2 /* empty and deleted */
+#define EA_INCREASE_RATIO 6 / 5 + 6
+#define EA_MAX_INCREASE UINT16_MAX
+#define EA_MAX_CAPA U32(lesser(IB_MAX_CAPA - EA_N_RESERVED_INDICES, MRB_INT_MAX))
+#define IB_MAX_CAPA (U32(1) << IB_MAX_BIT)
+#define IB_TYPE_BIT 32
+#define IB_INIT_BIT ( \
+ ib_upper_bound_for(32) <= AR_MAX_SIZE ? 6 : \
+ ib_upper_bound_for(16) <= AR_MAX_SIZE ? 5 : \
+ 4 \
+)
+#define IB_MAX_BIT (IB_TYPE_BIT - 1)
+#define AR_DEFAULT_CAPA 4
+#define AR_MAX_SIZE 16
+#define H_MAX_SIZE EA_MAX_CAPA
+
+mrb_static_assert1(offsetof(struct RHash, iv) == offsetof(struct RObject, iv));
+mrb_static_assert1(AR_MAX_SIZE < (1 << MRB_HASH_AR_EA_CAPA_BIT));
+
+typedef struct hash_entry {
+ mrb_value key;
+ mrb_value val;
+} hash_entry;
+
+typedef struct hash_table {
+ hash_entry *ea;
+#ifdef MRB_32BIT
+ uint32_t ea_capa;
+ uint32_t ea_n_used;
+#endif
+ uint32_t ib[];
+} hash_table;
+
+typedef struct index_buckets_iter {
+ struct RHash *h;
+ uint32_t bit;
+ uint32_t mask;
+ uint32_t pos;
+ uint32_t ary_index;
+ uint32_t ea_index;
+ uint32_t shift1;
+ uint32_t shift2;
+ uint32_t step;
+} index_buckets_iter;
- case MRB_TT_FLOAT:
- h = (khint_t)mrb_float_id(mrb_float(key));
- return kh_int_hash_func(mrb, h);
+/*
+ * `c_` :: receiver class (category)
+ * `n_` :: attribute name
+ * `t_` :: attribute type
+ * `p_` :: struct member path
+ * `k_` :: macro key
+ */
+#define DEFINE_GETTER(c_, n_, t_, p_) \
+ MRB_INLINE t_ c_##_##n_(const struct RHash *h) {return h->p_;}
+#define DEFINE_SETTER(c_, n_, t_, p_) \
+ MRB_INLINE void c_##_set_##n_(struct RHash *h, t_ v) {h->p_ = v;}
+#define DEFINE_ACCESSOR(c_, n_, t_, p_) \
+ DEFINE_GETTER(c_, n_, t_, p_) \
+ DEFINE_SETTER(c_, n_, t_, p_)
+#define DEFINE_FLAG_GETTER(c_, n_, t_, k_) \
+ MRB_INLINE t_ c_##_##n_(const struct RHash *h) { \
+ return (t_)((h->flags & MRB_HASH_##k_##_MASK) >> MRB_HASH_##k_##_SHIFT); \
+ }
+#define DEFINE_FLAG_SETTER(c_, n_, t_, k_) \
+ MRB_INLINE void c_##_set_##n_(struct RHash *h, t_ v) { \
+ h->flags &= ~MRB_HASH_##k_##_MASK; \
+ h->flags |= v << MRB_HASH_##k_##_SHIFT; \
+ }
+#define DEFINE_FLAG_ACCESSOR(c_, n_, t_, k_) \
+ DEFINE_FLAG_GETTER(c_, n_, t_, k_) \
+ DEFINE_FLAG_SETTER(c_, n_, t_, k_)
+#define DEFINE_INCREMENTER(c_, n_) \
+ MRB_INLINE void c_##_inc_##n_(struct RHash *h) { \
+ c_##_set_##n_(h, c_##_##n_(h) + 1); \
+ }
+#define DEFINE_DECREMENTER(c_, n_) \
+ MRB_INLINE void c_##_dec_##n_(struct RHash *h) { \
+ c_##_set_##n_(h, c_##_##n_(h) - 1); \
+ }
+#define DEFINE_SWITCHER(n_, k_) \
+ MRB_INLINE void h_##n_##_on(struct RHash *h) { \
+ h->flags |= MRB_HASH_##k_; \
+ } \
+ MRB_INLINE void h_##n_##_off(struct RHash *h) { \
+ h->flags &= ~MRB_HASH_##k_; \
+ } \
+ MRB_INLINE mrb_bool h_##n_##_p(const struct RHash *h) { \
+ return (h->flags & MRB_HASH_##k_) == MRB_HASH_##k_; \
+ }
+
+#ifdef MRB_64BIT
+DEFINE_ACCESSOR(ar, ea_capa, uint32_t, ea_capa)
+DEFINE_ACCESSOR(ar, ea_n_used, uint32_t, ea_n_used)
+DEFINE_ACCESSOR(ht, ea_capa, uint32_t, ea_capa)
+DEFINE_ACCESSOR(ht, ea_n_used, uint32_t, ea_n_used)
+#else
+DEFINE_FLAG_ACCESSOR(ar, ea_capa, uint32_t, AR_EA_CAPA)
+DEFINE_FLAG_ACCESSOR(ar, ea_n_used, uint32_t, AR_EA_N_USED)
+DEFINE_ACCESSOR(ht, ea_capa, uint32_t, hsh.ht->ea_capa)
+DEFINE_ACCESSOR(ht, ea_n_used, uint32_t, hsh.ht->ea_n_used)
+#endif
+DEFINE_FLAG_ACCESSOR(ib, bit, uint32_t, IB_BIT)
+DEFINE_ACCESSOR(ar, size, uint32_t, size)
+DEFINE_ACCESSOR(ar, ea, hash_entry*, hsh.ea)
+DEFINE_DECREMENTER(ar, size)
+DEFINE_ACCESSOR(ht, size, uint32_t, size)
+DEFINE_ACCESSOR(ht, ea, hash_entry*, hsh.ht->ea)
+DEFINE_GETTER(ht, ib, uint32_t*, hsh.ht->ib)
+DEFINE_INCREMENTER(ht, size)
+DEFINE_DECREMENTER(ht, size)
+DEFINE_GETTER(h, size, uint32_t, size)
+DEFINE_ACCESSOR(h, ht, hash_table*, hsh.ht)
+DEFINE_SWITCHER(ht, HT)
+
+#define ea_each_used(ea, n_used, entry_var, code) do { \
+ hash_entry *entry_var = ea, *ea_end__ = entry_var + (n_used); \
+ for (; entry_var < ea_end__; ++entry_var) { \
+ code; \
+ } \
+} while (0)
+
+#define ea_each(ea, size, entry_var, code) do { \
+ hash_entry *entry_var = ea; \
+ uint32_t size__ = size; \
+ for (; 0 < size__; ++entry_var) { \
+ if (entry_deleted_p(entry_var)) continue; \
+ --size__; \
+ code; \
+ } \
+} while (0)
+
+#define ib_cycle_by_key(mrb, h, key, it_var, code) do { \
+ index_buckets_iter it_var[1]; \
+ ib_it_init(mrb, it_var, h, key); \
+ for (;;) { \
+ ib_it_next(it_var); \
+ code; \
+ } \
+} while (0)
+
+#define ib_find_by_key(mrb, h_, key_, it_var, code) do { \
+ mrb_value ib_fbk_key__ = key_; \
+ ib_cycle_by_key(mrb, h_, ib_fbk_key__, it_var, { \
+ if (ib_it_empty_p(it_var)) break; \
+ if (ib_it_deleted_p(it_var)) continue; \
+ if (obj_eql(mrb, ib_fbk_key__, ib_it_entry(it_var)->key, it_var->h)) { \
+ code; \
+ break; \
+ } \
+ }); \
+} while (0)
+
+#define h_each(h, entry_var, code) do { \
+ struct RHash *h__ = h; \
+ hash_entry *h_e_ea__; \
+ uint32_t h_e_size__; \
+ h_ar_p(h) ? (h_e_ea__ = ar_ea(h__), h_e_size__ = ar_size(h__)) : \
+ (h_e_ea__ = ht_ea(h__), h_e_size__ = ht_size(h__)); \
+ ea_each(h_e_ea__, h_e_size__, entry_var, code); \
+} while (0)
+/*
+ * In `h_check_modified()`, in the case of `MRB_NO_BOXING`, `ht_ea()` or
+ * `ht_ea_capa()` for AR may read uninitialized area (#5332). Therefore, do
+ * not use those macros for AR in `MRB_NO_BOXING` (but in the case of
+ * `MRB_64BIT`, `ht_ea_capa()` is the same as `ar_ea_capa()`, so use it).
+ */
+#ifdef MRB_NO_BOXING
+# define H_CHECK_MODIFIED_USE_HT_EA_FOR_AR FALSE
+# ifdef MRB_64BIT
+# define H_CHECK_MODIFIED_USE_HT_EA_CAPA_FOR_AR TRUE
+# else
+# define H_CHECK_MODIFIED_USE_HT_EA_CAPA_FOR_AR FALSE
+# endif /* MRB_64BIT */
+#else
+# define H_CHECK_MODIFIED_USE_HT_EA_FOR_AR TRUE
+# define H_CHECK_MODIFIED_USE_HT_EA_CAPA_FOR_AR TRUE
+ /*
+ * `h_check_modified` raises an exception when a dangerous modification is
+ * made to `h` by executing `code`.
+ *
+ * `h_check_modified` macro is not called if `h->hsh.ht` (`h->hsh.ea`) is `NULL`
+ * (`Hash` size is zero). And because the `hash_entry` is rather large,
+ * `h->hsh.ht->ea` and `h->hsh.ht->ea_capa` are able to be safely accessed even for
+ * AR. This nature is used to eliminate branch of AR or HT.
+ *
+ * `HT_ASSERT_SAFE_READ` checks if members can be accessed according to its
+ * assumptions.
+ */
+# define HT_ASSERT_SAFE_READ(attr_name) \
+ mrb_static_assert1( \
+ offsetof(hash_table, attr_name) + sizeof(((hash_table*)0)->attr_name) <= \
+ sizeof(hash_entry))
+HT_ASSERT_SAFE_READ(ea);
+# ifdef MRB_32BIT
+HT_ASSERT_SAFE_READ(ea_capa);
+# endif
+# undef HT_ASSERT_SAFE_READ
+#endif /* MRB_NO_BOXING */
+
+/*
+ * `h_check_modified` raises an exception when a dangerous modification is
+ * made to `h` by executing `code`.
+ */
+#define h_check_modified(mrb, h, code) do { \
+ struct RHash *h__ = h; \
+ uint32_t mask__ = MRB_HASH_HT|MRB_HASH_IB_BIT_MASK|MRB_HASH_AR_EA_CAPA_MASK; \
+ uint32_t flags__ = h__->flags & mask__; \
+ void* tbl__ = (mrb_assert(h__->hsh.ht), h__->hsh.ht); \
+ uint32_t ht_ea_capa__ = 0; \
+ hash_entry *ht_ea__ = NULL; \
+ if (H_CHECK_MODIFIED_USE_HT_EA_CAPA_FOR_AR || h_ht_p(h__)) { \
+ ht_ea_capa__ = ht_ea_capa(h__); \
+ } \
+ if (H_CHECK_MODIFIED_USE_HT_EA_FOR_AR || h_ht_p(h__)) { \
+ ht_ea__ = ht_ea(h__); \
+ } \
+ code; \
+ if (flags__ != (h__->flags & mask__) || \
+ tbl__ != h__->hsh.ht || \
+ ((H_CHECK_MODIFIED_USE_HT_EA_CAPA_FOR_AR || h_ht_p(h__)) && \
+ ht_ea_capa__ != ht_ea_capa(h__)) || \
+ ((H_CHECK_MODIFIED_USE_HT_EA_FOR_AR || h_ht_p(h__)) && \
+ ht_ea__ != ht_ea(h__))) { \
+ mrb_raise(mrb, E_RUNTIME_ERROR, "hash modified"); \
+ } \
+} while (0)
+
+#define U32(v) ((uint32_t)(v))
+#define h_ar_p(h) (!h_ht_p(h))
+#define h_ar_on(h) h_ht_off(h)
+#define lesser(a, b) ((a) < (b) ? (a) : (b))
+#define RHASH_IFNONE(hash) mrb_iv_get(mrb, (hash), MRB_SYM(ifnone))
+#define RHASH_PROCDEFAULT(hash) RHASH_IFNONE(hash)
+
+static uint32_t ib_upper_bound_for(uint32_t capa);
+static uint32_t ib_bit_to_capa(uint32_t bit);
+static void ht_init(
+ mrb_state *mrb, struct RHash *h, uint32_t size,
+ hash_entry *ea, uint32_t ea_capa, hash_table *ht, uint32_t ib_bit);
+static void ht_set_without_ib_adjustment(
+ mrb_state *mrb, struct RHash *h, mrb_value key, mrb_value val);
+
+static uint32_t
+next_power2(uint32_t v)
+{
+ mrb_assert(v != 0);
+#ifdef __GNUC__
+ return U32(1) << ((sizeof(unsigned) * CHAR_BIT) - __builtin_clz(v));
+#else
+ v |= v >> 1;
+ v |= v >> 2;
+ v |= v >> 4;
+ v |= v >> 8;
+ v |= v >> 16;
+ ++v;
+ return v;
+#endif
+}
+
+static uint32_t
+obj_hash_code(mrb_state *mrb, mrb_value key, struct RHash *h)
+{
+ enum mrb_vtype tt = mrb_type(key);
+ uint32_t hash_code;
+ mrb_value hash_code_obj;
+ switch (tt) {
+ case MRB_TT_STRING:
+ hash_code = mrb_str_hash(mrb, key);
+ break;
+ case MRB_TT_TRUE:
+ case MRB_TT_FALSE:
+ case MRB_TT_SYMBOL:
+ case MRB_TT_INTEGER:
+#ifndef MRB_NO_FLOAT
+ case MRB_TT_FLOAT:
+#endif
+ hash_code = U32(mrb_obj_id(key));
+ break;
default:
- hv = mrb_funcall(mrb, key, "hash", 0);
- h = (khint_t)t ^ mrb_fixnum(hv);
- return kh_int_hash_func(mrb, h);
+ h_check_modified(mrb, h, {
+ hash_code_obj = mrb_funcall_argv(mrb, key, MRB_SYM(hash), 0, NULL);
+ });
+
+ hash_code = U32(tt) ^ U32(mrb_integer(hash_code_obj));
+ break;
}
+ return hash_code ^ (hash_code << 2) ^ (hash_code >> 2);
}
-static inline khint_t
-mrb_hash_ht_hash_equal(mrb_state *mrb, mrb_value a, mrb_value b)
+static mrb_bool
+obj_eql(mrb_state *mrb, mrb_value a, mrb_value b, struct RHash *h)
{
- enum mrb_vtype t = mrb_type(a);
+ enum mrb_vtype tt = mrb_type(a);
+ mrb_bool eql;
- switch (t) {
+ switch (tt) {
case MRB_TT_STRING:
return mrb_str_equal(mrb, a, b);
case MRB_TT_SYMBOL:
- if (mrb_type(b) != MRB_TT_SYMBOL) return FALSE;
+ if (!mrb_symbol_p(b)) return FALSE;
return mrb_symbol(a) == mrb_symbol(b);
- case MRB_TT_FIXNUM:
- switch (mrb_type(b)) {
- case MRB_TT_FIXNUM:
- return mrb_fixnum(a) == mrb_fixnum(b);
- case MRB_TT_FLOAT:
- return (mrb_float)mrb_fixnum(a) == mrb_float(b);
- default:
- return FALSE;
- }
+ case MRB_TT_INTEGER:
+ if (!mrb_integer_p(b)) return FALSE;
+ return mrb_integer(a) == mrb_integer(b);
+#ifndef MRB_NO_FLOAT
case MRB_TT_FLOAT:
- switch (mrb_type(b)) {
- case MRB_TT_FIXNUM:
- return mrb_float(a) == (mrb_float)mrb_fixnum(b);
- case MRB_TT_FLOAT:
- return mrb_float(a) == mrb_float(b);
- default:
- return FALSE;
- }
+ if (!mrb_float_p(b)) return FALSE;
+ return mrb_float(a) == mrb_float(b);
+#endif
default:
- return mrb_eql(mrb, a, b);
+ h_check_modified(mrb, h, {eql = mrb_eql(mrb, a, b);});
+ return eql;
+ }
+}
+
+static mrb_bool
+entry_deleted_p(const hash_entry* entry)
+{
+ return mrb_undef_p(entry->key);
+}
+
+static void
+entry_delete(hash_entry* entry)
+{
+ entry->key = mrb_undef_value();
+}
+
+static uint32_t
+ea_next_capa_for(uint32_t size, uint32_t max_capa)
+{
+ if (size < AR_DEFAULT_CAPA) {
+ return AR_DEFAULT_CAPA;
+ }
+ else {
+ /*
+ * For 32-bit CPU, the theoretical value of maximum EA capacity is
+ * `UINT32_MAX / sizeof (hash_entry)`. At this time, if
+ * `EA_INCREASE_RATIO` is the current value, 32-bit range will not be
+ * exceeded during the calculation of `capa`, so `size_t` is used.
+ */
+ size_t capa = (size_t)size * EA_INCREASE_RATIO, inc = capa - size;
+ if (EA_MAX_INCREASE < inc) capa = size + EA_MAX_INCREASE;
+ return capa <= max_capa ? U32(capa) : max_capa;
}
}
-typedef struct {
- mrb_value v;
- mrb_int n;
-} mrb_hash_value;
+static hash_entry*
+ea_resize(mrb_state *mrb, hash_entry *ea, uint32_t capa)
+{
+ return (hash_entry*)mrb_realloc(mrb, ea, sizeof(hash_entry) * capa);
+}
-KHASH_DECLARE(ht, mrb_value, mrb_hash_value, TRUE)
-KHASH_DEFINE (ht, mrb_value, mrb_hash_value, TRUE, mrb_hash_ht_hash_func, mrb_hash_ht_hash_equal)
+static void
+ea_compress(hash_entry *ea, uint32_t n_used)
+{
+ hash_entry *w_entry = ea;
+ ea_each_used(ea, n_used, r_entry, {
+ if (entry_deleted_p(r_entry)) continue;
+ if (r_entry != w_entry) *w_entry = *r_entry;
+ ++w_entry;
+ });
+}
-static void mrb_hash_modify(mrb_state *mrb, mrb_value hash);
+/*
+ * Increase or decrease capacity of `ea` to a standard size that can
+ * accommodate `*capap + 1` entries (but, not exceed `max_capa`). Set the
+ * changed capacity to `*capap` and return a pointer to `mrb_realloc`ed EA.
+ */
+static hash_entry*
+ea_adjust(mrb_state *mrb, hash_entry *ea, uint32_t *capap, uint32_t max_capa)
+{
+ *capap = ea_next_capa_for(*capap, max_capa);
+ return ea_resize(mrb, ea, *capap);
+}
-static inline mrb_value
-mrb_hash_ht_key(mrb_state *mrb, mrb_value key)
+static hash_entry*
+ea_dup(mrb_state *mrb, const hash_entry *ea, uint32_t capa)
{
- if (mrb_string_p(key))
- return mrb_str_dup(mrb, key);
- else
- return key;
+ size_t byte_size = sizeof(hash_entry) * capa;
+ hash_entry *new_ea = (hash_entry*)mrb_malloc(mrb, byte_size);
+ return (hash_entry*)memcpy(new_ea, ea, byte_size);
}
-#define KEY(key) mrb_hash_ht_key(mrb, key)
+static hash_entry*
+ea_get_by_key(mrb_state *mrb, hash_entry *ea, uint32_t size, mrb_value key,
+ struct RHash *h)
+{
+ ea_each(ea, size, entry, {
+ if (obj_eql(mrb, key, entry->key, h)) return entry;
+ });
+ return NULL;
+}
-void
-mrb_gc_mark_hash(mrb_state *mrb, struct RHash *hash)
+static hash_entry*
+ea_get(hash_entry *ea, uint32_t index)
{
- khiter_t k;
- khash_t(ht) *h = hash->ht;
+ return &ea[index];
+}
- if (!h) return;
- for (k = kh_begin(h); k != kh_end(h); k++) {
- if (kh_exist(h, k)) {
- mrb_value key = kh_key(h, k);
- mrb_value val = kh_value(h, k).v;
+static void
+ea_set(hash_entry *ea, uint32_t index, mrb_value key, mrb_value val)
+{
+ ea[index].key = key;
+ ea[index].val = val;
+}
- mrb_gc_mark_value(mrb, key);
- mrb_gc_mark_value(mrb, val);
+static void
+ar_init(struct RHash *h, uint32_t size,
+ hash_entry *ea, uint32_t ea_capa, uint32_t ea_n_used)
+{
+ h_ar_on(h);
+ ar_set_size(h, size);
+ ar_set_ea(h, ea);
+ ar_set_ea_capa(h, ea_capa);
+ ar_set_ea_n_used(h, ea_n_used);
+}
+
+static void
+ar_free(mrb_state *mrb, struct RHash *h)
+{
+ mrb_free(mrb, ar_ea(h));
+}
+
+static void
+ar_adjust_ea(mrb_state *mrb, struct RHash *h, uint32_t size, uint32_t max_ea_capa)
+{
+ uint32_t ea_capa = size;
+ hash_entry *ea = ea_adjust(mrb, ar_ea(h), &ea_capa, max_ea_capa);
+ ar_set_ea(h, ea);
+ ar_set_ea_capa(h, ea_capa);
+}
+
+static void
+ar_compress(mrb_state *mrb, struct RHash *h)
+{
+ uint32_t size = ar_size(h);
+ ea_compress(ar_ea(h), ar_ea_n_used(h));
+ ar_set_ea_n_used(h, size);
+ ar_adjust_ea(mrb, h, size, lesser(ar_ea_capa(h), AR_MAX_SIZE));
+}
+
+static mrb_bool
+ar_get(mrb_state *mrb, struct RHash *h, mrb_value key, mrb_value *valp)
+{
+ ea_each(ar_ea(h), ar_size(h), entry, {
+ if (!obj_eql(mrb, key, entry->key, h)) continue;
+ *valp = entry->val;
+ return TRUE;
+ });
+ return FALSE;
+}
+
+static void
+ar_set(mrb_state *mrb, struct RHash *h, mrb_value key, mrb_value val)
+{
+ uint32_t size = ar_size(h);
+ hash_entry *entry;
+ if ((entry = ea_get_by_key(mrb, ar_ea(h), size, key, h))) {
+ entry->val = val;
+ }
+ else {
+ uint32_t ea_capa = ar_ea_capa(h), ea_n_used = ar_ea_n_used(h);
+ if (ea_capa == ea_n_used) {
+ if (size == ea_n_used) {
+ if (size == AR_MAX_SIZE) {
+ hash_entry *ea = ea_adjust(mrb, ar_ea(h), &ea_capa, EA_MAX_CAPA);
+ ea_set(ea, ea_n_used, key, val);
+ ht_init(mrb, h, ++size, ea, ea_capa, NULL, IB_INIT_BIT);
+ return;
+ }
+ else {
+ ar_adjust_ea(mrb, h, size, AR_MAX_SIZE);
+ }
+ }
+ else {
+ ar_compress(mrb, h);
+ ea_n_used = size;
+ }
}
+ ea_set(ar_ea(h), ea_n_used, key, val);
+ ar_set_size(h, ++size);
+ ar_set_ea_n_used(h, ++ea_n_used);
}
}
-size_t
-mrb_gc_mark_hash_size(mrb_state *mrb, struct RHash *hash)
+static mrb_bool
+ar_delete(mrb_state *mrb, struct RHash *h, mrb_value key, mrb_value *valp)
{
- if (!hash->ht) return 0;
- return kh_size(hash->ht)*2;
+ hash_entry *entry = ea_get_by_key(mrb, ar_ea(h), ar_size(h), key, h);
+ if (!entry) return FALSE;
+ *valp = entry->val;
+ entry_delete(entry);
+ ar_dec_size(h);
+ return TRUE;
}
-void
-mrb_gc_free_hash(mrb_state *mrb, struct RHash *hash)
+static void
+ar_shift(mrb_state *mrb, struct RHash *h, mrb_value *keyp, mrb_value *valp)
{
- if (hash->ht) kh_destroy(ht, mrb, hash->ht);
+ uint32_t size = ar_size(h);
+ ea_each(ar_ea(h), size, entry, {
+ *keyp = entry->key;
+ *valp = entry->val;
+ entry_delete(entry);
+ ar_set_size(h, --size);
+ return;
+ });
}
+static void
+ar_rehash(mrb_state *mrb, struct RHash *h)
+{
+ /* see comments in `h_rehash` */
+ uint32_t size = ar_size(h), w_size = 0, ea_capa = ar_ea_capa(h);
+ hash_entry *ea = ar_ea(h), *w_entry;
+ ea_each(ea, size, r_entry, {
+ if ((w_entry = ea_get_by_key(mrb, ea, w_size, r_entry->key, h))) {
+ w_entry->val = r_entry->val;
+ ar_set_size(h, --size);
+ entry_delete(r_entry);
+ }
+ else {
+ if (w_size != U32(r_entry - ea)) {
+ ea_set(ea, w_size, r_entry->key, r_entry->val);
+ entry_delete(r_entry);
+ }
+ ++w_size;
+ }
+ });
+ mrb_assert(size == w_size);
+ ar_set_ea_n_used(h, size);
+ ar_adjust_ea(mrb, h, size, ea_capa);
+}
-MRB_API mrb_value
-mrb_hash_new_capa(mrb_state *mrb, int capa)
+static uint32_t
+ib_it_pos_for(index_buckets_iter *it, uint32_t v)
{
- struct RHash *h;
+ return v & it->mask;
+}
- h = (struct RHash*)mrb_obj_alloc(mrb, MRB_TT_HASH, mrb->hash_class);
- h->ht = kh_init(ht, mrb);
- if (capa > 0) {
- kh_resize(ht, mrb, h->ht, capa);
- }
- h->iv = 0;
- return mrb_obj_value(h);
+static uint32_t
+ib_it_empty_value(const index_buckets_iter *it)
+{
+ return it->mask;
}
-MRB_API mrb_value
-mrb_hash_new(mrb_state *mrb)
+static uint32_t
+ib_it_deleted_value(const index_buckets_iter *it)
{
- return mrb_hash_new_capa(mrb, 0);
+ return it->mask - 1;
}
-MRB_API mrb_value
-mrb_hash_get(mrb_state *mrb, mrb_value hash, mrb_value key)
+static mrb_bool
+ib_it_empty_p(const index_buckets_iter *it)
{
- khash_t(ht) *h = RHASH_TBL(hash);
- khiter_t k;
+ return it->ea_index == ib_it_empty_value(it);
+}
- if (h) {
- k = kh_get(ht, mrb, h, key);
- if (k != kh_end(h))
- return kh_value(h, k).v;
- }
+static mrb_bool
+ib_it_deleted_p(const index_buckets_iter *it)
+{
+ return it->ea_index == ib_it_deleted_value(it);
+}
- /* not found */
- if (MRB_RHASH_PROCDEFAULT_P(hash)) {
- return mrb_funcall(mrb, RHASH_PROCDEFAULT(hash), "call", 2, hash, key);
+static mrb_bool
+ib_it_active_p(const index_buckets_iter *it)
+{
+ return it->ea_index < ib_it_deleted_value(it);
+}
+
+static void
+ib_it_init(mrb_state *mrb, index_buckets_iter *it, struct RHash *h, mrb_value key)
+{
+ it->h = h;
+ it->bit = ib_bit(h);
+ it->mask = ib_bit_to_capa(it->bit) - 1;
+ it->pos = ib_it_pos_for(it, obj_hash_code(mrb, key, h));
+ it->step = 0;
+}
+
+static void
+ib_it_next(index_buckets_iter *it)
+{
+ /*
+ * [IB image]
+ *
+ * ary_index(1) --.
+ * \ .-- shift1(3) .-- shift2(29)
+ * pos(6) --. \ / /
+ * View | \ \ <-o-> <----------o---------->
+ * -------- +---------------------\----\--+-----------------------------+-----
+ * array | 0 `--. `-|--- o 1 | ...
+ * +---------+---------+-----+\--+-----+---------+---------+---+-----
+ * buckets | 0 | 1 | ... | o 6 | 7 | 8 | ...
+ * +---------+---------+-----+=========+---------+---------+---------
+ * bit set |1 1 1 0 0|0 0 0 1 1| ... |0 1 0 1 1|0 1 1 1 0|0 1 0 1 0| ...
+ * +---------+---------+-----+========*+---------+---------+---------
+ * <---o---> \
+ * \ `-- bit_pos(34)
+ * `-- bit(5)
+ */
+
+ /* Slide to handle as `capa == 32` to avoid 64-bit operations */
+ uint32_t slid_pos = it->pos & (IB_TYPE_BIT - 1);
+ uint32_t slid_bit_pos = it->bit * (slid_pos + 1) - 1;
+ uint32_t slid_ary_index = slid_bit_pos / IB_TYPE_BIT;
+ it->ary_index = slid_ary_index + it->pos / IB_TYPE_BIT * it->bit;
+ it->shift2 = (slid_ary_index + 1) * IB_TYPE_BIT - slid_bit_pos - 1;
+ it->ea_index = (ht_ib(it->h)[it->ary_index] >> it->shift2) & it->mask;
+ if (IB_TYPE_BIT - it->bit < it->shift2) {
+ it->shift1 = IB_TYPE_BIT - it->shift2;
+ it->ea_index |= (ht_ib(it->h)[it->ary_index - 1] << it->shift1) & it->mask;
}
- return RHASH_IFNONE(hash);
+ else {
+ it->shift1 = 0;
+ }
+ it->pos = ib_it_pos_for(it, it->pos + (++it->step));
}
-MRB_API mrb_value
-mrb_hash_fetch(mrb_state *mrb, mrb_value hash, mrb_value key, mrb_value def)
+static uint32_t
+ib_it_get(const index_buckets_iter *it)
{
- khash_t(ht) *h = RHASH_TBL(hash);
- khiter_t k;
+ return it->ea_index;
+}
- if (h) {
- k = kh_get(ht, mrb, h, key);
- if (k != kh_end(h))
- return kh_value(h, k).v;
+static void
+ib_it_set(index_buckets_iter *it, uint32_t ea_index)
+{
+ uint32_t mask, i;
+ it->ea_index = ea_index;
+ if (it->shift1) {
+ i = it->ary_index - 1;
+ mask = it->mask >> it->shift1;
+ ht_ib(it->h)[i] = (ht_ib(it->h)[i] & ~mask) | (ea_index >> it->shift1);
}
+ i = it->ary_index;
+ mask = it->mask << it->shift2;
+ ht_ib(it->h)[i] = (ht_ib(it->h)[i] & ~mask) | (ea_index << it->shift2);
+}
- /* not found */
- return def;
+static void
+ib_it_delete(index_buckets_iter *it)
+{
+ ib_it_set(it, ib_it_deleted_value(it));
}
-MRB_API void
-mrb_hash_set(mrb_state *mrb, mrb_value hash, mrb_value key, mrb_value val)
+static hash_entry*
+ib_it_entry(index_buckets_iter *it)
+{
+ return ea_get(ht_ea(it->h), it->ea_index);
+}
+
+static uint32_t
+ib_capa_to_bit(uint32_t capa)
{
- khash_t(ht) *h;
- khiter_t k;
- int r;
+#ifdef __GNUC__
+ return U32(__builtin_ctz(capa));
+#else
+ /* http://graphics.stanford.edu/~seander/bithacks.html#IntegerLogDeBruijn */
+ static const uint32_t MultiplyDeBruijnBitPosition2[] = {
+ 0, 1, 28, 2, 29, 14, 24, 3, 30, 22, 20, 15, 25, 17, 4, 8,
+ 31, 27, 13, 23, 21, 19, 16, 7, 26, 12, 18, 6, 11, 5, 10, 9
+ };
+ return MultiplyDeBruijnBitPosition2[U32(capa * 0x077CB531U) >> 27];
+#endif
+}
- mrb_hash_modify(mrb, hash);
- h = RHASH_TBL(hash);
+static uint32_t
+ib_bit_to_capa(uint32_t bit)
+{
+ return U32(1) << bit;
+}
- if (!h) h = RHASH_TBL(hash) = kh_init(ht, mrb);
- k = kh_put2(ht, mrb, h, key, &r);
- kh_value(h, k).v = val;
+static uint32_t
+ib_upper_bound_for(uint32_t capa)
+{
+ return (capa >> 2) | (capa >> 1); /* 3/4 */
+}
- if (r != 0) {
- /* expand */
- int ai = mrb_gc_arena_save(mrb);
- key = kh_key(h, k) = KEY(key);
- mrb_gc_arena_restore(mrb, ai);
- kh_value(h, k).n = kh_size(h)-1;
- }
+static uint32_t
+ib_bit_for(uint32_t size)
+{
+ uint32_t capa = next_power2(size);
+ if (capa != IB_MAX_CAPA && ib_upper_bound_for(capa) < size) capa *= 2;
+ return ib_capa_to_bit(capa);
+}
- mrb_field_write_barrier_value(mrb, (struct RBasic*)RHASH(hash), key);
- mrb_field_write_barrier_value(mrb, (struct RBasic*)RHASH(hash), val);
- return;
+static uint32_t
+ib_byte_size_for(uint32_t ib_bit)
+{
+ mrb_assert(IB_INIT_BIT <= ib_bit);
+ uint32_t ary_size = IB_INIT_BIT == 4 ?
+ ib_bit_to_capa(ib_bit) * 2 / IB_TYPE_BIT * ib_bit / 2 :
+ ib_bit_to_capa(ib_bit) / IB_TYPE_BIT * ib_bit;
+ return U32(sizeof(uint32_t) * ary_size);
}
-static mrb_value
-mrb_hash_dup(mrb_state *mrb, mrb_value hash)
+static void
+ib_init(mrb_state *mrb, struct RHash *h, uint32_t ib_bit, size_t ib_byte_size)
+{
+ hash_entry *ea = ht_ea(h);
+ memset(ht_ib(h), 0xff, ib_byte_size);
+ ib_set_bit(h, ib_bit);
+ ea_each_used(ea, ht_ea_n_used(h), entry, {
+ ib_cycle_by_key(mrb, h, entry->key, it, {
+ if (!ib_it_empty_p(it)) continue;
+ ib_it_set(it, U32(entry - ea));
+ break;
+ });
+ });
+}
+
+static void
+ht_init(mrb_state *mrb, struct RHash *h, uint32_t size,
+ hash_entry *ea, uint32_t ea_capa, hash_table *ht, uint32_t ib_bit)
+{
+ size_t ib_byte_size = ib_byte_size_for(ib_bit);
+ size_t ht_byte_size = sizeof(hash_table) + ib_byte_size;
+ h_ht_on(h);
+ h_set_ht(h, (hash_table*)mrb_realloc(mrb, ht, ht_byte_size));
+ ht_set_size(h, size);
+ ht_set_ea(h, ea);
+ ht_set_ea_capa(h, ea_capa);
+ ht_set_ea_n_used(h, size);
+ ib_init(mrb, h, ib_bit, ib_byte_size);
+}
+
+static void
+ht_free(mrb_state *mrb, struct RHash *h)
{
- struct RHash* ret;
- khash_t(ht) *h, *ret_h;
- khiter_t k, ret_k;
+ mrb_free(mrb, ht_ea(h));
+ mrb_free(mrb, h_ht(h));
+}
- h = RHASH_TBL(hash);
- ret = (struct RHash*)mrb_obj_alloc(mrb, MRB_TT_HASH, mrb->hash_class);
- ret->ht = kh_init(ht, mrb);
+static hash_table*
+ht_dup(mrb_state *mrb, const struct RHash *h)
+{
+ size_t ib_byte_size = ib_byte_size_for(ib_bit(h));
+ size_t ht_byte_size = sizeof(hash_table) + ib_byte_size;
+ hash_table *new_ht = (hash_table*)mrb_malloc(mrb, ht_byte_size);
+ return (hash_table*)memcpy(new_ht, h_ht(h), ht_byte_size);
+}
- if (kh_size(h) > 0) {
- ret_h = ret->ht;
+static void
+ht_adjust_ea(mrb_state *mrb, struct RHash *h, uint32_t size, uint32_t max_ea_capa)
+{
+ uint32_t ea_capa = size;
+ hash_entry *ea = ea_adjust(mrb, ht_ea(h), &ea_capa, max_ea_capa);
+ ht_set_ea(h, ea);
+ ht_set_ea_capa(h, ea_capa);
+}
- for (k = kh_begin(h); k != kh_end(h); k++) {
- if (kh_exist(h, k)) {
- int ai = mrb_gc_arena_save(mrb);
- ret_k = kh_put(ht, mrb, ret_h, KEY(kh_key(h, k)));
- mrb_gc_arena_restore(mrb, ai);
- kh_val(ret_h, ret_k) = kh_val(h, k);
+static void
+ht_to_ar(mrb_state *mrb, struct RHash *h)
+{
+ uint32_t size = ht_size(h), ea_capa = size;
+ hash_entry *ea = ht_ea(h);
+ ea_compress(ea, ht_ea_n_used(h));
+ ea = ea_adjust(mrb, ea, &ea_capa, AR_MAX_SIZE);
+ mrb_free(mrb, h_ht(h));
+ ar_init(h, size, ea, ea_capa, size);
+}
+
+static mrb_bool
+ht_get(mrb_state *mrb, struct RHash *h, mrb_value key, mrb_value *valp)
+{
+ ib_find_by_key(mrb, h, key, it, {
+ *valp = ib_it_entry(it)->val;
+ return TRUE;
+ });
+ return FALSE;
+}
+
+static void
+ht_set_as_ar(mrb_state *mrb, struct RHash *h, mrb_value key, mrb_value val)
+{
+ ht_to_ar(mrb, h);
+ ar_set(mrb, h, key, val);
+}
+
+static void
+ht_set_without_ib_adjustment(mrb_state *mrb, struct RHash *h,
+ mrb_value key, mrb_value val)
+{
+ mrb_assert(ht_size(h) < ib_bit_to_capa(ib_bit(h)));
+ ib_cycle_by_key(mrb, h, key, it, {
+ if (ib_it_active_p(it)) {
+ if (!obj_eql(mrb, key, ib_it_entry(it)->key, h)) continue;
+ ib_it_entry(it)->val = val;
+ }
+ else {
+ uint32_t ea_n_used = ht_ea_n_used(h);
+ if (ea_n_used == H_MAX_SIZE) {
+ mrb_assert(ht_size(h) == ea_n_used);
+ mrb_raise(mrb, E_ARGUMENT_ERROR, "hash too big");
}
+ if (ea_n_used == ht_ea_capa(h)) ht_adjust_ea(mrb, h, ea_n_used, EA_MAX_CAPA);
+ ib_it_set(it, ea_n_used);
+ ea_set(ht_ea(h), ea_n_used, key, val);
+ ht_inc_size(h);
+ ht_set_ea_n_used(h, ++ea_n_used);
}
+ return;
+ });
+}
+
+static void
+ht_set(mrb_state *mrb, struct RHash *h, mrb_value key, mrb_value val)
+{
+ uint32_t size = ht_size(h);
+ uint32_t ib_bit_width = ib_bit(h), ib_capa = ib_bit_to_capa(ib_bit_width);
+ if (ib_upper_bound_for(ib_capa) <= size) {
+ if (size != ht_ea_n_used(h)) ea_compress(ht_ea(h), ht_ea_n_used(h));
+ ht_init(mrb, h, size, ht_ea(h), ht_ea_capa(h), h_ht(h), ++ib_bit_width);
+ }
+ else if (size != ht_ea_n_used(h)) {
+ if (ib_capa - EA_N_RESERVED_INDICES <= ht_ea_n_used(h)) goto compress;
+ if (ht_ea_capa(h) == ht_ea_n_used(h)) {
+ if (size <= AR_MAX_SIZE) {ht_set_as_ar(mrb, h, key, val); return;}
+ if (ea_next_capa_for(size, EA_MAX_CAPA) <= ht_ea_capa(h)) {
+ compress:
+ ea_compress(ht_ea(h), ht_ea_n_used(h));
+ ht_adjust_ea(mrb, h, size, ht_ea_capa(h));
+ ht_init(mrb, h, size, ht_ea(h), ht_ea_capa(h), h_ht(h), ib_bit_width);
+ }
+ }
+ }
+ ht_set_without_ib_adjustment(mrb, h, key, val);
+}
+
+static mrb_bool
+ht_delete(mrb_state *mrb, struct RHash *h, mrb_value key, mrb_value *valp)
+{
+ ib_find_by_key(mrb, h, key, it, {
+ hash_entry *entry = ib_it_entry(it);
+ *valp = entry->val;
+ ib_it_delete(it);
+ entry_delete(entry);
+ ht_dec_size(h);
+ return TRUE;
+ });
+ return FALSE;
+}
+
+static void
+ht_shift(mrb_state *mrb, struct RHash *h, mrb_value *keyp, mrb_value *valp)
+{
+ hash_entry *ea = ht_ea(h);
+ ea_each(ea, ht_size(h), entry, {
+ ib_cycle_by_key(mrb, h, entry->key, it, {
+ if (ib_it_get(it) != U32(entry - ea)) continue;
+ *keyp = entry->key;
+ *valp = entry->val;
+ ib_it_delete(it);
+ entry_delete(entry);
+ ht_dec_size(h);
+ return;
+ });
+ });
+}
+
+static void
+ht_rehash(mrb_state *mrb, struct RHash *h)
+{
+ /* see comments in `h_rehash` */
+ uint32_t size = ht_size(h);
+ if (size <= AR_MAX_SIZE) {
+ ht_to_ar(mrb, h);
+ ar_rehash(mrb, h);
+ return;
}
+ uint32_t w_size = 0, ea_capa = ht_ea_capa(h);
+ hash_entry *ea = ht_ea(h);
+ ht_init(mrb, h, 0, ea, ea_capa, h_ht(h), ib_bit_for(size));
+ ht_set_size(h, size);
+ ht_set_ea_n_used(h, ht_ea_n_used(h));
+ ea_each(ea, size, r_entry, {
+ ib_cycle_by_key(mrb, h, r_entry->key, it, {
+ if (ib_it_active_p(it)) {
+ if (!obj_eql(mrb, r_entry->key, ib_it_entry(it)->key, h)) continue;
+ ib_it_entry(it)->val = r_entry->val;
+ ht_set_size(h, --size);
+ entry_delete(r_entry);
+ }
+ else {
+ if (w_size != U32(r_entry - ea)) {
+ ea_set(ea, w_size, r_entry->key, r_entry->val);
+ entry_delete(r_entry);
+ }
+ ib_it_set(it, w_size++);
+ }
+ break;
+ });
+ });
+ mrb_assert(size == w_size);
+ ht_set_ea_n_used(h, size);
+ size <= AR_MAX_SIZE ? ht_to_ar(mrb, h) : ht_adjust_ea(mrb, h, size, ea_capa);
+}
- return mrb_obj_value(ret);
+static mrb_value
+h_key_for(mrb_state *mrb, mrb_value key)
+{
+ if (mrb_string_p(key) && !MRB_FROZEN_P(mrb_str_ptr(key))) {
+ key = mrb_str_dup(mrb, key);
+ MRB_SET_FROZEN_FLAG(mrb_str_ptr(key));
+ }
+ return key;
+}
+
+static struct RHash*
+h_alloc(mrb_state *mrb)
+{
+ return MRB_OBJ_ALLOC(mrb, MRB_TT_HASH, mrb->hash_class);
+}
+
+static void
+h_init(struct RHash *h)
+{
+ ar_init(h, 0, NULL, 0, 0);
+}
+
+static void
+h_free_table(mrb_state *mrb, struct RHash *h)
+{
+ (h_ar_p(h) ? ar_free : ht_free)(mrb, h);
+}
+
+static void
+h_clear(mrb_state *mrb, struct RHash *h)
+{
+ h_free_table(mrb, h);
+ h_init(h);
+}
+
+static mrb_bool
+h_get(mrb_state *mrb, struct RHash *h, mrb_value key, mrb_value *valp)
+{
+ return (h_ar_p(h) ? ar_get : ht_get)(mrb, h, key, valp);
+}
+
+static void
+h_set(mrb_state *mrb, struct RHash *h, mrb_value key, mrb_value val)
+{
+ (h_ar_p(h) ? ar_set : ht_set)(mrb, h, key, val);
+}
+
+static mrb_bool
+h_delete(mrb_state *mrb, struct RHash *h, mrb_value key, mrb_value *valp)
+{
+ return (h_ar_p(h) ? ar_delete : ht_delete)(mrb, h, key, valp);
+}
+
+/* find first element in the table, and remove it. */
+static void
+h_shift(mrb_state *mrb, struct RHash *h, mrb_value *keyp, mrb_value *valp)
+{
+ (h_ar_p(h) ? ar_shift : ht_shift)(mrb, h, keyp, valp);
+}
+
+static void
+h_rehash(mrb_state *mrb, struct RHash *h)
+{
+ /*
+ * ==== Comments common to `ar_rehash` and `ht_rehash`
+ *
+ * - Because reindex (such as elimination of duplicate keys) must be
+ * guaranteed, it is necessary to set one by one.
+ *
+ * - To prevent EA from breaking if an exception occurs in the middle,
+ * delete the slot before moving when moving the entry, and update size
+ * at any time when overwriting.
+ */
+ (h_size(h) == 0 ? h_clear : h_ar_p(h) ? ar_rehash : ht_rehash)(mrb, h);
+}
+
+static void
+h_replace(mrb_state *mrb, struct RHash *h, struct RHash *orig_h)
+{
+ uint32_t size = h_size(orig_h);
+ if (size == 0) {
+ h_clear(mrb, h);
+ }
+ else if (h_ar_p(orig_h)) {
+ uint32_t ea_capa = ar_ea_capa(orig_h);
+ hash_entry *ea = ea_dup(mrb, ar_ea(orig_h), ea_capa);
+ h_free_table(mrb, h);
+ ar_init(h, size, ea, ea_capa, ar_ea_n_used(orig_h));
+ }
+ else { /* HT */
+ uint32_t ea_capa = ht_ea_capa(orig_h);
+ hash_entry *ea = ea_dup(mrb, ht_ea(orig_h), ea_capa);
+ hash_table *ht = ht_dup(mrb, orig_h);
+ h_free_table(mrb, h);
+ h_ht_on(h);
+ h_set_ht(h, ht);
+ ht_set_size(h, size);
+ ht_set_ea(h, ea);
+#ifdef MRB_64BIT
+ ht_set_ea_capa(h, ea_capa);
+ ht_set_ea_n_used(h, ht_ea_n_used(orig_h));
+#endif
+ ib_set_bit(h, ib_bit(orig_h));
+ }
+}
+
+void
+mrb_gc_mark_hash(mrb_state *mrb, struct RHash *h)
+{
+ h_each(h, entry, {
+ mrb_gc_mark_value(mrb, entry->key);
+ mrb_gc_mark_value(mrb, entry->val);
+ });
+}
+
+size_t
+mrb_gc_mark_hash_size(mrb_state *mrb, struct RHash *h)
+{
+ return h_size(h) * 2;
+}
+
+void
+mrb_gc_free_hash(mrb_state *mrb, struct RHash *h)
+{
+ h_free_table(mrb, h);
+}
+
+size_t
+mrb_hash_memsize(mrb_value self)
+{
+ struct RHash *h = mrb_hash_ptr(self);
+ return mrb_obj_iv_tbl_memsize(self) +
+ (h_ar_p(h) ? (ar_ea_capa(h) * sizeof(hash_entry)) :
+ (ht_ea_capa(h) * sizeof(hash_entry) +
+ sizeof(hash_table) +
+ ib_byte_size_for(ib_bit(h))));
+}
+
+/* Iterates over the key/value pairs. */
+MRB_API void
+mrb_hash_foreach(mrb_state *mrb, struct RHash *h, mrb_hash_foreach_func *func, void *data)
+{
+ h_each(h, entry, {
+ if (func(mrb, entry->key, entry->val, data) != 0) return;
+ });
}
MRB_API mrb_value
-mrb_check_hash_type(mrb_state *mrb, mrb_value hash)
+mrb_hash_new(mrb_state *mrb)
{
- return mrb_check_convert_type(mrb, hash, MRB_TT_HASH, "Hash", "to_hash");
+ struct RHash *h = h_alloc(mrb);
+ return mrb_obj_value(h);
}
-MRB_API khash_t(ht)*
-mrb_hash_tbl(mrb_state *mrb, mrb_value hash)
+/*
+ * Set the capacity of EA and IB to minimum capacity (and appropriate load
+ * factor) that does not cause expansion when inserting `capa` elements.
+ */
+MRB_API mrb_value
+mrb_hash_new_capa(mrb_state *mrb, mrb_int capa)
{
- khash_t(ht) *h = RHASH_TBL(hash);
+ if (capa < 0 || EA_MAX_CAPA < capa) {
+ mrb_raise(mrb, E_ARGUMENT_ERROR, "hash too big");
+ return mrb_nil_value(); /* not reached */
+ }
+ else if (capa == 0) {
+ return mrb_hash_new(mrb);
+ }
+ else {
+ uint32_t size = U32(capa);
+ struct RHash *h = h_alloc(mrb);
+ hash_entry *ea = ea_resize(mrb, NULL, size);
+ if (size <= AR_MAX_SIZE) {
+ ar_init(h, 0, ea, size, 0);
+ }
+ else {
+ ht_init(mrb, h, 0, ea, size, NULL, ib_bit_for(size));
+ }
+ return mrb_obj_value(h);
+ }
+}
- if (!h) {
- return RHASH_TBL(hash) = kh_init(ht, mrb);
+static mrb_value mrb_hash_default(mrb_state *mrb, mrb_value hash);
+
+static void
+hash_modify(mrb_state *mrb, mrb_value hash)
+{
+ mrb_check_frozen(mrb, mrb_hash_ptr(hash));
+}
+
+static mrb_value
+hash_default(mrb_state *mrb, mrb_value hash, mrb_value key)
+{
+ if (MRB_RHASH_DEFAULT_P(hash)) {
+ if (MRB_RHASH_PROCDEFAULT_P(hash)) {
+ return mrb_funcall_id(mrb, RHASH_PROCDEFAULT(hash), MRB_SYM(call), 2, hash, key);
+ }
+ else {
+ return RHASH_IFNONE(hash);
+ }
}
- return h;
+ return mrb_nil_value();
}
static void
-mrb_hash_modify(mrb_state *mrb, mrb_value hash)
+hash_replace(mrb_state *mrb, mrb_value self, mrb_value orig)
+{
+ struct RHash *h = mrb_hash_ptr(self), *orig_h = mrb_hash_ptr(orig);
+ uint32_t mask = MRB_HASH_DEFAULT | MRB_HASH_PROC_DEFAULT;
+ mrb_sym name;
+ h_replace(mrb, h, orig_h);
+ name = MRB_SYM(ifnone);
+ if (orig_h->flags & MRB_HASH_DEFAULT) {
+ mrb_iv_set(mrb, self, name, mrb_iv_get(mrb, orig, name));
+ }
+ else {
+ mrb_iv_remove(mrb, self, name);
+ }
+ h->flags &= ~mask;
+ h->flags |= orig_h->flags & mask;
+}
+
+static mrb_value
+mrb_hash_init_copy(mrb_state *mrb, mrb_value self)
+{
+ mrb_value orig;
+ mrb_get_args(mrb, "H", &orig);
+ hash_modify(mrb, self);
+ if (mrb_hash_ptr(self) != mrb_hash_ptr(orig)) hash_replace(mrb, self, orig);
+ return self;
+}
+
+void
+mrb_hash_check_kdict(mrb_state *mrb, mrb_value self)
+{
+ h_each(mrb_hash_ptr(self), entry, {
+ if (mrb_symbol_p(entry->key)) continue;
+ mrb_raise(mrb, E_ARGUMENT_ERROR, "keyword argument hash with non symbol keys");
+ });
+}
+
+MRB_API mrb_value
+mrb_hash_dup(mrb_state *mrb, mrb_value self)
{
- mrb_hash_tbl(mrb, hash);
+ struct RHash* copy_h = h_alloc(mrb);
+ mrb_value copy = mrb_obj_value(copy_h);
+ copy_h->c = mrb_hash_ptr(self)->c;
+ hash_replace(mrb, copy, self);
+ return copy;
+}
+
+MRB_API mrb_value
+mrb_hash_get(mrb_state *mrb, mrb_value hash, mrb_value key)
+{
+ mrb_value val;
+ mrb_sym mid;
+
+ if (h_get(mrb, mrb_hash_ptr(hash), key, &val)) {
+ return val;
+ }
+
+ mid = MRB_SYM(default);
+ if (mrb_func_basic_p(mrb, hash, mid, mrb_hash_default)) {
+ return hash_default(mrb, hash, key);
+ }
+ /* xxx mrb_funcall_tailcall(mrb, hash, "default", 1, key); */
+ return mrb_funcall_argv(mrb, hash, mid, 1, &key);
+}
+
+MRB_API mrb_value
+mrb_hash_fetch(mrb_state *mrb, mrb_value hash, mrb_value key, mrb_value def)
+{
+ mrb_value val;
+
+ if (h_get(mrb, mrb_hash_ptr(hash), key, &val)) {
+ return val;
+ }
+ /* not found */
+ return def;
+}
+
+MRB_API void
+mrb_hash_set(mrb_state *mrb, mrb_value hash, mrb_value key, mrb_value val)
+{
+ hash_modify(mrb, hash);
+ key = h_key_for(mrb, key);
+ h_set(mrb, mrb_hash_ptr(hash), key, val);
+ mrb_field_write_barrier_value(mrb, mrb_basic_ptr(hash), key);
+ mrb_field_write_barrier_value(mrb, mrb_basic_ptr(hash), val);
}
/* 15.2.13.4.16 */
@@ -293,22 +1265,22 @@ mrb_hash_modify(mrb_state *mrb, mrb_value hash)
* default value. It is the block's responsibility to store the value
* in the hash if required.
*
- * h = Hash.new("Go Fish")
- * h["a"] = 100
- * h["b"] = 200
- * h["a"] #=> 100
- * h["c"] #=> "Go Fish"
- * # The following alters the single default object
- * h["c"].upcase! #=> "GO FISH"
- * h["d"] #=> "GO FISH"
- * h.keys #=> ["a", "b"]
- *
- * # While this creates a new default object each time
- * h = Hash.new { |hash, key| hash[key] = "Go Fish: #{key}" }
- * h["c"] #=> "Go Fish: c"
- * h["c"].upcase! #=> "GO FISH: C"
- * h["d"] #=> "Go Fish: d"
- * h.keys #=> ["c", "d"]
+ * h = Hash.new("Go Fish")
+ * h["a"] = 100
+ * h["b"] = 200
+ * h["a"] #=> 100
+ * h["c"] #=> "Go Fish"
+ * # The following alters the single default object
+ * h["c"].upcase! #=> "GO FISH"
+ * h["d"] #=> "GO FISH"
+ * h.keys #=> ["a", "b"]
+ *
+ * # While this creates a new default object each time
+ * h = Hash.new { |hash, key| hash[key] = "Go Fish: #{key}" }
+ * h["c"] #=> "Go Fish: c"
+ * h["c"].upcase! #=> "GO FISH: C"
+ * h["d"] #=> "Go Fish: d"
+ * h.keys #=> ["c", "d"]
*
*/
@@ -320,15 +1292,18 @@ mrb_hash_init(mrb_state *mrb, mrb_value hash)
ifnone = mrb_nil_value();
mrb_get_args(mrb, "&|o?", &block, &ifnone, &ifnone_p);
- mrb_hash_modify(mrb, hash);
+ hash_modify(mrb, hash);
if (!mrb_nil_p(block)) {
if (ifnone_p) {
- mrb_raise(mrb, E_ARGUMENT_ERROR, "wrong number of arguments");
+ mrb_argnum_error(mrb, 1, 0, 0);
}
RHASH(hash)->flags |= MRB_HASH_PROC_DEFAULT;
ifnone = block;
}
- mrb_iv_set(mrb, hash, mrb_intern_lit(mrb, "ifnone"), ifnone);
+ if (!mrb_nil_p(ifnone)) {
+ RHASH(hash)->flags |= MRB_HASH_DEFAULT;
+ mrb_iv_set(mrb, hash, MRB_SYM(ifnone), ifnone);
+ }
return hash;
}
@@ -349,9 +1324,8 @@ mrb_hash_init(mrb_state *mrb, mrb_value hash)
static mrb_value
mrb_hash_aget(mrb_state *mrb, mrb_value self)
{
- mrb_value key;
+ mrb_value key = mrb_get_arg1(mrb);
- mrb_get_args(mrb, "o", &key);
return mrb_hash_get(mrb, self, key);
}
@@ -384,13 +1358,16 @@ mrb_hash_default(mrb_state *mrb, mrb_value hash)
mrb_bool given;
mrb_get_args(mrb, "|o?", &key, &given);
- if (MRB_RHASH_PROCDEFAULT_P(hash)) {
- if (!given) return mrb_nil_value();
- return mrb_funcall(mrb, RHASH_PROCDEFAULT(hash), "call", 2, hash, key);
- }
- else {
- return RHASH_IFNONE(hash);
+ if (MRB_RHASH_DEFAULT_P(hash)) {
+ if (MRB_RHASH_PROCDEFAULT_P(hash)) {
+ if (!given) return mrb_nil_value();
+ return mrb_funcall_id(mrb, RHASH_PROCDEFAULT(hash), MRB_SYM(call), 2, hash, key);
+ }
+ else {
+ return RHASH_IFNONE(hash);
+ }
}
+ return mrb_nil_value();
}
/* 15.2.13.4.6 */
@@ -417,13 +1394,17 @@ mrb_hash_default(mrb_state *mrb, mrb_value hash)
static mrb_value
mrb_hash_set_default(mrb_state *mrb, mrb_value hash)
{
- mrb_value ifnone;
-
- mrb_get_args(mrb, "o", &ifnone);
- mrb_hash_modify(mrb, hash);
- mrb_iv_set(mrb, hash, mrb_intern_lit(mrb, "ifnone"), ifnone);
- RHASH(hash)->flags &= ~(MRB_HASH_PROC_DEFAULT);
+ mrb_value ifnone = mrb_get_arg1(mrb);
+ hash_modify(mrb, hash);
+ mrb_iv_set(mrb, hash, MRB_SYM(ifnone), ifnone);
+ RHASH(hash)->flags &= ~MRB_HASH_PROC_DEFAULT;
+ if (!mrb_nil_p(ifnone)) {
+ RHASH(hash)->flags |= MRB_HASH_DEFAULT;
+ }
+ else {
+ RHASH(hash)->flags &= ~MRB_HASH_DEFAULT;
+ }
return ifnone;
}
@@ -442,7 +1423,6 @@ mrb_hash_set_default(mrb_state *mrb, mrb_value hash)
* a #=> [nil, nil, 4]
*/
-
static mrb_value
mrb_hash_default_proc(mrb_state *mrb, mrb_value hash)
{
@@ -468,12 +1448,18 @@ mrb_hash_default_proc(mrb_state *mrb, mrb_value hash)
static mrb_value
mrb_hash_set_default_proc(mrb_state *mrb, mrb_value hash)
{
- mrb_value ifnone;
+ mrb_value ifnone = mrb_get_arg1(mrb);
- mrb_get_args(mrb, "o", &ifnone);
- mrb_hash_modify(mrb, hash);
- mrb_iv_set(mrb, hash, mrb_intern_lit(mrb, "ifnone"), ifnone);
- RHASH(hash)->flags |= MRB_HASH_PROC_DEFAULT;
+ hash_modify(mrb, hash);
+ mrb_iv_set(mrb, hash, MRB_SYM(ifnone), ifnone);
+ if (!mrb_nil_p(ifnone)) {
+ RHASH(hash)->flags |= MRB_HASH_PROC_DEFAULT;
+ RHASH(hash)->flags |= MRB_HASH_DEFAULT;
+ }
+ else {
+ RHASH(hash)->flags &= ~MRB_HASH_DEFAULT;
+ RHASH(hash)->flags &= ~MRB_HASH_PROC_DEFAULT;
+ }
return ifnone;
}
@@ -481,53 +1467,21 @@ mrb_hash_set_default_proc(mrb_state *mrb, mrb_value hash)
MRB_API mrb_value
mrb_hash_delete_key(mrb_state *mrb, mrb_value hash, mrb_value key)
{
- khash_t(ht) *h = RHASH_TBL(hash);
- khiter_t k;
- mrb_value delVal;
- mrb_int n;
-
- if (h) {
- k = kh_get(ht, mrb, h, key);
- if (k != kh_end(h)) {
- delVal = kh_value(h, k).v;
- n = kh_value(h, k).n;
- kh_del(ht, mrb, h, k);
- for (k = kh_begin(h); k != kh_end(h); k++) {
- if (!kh_exist(h, k)) continue;
- if (kh_value(h, k).n > n) kh_value(h, k).n--;
- }
- return delVal;
- }
+ mrb_value del_val;
+
+ hash_modify(mrb, hash);
+ if (h_delete(mrb, mrb_hash_ptr(hash), key, &del_val)) {
+ return del_val;
}
/* not found */
return mrb_nil_value();
}
-/* 15.2.13.4.8 */
-/*
- * call-seq:
- * hsh.delete(key) -> value
- * hsh.delete(key) {| key | block } -> value
- *
- * Deletes and returns a key-value pair from <i>hsh</i> whose key is
- * equal to <i>key</i>. If the key is not found, returns the
- * <em>default value</em>. If the optional code block is given and the
- * key is not found, pass in the key and return the result of
- * <i>block</i>.
- *
- * h = { "a" => 100, "b" => 200 }
- * h.delete("a") #=> 100
- * h.delete("z") #=> nil
- * h.delete("z") { |el| "#{el} not found" } #=> "z not found"
- *
- */
static mrb_value
mrb_hash_delete(mrb_state *mrb, mrb_value self)
{
- mrb_value key;
-
- mrb_get_args(mrb, "o", &key);
+ mrb_value key = mrb_get_arg1(mrb);
return mrb_hash_delete_key(mrb, self, key);
}
@@ -540,37 +1494,26 @@ mrb_hash_delete(mrb_state *mrb, mrb_value self)
* two-item array <code>[</code> <i>key, value</i> <code>]</code>, or
* the hash's default value if the hash is empty.
*
- * h = { 1 => "a", 2 => "b", 3 => "c" }
- * h.shift #=> [1, "a"]
- * h #=> {2=>"b", 3=>"c"}
+ * h = { 1 => "a", 2 => "b", 3 => "c" }
+ * h.shift #=> [1, "a"]
+ * h #=> {2=>"b", 3=>"c"}
*/
static mrb_value
mrb_hash_shift(mrb_state *mrb, mrb_value hash)
{
- khash_t(ht) *h = RHASH_TBL(hash);
- khiter_t k;
- mrb_value delKey, delVal;
-
- mrb_hash_modify(mrb, hash);
- if (h && kh_size(h) > 0) {
- for (k = kh_begin(h); k != kh_end(h); k++) {
- if (!kh_exist(h, k)) continue;
-
- delKey = kh_key(h, k);
- mrb_gc_protect(mrb, delKey);
- delVal = mrb_hash_delete_key(mrb, hash, delKey);
- mrb_gc_protect(mrb, delVal);
+ struct RHash *h = mrb_hash_ptr(hash);
- return mrb_assoc_new(mrb, delKey, delVal);
- }
- }
-
- if (MRB_RHASH_PROCDEFAULT_P(hash)) {
- return mrb_funcall(mrb, RHASH_PROCDEFAULT(hash), "call", 2, hash, mrb_nil_value());
+ hash_modify(mrb, hash);
+ if (h_size(h) == 0) {
+ return mrb_nil_value();
}
else {
- return RHASH_IFNONE(hash);
+ mrb_value del_key, del_val;
+ h_shift(mrb, h, &del_key, &del_val);
+ mrb_gc_protect(mrb, del_key);
+ mrb_gc_protect(mrb, del_val);
+ return mrb_assoc_new(mrb, del_key, del_val);
}
}
@@ -579,19 +1522,18 @@ mrb_hash_shift(mrb_state *mrb, mrb_value hash)
* call-seq:
* hsh.clear -> hsh
*
- * Removes all key-value pairs from <i>hsh</i>.
+ * Removes all key-value pairs from `hsh`.
*
- * h = { "a" => 100, "b" => 200 } #=> {"a"=>100, "b"=>200}
- * h.clear #=> {}
+ * h = { "a" => 100, "b" => 200 } #=> {"a"=>100, "b"=>200}
+ * h.clear #=> {}
*
*/
MRB_API mrb_value
mrb_hash_clear(mrb_state *mrb, mrb_value hash)
{
- khash_t(ht) *h = RHASH_TBL(hash);
-
- if (h) kh_clear(ht, mrb, h);
+ hash_modify(mrb, hash);
+ h_clear(mrb, mrb_hash_ptr(hash));
return hash;
}
@@ -608,10 +1550,10 @@ mrb_hash_clear(mrb_state *mrb, mrb_value hash)
* use as a key (a <code>String</code> passed as a key will be
* duplicated and frozen).
*
- * h = { "a" => 100, "b" => 200 }
- * h["a"] = 9
- * h["c"] = 4
- * h #=> {"a"=>9, "b"=>200, "c"=>4}
+ * h = { "a" => 100, "b" => 200 }
+ * h["a"] = 9
+ * h["c"] = 4
+ * h #=> {"a"=>9, "b"=>200, "c"=>4}
*
*/
static mrb_value
@@ -624,12 +1566,18 @@ mrb_hash_aset(mrb_state *mrb, mrb_value self)
return val;
}
+MRB_API mrb_int
+mrb_hash_size(mrb_state *mrb, mrb_value hash)
+{
+ return (mrb_int)h_size(mrb_hash_ptr(hash));
+}
+
/* 15.2.13.4.20 */
/* 15.2.13.4.25 */
/*
* call-seq:
- * hsh.length -> fixnum
- * hsh.size -> fixnum
+ * hsh.length -> integer
+ * hsh.size -> integer
*
* Returns the number of key-value pairs in the hash.
*
@@ -641,10 +1589,14 @@ mrb_hash_aset(mrb_state *mrb, mrb_value self)
static mrb_value
mrb_hash_size_m(mrb_state *mrb, mrb_value self)
{
- khash_t(ht) *h = RHASH_TBL(self);
+ mrb_int size = mrb_hash_size(mrb, self);
+ return mrb_int_value(mrb, size);
+}
- if (!h) return mrb_fixnum_value(0);
- return mrb_fixnum_value(kh_size(h));
+MRB_API mrb_bool
+mrb_hash_empty_p(mrb_state *mrb, mrb_value self)
+{
+ return h_size(mrb_hash_ptr(self)) == 0;
}
/* 15.2.13.4.12 */
@@ -657,27 +1609,10 @@ mrb_hash_size_m(mrb_state *mrb, mrb_value self)
* {}.empty? #=> true
*
*/
-MRB_API mrb_value
-mrb_hash_empty_p(mrb_state *mrb, mrb_value self)
-{
- khash_t(ht) *h = RHASH_TBL(self);
-
- if (h) return mrb_bool_value(kh_size(h) == 0);
- return mrb_true_value();
-}
-
-/* 15.2.13.4.29 (x)*/
-/*
- * call-seq:
- * hsh.to_hash => hsh
- *
- * Returns +self+.
- */
-
static mrb_value
-mrb_hash_to_hash(mrb_state *mrb, mrb_value hash)
+mrb_hash_empty_m(mrb_state *mrb, mrb_value self)
{
- return hash;
+ return mrb_bool_value(mrb_hash_empty_p(mrb, self));
}
/* 15.2.13.4.19 */
@@ -696,23 +1631,11 @@ mrb_hash_to_hash(mrb_state *mrb, mrb_value hash)
MRB_API mrb_value
mrb_hash_keys(mrb_state *mrb, mrb_value hash)
{
- khash_t(ht) *h = RHASH_TBL(hash);
- khiter_t k;
- mrb_value ary;
- mrb_value *p;
-
- if (!h || kh_size(h) == 0) return mrb_ary_new(mrb);
- ary = mrb_ary_new_capa(mrb, kh_size(h));
- mrb_ary_set(mrb, ary, kh_size(h)-1, mrb_nil_value());
- p = mrb_ary_ptr(ary)->ptr;
- for (k = kh_begin(h); k != kh_end(h); k++) {
- if (kh_exist(h, k)) {
- mrb_value kv = kh_key(h, k);
- mrb_hash_value hv = kh_value(h, k);
-
- p[hv.n] = kv;
- }
- }
+ struct RHash *h = mrb_hash_ptr(hash);
+ mrb_value ary = mrb_ary_new_capa(mrb, (mrb_int)h_size(h));
+ h_each(h, entry, {
+ mrb_ary_push(mrb, ary, entry->key);
+ });
return ary;
}
@@ -729,22 +1652,14 @@ mrb_hash_keys(mrb_state *mrb, mrb_value hash)
*
*/
-static mrb_value
+MRB_API mrb_value
mrb_hash_values(mrb_state *mrb, mrb_value hash)
{
- khash_t(ht) *h = RHASH_TBL(hash);
- khiter_t k;
- mrb_value ary;
-
- if (!h) return mrb_ary_new(mrb);
- ary = mrb_ary_new_capa(mrb, kh_size(h));
- for (k = kh_begin(h); k != kh_end(h); k++) {
- if (kh_exist(h, k)) {
- mrb_hash_value hv = kh_value(h, k);
-
- mrb_ary_set(mrb, ary, hv.n, hv.v);
- }
- }
+ struct RHash *h = mrb_hash_ptr(hash);
+ mrb_value ary = mrb_ary_new_capa(mrb, (mrb_int)h_size(h));
+ h_each(h, entry, {
+ mrb_ary_push(mrb, ary, entry->val);
+ });
return ary;
}
@@ -767,21 +1682,21 @@ mrb_hash_values(mrb_state *mrb, mrb_value hash)
*
*/
+MRB_API mrb_bool
+mrb_hash_key_p(mrb_state *mrb, mrb_value hash, mrb_value key)
+{
+ mrb_value val;
+ return h_get(mrb, mrb_hash_ptr(hash), key, &val);
+}
+
static mrb_value
mrb_hash_has_key(mrb_state *mrb, mrb_value hash)
{
- mrb_value key;
- khash_t(ht) *h;
- khiter_t k;
+ mrb_value key = mrb_get_arg1(mrb);
+ mrb_bool key_p;
- mrb_get_args(mrb, "o", &key);
-
- h = RHASH_TBL(hash);
- if (h) {
- k = kh_get(ht, mrb, h, key);
- return mrb_bool_value(k != kh_end(h));
- }
- return mrb_false_value();
+ key_p = mrb_hash_key_p(mrb, hash, key);
+ return mrb_bool_value(key_p);
}
/* 15.2.13.4.14 */
@@ -802,23 +1717,60 @@ mrb_hash_has_key(mrb_state *mrb, mrb_value hash)
static mrb_value
mrb_hash_has_value(mrb_state *mrb, mrb_value hash)
{
- mrb_value val;
- khash_t(ht) *h;
- khiter_t k;
-
- mrb_get_args(mrb, "o", &val);
- h = RHASH_TBL(hash);
+ mrb_value val = mrb_get_arg1(mrb);
+ struct RHash *h = mrb_hash_ptr(hash);
+ h_each(h, entry, {
+ h_check_modified(mrb, h, {
+ if (mrb_equal(mrb, val, entry->val)) return mrb_true_value();
+ });
+ });
+ return mrb_false_value();
+}
- if (h) {
- for (k = kh_begin(h); k != kh_end(h); k++) {
- if (!kh_exist(h, k)) continue;
+MRB_API void
+mrb_hash_merge(mrb_state *mrb, mrb_value hash1, mrb_value hash2)
+{
+ struct RHash *h1, *h2;
+
+ hash_modify(mrb, hash1);
+ mrb_ensure_hash_type(mrb, hash2);
+ h1 = mrb_hash_ptr(hash1);
+ h2 = mrb_hash_ptr(hash2);
+
+ if (h1 == h2) return;
+ if (h_size(h2) == 0) return;
+ h_each(h2, entry, {
+ h_check_modified(mrb, h2, {h_set(mrb, h1, entry->key, entry->val);});
+ mrb_field_write_barrier_value(mrb, (struct RBasic *)h1, entry->key);
+ mrb_field_write_barrier_value(mrb, (struct RBasic *)h1, entry->val);
+ });
+}
- if (mrb_equal(mrb, kh_value(h, k).v, val)) {
- return mrb_true_value();
- }
- }
- }
- return mrb_false_value();
+/*
+ * 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>.
+ *
+ * keys = (1..17).map{|n| [n]}
+ * k = keys[0]
+ * h = {}
+ * keys.each{|key| h[key] = key[0]}
+ * h #=> { [1]=>1, [2]=>2, ... [16]=>16, [17]=>17}
+ * h[k] #=> 1
+ * k[0] = keys.size + 1
+ * h #=> {[18]=>1, [2]=>2, ... [16]=>16, [17]=>17}
+ * h[k] #=> nil
+ * h.rehash
+ * h[k] #=> 1
+ */
+static mrb_value
+mrb_hash_rehash(mrb_state *mrb, mrb_value self)
+{
+ h_rehash(mrb, mrb_hash_ptr(self));
+ return self;
}
void
@@ -826,32 +1778,32 @@ mrb_init_hash(mrb_state *mrb)
{
struct RClass *h;
- h = mrb->hash_class = mrb_define_class(mrb, "Hash", mrb->object_class); /* 15.2.13 */
+ mrb->hash_class = h = mrb_define_class(mrb, "Hash", mrb->object_class); /* 15.2.13 */
MRB_SET_INSTANCE_TT(h, MRB_TT_HASH);
mrb_define_method(mrb, h, "[]", mrb_hash_aget, MRB_ARGS_REQ(1)); /* 15.2.13.4.2 */
mrb_define_method(mrb, h, "[]=", mrb_hash_aset, MRB_ARGS_REQ(2)); /* 15.2.13.4.3 */
mrb_define_method(mrb, h, "clear", mrb_hash_clear, MRB_ARGS_NONE()); /* 15.2.13.4.4 */
- mrb_define_method(mrb, h, "default", mrb_hash_default, MRB_ARGS_ANY()); /* 15.2.13.4.5 */
+ mrb_define_method(mrb, h, "default", mrb_hash_default, MRB_ARGS_OPT(1)); /* 15.2.13.4.5 */
mrb_define_method(mrb, h, "default=", mrb_hash_set_default, MRB_ARGS_REQ(1)); /* 15.2.13.4.6 */
mrb_define_method(mrb, h, "default_proc", mrb_hash_default_proc,MRB_ARGS_NONE()); /* 15.2.13.4.7 */
mrb_define_method(mrb, h, "default_proc=", mrb_hash_set_default_proc,MRB_ARGS_REQ(1)); /* 15.2.13.4.7 */
mrb_define_method(mrb, h, "__delete", mrb_hash_delete, MRB_ARGS_REQ(1)); /* core of 15.2.13.4.8 */
- mrb_define_method(mrb, h, "empty?", mrb_hash_empty_p, MRB_ARGS_NONE()); /* 15.2.13.4.12 */
+ mrb_define_method(mrb, h, "empty?", mrb_hash_empty_m, MRB_ARGS_NONE()); /* 15.2.13.4.12 */
mrb_define_method(mrb, h, "has_key?", mrb_hash_has_key, MRB_ARGS_REQ(1)); /* 15.2.13.4.13 */
mrb_define_method(mrb, h, "has_value?", mrb_hash_has_value, MRB_ARGS_REQ(1)); /* 15.2.13.4.14 */
mrb_define_method(mrb, h, "include?", mrb_hash_has_key, MRB_ARGS_REQ(1)); /* 15.2.13.4.15 */
- mrb_define_method(mrb, h, "initialize", mrb_hash_init, MRB_ARGS_OPT(1)); /* 15.2.13.4.16 */
+ mrb_define_method(mrb, h, "initialize", mrb_hash_init, MRB_ARGS_OPT(1)|MRB_ARGS_BLOCK()); /* 15.2.13.4.16 */
+ mrb_define_method(mrb, h, "initialize_copy", mrb_hash_init_copy, MRB_ARGS_REQ(1)); /* 15.2.13.4.17 */
mrb_define_method(mrb, h, "key?", mrb_hash_has_key, MRB_ARGS_REQ(1)); /* 15.2.13.4.18 */
mrb_define_method(mrb, h, "keys", mrb_hash_keys, MRB_ARGS_NONE()); /* 15.2.13.4.19 */
mrb_define_method(mrb, h, "length", mrb_hash_size_m, MRB_ARGS_NONE()); /* 15.2.13.4.20 */
mrb_define_method(mrb, h, "member?", mrb_hash_has_key, MRB_ARGS_REQ(1)); /* 15.2.13.4.21 */
+ mrb_define_method(mrb, h, "replace", mrb_hash_init_copy, MRB_ARGS_REQ(1)); /* 15.2.13.4.23 */
mrb_define_method(mrb, h, "shift", mrb_hash_shift, MRB_ARGS_NONE()); /* 15.2.13.4.24 */
- mrb_define_method(mrb, h, "dup", mrb_hash_dup, MRB_ARGS_NONE());
mrb_define_method(mrb, h, "size", mrb_hash_size_m, MRB_ARGS_NONE()); /* 15.2.13.4.25 */
mrb_define_method(mrb, h, "store", mrb_hash_aset, MRB_ARGS_REQ(2)); /* 15.2.13.4.26 */
mrb_define_method(mrb, h, "value?", mrb_hash_has_value, MRB_ARGS_REQ(1)); /* 15.2.13.4.27 */
mrb_define_method(mrb, h, "values", mrb_hash_values, MRB_ARGS_NONE()); /* 15.2.13.4.28 */
-
- mrb_define_method(mrb, h, "to_hash", mrb_hash_to_hash, MRB_ARGS_NONE()); /* 15.2.13.4.29 (x)*/
+ mrb_define_method(mrb, h, "rehash", mrb_hash_rehash, MRB_ARGS_NONE());
}
diff --git a/src/init.c b/src/init.c
index 955d6e3a1..afd69975a 100644
--- a/src/init.c
+++ b/src/init.c
@@ -4,7 +4,7 @@
** See Copyright Notice in mruby.h
*/
-#include "mruby.h"
+#include <mruby.h>
void mrb_init_symtbl(mrb_state*);
void mrb_init_class(mrb_state*);
@@ -13,9 +13,9 @@ void mrb_init_kernel(mrb_state*);
void mrb_init_comparable(mrb_state*);
void mrb_init_enumerable(mrb_state*);
void mrb_init_symbol(mrb_state*);
+void mrb_init_string(mrb_state*);
void mrb_init_exception(mrb_state*);
void mrb_init_proc(mrb_state*);
-void mrb_init_string(mrb_state*);
void mrb_init_array(mrb_state*);
void mrb_init_hash(mrb_state*);
void mrb_init_numeric(mrb_state*);
@@ -38,9 +38,9 @@ mrb_init_core(mrb_state *mrb)
mrb_init_enumerable(mrb); DONE;
mrb_init_symbol(mrb); DONE;
+ mrb_init_string(mrb); DONE;
mrb_init_exception(mrb); DONE;
mrb_init_proc(mrb); DONE;
- mrb_init_string(mrb); DONE;
mrb_init_array(mrb); DONE;
mrb_init_hash(mrb); DONE;
mrb_init_numeric(mrb); DONE;
diff --git a/src/kernel.c b/src/kernel.c
index 22fe40218..615b68ac3 100644
--- a/src/kernel.c
+++ b/src/kernel.c
@@ -4,35 +4,37 @@
** See Copyright Notice in mruby.h
*/
-#include "mruby.h"
-#include "mruby/array.h"
-#include "mruby/class.h"
-#include "mruby/proc.h"
-#include "mruby/string.h"
-#include "mruby/variable.h"
-#include "mruby/error.h"
+#include <mruby.h>
+#include <mruby/array.h>
+#include <mruby/hash.h>
+#include <mruby/class.h>
+#include <mruby/proc.h>
+#include <mruby/string.h>
+#include <mruby/variable.h>
+#include <mruby/error.h>
+#include <mruby/istruct.h>
+#include <mruby/presym.h>
-typedef enum {
- NOEX_PUBLIC = 0x00,
- NOEX_NOSUPER = 0x01,
- NOEX_PRIVATE = 0x02,
- NOEX_PROTECTED = 0x04,
- NOEX_MASK = 0x06,
- NOEX_BASIC = 0x08,
- NOEX_UNDEF = NOEX_NOSUPER,
- NOEX_MODFUNC = 0x12,
- NOEX_SUPER = 0x20,
- NOEX_VCALL = 0x40,
- NOEX_RESPONDS = 0x80
-} mrb_method_flag_t;
+MRB_API mrb_bool
+mrb_func_basic_p(mrb_state *mrb, mrb_value obj, mrb_sym mid, mrb_func_t func)
+{
+ struct RClass *c = mrb_class(mrb, obj);
+ mrb_method_t m = mrb_method_search_vm(mrb, &c, mid);
+ const struct RProc *p;
+
+ if (MRB_METHOD_UNDEF_P(m)) return FALSE;
+ if (MRB_METHOD_FUNC_P(m))
+ return MRB_METHOD_FUNC(m) == func;
+ p = MRB_METHOD_PROC(m);
+ if (MRB_PROC_CFUNC_P(p) && (MRB_PROC_CFUNC(p) == func))
+ return TRUE;
+ return FALSE;
+}
static mrb_bool
mrb_obj_basic_to_s_p(mrb_state *mrb, mrb_value obj)
{
- struct RProc *me = mrb_method_search(mrb, mrb_class(mrb, obj), mrb_intern_lit(mrb, "to_s"));
- if (MRB_PROC_CFUNC_P(me) && (me->body.func == mrb_any_to_s))
- return TRUE;
- return FALSE;
+ return mrb_func_basic_p(mrb, obj, MRB_SYM(to_s), mrb_any_to_s);
}
/* 15.3.1.3.17 */
@@ -52,61 +54,12 @@ mrb_obj_basic_to_s_p(mrb_state *mrb, mrb_value obj)
MRB_API mrb_value
mrb_obj_inspect(mrb_state *mrb, mrb_value obj)
{
- if ((mrb_type(obj) == MRB_TT_OBJECT) && mrb_obj_basic_to_s_p(mrb, obj)) {
+ if (mrb_object_p(obj) && mrb_obj_basic_to_s_p(mrb, obj)) {
return mrb_obj_iv_inspect(mrb, mrb_obj_ptr(obj));
}
return mrb_any_to_s(mrb, obj);
}
-/* 15.3.1.3.1 */
-/* 15.3.1.3.10 */
-/* 15.3.1.3.11 */
-/*
- * call-seq:
- * obj == other -> true or false
- * obj.equal?(other) -> true or false
- * obj.eql?(other) -> true or false
- *
- * Equality---At the <code>Object</code> level, <code>==</code> returns
- * <code>true</code> only if <i>obj</i> and <i>other</i> are the
- * same object. Typically, this method is overridden in descendant
- * classes to provide class-specific meaning.
- *
- * Unlike <code>==</code>, the <code>equal?</code> method should never be
- * overridden by subclasses: it is used to determine object identity
- * (that is, <code>a.equal?(b)</code> iff <code>a</code> is the same
- * object as <code>b</code>).
- *
- * The <code>eql?</code> method returns <code>true</code> if
- * <i>obj</i> and <i>anObject</i> have the same value. Used by
- * <code>Hash</code> to test members for equality. For objects of
- * class <code>Object</code>, <code>eql?</code> is synonymous with
- * <code>==</code>. Subclasses normally continue this tradition, but
- * there are exceptions. <code>Numeric</code> types, for example,
- * perform type conversion across <code>==</code>, but not across
- * <code>eql?</code>, so:
- *
- * 1 == 1.0 #=> true
- * 1.eql? 1.0 #=> false
- */
-static mrb_value
-mrb_obj_equal_m(mrb_state *mrb, mrb_value self)
-{
- mrb_value arg;
-
- mrb_get_args(mrb, "o", &arg);
- return mrb_bool_value(mrb_obj_equal(mrb, self, arg));
-}
-
-static mrb_value
-mrb_obj_not_equal_m(mrb_state *mrb, mrb_value self)
-{
- mrb_value arg;
-
- mrb_get_args(mrb, "o", &arg);
- return mrb_bool_value(!mrb_equal(mrb, self, arg));
-}
-
/* 15.3.1.3.2 */
/*
* call-seq:
@@ -119,9 +72,8 @@ mrb_obj_not_equal_m(mrb_state *mrb, mrb_value self)
static mrb_value
mrb_equal_m(mrb_state *mrb, mrb_value self)
{
- mrb_value arg;
+ mrb_value arg = mrb_get_arg1(mrb);
- mrb_get_args(mrb, "o", &arg);
return mrb_bool_value(mrb_equal(mrb, self, arg));
}
@@ -142,10 +94,22 @@ mrb_equal_m(mrb_state *mrb, mrb_value self)
* <code>:name</code> notation, which returns the symbol id of
* <code>name</code>. Replaces the deprecated <code>Object#id</code>.
*/
-static mrb_value
+mrb_value
mrb_obj_id_m(mrb_state *mrb, mrb_value self)
{
- return mrb_fixnum_value(mrb_obj_id(self));
+ return mrb_int_value(mrb, mrb_obj_id(self));
+}
+
+static int
+env_bidx(struct REnv *e)
+{
+ int bidx;
+
+ /* use saved block arg position */
+ bidx = MRB_ENV_BIDX(e);
+ /* bidx may be useless (e.g. define_method) */
+ if (bidx >= MRB_ENV_LEN(e)) return -1;
+ return bidx;
}
/* 15.3.1.2.2 */
@@ -175,30 +139,65 @@ mrb_obj_id_m(mrb_state *mrb, mrb_value self)
static mrb_value
mrb_f_block_given_p_m(mrb_state *mrb, mrb_value self)
{
- mrb_callinfo *ci = mrb->c->ci;
+ mrb_callinfo *ci = &mrb->c->ci[-1];
+ mrb_callinfo *cibase = mrb->c->cibase;
mrb_value *bp;
- mrb_bool given_p;
+ int bidx;
+ struct REnv *e = NULL;
+ const struct RProc *p;
- bp = ci->stackent + 1;
- ci--;
- if (ci <= mrb->c->cibase) {
- given_p = FALSE;
+ if (ci <= cibase) {
+ /* toplevel does not have block */
+ return mrb_false_value();
+ }
+ p = ci->proc;
+ /* search method/class/module proc */
+ while (p) {
+ if (MRB_PROC_SCOPE_P(p)) break;
+ e = MRB_PROC_ENV(p);
+ p = p->upper;
+ }
+ if (p == NULL) return mrb_false_value();
+ if (e) {
+ bidx = env_bidx(e);
+ if (bidx < 0) return mrb_false_value();
+ bp = &e->stack[bidx];
+ goto block_given;
+ }
+ /* search ci corresponding to proc */
+ while (cibase < ci) {
+ if (ci->proc == p) break;
+ ci--;
+ }
+ if (ci == cibase) {
+ /* proc is closure */
+ if (!MRB_PROC_ENV_P(p)) return mrb_false_value();
+ e = MRB_PROC_ENV(p);
+ bidx = env_bidx(e);
+ if (bidx < 0) return mrb_false_value();
+ bp = &e->stack[bidx];
+ }
+ else if ((e = mrb_vm_ci_env(ci)) != NULL) {
+ /* top-level does not have block slot (always false) */
+ if (e->stack == mrb->c->stbase) return mrb_false_value();
+ bidx = env_bidx(e);
+ /* bidx may be useless (e.g. define_method) */
+ if (bidx < 0) return mrb_false_value();
+ bp = &e->stack[bidx];
}
else {
- /* block_given? called within block; check upper scope */
- if (ci->proc->env && ci->proc->env->stack) {
- given_p = !(ci->proc->env->stack == mrb->c->stbase ||
- mrb_nil_p(ci->proc->env->stack[1]));
+ bp = ci->stack+1;
+ if (ci->argc >= 0) {
+ bp += ci->argc;
}
else {
- if (ci->argc > 0) {
- bp += ci->argc;
- }
- given_p = !mrb_nil_p(*bp);
+ bp++;
}
}
-
- return mrb_bool_value(given_p);
+ block_given:
+ if (mrb_nil_p(*bp))
+ return mrb_false_value();
+ return mrb_true_value();
}
/* 15.3.1.3.7 */
@@ -210,7 +209,7 @@ mrb_f_block_given_p_m(mrb_state *mrb, mrb_value self)
* called with an explicit receiver, as <code>class</code> is also a
* reserved word in Ruby.
*
- * 1.class #=> Fixnum
+ * 1.class #=> Integer
* self.class #=> Object
*/
static mrb_value
@@ -219,164 +218,20 @@ mrb_obj_class_m(mrb_state *mrb, mrb_value self)
return mrb_obj_value(mrb_obj_class(mrb, self));
}
-static struct RClass*
-mrb_singleton_class_clone(mrb_state *mrb, mrb_value obj)
-{
- struct RClass *klass = mrb_basic_ptr(obj)->c;
-
- if (klass->tt != MRB_TT_SCLASS)
- return klass;
- else {
- /* 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 */
- 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));
- mrb_obj_iv_set(mrb, (struct RObject*)clone, mrb_intern_lit(mrb, "__attached__"), obj);
- }
- if (klass->mt) {
- clone->mt = kh_copy(mt, mrb, klass->mt);
- }
- else {
- clone->mt = kh_init(mt, mrb);
- }
- clone->tt = MRB_TT_SCLASS;
- return clone;
- }
-}
-
-static void
-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);
- dc->mt = kh_copy(mt, mrb, sc->mt);
- dc->super = sc->super;
-}
-
-static void
-init_copy(mrb_state *mrb, mrb_value dest, mrb_value obj)
-{
- switch (mrb_type(obj)) {
- case MRB_TT_CLASS:
- case MRB_TT_MODULE:
- copy_class(mrb, dest, obj);
- /* fall through */
- case MRB_TT_OBJECT:
- case MRB_TT_SCLASS:
- case MRB_TT_HASH:
- case MRB_TT_DATA:
- case MRB_TT_EXCEPTION:
- mrb_iv_copy(mrb, dest, obj);
- break;
-
- default:
- break;
- }
- mrb_funcall(mrb, dest, "initialize_copy", 1, obj);
-}
-
-/* 15.3.1.3.8 */
-/*
- * call-seq:
- * obj.clone -> an_object
- *
- * Produces a shallow copy of <i>obj</i>---the instance variables of
- * <i>obj</i> are copied, but not the objects they reference. Copies
- * the frozen state of <i>obj</i>. See also the discussion
- * under <code>Object#dup</code>.
- *
- * class Klass
- * attr_accessor :str
- * end
- * s1 = Klass.new #=> #<Klass:0x401b3a38>
- * s1.str = "Hello" #=> "Hello"
- * s2 = s1.clone #=> #<Klass:0x401b3998 @str="Hello">
- * s2.str[1,4] = "i" #=> "i"
- * s1.inspect #=> "#<Klass:0x401b3a38 @str=\"Hi\">"
- * s2.inspect #=> "#<Klass:0x401b3998 @str=\"Hi\">"
- *
- * This method may have class-specific behavior. If so, that
- * behavior will be documented under the #+initialize_copy+ method of
- * the class.
- *
- * Some Class(True False Nil Symbol Fixnum Float) Object cannot clone.
- */
-MRB_API mrb_value
-mrb_obj_clone(mrb_state *mrb, mrb_value self)
-{
- struct RObject *p;
- mrb_value clone;
-
- if (mrb_immediate_p(self)) {
- mrb_raisef(mrb, E_TYPE_ERROR, "can't clone %S", self);
- }
- p = (struct RObject*)mrb_obj_alloc(mrb, mrb_type(self), mrb_obj_class(mrb, self));
- p->c = mrb_singleton_class_clone(mrb, self);
- clone = mrb_obj_value(p);
- init_copy(mrb, clone, self);
-
- return clone;
-}
-
-/* 15.3.1.3.9 */
-/*
- * call-seq:
- * obj.dup -> an_object
- *
- * Produces a shallow copy of <i>obj</i>---the instance variables of
- * <i>obj</i> are copied, but not the objects they reference.
- * <code>dup</code> copies the frozen state of <i>obj</i>. See also
- * the discussion under <code>Object#clone</code>. In general,
- * <code>clone</code> and <code>dup</code> may have different semantics
- * in descendant classes. While <code>clone</code> is used to duplicate
- * an object, including its internal state, <code>dup</code> typically
- * uses the class of the descendant object to create the new instance.
- *
- * This method may have class-specific behavior. If so, that
- * behavior will be documented under the #+initialize_copy+ method of
- * the class.
- */
-
-MRB_API mrb_value
-mrb_obj_dup(mrb_state *mrb, mrb_value obj)
-{
- struct RBasic *p;
- mrb_value dup;
-
- if (mrb_immediate_p(obj)) {
- mrb_raisef(mrb, E_TYPE_ERROR, "can't dup %S", obj);
- }
- p = mrb_obj_alloc(mrb, mrb_type(obj), mrb_obj_class(mrb, obj));
- dup = mrb_obj_value(p);
- init_copy(mrb, dup, obj);
-
- return dup;
-}
-
static mrb_value
-mrb_obj_extend(mrb_state *mrb, mrb_int argc, mrb_value *argv, mrb_value obj)
+mrb_obj_extend(mrb_state *mrb, mrb_int argc, const mrb_value *argv, mrb_value obj)
{
mrb_int i;
if (argc == 0) {
- mrb_raise(mrb, E_ARGUMENT_ERROR, "wrong number of arguments (at least 1)");
+ mrb_argnum_error(mrb, argc, 1, -1);
}
for (i = 0; i < argc; i++) {
mrb_check_type(mrb, argv[i], MRB_TT_MODULE);
}
while (argc--) {
- mrb_funcall(mrb, argv[argc], "extend_object", 1, obj);
- mrb_funcall(mrb, argv[argc], "extended", 1, obj);
+ mrb_funcall_id(mrb, argv[argc], MRB_SYM(extend_object), 1, obj);
+ mrb_funcall_id(mrb, argv[argc], MRB_SYM(extended), 1, obj);
}
return obj;
}
@@ -409,37 +264,55 @@ mrb_obj_extend(mrb_state *mrb, mrb_int argc, mrb_value *argv, mrb_value obj)
static mrb_value
mrb_obj_extend_m(mrb_state *mrb, mrb_value self)
{
- mrb_value *argv;
+ const mrb_value *argv;
mrb_int argc;
mrb_get_args(mrb, "*", &argv, &argc);
return mrb_obj_extend(mrb, argc, argv, self);
}
+MRB_API mrb_value
+mrb_obj_freeze(mrb_state *mrb, mrb_value self)
+{
+ if (!mrb_immediate_p(self)) {
+ struct RBasic *b = mrb_basic_ptr(self);
+ if (!mrb_frozen_p(b)) {
+ MRB_SET_FROZEN_FLAG(b);
+ if (b->c->tt == MRB_TT_SCLASS) MRB_SET_FROZEN_FLAG(b->c);
+ }
+ }
+ return self;
+}
+
+static mrb_value
+mrb_obj_frozen(mrb_state *mrb, mrb_value self)
+{
+ return mrb_bool_value(mrb_immediate_p(self) || mrb_frozen_p(mrb_basic_ptr(self)));
+}
+
/* 15.3.1.3.15 */
/*
* call-seq:
* obj.hash -> fixnum
*
- * Generates a <code>Fixnum</code> hash value for this object. This
+ * Generates a <code>Integer</code> hash value for this object. This
* function must have the property that <code>a.eql?(b)</code> implies
* <code>a.hash == b.hash</code>. The hash value is used by class
* <code>Hash</code>. Any hash value that exceeds the capacity of a
- * <code>Fixnum</code> will be truncated before being used.
+ * <code>Integer</code> will be truncated before being used.
*/
-MRB_API mrb_value
+static mrb_value
mrb_obj_hash(mrb_state *mrb, mrb_value self)
{
- return mrb_fixnum_value(mrb_obj_id(self));
+ return mrb_int_value(mrb, mrb_obj_id(self));
}
/* 15.3.1.3.16 */
-static mrb_value
+mrb_value
mrb_obj_init_copy(mrb_state *mrb, mrb_value self)
{
- mrb_value orig;
+ mrb_value orig = mrb_get_arg1(mrb);
- mrb_get_args(mrb, "o", &orig);
if (mrb_obj_equal(mrb, self, orig)) return self;
if ((mrb_type(self) != mrb_type(orig)) || (mrb_obj_class(mrb, self) != mrb_obj_class(mrb, orig))) {
mrb_raise(mrb, E_TYPE_ERROR, "initialize_copy should take same class object");
@@ -447,10 +320,6 @@ mrb_obj_init_copy(mrb_state *mrb, mrb_value self)
return self;
}
-
-/* implementation of instance_eval */
-mrb_value mrb_obj_instance_eval(mrb_state*, mrb_value);
-
MRB_API mrb_bool
mrb_obj_is_instance_of(mrb_state *mrb, mrb_value obj, struct RClass* c)
{
@@ -469,101 +338,11 @@ mrb_obj_is_instance_of(mrb_state *mrb, mrb_value obj, struct RClass* c)
static mrb_value
obj_is_instance_of(mrb_state *mrb, mrb_value self)
{
- mrb_value arg;
-
- mrb_get_args(mrb, "C", &arg);
-
- return mrb_bool_value(mrb_obj_is_instance_of(mrb, self, mrb_class_ptr(arg)));
-}
-
-/* 15.3.1.3.20 */
-/*
- * call-seq:
- * obj.instance_variable_defined?(symbol) -> true or false
- *
- * Returns <code>true</code> if the given instance variable is
- * defined in <i>obj</i>.
- *
- * class Fred
- * def initialize(p1, p2)
- * @a, @b = p1, p2
- * end
- * end
- * fred = Fred.new('cat', 99)
- * fred.instance_variable_defined?(:@a) #=> true
- * fred.instance_variable_defined?("@b") #=> true
- * fred.instance_variable_defined?("@c") #=> false
- */
-static mrb_value
-mrb_obj_ivar_defined(mrb_state *mrb, mrb_value self)
-{
- mrb_sym sym;
+ struct RClass *c;
- mrb_get_args(mrb, "n", &sym);
- mrb_iv_check(mrb, sym);
- return mrb_bool_value(mrb_iv_defined(mrb, self, sym));
-}
+ mrb_get_args(mrb, "c", &c);
-/* 15.3.1.3.21 */
-/*
- * call-seq:
- * obj.instance_variable_get(symbol) -> obj
- *
- * Returns the value of the given instance variable, or nil if the
- * instance variable is not set. The <code>@</code> part of the
- * variable name should be included for regular instance
- * variables. Throws a <code>NameError</code> exception if the
- * supplied symbol is not valid as an instance variable name.
- *
- * class Fred
- * def initialize(p1, p2)
- * @a, @b = p1, p2
- * end
- * end
- * fred = Fred.new('cat', 99)
- * fred.instance_variable_get(:@a) #=> "cat"
- * fred.instance_variable_get("@b") #=> 99
- */
-static mrb_value
-mrb_obj_ivar_get(mrb_state *mrb, mrb_value self)
-{
- mrb_sym iv_name;
-
- mrb_get_args(mrb, "n", &iv_name);
- mrb_iv_check(mrb, iv_name);
- return mrb_iv_get(mrb, self, iv_name);
-}
-
-/* 15.3.1.3.22 */
-/*
- * call-seq:
- * obj.instance_variable_set(symbol, obj) -> obj
- *
- * Sets the instance variable names by <i>symbol</i> to
- * <i>object</i>, thereby frustrating the efforts of the class's
- * author to attempt to provide proper encapsulation. The variable
- * did not have to exist prior to this call.
- *
- * class Fred
- * def initialize(p1, p2)
- * @a, @b = p1, p2
- * end
- * end
- * fred = Fred.new('cat', 99)
- * fred.instance_variable_set(:@a, 'dog') #=> "dog"
- * fred.instance_variable_set(:@c, 'cat') #=> "cat"
- * fred.inspect #=> "#<Fred:0x401b3da8 @a=\"dog\", @b=99, @c=\"cat\">"
- */
-static mrb_value
-mrb_obj_ivar_set(mrb_state *mrb, mrb_value self)
-{
- mrb_sym iv_name;
- mrb_value val;
-
- mrb_get_args(mrb, "no", &iv_name, &val);
- mrb_iv_check(mrb, iv_name);
- mrb_iv_set(mrb, self, iv_name, val);
- return val;
+ return mrb_bool_value(mrb_obj_is_instance_of(mrb, self, c));
}
/* 15.3.1.3.24 */
@@ -596,126 +375,11 @@ mrb_obj_ivar_set(mrb_state *mrb, mrb_value self)
static mrb_value
mrb_obj_is_kind_of_m(mrb_state *mrb, mrb_value self)
{
- mrb_value arg;
-
- mrb_get_args(mrb, "C", &arg);
-
- return mrb_bool_value(mrb_obj_is_kind_of(mrb, self, mrb_class_ptr(arg)));
-}
-
-KHASH_DECLARE(st, mrb_sym, char, FALSE)
-KHASH_DEFINE(st, mrb_sym, char, FALSE, kh_int_hash_func, kh_int_hash_equal)
-
-static void
-method_entry_loop(mrb_state *mrb, struct RClass* klass, khash_t(st)* set)
-{
- khint_t i;
-
- khash_t(mt) *h = klass->mt;
- if (!h) return;
- for (i=0;i<kh_end(h);i++) {
- if (kh_exist(h, i) && kh_value(h, i)) {
- kh_put(st, mrb, set, kh_key(h, i));
- }
- }
-}
-
-mrb_value
-mrb_class_instance_method_list(mrb_state *mrb, mrb_bool recur, struct RClass* klass, int obj)
-{
- khint_t i;
- mrb_value ary;
- struct RClass* oldklass;
- khash_t(st)* set = kh_init(st, mrb);
-
- oldklass = 0;
- while (klass && (klass != oldklass)) {
- method_entry_loop(mrb, klass, set);
- if ((klass->tt == MRB_TT_ICLASS) ||
- (klass->tt == MRB_TT_SCLASS)) {
- }
- else {
- if (!recur) break;
- }
- oldklass = klass;
- klass = klass->super;
- }
-
- ary = mrb_ary_new(mrb);
- for (i=0;i<kh_end(set);i++) {
- if (kh_exist(set, i)) {
- mrb_ary_push(mrb, ary, mrb_symbol_value(kh_key(set, i)));
- }
- }
- kh_destroy(st, mrb, set);
-
- return ary;
-}
-
-static mrb_value
-mrb_obj_singleton_methods(mrb_state *mrb, mrb_bool recur, mrb_value obj)
-{
- khint_t i;
- mrb_value ary;
- struct RClass* klass;
- khash_t(st)* set = kh_init(st, mrb);
+ struct RClass *c;
- klass = mrb_class(mrb, obj);
+ mrb_get_args(mrb, "c", &c);
- if (klass && (klass->tt == MRB_TT_SCLASS)) {
- method_entry_loop(mrb, klass, set);
- klass = klass->super;
- }
- if (recur) {
- while (klass && ((klass->tt == MRB_TT_SCLASS) || (klass->tt == MRB_TT_ICLASS))) {
- method_entry_loop(mrb, klass, set);
- klass = klass->super;
- }
- }
-
- ary = mrb_ary_new(mrb);
- for (i=0;i<kh_end(set);i++) {
- if (kh_exist(set, i)) {
- mrb_ary_push(mrb, ary, mrb_symbol_value(kh_key(set, i)));
- }
- }
- kh_destroy(st, mrb, set);
-
- return ary;
-}
-
-static mrb_value
-mrb_obj_methods(mrb_state *mrb, mrb_bool recur, mrb_value obj, mrb_method_flag_t flag)
-{
- if (recur)
- return mrb_class_instance_method_list(mrb, recur, mrb_class(mrb, obj), 0);
- return mrb_obj_singleton_methods(mrb, recur, obj);
-}
-/* 15.3.1.3.31 */
-/*
- * call-seq:
- * obj.methods -> array
- *
- * Returns a list of the names of methods publicly accessible in
- * <i>obj</i>. This will include all the methods accessible in
- * <i>obj</i>'s ancestors.
- *
- * class Klass
- * def kMethod()
- * end
- * end
- * k = Klass.new
- * k.methods[0..9] #=> [:kMethod, :respond_to?, :nil?, :is_a?,
- * # :class, :instance_variable_set,
- * # :methods, :extend, :__send__, :instance_eval]
- * k.methods.length #=> 42
- */
-static mrb_value
-mrb_obj_methods_m(mrb_state *mrb, mrb_value self)
-{
- mrb_bool recur = TRUE;
- mrb_get_args(mrb, "|b", &recur);
- return mrb_obj_methods(mrb, recur, self, (mrb_method_flag_t)0); /* everything but private */
+ return mrb_bool_value(mrb_obj_is_kind_of(mrb, self, c));
}
/* 15.3.1.3.32 */
@@ -732,57 +396,6 @@ mrb_false(mrb_state *mrb, mrb_value self)
return mrb_false_value();
}
-/* 15.3.1.3.36 */
-/*
- * call-seq:
- * obj.private_methods(all=true) -> array
- *
- * Returns the list of private methods accessible to <i>obj</i>. If
- * the <i>all</i> parameter is set to <code>false</code>, only those methods
- * in the receiver will be listed.
- */
-static mrb_value
-mrb_obj_private_methods(mrb_state *mrb, mrb_value self)
-{
- mrb_bool recur = TRUE;
- mrb_get_args(mrb, "|b", &recur);
- return mrb_obj_methods(mrb, recur, self, NOEX_PRIVATE); /* private attribute not define */
-}
-
-/* 15.3.1.3.37 */
-/*
- * call-seq:
- * obj.protected_methods(all=true) -> array
- *
- * Returns the list of protected methods accessible to <i>obj</i>. If
- * the <i>all</i> parameter is set to <code>false</code>, only those methods
- * in the receiver will be listed.
- */
-static mrb_value
-mrb_obj_protected_methods(mrb_state *mrb, mrb_value self)
-{
- mrb_bool recur = TRUE;
- mrb_get_args(mrb, "|b", &recur);
- return mrb_obj_methods(mrb, recur, self, NOEX_PROTECTED); /* protected attribute not define */
-}
-
-/* 15.3.1.3.38 */
-/*
- * call-seq:
- * obj.public_methods(all=true) -> array
- *
- * Returns the list of public methods accessible to <i>obj</i>. If
- * the <i>all</i> parameter is set to <code>false</code>, only those methods
- * in the receiver will be listed.
- */
-static mrb_value
-mrb_obj_public_methods(mrb_state *mrb, mrb_value self)
-{
- mrb_bool recur = TRUE;
- mrb_get_args(mrb, "|b", &recur);
- return mrb_obj_methods(mrb, recur, self, NOEX_PUBLIC); /* public attribute not define */
-}
-
/* 15.3.1.2.12 */
/* 15.3.1.3.40 */
/*
@@ -808,24 +421,23 @@ MRB_API mrb_value
mrb_f_raise(mrb_state *mrb, mrb_value self)
{
mrb_value a[2], exc;
- int argc;
-
+ mrb_int argc;
argc = mrb_get_args(mrb, "|oo", &a[0], &a[1]);
+ mrb->c->ci->mid = 0;
switch (argc) {
case 0:
mrb_raise(mrb, E_RUNTIME_ERROR, "");
break;
case 1:
- a[1] = mrb_check_string_type(mrb, a[0]);
- if (!mrb_nil_p(a[1])) {
+ if (mrb_string_p(a[0])) {
+ a[1] = a[0];
argc = 2;
a[0] = mrb_obj_value(E_RUNTIME_ERROR);
}
/* fall through */
default:
exc = mrb_make_exception(mrb, argc, a);
- mrb_obj_iv_set(mrb, mrb_obj_ptr(exc), mrb_intern_lit(mrb, "lastpc"), mrb_cptr_value(mrb, mrb->c->ci->pc));
mrb_exc_raise(mrb, exc);
break;
}
@@ -861,19 +473,73 @@ mrb_obj_remove_instance_variable(mrb_state *mrb, mrb_value self)
mrb_value val;
mrb_get_args(mrb, "n", &sym);
- mrb_iv_check(mrb, sym);
+ mrb_iv_name_sym_check(mrb, sym);
val = mrb_iv_remove(mrb, self, sym);
if (mrb_undef_p(val)) {
- mrb_name_error(mrb, sym, "instance variable %S not defined", mrb_sym2str(mrb, sym));
+ mrb_name_error(mrb, sym, "instance variable %n not defined", sym);
}
return val;
}
+void
+mrb_method_missing(mrb_state *mrb, mrb_sym name, mrb_value self, mrb_value args)
+{
+ mrb_no_method_error(mrb, name, args, "undefined method '%n'", name);
+}
+
+/* 15.3.1.3.30 */
+/*
+ * call-seq:
+ * obj.method_missing(symbol [, *args] ) -> result
+ *
+ * Invoked by Ruby when <i>obj</i> is sent a message it cannot handle.
+ * <i>symbol</i> is the symbol for the method called, and <i>args</i>
+ * are any arguments that were passed to it. By default, the interpreter
+ * raises an error when this method is called. However, it is possible
+ * to override the method to provide more dynamic behavior.
+ * If it is decided that a particular method should not be handled, then
+ * <i>super</i> should be called, so that ancestors can pick up the
+ * missing method.
+ * The example below creates
+ * a class <code>Roman</code>, which responds to methods with names
+ * consisting of roman numerals, returning the corresponding integer
+ * values.
+ *
+ * class Roman
+ * def romanToInt(str)
+ * # ...
+ * end
+ * def method_missing(methId)
+ * str = methId.to_s
+ * romanToInt(str)
+ * end
+ * end
+ *
+ * r = Roman.new
+ * r.iv #=> 4
+ * r.xxiii #=> 23
+ * r.mm #=> 2000
+ */
+mrb_value
+mrb_obj_missing(mrb_state *mrb, mrb_value mod)
+{
+ mrb_sym name;
+ const mrb_value *a;
+ mrb_int alen;
+
+ mrb->c->ci->mid = 0;
+ mrb_get_args(mrb, "n*!", &name, &a, &alen);
+ mrb_method_missing(mrb, name, mod, mrb_ary_new_from_values(mrb, alen, a));
+ /* not reached */
+ return mrb_nil_value();
+}
+
static inline mrb_bool
basic_obj_respond_to(mrb_state *mrb, mrb_value obj, mrb_sym id, int pub)
{
return mrb_respond_to(mrb, obj, id);
}
+
/* 15.3.1.3.43 */
/*
* call-seq:
@@ -893,117 +559,50 @@ basic_obj_respond_to(mrb_state *mrb, mrb_value obj, mrb_sym id, int pub)
static mrb_value
obj_respond_to(mrb_state *mrb, mrb_value self)
{
- mrb_value mid;
mrb_sym id, rtm_id;
- mrb_bool priv = FALSE, respond_to_p = TRUE;
-
- mrb_get_args(mrb, "o|b", &mid, &priv);
-
- if (mrb_symbol_p(mid)) {
- id = mrb_symbol(mid);
- }
- else {
- mrb_value tmp;
- if (!mrb_string_p(mid)) {
- tmp = mrb_check_string_type(mrb, mid);
- if (mrb_nil_p(tmp)) {
- tmp = mrb_inspect(mrb, mid);
- mrb_raisef(mrb, E_TYPE_ERROR, "%S is not a symbol", tmp);
- }
- }
- tmp = mrb_check_intern_str(mrb, mid);
- if (mrb_nil_p(tmp)) {
- respond_to_p = FALSE;
- }
- else {
- id = mrb_symbol(tmp);
- }
- }
-
- if (respond_to_p) {
- respond_to_p = basic_obj_respond_to(mrb, self, id, !priv);
- }
+ mrb_bool priv = FALSE, respond_to_p;
+ mrb_get_args(mrb, "n|b", &id, &priv);
+ respond_to_p = basic_obj_respond_to(mrb, self, id, !priv);
if (!respond_to_p) {
- rtm_id = mrb_intern_lit(mrb, "respond_to_missing?");
+ rtm_id = MRB_SYM_Q(respond_to_missing);
if (basic_obj_respond_to(mrb, self, rtm_id, !priv)) {
- mrb_value args[2];
- args[0] = mid;
+ mrb_value args[2], v;
+ args[0] = mrb_symbol_value(id);
args[1] = mrb_bool_value(priv);
- return mrb_funcall_argv(mrb, self, rtm_id, 2, args);
+ v = mrb_funcall_argv(mrb, self, rtm_id, 2, args);
+ return mrb_bool_value(mrb_bool(v));
}
}
return mrb_bool_value(respond_to_p);
}
-/* 15.3.1.3.45 */
-/*
- * call-seq:
- * obj.singleton_methods(all=true) -> array
- *
- * Returns an array of the names of singleton methods for <i>obj</i>.
- * If the optional <i>all</i> parameter is true, the list will include
- * methods in modules included in <i>obj</i>.
- * Only public and protected singleton methods are returned.
- *
- * module Other
- * def three() end
- * end
- *
- * class Single
- * def Single.four() end
- * end
- *
- * a = Single.new
- *
- * def a.one()
- * end
- *
- * class << a
- * include Other
- * def two()
- * end
- * end
- *
- * Single.singleton_methods #=> [:four]
- * a.singleton_methods(false) #=> [:two, :one]
- * a.singleton_methods #=> [:two, :one, :three]
- */
-static mrb_value
-mrb_obj_singleton_methods_m(mrb_state *mrb, mrb_value self)
-{
- mrb_bool recur = TRUE;
- mrb_get_args(mrb, "|b", &recur);
- return mrb_obj_singleton_methods(mrb, recur, self);
-}
-
-static mrb_value
-mod_define_singleton_method(mrb_state *mrb, mrb_value self)
-{
- struct RProc *p;
- mrb_sym mid;
- mrb_value blk = mrb_nil_value();
-
- mrb_get_args(mrb, "n&", &mid, &blk);
- if (mrb_nil_p(blk)) {
- mrb_raise(mrb, E_ARGUMENT_ERROR, "no block given");
- }
- p = (struct RProc*)mrb_obj_alloc(mrb, MRB_TT_PROC, mrb->proc_class);
- mrb_proc_copy(p, mrb_proc_ptr(blk));
- p->flags |= MRB_PROC_STRICT;
- mrb_define_method_raw(mrb, mrb_class_ptr(mrb_singleton_class(mrb, self)), mid, p);
- return mrb_symbol_value(mid);
-}
-
static mrb_value
mrb_obj_ceqq(mrb_state *mrb, mrb_value self)
{
- mrb_value v;
+ mrb_value v = mrb_get_arg1(mrb);
mrb_int i, len;
- mrb_sym eqq = mrb_intern_lit(mrb, "===");
- mrb_value ary = mrb_ary_splat(mrb, self);
+ mrb_sym eqq = MRB_OPSYM(eqq);
+ mrb_value ary;
- mrb_get_args(mrb, "o", &v);
+ if (mrb_array_p(self)) {
+ ary = self;
+ }
+ else if (mrb_nil_p(self)) {
+ return mrb_false_value();
+ }
+ else if (!mrb_respond_to(mrb, self, mrb_intern_lit(mrb, "to_a"))) {
+ mrb_value c = mrb_funcall_argv(mrb, self, eqq, 1, &v);
+ if (mrb_test(c)) return mrb_true_value();
+ return mrb_false_value();
+ }
+ else {
+ ary = mrb_funcall(mrb, self, "to_a", 0);
+ if (mrb_nil_p(ary)) {
+ return mrb_funcall_argv(mrb, self, eqq, 1, &v);
+ }
+ mrb_ensure_array_type(mrb, ary);
+ }
len = RARRAY_LEN(ary);
for (i=0; i<len; i++) {
mrb_value c = mrb_funcall_argv(mrb, mrb_ary_entry(ary, i), eqq, 1, &v);
@@ -1012,107 +611,47 @@ mrb_obj_ceqq(mrb_state *mrb, mrb_value self)
return mrb_false_value();
}
-static mrb_value
-mrb_local_variables(mrb_state *mrb, mrb_value self)
-{
- mrb_value ret;
- struct RProc *proc;
- struct mrb_irep *irep;
- size_t i;
-
- proc = mrb->c->ci[-1].proc;
-
- if (MRB_PROC_CFUNC_P(proc)) {
- return mrb_ary_new(mrb);
- }
-
- irep = proc->body.irep;
- if (!irep->lv) {
- return mrb_ary_new(mrb);
- }
- ret = mrb_ary_new_capa(mrb, irep->nlocals - 1);
- for (i = 0; i + 1 < irep->nlocals; ++i) {
- if (irep->lv[i].name) {
- mrb_ary_push(mrb, ret, mrb_symbol_value(irep->lv[i].name));
- }
- }
- if (proc->env) {
- struct REnv *e = proc->env;
-
- while (e) {
- if (!MRB_PROC_CFUNC_P(mrb->c->cibase[e->cioff].proc)) {
- irep = mrb->c->cibase[e->cioff].proc->body.irep;
- if (irep->lv) {
- for (i = 0; i + 1 < irep->nlocals; ++i) {
- if (irep->lv[i].name) {
- mrb_ary_push(mrb, ret, mrb_symbol_value(irep->lv[i].name));
- }
- }
- }
- }
- e = (struct REnv*)e->c;
- }
- }
-
- return ret;
-}
+mrb_value mrb_obj_equal_m(mrb_state *mrb, mrb_value);
void
mrb_init_kernel(mrb_state *mrb)
{
struct RClass *krn;
- krn = mrb->kernel_module = mrb_define_module(mrb, "Kernel"); /* 15.3.1 */
+ mrb->kernel_module = krn = mrb_define_module(mrb, "Kernel"); /* 15.3.1 */
mrb_define_class_method(mrb, krn, "block_given?", mrb_f_block_given_p_m, MRB_ARGS_NONE()); /* 15.3.1.2.2 */
- mrb_define_class_method(mrb, krn, "global_variables", mrb_f_global_variables, MRB_ARGS_NONE()); /* 15.3.1.2.4 */
mrb_define_class_method(mrb, krn, "iterator?", mrb_f_block_given_p_m, MRB_ARGS_NONE()); /* 15.3.1.2.5 */
- mrb_define_class_method(mrb, krn, "local_variables", mrb_local_variables, MRB_ARGS_NONE()); /* 15.3.1.2.7 */
; /* 15.3.1.2.11 */
mrb_define_class_method(mrb, krn, "raise", mrb_f_raise, MRB_ARGS_OPT(2)); /* 15.3.1.2.12 */
- mrb_define_method(mrb, krn, "singleton_class", mrb_singleton_class, MRB_ARGS_NONE());
- mrb_define_method(mrb, krn, "==", mrb_obj_equal_m, MRB_ARGS_REQ(1)); /* 15.3.1.3.1 */
- mrb_define_method(mrb, krn, "!=", mrb_obj_not_equal_m, MRB_ARGS_REQ(1));
mrb_define_method(mrb, krn, "===", mrb_equal_m, MRB_ARGS_REQ(1)); /* 15.3.1.3.2 */
- mrb_define_method(mrb, krn, "__id__", mrb_obj_id_m, MRB_ARGS_NONE()); /* 15.3.1.3.3 */
- mrb_define_method(mrb, krn, "__send__", mrb_f_send, MRB_ARGS_ANY()); /* 15.3.1.3.4 */
mrb_define_method(mrb, krn, "block_given?", mrb_f_block_given_p_m, MRB_ARGS_NONE()); /* 15.3.1.3.6 */
mrb_define_method(mrb, krn, "class", mrb_obj_class_m, MRB_ARGS_NONE()); /* 15.3.1.3.7 */
mrb_define_method(mrb, krn, "clone", mrb_obj_clone, MRB_ARGS_NONE()); /* 15.3.1.3.8 */
mrb_define_method(mrb, krn, "dup", mrb_obj_dup, MRB_ARGS_NONE()); /* 15.3.1.3.9 */
mrb_define_method(mrb, krn, "eql?", mrb_obj_equal_m, MRB_ARGS_REQ(1)); /* 15.3.1.3.10 */
- mrb_define_method(mrb, krn, "equal?", mrb_obj_equal_m, MRB_ARGS_REQ(1)); /* 15.3.1.3.11 */
mrb_define_method(mrb, krn, "extend", mrb_obj_extend_m, MRB_ARGS_ANY()); /* 15.3.1.3.13 */
- mrb_define_method(mrb, krn, "global_variables", mrb_f_global_variables, MRB_ARGS_NONE()); /* 15.3.1.3.14 */
+ mrb_define_method(mrb, krn, "freeze", mrb_obj_freeze, MRB_ARGS_NONE());
+ mrb_define_method(mrb, krn, "frozen?", mrb_obj_frozen, MRB_ARGS_NONE());
mrb_define_method(mrb, krn, "hash", mrb_obj_hash, MRB_ARGS_NONE()); /* 15.3.1.3.15 */
mrb_define_method(mrb, krn, "initialize_copy", mrb_obj_init_copy, MRB_ARGS_REQ(1)); /* 15.3.1.3.16 */
mrb_define_method(mrb, krn, "inspect", mrb_obj_inspect, MRB_ARGS_NONE()); /* 15.3.1.3.17 */
- mrb_define_method(mrb, krn, "instance_eval", mrb_obj_instance_eval, MRB_ARGS_ANY()); /* 15.3.1.3.18 */
mrb_define_method(mrb, krn, "instance_of?", obj_is_instance_of, MRB_ARGS_REQ(1)); /* 15.3.1.3.19 */
- mrb_define_method(mrb, krn, "instance_variable_defined?", mrb_obj_ivar_defined, MRB_ARGS_REQ(1)); /* 15.3.1.3.20 */
- mrb_define_method(mrb, krn, "instance_variable_get", mrb_obj_ivar_get, MRB_ARGS_REQ(1)); /* 15.3.1.3.21 */
- mrb_define_method(mrb, krn, "instance_variable_set", mrb_obj_ivar_set, MRB_ARGS_REQ(2)); /* 15.3.1.3.22 */
- mrb_define_method(mrb, krn, "instance_variables", mrb_obj_instance_variables, MRB_ARGS_NONE()); /* 15.3.1.3.23 */
+
mrb_define_method(mrb, krn, "is_a?", mrb_obj_is_kind_of_m, MRB_ARGS_REQ(1)); /* 15.3.1.3.24 */
mrb_define_method(mrb, krn, "iterator?", mrb_f_block_given_p_m, MRB_ARGS_NONE()); /* 15.3.1.3.25 */
mrb_define_method(mrb, krn, "kind_of?", mrb_obj_is_kind_of_m, MRB_ARGS_REQ(1)); /* 15.3.1.3.26 */
- mrb_define_method(mrb, krn, "local_variables", mrb_local_variables, MRB_ARGS_NONE()); /* 15.3.1.3.28 */
- mrb_define_method(mrb, krn, "methods", mrb_obj_methods_m, MRB_ARGS_OPT(1)); /* 15.3.1.3.31 */
+ mrb_define_method(mrb, krn, "method_missing", mrb_obj_missing, MRB_ARGS_ANY()); /* 15.3.1.3.30 */
mrb_define_method(mrb, krn, "nil?", mrb_false, MRB_ARGS_NONE()); /* 15.3.1.3.32 */
mrb_define_method(mrb, krn, "object_id", mrb_obj_id_m, MRB_ARGS_NONE()); /* 15.3.1.3.33 */
- mrb_define_method(mrb, krn, "private_methods", mrb_obj_private_methods, MRB_ARGS_OPT(1)); /* 15.3.1.3.36 */
- mrb_define_method(mrb, krn, "protected_methods", mrb_obj_protected_methods, MRB_ARGS_OPT(1)); /* 15.3.1.3.37 */
- mrb_define_method(mrb, krn, "public_methods", mrb_obj_public_methods, MRB_ARGS_OPT(1)); /* 15.3.1.3.38 */
mrb_define_method(mrb, krn, "raise", mrb_f_raise, MRB_ARGS_ANY()); /* 15.3.1.3.40 */
mrb_define_method(mrb, krn, "remove_instance_variable", mrb_obj_remove_instance_variable,MRB_ARGS_REQ(1)); /* 15.3.1.3.41 */
- mrb_define_method(mrb, krn, "respond_to?", obj_respond_to, MRB_ARGS_ANY()); /* 15.3.1.3.43 */
- mrb_define_method(mrb, krn, "send", mrb_f_send, MRB_ARGS_ANY()); /* 15.3.1.3.44 */
- mrb_define_method(mrb, krn, "singleton_methods", mrb_obj_singleton_methods_m, MRB_ARGS_OPT(1)); /* 15.3.1.3.45 */
- mrb_define_method(mrb, krn, "define_singleton_method", mod_define_singleton_method, MRB_ARGS_ANY());
+ mrb_define_method(mrb, krn, "respond_to?", obj_respond_to, MRB_ARGS_ARG(1,1)); /* 15.3.1.3.43 */
mrb_define_method(mrb, krn, "to_s", mrb_any_to_s, MRB_ARGS_NONE()); /* 15.3.1.3.46 */
mrb_define_method(mrb, krn, "__case_eqq", mrb_obj_ceqq, MRB_ARGS_REQ(1)); /* internal */
+ mrb_define_method(mrb, krn, "__to_int", mrb_to_int, MRB_ARGS_NONE()); /* internal */
+ mrb_define_method(mrb, krn, "__to_str", mrb_to_str, MRB_ARGS_NONE()); /* internal */
mrb_include_module(mrb, mrb->object_class, mrb->kernel_module);
- mrb_alias_method(mrb, mrb->module_class, mrb_intern_lit(mrb, "dup"), mrb_intern_lit(mrb, "clone"));
}
diff --git a/src/keywords b/src/keywords
deleted file mode 100644
index 9cb86608c..000000000
--- a/src/keywords
+++ /dev/null
@@ -1,50 +0,0 @@
-%{
-struct kwtable {const char *name; int id[2]; enum mrb_lex_state_enum state;};
-const struct kwtable *mrb_reserved_word(const char *, unsigned int);
-static const struct kwtable *reserved_word(const char *, unsigned int);
-#define mrb_reserved_word(str, len) reserved_word(str, len)
-%}
-
-struct kwtable;
-%%
-__ENCODING__, {keyword__ENCODING__, keyword__ENCODING__}, EXPR_END
-__FILE__, {keyword__FILE__, keyword__FILE__}, EXPR_END
-__LINE__, {keyword__LINE__, keyword__LINE__}, EXPR_END
-BEGIN, {keyword_BEGIN, keyword_BEGIN}, EXPR_END
-END, {keyword_END, keyword_END}, EXPR_END
-alias, {keyword_alias, keyword_alias}, EXPR_FNAME
-and, {keyword_and, keyword_and}, EXPR_VALUE
-begin, {keyword_begin, keyword_begin}, EXPR_BEG
-break, {keyword_break, keyword_break}, EXPR_MID
-case, {keyword_case, keyword_case}, EXPR_VALUE
-class, {keyword_class, keyword_class}, EXPR_CLASS
-def, {keyword_def, keyword_def}, EXPR_FNAME
-do, {keyword_do, keyword_do}, EXPR_BEG
-else, {keyword_else, keyword_else}, EXPR_BEG
-elsif, {keyword_elsif, keyword_elsif}, EXPR_VALUE
-end, {keyword_end, keyword_end}, EXPR_END
-ensure, {keyword_ensure, keyword_ensure}, EXPR_BEG
-false, {keyword_false, keyword_false}, EXPR_END
-for, {keyword_for, keyword_for}, EXPR_VALUE
-if, {keyword_if, modifier_if}, EXPR_VALUE
-in, {keyword_in, keyword_in}, EXPR_VALUE
-module, {keyword_module, keyword_module}, EXPR_VALUE
-next, {keyword_next, keyword_next}, EXPR_MID
-nil, {keyword_nil, keyword_nil}, EXPR_END
-not, {keyword_not, keyword_not}, EXPR_ARG
-or, {keyword_or, keyword_or}, EXPR_VALUE
-redo, {keyword_redo, keyword_redo}, EXPR_END
-rescue, {keyword_rescue, modifier_rescue}, EXPR_MID
-retry, {keyword_retry, keyword_retry}, EXPR_END
-return, {keyword_return, keyword_return}, EXPR_MID
-self, {keyword_self, keyword_self}, EXPR_END
-super, {keyword_super, keyword_super}, EXPR_ARG
-then, {keyword_then, keyword_then}, EXPR_BEG
-true, {keyword_true, keyword_true}, EXPR_END
-undef, {keyword_undef, keyword_undef}, EXPR_FNAME
-unless, {keyword_unless, modifier_unless}, EXPR_VALUE
-until, {keyword_until, modifier_until}, EXPR_VALUE
-when, {keyword_when, keyword_when}, EXPR_VALUE
-while, {keyword_while, modifier_while}, EXPR_VALUE
-yield, {keyword_yield, keyword_yield}, EXPR_ARG
-%%
diff --git a/src/lex.def b/src/lex.def
deleted file mode 100644
index ea456a843..000000000
--- a/src/lex.def
+++ /dev/null
@@ -1,212 +0,0 @@
-/* ANSI-C code produced by gperf version 3.0.3 */
-/* Command-line: gperf -L ANSI-C -C -p -j1 -i 1 -g -o -t -N mrb_reserved_word -k'1,3,$' src/keywords */
-
-#if !((' ' == 32) && ('!' == 33) && ('"' == 34) && ('#' == 35) \
- && ('%' == 37) && ('&' == 38) && ('\'' == 39) && ('(' == 40) \
- && (')' == 41) && ('*' == 42) && ('+' == 43) && (',' == 44) \
- && ('-' == 45) && ('.' == 46) && ('/' == 47) && ('0' == 48) \
- && ('1' == 49) && ('2' == 50) && ('3' == 51) && ('4' == 52) \
- && ('5' == 53) && ('6' == 54) && ('7' == 55) && ('8' == 56) \
- && ('9' == 57) && (':' == 58) && (';' == 59) && ('<' == 60) \
- && ('=' == 61) && ('>' == 62) && ('?' == 63) && ('A' == 65) \
- && ('B' == 66) && ('C' == 67) && ('D' == 68) && ('E' == 69) \
- && ('F' == 70) && ('G' == 71) && ('H' == 72) && ('I' == 73) \
- && ('J' == 74) && ('K' == 75) && ('L' == 76) && ('M' == 77) \
- && ('N' == 78) && ('O' == 79) && ('P' == 80) && ('Q' == 81) \
- && ('R' == 82) && ('S' == 83) && ('T' == 84) && ('U' == 85) \
- && ('V' == 86) && ('W' == 87) && ('X' == 88) && ('Y' == 89) \
- && ('Z' == 90) && ('[' == 91) && ('\\' == 92) && (']' == 93) \
- && ('^' == 94) && ('_' == 95) && ('a' == 97) && ('b' == 98) \
- && ('c' == 99) && ('d' == 100) && ('e' == 101) && ('f' == 102) \
- && ('g' == 103) && ('h' == 104) && ('i' == 105) && ('j' == 106) \
- && ('k' == 107) && ('l' == 108) && ('m' == 109) && ('n' == 110) \
- && ('o' == 111) && ('p' == 112) && ('q' == 113) && ('r' == 114) \
- && ('s' == 115) && ('t' == 116) && ('u' == 117) && ('v' == 118) \
- && ('w' == 119) && ('x' == 120) && ('y' == 121) && ('z' == 122) \
- && ('{' == 123) && ('|' == 124) && ('}' == 125) && ('~' == 126))
-/* The character set is not based on ISO-646. */
-#error "gperf generated tables don't work with this execution character set. Please report a bug to <[email protected]>."
-#endif
-
-#line 1 "src/keywords"
-
-struct kwtable {const char *name; int id[2]; enum mrb_lex_state_enum state;};
-const struct kwtable *mrb_reserved_word(const char *, unsigned int);
-static const struct kwtable *reserved_word(const char *, unsigned int);
-#define mrb_reserved_word(str, len) reserved_word(str, len)
-#line 8 "src/keywords"
-struct kwtable;
-
-#define TOTAL_KEYWORDS 40
-#define MIN_WORD_LENGTH 2
-#define MAX_WORD_LENGTH 12
-#define MIN_HASH_VALUE 8
-#define MAX_HASH_VALUE 50
-/* maximum key range = 43, duplicates = 0 */
-
-#ifdef __GNUC__
-__inline
-#else
-#ifdef __cplusplus
-inline
-#endif
-#endif
-static unsigned int
-hash (register const char *str, register unsigned int len)
-{
- static const unsigned char asso_values[] =
- {
- 51, 51, 51, 51, 51, 51, 51, 51, 51, 51,
- 51, 51, 51, 51, 51, 51, 51, 51, 51, 51,
- 51, 51, 51, 51, 51, 51, 51, 51, 51, 51,
- 51, 51, 51, 51, 51, 51, 51, 51, 51, 51,
- 51, 51, 51, 51, 51, 51, 51, 51, 51, 51,
- 51, 51, 51, 51, 51, 51, 51, 51, 51, 51,
- 51, 51, 51, 51, 51, 51, 14, 51, 16, 8,
- 11, 13, 51, 51, 51, 51, 10, 51, 13, 51,
- 51, 51, 51, 51, 51, 51, 51, 51, 51, 51,
- 51, 51, 51, 51, 51, 11, 51, 13, 1, 26,
- 4, 1, 8, 28, 51, 23, 51, 1, 1, 27,
- 5, 19, 21, 51, 8, 3, 3, 11, 51, 21,
- 24, 16, 51, 51, 51, 51, 51, 51, 51, 51,
- 51, 51, 51, 51, 51, 51, 51, 51, 51, 51,
- 51, 51, 51, 51, 51, 51, 51, 51, 51, 51,
- 51, 51, 51, 51, 51, 51, 51, 51, 51, 51,
- 51, 51, 51, 51, 51, 51, 51, 51, 51, 51,
- 51, 51, 51, 51, 51, 51, 51, 51, 51, 51,
- 51, 51, 51, 51, 51, 51, 51, 51, 51, 51,
- 51, 51, 51, 51, 51, 51, 51, 51, 51, 51,
- 51, 51, 51, 51, 51, 51, 51, 51, 51, 51,
- 51, 51, 51, 51, 51, 51, 51, 51, 51, 51,
- 51, 51, 51, 51, 51, 51, 51, 51, 51, 51,
- 51, 51, 51, 51, 51, 51, 51, 51, 51, 51,
- 51, 51, 51, 51, 51, 51, 51, 51, 51, 51,
- 51, 51, 51, 51, 51, 51
- };
- register int hval = len;
-
- switch (hval)
- {
- default:
- hval += asso_values[(unsigned char)str[2]];
- /*FALLTHROUGH*/
- case 2:
- case 1:
- hval += asso_values[(unsigned char)str[0]];
- break;
- }
- return hval + asso_values[(unsigned char)str[len - 1]];
-}
-
-#ifdef __GNUC__
-__inline
-#ifdef __GNUC_STDC_INLINE__
-__attribute__ ((__gnu_inline__))
-#endif
-#endif
-const struct kwtable *
-mrb_reserved_word (register const char *str, register unsigned int len)
-{
- static const struct kwtable wordlist[] =
- {
- {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
-#line 18 "src/keywords"
- {"break", {keyword_break, keyword_break}, EXPR_MID},
-#line 23 "src/keywords"
- {"else", {keyword_else, keyword_else}, EXPR_BEG},
-#line 33 "src/keywords"
- {"nil", {keyword_nil, keyword_nil}, EXPR_END},
-#line 26 "src/keywords"
- {"ensure", {keyword_ensure, keyword_ensure}, EXPR_BEG},
-#line 25 "src/keywords"
- {"end", {keyword_end, keyword_end}, EXPR_END},
-#line 42 "src/keywords"
- {"then", {keyword_then, keyword_then}, EXPR_BEG},
-#line 34 "src/keywords"
- {"not", {keyword_not, keyword_not}, EXPR_ARG},
-#line 27 "src/keywords"
- {"false", {keyword_false, keyword_false}, EXPR_END},
-#line 40 "src/keywords"
- {"self", {keyword_self, keyword_self}, EXPR_END},
-#line 24 "src/keywords"
- {"elsif", {keyword_elsif, keyword_elsif}, EXPR_VALUE},
-#line 37 "src/keywords"
- {"rescue", {keyword_rescue, modifier_rescue}, EXPR_MID},
-#line 43 "src/keywords"
- {"true", {keyword_true, keyword_true}, EXPR_END},
-#line 46 "src/keywords"
- {"until", {keyword_until, modifier_until}, EXPR_VALUE},
-#line 45 "src/keywords"
- {"unless", {keyword_unless, modifier_unless}, EXPR_VALUE},
-#line 39 "src/keywords"
- {"return", {keyword_return, keyword_return}, EXPR_MID},
-#line 21 "src/keywords"
- {"def", {keyword_def, keyword_def}, EXPR_FNAME},
-#line 16 "src/keywords"
- {"and", {keyword_and, keyword_and}, EXPR_VALUE},
-#line 22 "src/keywords"
- {"do", {keyword_do, keyword_do}, EXPR_BEG},
-#line 49 "src/keywords"
- {"yield", {keyword_yield, keyword_yield}, EXPR_ARG},
-#line 28 "src/keywords"
- {"for", {keyword_for, keyword_for}, EXPR_VALUE},
-#line 44 "src/keywords"
- {"undef", {keyword_undef, keyword_undef}, EXPR_FNAME},
-#line 35 "src/keywords"
- {"or", {keyword_or, keyword_or}, EXPR_VALUE},
-#line 30 "src/keywords"
- {"in", {keyword_in, keyword_in}, EXPR_VALUE},
-#line 47 "src/keywords"
- {"when", {keyword_when, keyword_when}, EXPR_VALUE},
-#line 38 "src/keywords"
- {"retry", {keyword_retry, keyword_retry}, EXPR_END},
-#line 29 "src/keywords"
- {"if", {keyword_if, modifier_if}, EXPR_VALUE},
-#line 19 "src/keywords"
- {"case", {keyword_case, keyword_case}, EXPR_VALUE},
-#line 36 "src/keywords"
- {"redo", {keyword_redo, keyword_redo}, EXPR_END},
-#line 32 "src/keywords"
- {"next", {keyword_next, keyword_next}, EXPR_MID},
-#line 41 "src/keywords"
- {"super", {keyword_super, keyword_super}, EXPR_ARG},
-#line 31 "src/keywords"
- {"module", {keyword_module, keyword_module}, EXPR_VALUE},
-#line 17 "src/keywords"
- {"begin", {keyword_begin, keyword_begin}, EXPR_BEG},
-#line 12 "src/keywords"
- {"__LINE__", {keyword__LINE__, keyword__LINE__}, EXPR_END},
-#line 11 "src/keywords"
- {"__FILE__", {keyword__FILE__, keyword__FILE__}, EXPR_END},
-#line 10 "src/keywords"
- {"__ENCODING__", {keyword__ENCODING__, keyword__ENCODING__}, EXPR_END},
-#line 14 "src/keywords"
- {"END", {keyword_END, keyword_END}, EXPR_END},
-#line 15 "src/keywords"
- {"alias", {keyword_alias, keyword_alias}, EXPR_FNAME},
-#line 13 "src/keywords"
- {"BEGIN", {keyword_BEGIN, keyword_BEGIN}, EXPR_END},
- {""},
-#line 20 "src/keywords"
- {"class", {keyword_class, keyword_class}, EXPR_CLASS},
- {""}, {""},
-#line 48 "src/keywords"
- {"while", {keyword_while, modifier_while}, EXPR_VALUE}
- };
-
- if (len <= MAX_WORD_LENGTH && len >= MIN_WORD_LENGTH)
- {
- register int key = hash (str, len);
-
- if (key <= MAX_HASH_VALUE && key >= 0)
- {
- register const char *s = wordlist[key].name;
-
- if (*str == *s && !strcmp (str + 1, s + 1))
- return &wordlist[key];
- }
- }
- return 0;
-}
-#line 50 "src/keywords"
-
diff --git a/src/load.c b/src/load.c
index 7640d8fda..2e637aa19 100644
--- a/src/load.c
+++ b/src/load.c
@@ -7,55 +7,74 @@
#include <limits.h>
#include <stdlib.h>
#include <string.h>
-#include "mruby/dump.h"
-#include "mruby/irep.h"
-#include "mruby/proc.h"
-#include "mruby/string.h"
-#include "mruby/debug.h"
-#include "mruby/error.h"
-
-#define FLAG_BYTEORDER_NATIVE 2
-#define FLAG_BYTEORDER_NONATIVE 0
-#define FLAG_SRC_MALLOC 1
-#define FLAG_SRC_STATIC 0
+#include <math.h>
+#include <mruby/dump.h>
+#include <mruby/irep.h>
+#include <mruby/proc.h>
+#include <mruby/string.h>
+#include <mruby/debug.h>
+#include <mruby/error.h>
+#include <mruby/data.h>
+#include <mruby/endian.h>
#if SIZE_MAX < UINT32_MAX
-# define SIZE_ERROR_MUL(x, y) ((x) > SIZE_MAX / (y))
-# define SIZE_ERROR(x) ((x) > SIZE_MAX)
-#else
-# define SIZE_ERROR_MUL(x, y) (0)
-# define SIZE_ERROR(x) (0)
+# error size_t must be at least 32 bits wide
#endif
-#if UINT32_MAX > SIZE_MAX
-# error This code cannot be built on your environment.
-#endif
+#define FLAG_SRC_MALLOC 1
+#define FLAG_SRC_STATIC 0
-static size_t
-skip_padding(const uint8_t *buf)
-{
- const size_t align = MRB_DUMP_ALIGNMENT;
- return -(intptr_t)buf & (align-1);
-}
+#define SIZE_ERROR_MUL(nmemb, size) ((size_t)(nmemb) > SIZE_MAX / (size))
+
+#define DEFINE_READ_IREP_FUNC(funcdecl, basecall) \
+ funcdecl \
+ { \
+ int ai = mrb_gc_arena_save(mrb); \
+ struct RProc *proc = basecall; \
+ struct mrb_irep *irep = (mrb_irep*)(proc ? proc->body.irep : NULL); \
+ if (irep) proc->body.irep = NULL; \
+ mrb_gc_arena_restore(mrb, ai); \
+ return irep; \
+ }
-static size_t
-offset_crc_body(void)
+#ifndef MRB_NO_FLOAT
+static double
+str_to_double(mrb_state *mrb, const char *p)
{
- struct rite_binary_header header;
- return ((uint8_t *)header.binary_crc - (uint8_t *)&header) + sizeof(header.binary_crc);
+ /* dump IEEE754 little endian binary */
+ union {
+ char s[sizeof(double)];
+ double f;
+ } u;
+
+ if (littleendian) {
+ memcpy(u.s, p, sizeof(double));
+ }
+ else {
+ size_t i;
+ for (i=0; i<sizeof(double); i++) {
+ u.s[i] = p[sizeof(double)-i-1];
+ }
+ }
+ return u.f;
}
+#endif
-static mrb_irep*
-read_irep_record_1(mrb_state *mrb, const uint8_t *bin, size_t *len, uint8_t flags)
+static mrb_bool
+read_irep_record_1(mrb_state *mrb, const uint8_t *bin, size_t *len, uint8_t flags, mrb_irep **irepp)
{
- size_t i;
+ int i;
const uint8_t *src = bin;
ptrdiff_t diff;
uint16_t tt, pool_data_len, snl;
- size_t plen;
+ int plen;
+ mrb_pool_value *pool;
+ mrb_sym *syms;
int ai = mrb_gc_arena_save(mrb);
mrb_irep *irep = mrb_add_irep(mrb);
+ *irepp = irep;
+
/* skip record size */
src += sizeof(uint32_t);
@@ -68,226 +87,213 @@ read_irep_record_1(mrb_state *mrb, const uint8_t *bin, size_t *len, uint8_t flag
src += sizeof(uint16_t);
/* number of child irep */
- irep->rlen = (size_t)bin_to_uint16(src);
+ irep->rlen = (uint8_t)bin_to_uint16(src);
src += sizeof(uint16_t);
/* Binary Data Section */
- /* ISEQ BLOCK */
- irep->ilen = (size_t)bin_to_uint32(src);
- src += sizeof(uint32_t);
- src += skip_padding(src);
+ /* ISEQ BLOCK (and CATCH HANDLER TABLE BLOCK) */
+ irep->clen = bin_to_uint16(src); /* number of catch handler */
+ src += sizeof(uint16_t);
+ irep->ilen = bin_to_uint16(src);
+ src += sizeof(uint16_t);
if (irep->ilen > 0) {
- if (SIZE_ERROR_MUL(sizeof(mrb_code), irep->ilen)) {
- return NULL;
+ size_t data_len = sizeof(mrb_code) * irep->ilen +
+ sizeof(struct mrb_irep_catch_handler) * irep->clen;
+ mrb_static_assert1(sizeof(struct mrb_irep_catch_handler) == 13);
+ if (SIZE_ERROR_MUL(irep->ilen, sizeof(mrb_code))) {
+ return FALSE;
}
- if ((flags & FLAG_SRC_MALLOC) == 0 &&
- (flags & FLAG_BYTEORDER_NATIVE)) {
+ if ((flags & FLAG_SRC_MALLOC) == 0) {
irep->iseq = (mrb_code*)src;
- src += sizeof(uint32_t) * irep->ilen;
irep->flags |= MRB_ISEQ_NO_FREE;
}
else {
- irep->iseq = (mrb_code *)mrb_malloc(mrb, sizeof(mrb_code) * irep->ilen);
- if (flags & FLAG_BYTEORDER_NATIVE) {
- memcpy(irep->iseq, src, sizeof(uint32_t) * irep->ilen);
- src += sizeof(uint32_t) * irep->ilen;
- }
- else {
- for (i = 0; i < irep->ilen; i++) {
- irep->iseq[i] = (mrb_code)bin_to_uint32(src); /* iseq */
- src += sizeof(uint32_t);
- }
- }
+ void *buf = mrb_malloc(mrb, data_len);
+ irep->iseq = (mrb_code *)buf;
+ memcpy(buf, src, data_len);
}
+ src += data_len;
}
/* POOL BLOCK */
- plen = (size_t)bin_to_uint32(src); /* number of pool */
- src += sizeof(uint32_t);
+ plen = bin_to_uint16(src); /* number of pool */
+ src += sizeof(uint16_t);
if (plen > 0) {
- if (SIZE_ERROR_MUL(sizeof(mrb_value), plen)) {
- return NULL;
+ if (SIZE_ERROR_MUL(plen, sizeof(mrb_value))) {
+ return FALSE;
}
- irep->pool = (mrb_value*)mrb_malloc(mrb, sizeof(mrb_value) * plen);
+ irep->pool = pool = (mrb_pool_value*)mrb_calloc(mrb, sizeof(mrb_pool_value), plen);
for (i = 0; i < plen; i++) {
- mrb_value s;
+ mrb_bool st = (flags & FLAG_SRC_MALLOC)==0;
tt = *src++; /* pool TT */
- pool_data_len = bin_to_uint16(src); /* pool data length */
- src += sizeof(uint16_t);
- if (flags & FLAG_SRC_MALLOC) {
- s = mrb_str_new(mrb, (char *)src, pool_data_len);
- }
- else {
- s = mrb_str_new_static(mrb, (char *)src, pool_data_len);
- }
- src += pool_data_len;
switch (tt) { /* pool data */
- case IREP_TT_FIXNUM:
- irep->pool[i] = mrb_str_to_inum(mrb, s, 10, FALSE);
+ case IREP_TT_INT32:
+ {
+ mrb_int v = (int32_t)bin_to_uint32(src);
+ src += sizeof(uint32_t);
+#ifdef MRB_64BIT
+ pool[i].tt = IREP_TT_INT64;
+ pool[i].u.i64 = (int64_t)v;
+#else
+ pool[i].tt = IREP_TT_INT32;
+ pool[i].u.i32 = v;
+#endif
+ }
+ break;
+ case IREP_TT_INT64:
+#ifdef MRB_INT64
+ {
+ uint64_t i64 = bin_to_uint32(src);
+ src += sizeof(uint32_t);
+ i64 <<= 32;
+ i64 |= bin_to_uint32(src);
+ src += sizeof(uint32_t);
+ pool[i].tt = tt;
+ pool[i].u.i64 = (int64_t)i64;
+ }
+ break;
+#else
+ return FALSE;
+#endif
+
+ case IREP_TT_BIGINT:
+ pool_data_len = bin_to_uint8(src); /* pool data length */
+ src += sizeof(uint8_t);
+ {
+ char *p;
+ pool[i].tt = IREP_TT_BIGINT;
+ p = (char*)mrb_malloc(mrb, pool_data_len+2);
+ memcpy(p, src, pool_data_len+2);
+ pool[i].u.str = (const char*)p;
+ }
+ src += pool_data_len + 2;
break;
case IREP_TT_FLOAT:
- irep->pool[i] = mrb_float_pool(mrb, mrb_str_to_dbl(mrb, s, FALSE));
+#ifndef MRB_NO_FLOAT
+ pool[i].tt = tt;
+ pool[i].u.f = str_to_double(mrb, (const char*)src);
+ src += sizeof(double);
break;
+#else
+ return FALSE; /* MRB_NO_FLOAT */
+#endif
- case IREP_TT_STRING:
- irep->pool[i] = mrb_str_pool(mrb, s);
+ case IREP_TT_STR:
+ pool_data_len = bin_to_uint16(src); /* pool data length */
+ src += sizeof(uint16_t);
+ if (st) {
+ pool[i].tt = (pool_data_len<<2) | IREP_TT_SSTR;
+ pool[i].u.str = (const char*)src;
+ }
+ else {
+ char *p;
+ pool[i].tt = (pool_data_len<<2) | IREP_TT_STR;
+ p = (char*)mrb_malloc(mrb, pool_data_len+1);
+ memcpy(p, src, pool_data_len+1);
+ pool[i].u.str = (const char*)p;
+ }
+ src += pool_data_len + 1;
break;
default:
/* should not happen */
- irep->pool[i] = mrb_nil_value();
- break;
+ return FALSE;
}
- irep->plen++;
- mrb_gc_arena_restore(mrb, ai);
+ irep->plen = i+1;
}
}
/* SYMS BLOCK */
- irep->slen = (size_t)bin_to_uint32(src); /* syms length */
- src += sizeof(uint32_t);
+ irep->slen = bin_to_uint16(src); /* syms length */
+ src += sizeof(uint16_t);
if (irep->slen > 0) {
- if (SIZE_ERROR_MUL(sizeof(mrb_sym), irep->slen)) {
- return NULL;
+ if (SIZE_ERROR_MUL(irep->slen, sizeof(mrb_sym))) {
+ return FALSE;
}
- irep->syms = (mrb_sym *)mrb_malloc(mrb, sizeof(mrb_sym) * irep->slen);
+ irep->syms = syms = (mrb_sym *)mrb_malloc(mrb, sizeof(mrb_sym) * irep->slen);
for (i = 0; i < irep->slen; i++) {
snl = bin_to_uint16(src); /* symbol name length */
src += sizeof(uint16_t);
if (snl == MRB_DUMP_NULL_SYM_LEN) {
- irep->syms[i] = 0;
+ syms[i] = 0;
continue;
}
if (flags & FLAG_SRC_MALLOC) {
- irep->syms[i] = mrb_intern(mrb, (char *)src, snl);
+ syms[i] = mrb_intern(mrb, (char *)src, snl);
}
else {
- irep->syms[i] = mrb_intern_static(mrb, (char *)src, snl);
+ syms[i] = mrb_intern_static(mrb, (char *)src, snl);
}
src += snl + 1;
-
mrb_gc_arena_restore(mrb, ai);
}
}
- irep->reps = (mrb_irep**)mrb_malloc(mrb, sizeof(mrb_irep*)*irep->rlen);
-
diff = src - bin;
mrb_assert_int_fit(ptrdiff_t, diff, size_t, SIZE_MAX);
*len = (size_t)diff;
- return irep;
+ return TRUE;
}
-static mrb_irep*
-read_irep_record(mrb_state *mrb, const uint8_t *bin, size_t *len, uint8_t flags)
+static mrb_bool
+read_irep_record(mrb_state *mrb, const uint8_t *bin, size_t *len, uint8_t flags, mrb_irep **irepp)
{
- mrb_irep *irep = read_irep_record_1(mrb, bin, len, flags);
- size_t i;
+ int ai = mrb_gc_arena_save(mrb);
+ mrb_bool readsuccess = read_irep_record_1(mrb, bin, len, flags, irepp);
+ mrb_irep **reps;
+ int i;
- if (irep == NULL) {
- return NULL;
+ mrb_gc_arena_restore(mrb, ai);
+ if (!readsuccess) {
+ return FALSE;
}
+ reps = (mrb_irep**)mrb_calloc(mrb, (*irepp)->rlen, sizeof(mrb_irep*));
+ (*irepp)->reps = (const mrb_irep**)reps;
+
bin += *len;
- for (i=0; i<irep->rlen; i++) {
+ for (i=0; i<(*irepp)->rlen; i++) {
size_t rlen;
- irep->reps[i] = read_irep_record(mrb, bin, &rlen, flags);
- if (irep->reps[i] == NULL) {
- return NULL;
+ readsuccess = read_irep_record(mrb, bin, &rlen, flags, &reps[i]);
+ mrb_gc_arena_restore(mrb, ai);
+ if (!readsuccess) {
+ return FALSE;
}
bin += rlen;
*len += rlen;
}
- return irep;
+
+ return TRUE;
}
static mrb_irep*
-read_section_irep(mrb_state *mrb, const uint8_t *bin, uint8_t flags)
+read_section_irep(mrb_state *mrb, const uint8_t *bin, uint8_t flags, struct RProc **proc)
{
size_t len;
- bin += sizeof(struct rite_section_irep_header);
- return read_irep_record(mrb, bin, &len, flags);
-}
-
-static int
-read_lineno_record_1(mrb_state *mrb, const uint8_t *bin, mrb_irep *irep, size_t *len)
-{
- size_t i, fname_len, niseq;
- char *fname;
- uint16_t *lines;
-
- *len = 0;
- bin += sizeof(uint32_t); /* record size */
- *len += sizeof(uint32_t);
- fname_len = bin_to_uint16(bin);
- bin += sizeof(uint16_t);
- *len += sizeof(uint16_t);
- if (SIZE_ERROR(fname_len + 1)) {
- return MRB_DUMP_GENERAL_FAILURE;
- }
- fname = (char *)mrb_malloc(mrb, fname_len + 1);
- memcpy(fname, bin, fname_len);
- fname[fname_len] = '\0';
- bin += fname_len;
- *len += fname_len;
+ /*
+ * This proc object keeps all the data in progress to avoid memory leaks
+ * if something goes wrong while reading irep.
+ */
+ *proc = mrb_proc_new(mrb, NULL);
- niseq = (size_t)bin_to_uint32(bin);
- bin += sizeof(uint32_t); /* niseq */
- *len += sizeof(uint32_t);
-
- if (SIZE_ERROR_MUL(niseq, sizeof(uint16_t))) {
- return MRB_DUMP_GENERAL_FAILURE;
- }
- lines = (uint16_t *)mrb_malloc(mrb, niseq * sizeof(uint16_t));
- for (i = 0; i < niseq; i++) {
- lines[i] = bin_to_uint16(bin);
- bin += sizeof(uint16_t); /* niseq */
- *len += sizeof(uint16_t);
+ mrb_irep **irepp = (mrb_irep**)&(*proc)->body.irep;
+ bin += sizeof(struct rite_section_irep_header);
+ if (read_irep_record(mrb, bin, &len, flags, irepp)) {
+ return *irepp;
}
-
- irep->filename = fname;
- irep->lines = lines;
- return MRB_DUMP_OK;
-}
-
-static int
-read_lineno_record(mrb_state *mrb, const uint8_t *bin, mrb_irep *irep, size_t *lenp)
-{
- int result = read_lineno_record_1(mrb, bin, irep, lenp);
- size_t i;
-
- if (result != MRB_DUMP_OK) return result;
- for (i = 0; i < irep->rlen; i++) {
- size_t len;
-
- result = read_lineno_record(mrb, bin, irep->reps[i], &len);
- if (result != MRB_DUMP_OK) break;
- bin += len;
- *lenp += len;
+ else {
+ return NULL;
}
- return result;
-}
-
-static int
-read_section_lineno(mrb_state *mrb, const uint8_t *bin, mrb_irep *irep)
-{
- size_t len;
-
- len = 0;
- bin += sizeof(struct rite_section_lineno_header);
-
- /* Read Binary Data Section */
- return read_lineno_record(mrb, bin, irep, &len);
}
static int
@@ -295,28 +301,29 @@ read_debug_record(mrb_state *mrb, const uint8_t *start, mrb_irep* irep, size_t *
{
const uint8_t *bin = start;
ptrdiff_t diff;
- size_t record_size, i;
+ size_t record_size;
uint16_t f_idx;
+ int i;
+ mrb_irep_debug_info *debug;
if (irep->debug_info) { return MRB_DUMP_INVALID_IREP; }
- irep->debug_info = (mrb_irep_debug_info*)mrb_malloc(mrb, sizeof(mrb_irep_debug_info));
- irep->debug_info->pc_count = irep->ilen;
+ irep->debug_info = debug = (mrb_irep_debug_info*)mrb_calloc(mrb, 1, sizeof(mrb_irep_debug_info));
+ debug->pc_count = (uint32_t)irep->ilen;
record_size = (size_t)bin_to_uint32(bin);
bin += sizeof(uint32_t);
- irep->debug_info->flen = bin_to_uint16(bin);
- irep->debug_info->files = (mrb_irep_debug_info_file**)mrb_malloc(mrb, sizeof(mrb_irep_debug_info*) * irep->debug_info->flen);
+ debug->flen = bin_to_uint16(bin);
+ debug->files = (mrb_irep_debug_info_file**)mrb_calloc(mrb, irep->debug_info->flen, sizeof(mrb_irep_debug_info*));
bin += sizeof(uint16_t);
- for (f_idx = 0; f_idx < irep->debug_info->flen; ++f_idx) {
+ for (f_idx = 0; f_idx < debug->flen; ++f_idx) {
mrb_irep_debug_info_file *file;
uint16_t filename_idx;
- mrb_int len;
- file = (mrb_irep_debug_info_file *)mrb_malloc(mrb, sizeof(*file));
- irep->debug_info->files[f_idx] = file;
+ file = (mrb_irep_debug_info_file *)mrb_calloc(mrb, 1, sizeof(*file));
+ debug->files[f_idx] = file;
file->start_pos = bin_to_uint32(bin);
bin += sizeof(uint32_t);
@@ -326,8 +333,6 @@ read_debug_record(mrb_state *mrb, const uint8_t *start, mrb_irep* irep, size_t *
bin += sizeof(uint16_t);
mrb_assert(filename_idx < filenames_len);
file->filename_sym = filenames[filename_idx];
- len = 0;
- file->filename = mrb_sym2name_len(mrb, file->filename_sym, &len);
file->line_entry_count = bin_to_uint32(bin);
bin += sizeof(uint32_t);
@@ -347,8 +352,8 @@ read_debug_record(mrb_state *mrb, const uint8_t *start, mrb_irep* irep, size_t *
case mrb_debug_line_flat_map: {
uint32_t l;
- file->lines.flat_map = (mrb_irep_debug_info_line*)mrb_malloc(
- mrb, sizeof(mrb_irep_debug_info_line) * (size_t)(file->line_entry_count));
+ file->lines.flat_map = (mrb_irep_debug_info_line*)mrb_calloc(
+ mrb, (size_t)(file->line_entry_count), sizeof(mrb_irep_debug_info_line));
for (l = 0; l < file->line_entry_count; ++l) {
file->lines.flat_map[l].start_pos = bin_to_uint32(bin);
bin += sizeof(uint32_t);
@@ -372,7 +377,7 @@ read_debug_record(mrb_state *mrb, const uint8_t *start, mrb_irep* irep, size_t *
size_t len;
int ret;
- ret = read_debug_record(mrb, bin, irep->reps[i], &len, filenames, filenames_len);
+ ret = read_debug_record(mrb, bin, (mrb_irep*)irep->reps[i], &len, filenames, filenames_len);
if (ret != MRB_DUMP_OK) return ret;
bin += len;
}
@@ -395,6 +400,7 @@ read_section_debug(mrb_state *mrb, const uint8_t *start, mrb_irep *irep, uint8_t
int result;
uint16_t filenames_len;
mrb_sym *filenames;
+ mrb_value filenames_obj;
bin = start;
header = (struct rite_section_debug_header *)bin;
@@ -402,7 +408,8 @@ read_section_debug(mrb_state *mrb, const uint8_t *start, mrb_irep *irep, uint8_t
filenames_len = bin_to_uint16(bin);
bin += sizeof(uint16_t);
- filenames = (mrb_sym*)mrb_malloc(mrb, sizeof(mrb_sym) * (size_t)filenames_len);
+ filenames_obj = mrb_str_new(mrb, NULL, sizeof(mrb_sym) * (size_t)filenames_len);
+ filenames = (mrb_sym*)RSTRING_PTR(filenames_obj);
for (i = 0; i < filenames_len; ++i) {
uint16_t f_len = bin_to_uint16(bin);
bin += sizeof(uint16_t);
@@ -426,7 +433,7 @@ read_section_debug(mrb_state *mrb, const uint8_t *start, mrb_irep *irep, uint8_t
}
debug_exit:
- mrb_free(mrb, filenames);
+ mrb_str_resize(mrb, filenames_obj, 0);
return result;
}
@@ -434,34 +441,31 @@ static int
read_lv_record(mrb_state *mrb, const uint8_t *start, mrb_irep *irep, size_t *record_len, mrb_sym const *syms, uint32_t syms_len)
{
const uint8_t *bin = start;
- size_t i;
+ mrb_sym *lv;
ptrdiff_t diff;
+ int i;
- irep->lv = (struct mrb_locals*)mrb_malloc(mrb, sizeof(struct mrb_locals) * (irep->nlocals - 1));
+ irep->lv = lv = (mrb_sym*)mrb_malloc(mrb, sizeof(mrb_sym) * (irep->nlocals - 1));
- for (i = 0; i + 1< irep->nlocals; ++i) {
+ for (i = 0; i + 1 < irep->nlocals; ++i) {
uint16_t const sym_idx = bin_to_uint16(bin);
bin += sizeof(uint16_t);
if (sym_idx == RITE_LV_NULL_MARK) {
- irep->lv[i].name = 0;
- irep->lv[i].r = 0;
+ lv[i] = 0;
}
else {
if (sym_idx >= syms_len) {
return MRB_DUMP_GENERAL_FAILURE;
}
- irep->lv[i].name = syms[sym_idx];
-
- irep->lv[i].r = bin_to_uint16(bin);
+ lv[i] = syms[sym_idx];
}
- bin += sizeof(uint16_t);
}
for (i = 0; i < irep->rlen; ++i) {
size_t len;
int ret;
- ret = read_lv_record(mrb, bin, irep->reps[i], &len, syms, syms_len);
+ ret = read_lv_record(mrb, bin, (mrb_irep*)irep->reps[i], &len, syms, syms_len);
if (ret != MRB_DUMP_OK) return ret;
bin += len;
}
@@ -484,6 +488,7 @@ read_section_lv(mrb_state *mrb, const uint8_t *start, mrb_irep *irep, uint8_t fl
int result;
uint32_t syms_len;
mrb_sym *syms;
+ mrb_value syms_obj;
mrb_sym (*intern_func)(mrb_state*, const char*, size_t) =
(flags & FLAG_SRC_MALLOC)? mrb_intern : mrb_intern_static;
@@ -493,7 +498,8 @@ read_section_lv(mrb_state *mrb, const uint8_t *start, mrb_irep *irep, uint8_t fl
syms_len = bin_to_uint32(bin);
bin += sizeof(uint32_t);
- syms = (mrb_sym*)mrb_malloc(mrb, sizeof(mrb_sym) * (size_t)syms_len);
+ syms_obj = mrb_str_new(mrb, NULL, sizeof(mrb_sym) * (size_t)syms_len);
+ syms = (mrb_sym*)RSTRING_PTR(syms_obj);
for (i = 0; i < syms_len; ++i) {
uint16_t const str_len = bin_to_uint16(bin);
bin += sizeof(uint16_t);
@@ -513,87 +519,74 @@ read_section_lv(mrb_state *mrb, const uint8_t *start, mrb_irep *irep, uint8_t fl
}
lv_exit:
- mrb_free(mrb, syms);
+ mrb_str_resize(mrb, syms_obj, 0);
return result;
}
static int
-read_binary_header(const uint8_t *bin, size_t *bin_size, uint16_t *crc, uint8_t *flags)
+read_binary_header(const uint8_t *bin, size_t bufsize, size_t *bin_size, uint8_t *flags)
{
const struct rite_binary_header *header = (const struct rite_binary_header *)bin;
- uint32_t ident = 0;
- size_t i;
- /* create native byteorder version of RITE_BINARY_IDENTIFIER */
- for(i=0; i<sizeof(ident); i++) {
- ident<<=8;
- ident|=RITE_BINARY_IDENTIFIER[i];
+ if (bufsize < sizeof(struct rite_binary_header)) {
+ return MRB_DUMP_READ_FAULT;
}
- if (memcmp(header->binary_identify, &ident, sizeof(header->binary_identify)) == 0) {
- *flags |= FLAG_BYTEORDER_NATIVE;
- }
- else if (memcmp(header->binary_identify, RITE_BINARY_IDENTIFIER, sizeof(header->binary_identify)) != 0) {
+
+ if (memcmp(header->binary_ident, RITE_BINARY_IDENT, sizeof(header->binary_ident)) != 0) {
return MRB_DUMP_INVALID_FILE_HEADER;
}
- if (memcmp(header->binary_version, RITE_BINARY_FORMAT_VER, sizeof(header->binary_version)) != 0) {
+ /* if major version is different, they are incompatible */
+ if (memcmp(header->major_version, RITE_BINARY_MAJOR_VER, sizeof(header->major_version)) != 0) {
return MRB_DUMP_INVALID_FILE_HEADER;
}
-
- if (crc) {
- *crc = bin_to_uint16(header->binary_crc);
+ /* if minor version is different, we can accept the older version */
+ if (memcmp(header->minor_version, RITE_BINARY_MINOR_VER, sizeof(header->minor_version)) > 0) {
+ return MRB_DUMP_INVALID_FILE_HEADER;
}
+
*bin_size = (size_t)bin_to_uint32(header->binary_size);
+ if (bufsize < *bin_size) {
+ return MRB_DUMP_READ_FAULT;
+ }
+
return MRB_DUMP_OK;
}
-MRB_API mrb_irep*
-read_irep(mrb_state *mrb, const uint8_t *bin, uint8_t flags)
+static struct RProc*
+read_irep(mrb_state *mrb, const uint8_t *bin, size_t bufsize, uint8_t flags)
{
int result;
+ struct RProc *proc = NULL;
mrb_irep *irep = NULL;
const struct rite_section_header *section_header;
- uint16_t crc;
size_t bin_size = 0;
- size_t n;
if ((mrb == NULL) || (bin == NULL)) {
return NULL;
}
- result = read_binary_header(bin, &bin_size, &crc, &flags);
+ result = read_binary_header(bin, bufsize, &bin_size, &flags);
if (result != MRB_DUMP_OK) {
return NULL;
}
- n = offset_crc_body();
- if (crc != calc_crc_16_ccitt(bin + n, bin_size - n, 0)) {
- return NULL;
- }
-
bin += sizeof(struct rite_binary_header);
do {
section_header = (const struct rite_section_header *)bin;
- if (memcmp(section_header->section_identify, RITE_SECTION_IREP_IDENTIFIER, sizeof(section_header->section_identify)) == 0) {
- irep = read_section_irep(mrb, bin, flags);
+ if (memcmp(section_header->section_ident, RITE_SECTION_IREP_IDENT, sizeof(section_header->section_ident)) == 0) {
+ irep = read_section_irep(mrb, bin, flags, &proc);
if (!irep) return NULL;
}
- else if (memcmp(section_header->section_identify, RITE_SECTION_LINENO_IDENTIFIER, sizeof(section_header->section_identify)) == 0) {
- if (!irep) return NULL; /* corrupted data */
- result = read_section_lineno(mrb, bin, irep);
- if (result < MRB_DUMP_OK) {
- return NULL;
- }
- }
- else if (memcmp(section_header->section_identify, RITE_SECTION_DEBUG_IDENTIFIER, sizeof(section_header->section_identify)) == 0) {
+ else if (memcmp(section_header->section_ident, RITE_SECTION_DEBUG_IDENT, sizeof(section_header->section_ident)) == 0) {
if (!irep) return NULL; /* corrupted data */
result = read_section_debug(mrb, bin, irep, flags);
if (result < MRB_DUMP_OK) {
return NULL;
}
}
- else if (memcmp(section_header->section_identify, RITE_SECTION_LV_IDENTIFIER, sizeof(section_header->section_identify)) == 0) {
+ else if (memcmp(section_header->section_ident, RITE_SECTION_LV_IDENT, sizeof(section_header->section_ident)) == 0) {
if (!irep) return NULL;
result = read_section_lv(mrb, bin, irep, flags);
if (result < MRB_DUMP_OK) {
@@ -601,43 +594,72 @@ read_irep(mrb_state *mrb, const uint8_t *bin, uint8_t flags)
}
}
bin += bin_to_uint32(section_header->section_size);
- } while (memcmp(section_header->section_identify, RITE_BINARY_EOF, sizeof(section_header->section_identify)) != 0);
+ } while (memcmp(section_header->section_ident, RITE_BINARY_EOF, sizeof(section_header->section_ident)) != 0);
- return irep;
+ return proc;
}
-MRB_API mrb_irep*
-mrb_read_irep(mrb_state *mrb, const uint8_t *bin)
+static struct RProc*
+mrb_proc_read_irep(mrb_state *mrb, const uint8_t *bin)
{
-#ifdef MRB_USE_ETEXT_EDATA
+#if defined(MRB_USE_LINK_TIME_RO_DATA_P) || defined(MRB_USE_CUSTOM_RO_DATA_P)
uint8_t flags = mrb_ro_data_p((char*)bin) ? FLAG_SRC_STATIC : FLAG_SRC_MALLOC;
#else
uint8_t flags = FLAG_SRC_STATIC;
#endif
- return read_irep(mrb, bin, flags);
+ return read_irep(mrb, bin, (size_t)-1, flags);
}
+DEFINE_READ_IREP_FUNC(
+ mrb_irep *mrb_read_irep(mrb_state *mrb, const uint8_t *bin),
+ mrb_proc_read_irep(mrb, bin))
+
+static struct RProc*
+mrb_proc_read_irep_buf(mrb_state *mrb, const void *buf, size_t bufsize)
+{
+ return read_irep(mrb, (const uint8_t *)buf, bufsize, FLAG_SRC_MALLOC);
+}
+
+DEFINE_READ_IREP_FUNC(
+ MRB_API mrb_irep *mrb_read_irep_buf(mrb_state *mrb, const void *buf, size_t bufsize),
+ mrb_proc_read_irep_buf(mrb, buf, bufsize))
+
+void mrb_exc_set(mrb_state *mrb, mrb_value exc);
+
static void
irep_error(mrb_state *mrb)
{
- mrb->exc = mrb_obj_ptr(mrb_exc_new_str_lit(mrb, E_SCRIPT_ERROR, "irep load error"));
+ mrb_exc_set(mrb, mrb_exc_new_lit(mrb, E_SCRIPT_ERROR, "irep load error"));
}
-MRB_API mrb_value
-mrb_load_irep_cxt(mrb_state *mrb, const uint8_t *bin, mrbc_context *c)
-{
- mrb_irep *irep = mrb_read_irep(mrb, bin);
- struct RProc *proc;
+void mrb_codedump_all(mrb_state*, struct RProc*);
- if (!irep) {
+static mrb_value
+load_irep(mrb_state *mrb, struct RProc *proc, mrbc_context *c)
+{
+ if (!proc || !proc->body.irep) {
irep_error(mrb);
return mrb_nil_value();
}
- proc = mrb_proc_new(mrb, irep);
- mrb_irep_decref(mrb, irep);
+ proc->c = NULL;
+ if (c && c->dump_result) mrb_codedump_all(mrb, proc);
if (c && c->no_exec) return mrb_obj_value(proc);
- return mrb_toplevel_run(mrb, proc);
+ return mrb_top_run(mrb, proc, mrb_top_self(mrb), 0);
+}
+
+MRB_API mrb_value
+mrb_load_irep_cxt(mrb_state *mrb, const uint8_t *bin, mrbc_context *c)
+{
+ struct RProc *proc = mrb_proc_read_irep(mrb, bin);
+ if (!proc) return mrb_undef_value();
+ return load_irep(mrb, proc, c);
+}
+
+MRB_API mrb_value
+mrb_load_irep_buf_cxt(mrb_state *mrb, const void *buf, size_t bufsize, mrbc_context *c)
+{
+ return load_irep(mrb, mrb_proc_read_irep_buf(mrb, buf, bufsize), c);
}
MRB_API mrb_value
@@ -646,64 +668,62 @@ mrb_load_irep(mrb_state *mrb, const uint8_t *bin)
return mrb_load_irep_cxt(mrb, bin, NULL);
}
-#ifdef ENABLE_STDIO
+MRB_API mrb_value
+mrb_load_irep_buf(mrb_state *mrb, const void *buf, size_t bufsize)
+{
+ return mrb_load_irep_buf_cxt(mrb, buf, bufsize, NULL);
+}
-MRB_API mrb_irep*
-mrb_read_irep_file(mrb_state *mrb, FILE* fp)
+MRB_API mrb_value
+mrb_load_proc(mrb_state *mrb, const struct RProc *proc)
{
- mrb_irep *irep = NULL;
+ return mrb_vm_run(mrb, proc, mrb_top_self(mrb), 0);
+}
+
+#ifndef MRB_NO_STDIO
+
+static struct RProc*
+mrb_proc_read_irep_file(mrb_state *mrb, FILE *fp)
+{
+ struct RProc *proc = NULL;
uint8_t *buf;
const size_t header_size = sizeof(struct rite_binary_header);
size_t buf_size = 0;
- uint8_t flags;
+ uint8_t flags = 0;
int result;
if ((mrb == NULL) || (fp == NULL)) {
return NULL;
}
- /* You don't need use SIZE_ERROR as buf_size is enough small. */
buf = (uint8_t*)mrb_malloc(mrb, header_size);
if (fread(buf, header_size, 1, fp) == 0) {
- mrb_free(mrb, buf);
- return NULL;
+ goto irep_exit;
}
- result = read_binary_header(buf, &buf_size, NULL, &flags);
- if (result != MRB_DUMP_OK) {
- mrb_free(mrb, buf);
- return NULL;
+ result = read_binary_header(buf, (size_t)-1, &buf_size, &flags);
+ if (result != MRB_DUMP_OK || buf_size <= header_size) {
+ goto irep_exit;
}
buf = (uint8_t*)mrb_realloc(mrb, buf, buf_size);
if (fread(buf+header_size, buf_size-header_size, 1, fp) == 0) {
- mrb_free(mrb, buf);
- return NULL;
+ goto irep_exit;
}
- irep = read_irep(mrb, buf, FLAG_SRC_MALLOC);
- mrb_free(mrb, buf);
+ proc = read_irep(mrb, buf, (size_t)-1, FLAG_SRC_MALLOC);
- return irep;
+irep_exit:
+ mrb_free(mrb, buf);
+ return proc;
}
-void mrb_codedump_all(mrb_state*, struct RProc*);
+DEFINE_READ_IREP_FUNC(
+ mrb_irep *mrb_read_irep_file(mrb_state *mrb, FILE *fp),
+ mrb_proc_read_irep_file(mrb, fp))
MRB_API mrb_value
mrb_load_irep_file_cxt(mrb_state *mrb, FILE* fp, mrbc_context *c)
{
- mrb_irep *irep = mrb_read_irep_file(mrb, fp);
- mrb_value val;
- struct RProc *proc;
-
- if (!irep) {
- irep_error(mrb);
- return mrb_nil_value();
- }
- proc = mrb_proc_new(mrb, irep);
- mrb_irep_decref(mrb, irep);
- if (c && c->dump_result) mrb_codedump_all(mrb, proc);
- if (c && c->no_exec) return mrb_obj_value(proc);
- val = mrb_toplevel_run(mrb, proc);
- return val;
+ return load_irep(mrb, mrb_proc_read_irep_file(mrb, fp), c);
}
MRB_API mrb_value
@@ -711,4 +731,4 @@ mrb_load_irep_file(mrb_state *mrb, FILE* fp)
{
return mrb_load_irep_file_cxt(mrb, fp, NULL);
}
-#endif /* ENABLE_STDIO */
+#endif /* MRB_NO_STDIO */
diff --git a/src/mrb_throw.h b/src/mrb_throw.h
deleted file mode 100644
index 3c7407a8d..000000000
--- a/src/mrb_throw.h
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
-** mrb_throw.h - mruby exception throwing handler
-**
-** See Copyright Notice in mruby.h
-*/
-
-#ifndef MRB_THROW_H
-#define MRB_THROW_H
-
-#ifdef MRB_ENABLE_CXX_EXCEPTION
-
-#define MRB_TRY(buf) do { try {
-#define MRB_CATCH(buf) } catch(mrb_jmpbuf_impl e) { if (e != (buf)->impl) { throw e; }
-#define MRB_END_EXC(buf) } } while(0)
-
-#define MRB_THROW(buf) throw((buf)->impl)
-typedef mrb_int mrb_jmpbuf_impl;
-
-#else
-
-#include <setjmp.h>
-
-#define MRB_TRY(buf) do { if (setjmp((buf)->impl) == 0) {
-#define MRB_CATCH(buf) } else {
-#define MRB_END_EXC(buf) } } while(0)
-
-#define MRB_THROW(buf) longjmp((buf)->impl, 1);
-#define mrb_jmpbuf_impl jmp_buf
-
-#endif
-
-struct mrb_jmpbuf {
- mrb_jmpbuf_impl impl;
-
-#ifdef MRB_ENABLE_CXX_EXCEPTION
- static mrb_int jmpbuf_id;
- mrb_jmpbuf() : impl(jmpbuf_id++) {}
-#endif
-};
-
-#endif /* MRB_THROW_H */
diff --git a/src/mruby_core.rake b/src/mruby_core.rake
deleted file mode 100644
index 88fca83fc..000000000
--- a/src/mruby_core.rake
+++ /dev/null
@@ -1,78 +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}"
-
- lex_def = "#{current_dir}/lex.def"
- objs = Dir.glob("#{current_dir}/*.c").map { |f|
- next nil if cxx_abi_enabled? and f =~ /(codegen|error|vm).c$/
- objfile(f.pathmap("#{current_build_dir}/%n"))
- }.compact
-
- if cxx_abi_enabled?
- cxx_abi_dependency = %w(codegen error vm)
- cxx_abi_objs = cxx_abi_dependency.map { |v|
- src = "#{current_build_dir}/#{v}.cxx"
- file src => ["#{current_dir}/#{v}.c", __FILE__] do |t|
- File.open(t.name, 'w') do |f|
- f.write <<EOS
-#define __STDC_CONSTANT_MACROS
-#define __STDC_LIMIT_MACROS
-
-extern "C" {
-#include "#{MRUBY_ROOT}/#{t.prerequisites.first}"
-}
-
-
-#{v == 'error'? 'mrb_int mrb_jmpbuf::jmpbuf_id = 0;' : ''}
-EOS
- end
- end
-
- file objfile(src) => src do |t|
- cxx.run t.name, t.prerequisites.first, [], [current_dir]
- end
-
- objfile src
- }
- cxx_abi_objs << objfile("#{current_build_dir}/y.tab")
-
- file "#{current_build_dir}/y.tab.cxx" => ["#{current_build_dir}/y.tab.c", __FILE__] do |t|
- File.open(t.name, 'w') do |f|
- f.write <<EOS
-#define __STDC_CONSTANT_MACROS
-#define __STDC_LIMIT_MACROS
-
-extern "C" {
-#include "#{t.prerequisites.first}"
-}
-EOS
- end
- end
- file objfile("#{current_build_dir}/y.tab") => ["#{current_build_dir}/y.tab.cxx", lex_def] do |t|
- cxx.run t.name, t.prerequisites.first, [], [current_dir]
- end
-
- objs += cxx_abi_objs
- else
- objs += [objfile("#{current_build_dir}/y.tab")]
- file objfile("#{current_build_dir}/y.tab") => ["#{current_build_dir}/y.tab.c", lex_def] do |t|
- cc.run t.name, t.prerequisites.first, [], [current_dir]
- end
- end
- self.libmruby << objs
-
- file libfile("#{build_dir}/lib/libmruby_core") => objs do |t|
- archiver.run t.name, t.prerequisites
- end
-
- # Parser
- file "#{current_build_dir}/y.tab.c" => ["#{current_dir}/parse.y"] do |t|
- yacc.run t.name, t.prerequisites.first
- end
-
- # Lexical analyzer
- file lex_def => "#{current_dir}/keywords" do |t|
- gperf.run t.name, t.prerequisites.first
- end
-end
diff --git a/src/node.h b/src/node.h
deleted file mode 100644
index 532a8323a..000000000
--- a/src/node.h
+++ /dev/null
@@ -1,117 +0,0 @@
-/*
-** node.h - nodes of abstract syntax tree
-**
-** See Copyright Notice in mruby.h
-*/
-
-#ifndef NODE_H
-#define NODE_H
-
-enum node_type {
- NODE_METHOD,
- NODE_FBODY,
- NODE_CFUNC,
- NODE_SCOPE,
- NODE_BLOCK,
- NODE_IF,
- NODE_CASE,
- NODE_WHEN,
- NODE_OPT_N,
- NODE_WHILE,
- NODE_UNTIL,
- NODE_ITER,
- NODE_FOR,
- NODE_BREAK,
- NODE_NEXT,
- NODE_REDO,
- NODE_RETRY,
- NODE_BEGIN,
- NODE_RESCUE,
- NODE_ENSURE,
- NODE_AND,
- NODE_OR,
- NODE_NOT,
- NODE_MASGN,
- NODE_ASGN,
- NODE_CDECL,
- NODE_CVASGN,
- NODE_CVDECL,
- NODE_OP_ASGN,
- NODE_CALL,
- NODE_FCALL,
- NODE_VCALL,
- NODE_SUPER,
- NODE_ZSUPER,
- NODE_ARRAY,
- NODE_ZARRAY,
- NODE_HASH,
- NODE_RETURN,
- NODE_YIELD,
- NODE_LVAR,
- NODE_DVAR,
- NODE_GVAR,
- NODE_IVAR,
- NODE_CONST,
- NODE_CVAR,
- NODE_NTH_REF,
- NODE_BACK_REF,
- NODE_MATCH,
- NODE_MATCH2,
- NODE_MATCH3,
- NODE_INT,
- NODE_FLOAT,
- NODE_NEGATE,
- NODE_LAMBDA,
- NODE_SYM,
- NODE_STR,
- NODE_DSTR,
- NODE_XSTR,
- NODE_DXSTR,
- NODE_REGX,
- NODE_DREGX,
- NODE_DREGX_ONCE,
- NODE_LIST,
- NODE_ARG,
- NODE_ARGSCAT,
- NODE_ARGSPUSH,
- NODE_SPLAT,
- NODE_TO_ARY,
- NODE_SVALUE,
- NODE_BLOCK_ARG,
- NODE_DEF,
- NODE_SDEF,
- NODE_ALIAS,
- NODE_UNDEF,
- NODE_CLASS,
- NODE_MODULE,
- NODE_SCLASS,
- NODE_COLON2,
- NODE_COLON3,
- NODE_CREF,
- NODE_DOT2,
- NODE_DOT3,
- NODE_FLIP2,
- NODE_FLIP3,
- NODE_ATTRSET,
- NODE_SELF,
- NODE_NIL,
- NODE_TRUE,
- NODE_FALSE,
- NODE_DEFINED,
- NODE_NEWLINE,
- NODE_POSTEXE,
- NODE_ALLOCA,
- NODE_DMETHOD,
- NODE_BMETHOD,
- NODE_MEMO,
- NODE_IFUNC,
- NODE_DSYM,
- NODE_ATTRASGN,
- NODE_HEREDOC,
- NODE_LITERAL_DELIM,
- NODE_WORDS,
- NODE_SYMBOLS,
- NODE_LAST
-};
-
-#endif /* NODE_H */
diff --git a/src/numeric.c b/src/numeric.c
index e01e661a5..306aab803 100644
--- a/src/numeric.c
+++ b/src/numeric.c
@@ -1,45 +1,64 @@
/*
-** numeric.c - Numeric, Integer, Float, Fixnum class
+** numeric.c - Numeric, Integer, Float class
**
** See Copyright Notice in mruby.h
*/
+#ifndef MRB_NO_FLOAT
#include <float.h>
-#include <limits.h>
#include <math.h>
+#endif
+#include <limits.h>
#include <stdlib.h>
-
-#include "mruby.h"
-#include "mruby/array.h"
-#include "mruby/numeric.h"
-#include "mruby/string.h"
-
-#ifdef MRB_USE_FLOAT
-#define floor(f) floorf(f)
-#define ceil(f) ceilf(f)
+#include <string.h>
+
+#include <mruby.h>
+#include <mruby/array.h>
+#include <mruby/numeric.h>
+#include <mruby/string.h>
+#include <mruby/class.h>
+#include <mruby/presym.h>
+
+#ifndef MRB_NO_FLOAT
+#ifdef MRB_USE_FLOAT32
+#define trunc(f) truncf(f)
#define fmod(x,y) fmodf(x,y)
-#define FLO_MAX_DIGITS 7
-#define FLO_MAX_SIGN_LENGTH 3
-#define FLO_EPSILON FLT_EPSILON
#else
-#define FLO_MAX_DIGITS 14
-#define FLO_MAX_SIGN_LENGTH 10
-#define FLO_EPSILON DBL_EPSILON
+#endif
#endif
+#ifndef MRB_NO_FLOAT
MRB_API mrb_float
-mrb_to_flo(mrb_state *mrb, mrb_value val)
+mrb_as_float(mrb_state *mrb, mrb_value val)
{
switch (mrb_type(val)) {
- case MRB_TT_FIXNUM:
- return (mrb_float)mrb_fixnum(val);
+ case MRB_TT_INTEGER:
+ return (mrb_float)mrb_integer(val);
case MRB_TT_FLOAT:
break;
- default:
+ case MRB_TT_STRING:
+ case MRB_TT_FALSE:
+ case MRB_TT_TRUE:
mrb_raise(mrb, E_TYPE_ERROR, "non float value");
+ default:
+ val = mrb_type_convert(mrb, val, MRB_TT_FLOAT, MRB_SYM(to_f));
+ break;
}
return mrb_float(val);
}
+#endif
+
+static void
+int_overflow(mrb_state *mrb, const char *reason)
+{
+ mrb_raisef(mrb, E_RANGE_ERROR, "integer overflow in %s", reason);
+}
+
+static void
+int_zerodiv(mrb_state *mrb)
+{
+ mrb_raise(mrb, E_ZERODIV_ERROR, "divided by 0");
+}
/*
* call-seq:
@@ -51,34 +70,94 @@ mrb_to_flo(mrb_state *mrb, mrb_value val)
* 2.0**3 #=> 8.0
*/
static mrb_value
-num_pow(mrb_state *mrb, mrb_value x)
+int_pow(mrb_state *mrb, mrb_value x)
{
- mrb_value y;
- mrb_float d, yv;
+ mrb_int base = mrb_integer(x);
+ mrb_int result = 1;
+ mrb_int exp;
- mrb_get_args(mrb, "o", &y);
- yv = mrb_to_flo(mrb, y);
- d = pow(mrb_to_flo(mrb, x), yv);
- if (mrb_fixnum_p(x) && mrb_fixnum_p(y) && FIXABLE(d) && yv > 0)
- return mrb_fixnum_value((mrb_int)d);
- return mrb_float_value(mrb, d);
+#ifndef MRB_NO_FLOAT
+ mrb_value y = mrb_get_arg1(mrb);
+
+ if (mrb_float_p(y)) {
+ return mrb_float_value(mrb, pow((double)base, mrb_float(y)));
+ }
+ else if (mrb_integer_p(y)) {
+ exp = mrb_integer(y);
+ }
+ else
+#endif
+ {
+ mrb_get_args(mrb, "i", &exp);
+ }
+ if (exp < 0) {
+#ifndef MRB_NO_FLOAT
+ return mrb_float_value(mrb, pow((double)base, (double)exp));
+#else
+ int_overflow(mrb, "negative power");
+#endif
+ }
+ for (;;) {
+ if (exp & 1) {
+ if (mrb_int_mul_overflow(result, base, &result)) {
+ int_overflow(mrb, "power");
+ }
+ }
+ exp >>= 1;
+ if (exp == 0) break;
+ if (mrb_int_mul_overflow(base, base, &base)) {
+ int_overflow(mrb, "power");
+ }
+ }
+ return mrb_int_value(mrb, result);
+}
+
+mrb_int
+mrb_div_int(mrb_state *mrb, mrb_int x, mrb_int y)
+{
+ if (y == 0) {
+ int_zerodiv(mrb);
+ }
+ else if(x == MRB_INT_MIN && y == -1) {
+ int_overflow(mrb, "division");
+ }
+ else {
+ mrb_int div = x / y;
+
+ if ((x ^ y) < 0 && x != div * y) {
+ div -= 1;
+ }
+ return div;
+ }
+ /* not reached */
+ return 0;
}
/* 15.2.8.3.4 */
/* 15.2.9.3.4 */
/*
* call-seq:
- * num / other -> num
+ * int / other -> num
*
* Performs division: the class of the resulting object depends on
* the class of <code>num</code> and on the magnitude of the
* result.
*/
-
-mrb_value
-mrb_num_div(mrb_state *mrb, mrb_value x, mrb_value y)
+static mrb_value
+int_div(mrb_state *mrb, mrb_value x)
{
- return mrb_float_value(mrb, mrb_to_flo(mrb, x) / mrb_to_flo(mrb, y));
+ mrb_value y = mrb_get_arg1(mrb);
+ mrb_int a = mrb_integer(x);
+
+ if (mrb_integer_p(y)) {
+ mrb_int div = mrb_div_int(mrb, a, mrb_integer(y));
+ return mrb_int_value(mrb, div);
+ }
+#ifdef MRB_NO_FLOAT
+ mrb_raise(mrb, E_TYPE_ERROR, "non integer division");
+#else
+ return mrb_float_value(mrb, mrb_div_float((mrb_float)a, mrb_as_float(mrb, y)));
+#endif
}
/* 15.2.9.3.19(x) */
@@ -89,175 +168,198 @@ mrb_num_div(mrb_state *mrb, mrb_value x, mrb_value y)
* Returns most exact division.
*/
+/*
+ * call-seq:
+ * int.div(other) -> int
+ *
+ * Performs division: resulting integer.
+ */
static mrb_value
-num_div(mrb_state *mrb, mrb_value x)
+int_idiv(mrb_state *mrb, mrb_value x)
{
+ mrb_int y;
+
+ mrb_get_args(mrb, "i", &y);
+ if (y == 0) {
+ int_zerodiv(mrb);
+ }
+ return mrb_fixnum_value(mrb_integer(x) / y);
+}
+
+static mrb_value
+int_quo(mrb_state *mrb, mrb_value xv)
+{
+#ifdef MRB_NO_FLOAT
+ return int_idiv(mrb, xv);
+#else
mrb_float y;
mrb_get_args(mrb, "f", &y);
- return mrb_float_value(mrb, mrb_to_flo(mrb, x) / y);
+ if (y == 0) {
+ int_zerodiv(mrb);
+ }
+ return mrb_float_value(mrb, mrb_integer(xv) / y);
+#endif
+}
+
+static mrb_value
+coerce_step_counter(mrb_state *mrb, mrb_value self)
+{
+ mrb_value num, step;
+
+ mrb_get_args(mrb, "oo", &num, &step);
+
+#ifndef MRB_NO_FLOAT
+ if (mrb_float_p(self) || mrb_float_p(num) || mrb_float_p(step)) {
+ return mrb_to_float(mrb, self);
+ }
+#endif
+
+ return self;
}
+#ifndef MRB_NO_FLOAT
/********************************************************************
*
* Document-class: Float
*
* <code>Float</code> objects represent inexact real numbers using
- * the native architecture's double-precision floating point
+ * the native architecture's double-precision floating-point
* representation.
*/
static mrb_value
-mrb_flo_to_str(mrb_state *mrb, mrb_float flo)
+flo_pow(mrb_state *mrb, mrb_value x)
{
- double n = (double)flo;
- int max_digits = FLO_MAX_DIGITS;
+ mrb_value y = mrb_get_arg1(mrb);
+ mrb_float d = pow(mrb_as_float(mrb, x), mrb_as_float(mrb, y));
+ return mrb_float_value(mrb, d);
+}
+
+static mrb_value
+flo_idiv(mrb_state *mrb, mrb_value xv)
+{
+ mrb_int y, div;
+
+ mrb_get_args(mrb, "i", &y);
+ div = mrb_div_int(mrb, (mrb_int)mrb_float(xv), y);
+ return mrb_int_value(mrb, (mrb_int)div);
+}
- if (isnan(n)) {
- return mrb_str_new_lit(mrb, "NaN");
+mrb_float
+mrb_div_float(mrb_float x, mrb_float y)
+{
+ if (y != 0.0) {
+ return x / y;
}
- else if (isinf(n)) {
- if (n < 0) {
- return mrb_str_new_lit(mrb, "-inf");
- }
- else {
- return mrb_str_new_lit(mrb, "inf");
- }
+ else if (x == 0.0) {
+ return NAN;
}
else {
- int digit;
- int m = 0;
- int exp;
- mrb_bool e = FALSE;
- char s[48];
- char *c = &s[0];
- int length = 0;
-
- if (signbit(n)) {
- n = -n;
- *(c++) = '-';
- }
-
- if (n != 0.0) {
- if (n > 1.0) {
- exp = (int)floor(log10(n));
- }
- else {
- exp = (int)-ceil(-log10(n));
- }
- }
- else {
- exp = 0;
- }
-
- /* preserve significands */
- if (exp < 0) {
- int i, beg = -1, end = 0;
- double f = n;
- double fd = 0;
- for (i = 0; i < FLO_MAX_DIGITS; ++i) {
- f = (f - fd) * 10.0;
- fd = floor(f + FLO_EPSILON);
- if (fd != 0) {
- if (beg < 0) beg = i;
- end = i + 1;
- }
- }
- if (beg >= 0) length = end - beg;
- if (length > FLO_MAX_SIGN_LENGTH) length = FLO_MAX_SIGN_LENGTH;
- }
-
- if (abs(exp) + length >= FLO_MAX_DIGITS) {
- /* exponent representation */
- e = TRUE;
- n = n / pow(10.0, exp);
- if (isinf(n)) {
- if (s < c) { /* s[0] == '-' */
- return mrb_str_new_lit(mrb, "-0.0");
- }
- else {
- return mrb_str_new_lit(mrb, "0.0");
- }
- }
- }
- else {
- /* un-exponent (normal) representation */
- if (exp > 0) {
- m = exp;
- }
- }
-
- /* puts digits */
- while (max_digits >= 0) {
- double weight = (m < 0) ? 0.0 : pow(10.0, m);
- double fdigit = (m < 0) ? n * 10.0 : n / weight;
+ return x * (signbit(y) ? -1.0 : 1.0) * INFINITY;
+ }
+}
- if (fdigit < 0) fdigit = n = 0;
- if (m < -1 && fdigit < FLO_EPSILON) {
- if (e || exp > 0 || m <= -abs(exp)) {
- break;
- }
- }
- digit = (int)floor(fdigit + FLO_EPSILON);
- if (m == 0 && digit > 9) {
- n /= 10.0;
- exp++;
- continue;
- }
- *(c++) = '0' + digit;
- n = (m < 0) ? n * 10.0 - digit : n - (digit * weight);
- max_digits--;
- if (m-- == 0) {
- *(c++) = '.';
- }
- }
- if (c[-1] == '0') {
- while (&s[0] < c && c[-1] == '0') {
- c--;
- }
- c++;
- }
+static mrb_value
+flo_div(mrb_state *mrb, mrb_value x)
+{
+ mrb_value y = mrb_get_arg1(mrb);
+ mrb_float a = mrb_float(x);
- if (e) {
- *(c++) = 'e';
- if (exp > 0) {
- *(c++) = '+';
- }
- else {
- *(c++) = '-';
- exp = -exp;
- }
+ if (mrb_float_p(y)) {
+ a = mrb_div_float(a, mrb_float(y));
+ }
+ else {
+ a = mrb_div_float(a, mrb_as_float(mrb, y));
+ }
+ return mrb_float_value(mrb, a);
+}
- if (exp >= 100) {
- *(c++) = '0' + exp / 100;
- exp -= exp / 100 * 100;
- }
+/* the argument `fmt` is no longer used; you can pass `NULL` */
+mrb_value
+mrb_float_to_str(mrb_state *mrb, mrb_value flo, const char *fmt)
+{
+ char buf[25];
+#ifdef MRB_USE_FLOAT32
+ const int prec = 7;
+#else
+ const int prec = 15;
+#endif
- *(c++) = '0' + exp / 10;
- *(c++) = '0' + exp % 10;
+ mrb_format_float(mrb_float(flo), buf, sizeof(buf), 'g', prec, '\0');
+ for (char *p = buf; *p; p++) {
+ if (*p == '.') goto exit;
+ if (*p == 'e') {
+ memmove(p+2, p, strlen(p)+1);
+ memcpy(p, ".0", 2);
+ goto exit;
}
-
- *c = '\0';
-
- return mrb_str_new(mrb, &s[0], c - &s[0]);
}
+ strcat(buf, ".0");
+ exit:
+ return mrb_str_new_cstr(mrb, buf);
}
/* 15.2.9.3.16(x) */
/*
* call-seq:
* flt.to_s -> string
+ * flt.inspect -> string
*
* Returns a string containing a representation of self. As well as a
* fixed or exponential form of the number, the call may return
- * ``<code>NaN</code>'', ``<code>Infinity</code>'', and
- * ``<code>-Infinity</code>''.
+ * "<code>NaN</code>", "<code>Infinity</code>", and
+ * "<code>-Infinity</code>".
+ *
+ * 3.0.to_s #=> 3.0
+ * 3.25.to_s #=> 3.25
*/
static mrb_value
flo_to_s(mrb_state *mrb, mrb_value flt)
{
- return mrb_flo_to_str(mrb, mrb_float(flt));
+ mrb_float f = mrb_float(flt);
+ mrb_value str;
+
+ if (isinf(f)) {
+ str = f < 0 ? mrb_str_new_lit(mrb, "-Infinity")
+ : mrb_str_new_lit(mrb, "Infinity");
+ }
+ else if (isnan(f)) {
+ str = mrb_str_new_lit(mrb, "NaN");
+ }
+ else {
+ str = mrb_float_to_str(mrb, flt, NULL);
+ }
+
+ RSTR_SET_ASCII_FLAG(mrb_str_ptr(str));
+ return str;
+}
+
+/* 15.2.9.3.1 */
+/*
+ * call-seq:
+ * float + other -> float
+ *
+ * Returns a new float which is the sum of <code>float</code>
+ * and <code>other</code>.
+ */
+static mrb_value
+flo_add(mrb_state *mrb, mrb_value x)
+{
+ mrb_value y = mrb_get_arg1(mrb);
+ mrb_float a = mrb_float(x);
+
+ switch (mrb_type(y)) {
+ case MRB_TT_FLOAT:
+ return mrb_float_value(mrb, a + mrb_float(y));
+#if defined(MRB_USE_COMPLEX)
+ case MRB_TT_COMPLEX:
+ return mrb_funcall_id(mrb, y, MRB_OPSYM(add), 1, x);
+#endif
+ default:
+ return mrb_float_value(mrb, a + mrb_as_float(mrb, y));
+ }
}
/* 15.2.9.3.2 */
@@ -270,12 +372,22 @@ flo_to_s(mrb_state *mrb, mrb_value flt)
*/
static mrb_value
-flo_minus(mrb_state *mrb, mrb_value x)
+flo_sub(mrb_state *mrb, mrb_value x)
{
- mrb_value y;
+ mrb_value y = mrb_get_arg1(mrb);
+ mrb_float a = mrb_float(x);
- mrb_get_args(mrb, "o", &y);
- return mrb_float_value(mrb, mrb_float(x) - mrb_to_flo(mrb, y));
+ switch (mrb_type(y)) {
+ case MRB_TT_FLOAT:
+ return mrb_float_value(mrb, a - mrb_float(y));
+#if defined(MRB_USE_COMPLEX)
+ case MRB_TT_COMPLEX:
+ x = mrb_funcall_id(mrb, y, MRB_OPSYM(sub), 1, x);
+ return mrb_funcall_id(mrb, x, MRB_OPSYM(minus), 0);
+#endif
+ default:
+ return mrb_float_value(mrb, a - mrb_as_float(mrb, y));
+ }
}
/* 15.2.9.3.3 */
@@ -290,34 +402,54 @@ flo_minus(mrb_state *mrb, mrb_value x)
static mrb_value
flo_mul(mrb_state *mrb, mrb_value x)
{
- mrb_value y;
+ mrb_value y = mrb_get_arg1(mrb);
+ mrb_float a = mrb_float(x);
- mrb_get_args(mrb, "o", &y);
- return mrb_float_value(mrb, mrb_float(x) * mrb_to_flo(mrb, y));
+ switch (mrb_type(y)) {
+ case MRB_TT_FLOAT:
+ return mrb_float_value(mrb, a * mrb_float(y));
+#if defined(MRB_USE_COMPLEX)
+ case MRB_TT_COMPLEX:
+ return mrb_funcall_id(mrb, y, MRB_OPSYM(mul), 1, x);
+#endif
+ default:
+ return mrb_float_value(mrb, a * mrb_as_float(mrb, y));
+ }
}
static void
-flodivmod(mrb_state *mrb, mrb_float x, mrb_float y, mrb_float *divp, mrb_float *modp)
+flodivmod(mrb_state *mrb, double x, double y, mrb_float *divp, mrb_float *modp)
{
- mrb_float div;
- mrb_float mod;
+ double div, mod;
+ if (isnan(y)) {
+ /* y is NaN so all results are NaN */
+ div = mod = y;
+ goto exit;
+ }
if (y == 0.0) {
- div = INFINITY;
- mod = NAN;
+ int_zerodiv(mrb);
+ }
+ if (isinf(y) && !isinf(x)) {
+ mod = x;
}
else {
mod = fmod(x, y);
- if (isinf(x) && isfinite(y))
- div = x;
- else
- div = (x - mod) / y;
- if (y*mod < 0) {
- mod += y;
- div -= 1.0;
- }
}
-
+ if (isinf(x) && !isinf(y)) {
+ div = x;
+ }
+ else {
+ div = (x - mod) / y;
+ if (modp && divp) div = round(div);
+ }
+ if (div == 0) div = 0.0;
+ if (mod == 0) mod = 0.0;
+ if (y*mod < 0) {
+ mod += y;
+ div -= 1.0;
+ }
+ exit:
if (modp) *modp = mod;
if (divp) *divp = div;
}
@@ -337,14 +469,13 @@ flodivmod(mrb_state *mrb, mrb_float x, mrb_float y, mrb_float *divp, mrb_float *
static mrb_value
flo_mod(mrb_state *mrb, mrb_value x)
{
- mrb_value y;
+ mrb_value y = mrb_get_arg1(mrb);
mrb_float mod;
- mrb_get_args(mrb, "o", &y);
-
- flodivmod(mrb, mrb_float(x), mrb_to_flo(mrb, y), 0, &mod);
+ flodivmod(mrb, mrb_float(x), mrb_as_float(mrb, y), 0, &mod);
return mrb_float_value(mrb, mod);
}
+#endif
/* 15.2.8.3.16 */
/*
@@ -359,23 +490,22 @@ flo_mod(mrb_state *mrb, mrb_value x)
* (1.0).eql?(1.0) #=> true
*/
static mrb_value
-fix_eql(mrb_state *mrb, mrb_value x)
+int_eql(mrb_state *mrb, mrb_value x)
{
- mrb_value y;
+ mrb_value y = mrb_get_arg1(mrb);
- mrb_get_args(mrb, "o", &y);
- if (!mrb_fixnum_p(y)) return mrb_false_value();
- return mrb_bool_value(mrb_fixnum(x) == mrb_fixnum(y));
+ if (!mrb_integer_p(y)) return mrb_false_value();
+ return mrb_bool_value(mrb_integer(x) == mrb_integer(y));
}
+#ifndef MRB_NO_FLOAT
static mrb_value
flo_eql(mrb_state *mrb, mrb_value x)
{
- mrb_value y;
+ mrb_value y = mrb_get_arg1(mrb);
- mrb_get_args(mrb, "o", &y);
if (!mrb_float_p(y)) return mrb_false_value();
- return mrb_bool_value(mrb_float(x) == (mrb_float)mrb_fixnum(y));
+ return mrb_bool_value(mrb_float(x) == mrb_float(y));
}
/* 15.2.9.3.7 */
@@ -394,43 +524,157 @@ flo_eql(mrb_state *mrb, mrb_value x)
static mrb_value
flo_eq(mrb_state *mrb, mrb_value x)
{
- mrb_value y;
- mrb_get_args(mrb, "o", &y);
+ mrb_value y = mrb_get_arg1(mrb);
switch (mrb_type(y)) {
- case MRB_TT_FIXNUM:
- return mrb_bool_value(mrb_float(x) == (mrb_float)mrb_fixnum(y));
+ case MRB_TT_INTEGER:
+ return mrb_bool_value(mrb_float(x) == (mrb_float)mrb_integer(y));
case MRB_TT_FLOAT:
return mrb_bool_value(mrb_float(x) == mrb_float(y));
+#ifdef MRB_USE_RATIONAL
+ case MRB_TT_RATIONAL:
+ return mrb_bool_value(mrb_float(x) == mrb_as_float(mrb, y));
+#endif
+#ifdef MRB_USE_COMPLEX
+ case MRB_TT_COMPLEX:
+ return mrb_bool_value(mrb_equal(mrb, y, x));
+#endif
default:
return mrb_false_value();
}
}
-/* 15.2.8.3.18 */
-/*
- * call-seq:
- * flt.hash -> integer
- *
- * Returns a hash code for this float.
- */
+static int64_t
+value_int64(mrb_state *mrb, mrb_value x)
+{
+ switch (mrb_type(x)) {
+ case MRB_TT_INTEGER:
+ return (int64_t)mrb_integer(x);
+ case MRB_TT_FLOAT:
+ {
+ double f = mrb_float(x);
+
+ if ((mrb_float)INT64_MAX >= f && f >= (mrb_float)INT64_MIN)
+ return (int64_t)f;
+ }
+ default:
+ mrb_raise(mrb, E_TYPE_ERROR, "cannot convert to Integer");
+ break;
+ }
+ /* not reached */
+ return 0;
+}
+
+static mrb_value
+int64_value(mrb_state *mrb, int64_t v)
+{
+ if (!TYPED_FIXABLE(v,int64_t)) {
+ int_overflow(mrb, "bit operation");
+ }
+ return mrb_fixnum_value((mrb_int)v);
+}
+
+static mrb_value
+flo_rev(mrb_state *mrb, mrb_value x)
+{
+ int64_t v1 = value_int64(mrb, x);
+ return int64_value(mrb, ~v1);
+}
+
+static mrb_value
+flo_and(mrb_state *mrb, mrb_value x)
+{
+ mrb_value y = mrb_get_arg1(mrb);
+ int64_t v1, v2;
+
+ v1 = value_int64(mrb, x);
+ v2 = value_int64(mrb, y);
+ return int64_value(mrb, v1 & v2);
+}
+
static mrb_value
-flo_hash(mrb_state *mrb, mrb_value num)
+flo_or(mrb_state *mrb, mrb_value x)
{
- mrb_float d;
- char *c;
- size_t i;
- int hash;
+ mrb_value y = mrb_get_arg1(mrb);
+ int64_t v1, v2;
- d = (mrb_float)mrb_fixnum(num);
- /* normalize -0.0 to 0.0 */
- if (d == 0) d = 0.0;
- c = (char*)&d;
- for (hash=0, i=0; i<sizeof(mrb_float);i++) {
- hash = (hash * 971) ^ (unsigned char)c[i];
+ v1 = value_int64(mrb, x);
+ v2 = value_int64(mrb, y);
+ return int64_value(mrb, v1 | v2);
+}
+
+static mrb_value
+flo_xor(mrb_state *mrb, mrb_value x)
+{
+ mrb_value y = mrb_get_arg1(mrb);
+ int64_t v1, v2;
+
+ v1 = value_int64(mrb, x);
+ v2 = value_int64(mrb, y);
+ return int64_value(mrb, v1 ^ v2);
+}
+
+static mrb_value
+flo_shift(mrb_state *mrb, mrb_value x, mrb_int width)
+{
+ mrb_float val;
+
+ if (width == 0) {
+ return x;
+ }
+ val = mrb_float(x);
+ if (width < -MRB_INT_BIT/2) {
+ if (val < 0) return mrb_fixnum_value(-1);
+ return mrb_fixnum_value(0);
+ }
+ if (width < 0) {
+ while (width++) {
+ val /= 2;
+ if (val < 1.0) {
+ val = 0;
+ break;
+ }
+ }
+#if defined(_ISOC99_SOURCE)
+ val = trunc(val);
+#else
+ if (val > 0){
+ val = floor(val);
+ } else {
+ val = ceil(val);
+ }
+#endif
+ if (val == 0 && mrb_float(x) < 0) {
+ return mrb_fixnum_value(-1);
+ }
}
- if (hash < 0) hash = -hash;
- return mrb_fixnum_value(hash);
+ else {
+ while (width--) {
+ val *= 2;
+ }
+ }
+ if (FIXABLE_FLOAT(val))
+ return mrb_int_value(mrb, (mrb_int)val);
+ return mrb_float_value(mrb, val);
+}
+
+static mrb_value
+flo_rshift(mrb_state *mrb, mrb_value x)
+{
+ mrb_int width;
+
+ mrb_get_args(mrb, "i", &width);
+ if (width == MRB_INT_MIN) return flo_shift(mrb, x, -MRB_INT_BIT);
+ return flo_shift(mrb, x, -width);
+}
+
+static mrb_value
+flo_lshift(mrb_state *mrb, mrb_value x)
+{
+ mrb_int width;
+
+ mrb_get_args(mrb, "i", &width);
+ return flo_shift(mrb, x, width);
}
/* 15.2.9.3.13 */
@@ -488,53 +732,138 @@ flo_finite_p(mrb_state *mrb, mrb_value num)
return mrb_bool_value(isfinite(mrb_float(num)));
}
+void
+mrb_check_num_exact(mrb_state *mrb, mrb_float num)
+{
+ if (isinf(num)) {
+ mrb_raise(mrb, E_FLOATDOMAIN_ERROR, num < 0 ? "-Infinity" : "Infinity");
+ }
+ if (isnan(num)) {
+ mrb_raise(mrb, E_FLOATDOMAIN_ERROR, "NaN");
+ }
+}
+
+static mrb_value
+flo_ceil_floor(mrb_state *mrb, mrb_value num, double (*func)(double))
+{
+ mrb_float f = mrb_float(num);
+ mrb_int ndigits = 0;
+#ifdef MRB_USE_FLOAT32
+ const int fprec = 7;
+#else
+ const int fprec = 15;
+#endif
+
+ mrb_get_args(mrb, "|i", &ndigits);
+ if (f == 0.0) {
+ return ndigits > 0 ? mrb_float_value(mrb, f) : mrb_fixnum_value(0);
+ }
+ if (ndigits > 0) {
+ if (ndigits > fprec) return num;
+ mrb_float d = pow(10, ndigits);
+ f = func(f * d) / d;
+ return mrb_float_value(mrb, f);
+ }
+ if (ndigits < 0) {
+ mrb_float d = pow(10, -ndigits);
+ f = func(f / d) * d;
+ }
+ else { /* ndigits == 0 */
+ f = func(f);
+ }
+ mrb_check_num_exact(mrb, f);
+ return mrb_int_value(mrb, (mrb_int)f);
+}
+
/* 15.2.9.3.10 */
/*
* call-seq:
- * flt.floor -> integer
+ * float.floor([ndigits]) -> integer or float
+ *
+ * Returns the largest number less than or equal to +float+ with
+ * a precision of +ndigits+ decimal digits (default: 0).
*
- * Returns the largest integer less than or equal to <i>flt</i>.
+ * When the precision is negative, the returned value is an integer
+ * with at least <code>ndigits.abs</code> trailing zeros.
+ *
+ * Returns a floating point number when +ndigits+ is positive,
+ * otherwise returns an integer.
*
* 1.2.floor #=> 1
* 2.0.floor #=> 2
* (-1.2).floor #=> -2
* (-2.0).floor #=> -2
+ *
+ * 1.234567.floor(2) #=> 1.23
+ * 1.234567.floor(3) #=> 1.234
+ * 1.234567.floor(4) #=> 1.2345
+ * 1.234567.floor(5) #=> 1.23456
+ *
+ * 34567.89.floor(-5) #=> 0
+ * 34567.89.floor(-4) #=> 30000
+ * 34567.89.floor(-3) #=> 34000
+ * 34567.89.floor(-2) #=> 34500
+ * 34567.89.floor(-1) #=> 34560
+ * 34567.89.floor(0) #=> 34567
+ * 34567.89.floor(1) #=> 34567.8
+ * 34567.89.floor(2) #=> 34567.89
+ * 34567.89.floor(3) #=> 34567.89
+ *
+ * Note that the limited precision of floating point arithmetic
+ * might lead to surprising results:
+ *
+ * (0.3 / 0.1).floor #=> 2 (!)
*/
-
static mrb_value
flo_floor(mrb_state *mrb, mrb_value num)
{
- mrb_float f = floor(mrb_float(num));
-
- if (!FIXABLE(f)) {
- return mrb_float_value(mrb, f);
- }
- return mrb_fixnum_value((mrb_int)f);
+ return flo_ceil_floor(mrb, num, floor);
}
/* 15.2.9.3.8 */
/*
* call-seq:
- * flt.ceil -> integer
+ * float.ceil([ndigits]) -> integer or float
+ *
+ * Returns the smallest number greater than or equal to +float+ with
+ * a precision of +ndigits+ decimal digits (default: 0).
*
- * Returns the smallest <code>Integer</code> greater than or equal to
- * <i>flt</i>.
+ * When the precision is negative, the returned value is an integer
+ * with at least <code>ndigits.abs</code> trailing zeros.
+ *
+ * Returns a floating point number when +ndigits+ is positive,
+ * otherwise returns an integer.
*
* 1.2.ceil #=> 2
* 2.0.ceil #=> 2
* (-1.2).ceil #=> -1
* (-2.0).ceil #=> -2
+ *
+ * 1.234567.ceil(2) #=> 1.24
+ * 1.234567.ceil(3) #=> 1.235
+ * 1.234567.ceil(4) #=> 1.2346
+ * 1.234567.ceil(5) #=> 1.23457
+ *
+ * 34567.89.ceil(-5) #=> 100000
+ * 34567.89.ceil(-4) #=> 40000
+ * 34567.89.ceil(-3) #=> 35000
+ * 34567.89.ceil(-2) #=> 34600
+ * 34567.89.ceil(-1) #=> 34570
+ * 34567.89.ceil(0) #=> 34568
+ * 34567.89.ceil(1) #=> 34567.9
+ * 34567.89.ceil(2) #=> 34567.89
+ * 34567.89.ceil(3) #=> 34567.89
+ *
+ * Note that the limited precision of floating point arithmetic
+ * might lead to surprising results:
+ *
+ * (2.1 / 0.7).ceil #=> 4 (!)
*/
static mrb_value
flo_ceil(mrb_state *mrb, mrb_value num)
{
- mrb_float f = ceil(mrb_float(num));
-
- if (!FIXABLE(f)) {
- return mrb_float_value(mrb, f);
- }
- return mrb_fixnum_value((mrb_int)f);
+ return flo_ceil_floor(mrb, num, ceil);
}
/* 15.2.9.3.12 */
@@ -543,7 +872,7 @@ flo_ceil(mrb_state *mrb, mrb_value num)
* flt.round([ndigits]) -> integer or float
*
* Rounds <i>flt</i> to a given precision in decimal digits (default 0 digits).
- * Precision may be negative. Returns a floating point number when ndigits
+ * Precision may be negative. Returns a floating-point number when ndigits
* is more than zero.
*
* 1.4.round #=> 1
@@ -573,22 +902,19 @@ flo_round(mrb_state *mrb, mrb_value num)
{
double number, f;
mrb_int ndigits = 0;
- int i;
+ mrb_int i;
mrb_get_args(mrb, "|i", &ndigits);
number = mrb_float(num);
- if (isinf(number)) {
- if (0 < ndigits) return num;
- else mrb_raise(mrb, E_FLOATDOMAIN_ERROR, number < 0 ? "-Infinity" : "Infinity");
- }
- if (isnan(number)) {
- if (0 < ndigits) return num;
- else mrb_raise(mrb, E_FLOATDOMAIN_ERROR, "NaN");
+ if (0 < ndigits && (isinf(number) || isnan(number))) {
+ return num;
}
+ mrb_check_num_exact(mrb, number);
f = 1.0;
- i = abs(ndigits);
+ i = ndigits >= 0 ? ndigits : -ndigits;
+ if (ndigits > DBL_DIG+2) return num;
while (--i >= 0)
f = f*10.0;
@@ -619,15 +945,28 @@ flo_round(mrb_state *mrb, mrb_value num)
if (!isfinite(number)) return num;
return mrb_float_value(mrb, number);
}
- return mrb_fixnum_value((mrb_int)number);
+ if (!FIXABLE_FLOAT(number))
+ return mrb_float_value(mrb, number);
+ return mrb_int_value(mrb, (mrb_int)number);
}
/* 15.2.9.3.14 */
+static mrb_value
+flo_to_i(mrb_state *mrb, mrb_value num)
+{
+ mrb_float f = mrb_float(num);
+
+ if (f > 0.0) f = floor(f);
+ if (f < 0.0) f = ceil(f);
+
+ mrb_check_num_exact(mrb, f);
+ return mrb_int_value(mrb, (mrb_int)f);
+}
+
/* 15.2.9.3.15 */
/*
* call-seq:
* flt.to_i -> integer
- * flt.to_int -> integer
* flt.truncate -> integer
*
* Returns <i>flt</i> truncated to an <code>Integer</code>.
@@ -636,15 +975,8 @@ flo_round(mrb_state *mrb, mrb_value num)
static mrb_value
flo_truncate(mrb_state *mrb, mrb_value num)
{
- mrb_float f = mrb_float(num);
-
- if (f > 0.0) f = floor(f);
- if (f < 0.0) f = ceil(f);
-
- if (!FIXABLE(f)) {
- return mrb_float_value(mrb, f);
- }
- return mrb_fixnum_value((mrb_int)f);
+ if (signbit(mrb_float(num))) return flo_ceil(mrb, num);
+ return flo_floor(mrb, num);
}
static mrb_value
@@ -652,12 +984,12 @@ flo_nan_p(mrb_state *mrb, mrb_value num)
{
return mrb_bool_value(isnan(mrb_float(num)));
}
+#endif
/*
* Document-class: Integer
*
- * <code>Integer</code> is the basis for the two concrete classes that
- * hold whole numbers, <code>Bignum</code> and <code>Fixnum</code>.
+ * <code>Integer</code> is hold whole numbers.
*
*/
@@ -665,7 +997,6 @@ flo_nan_p(mrb_state *mrb, mrb_value num)
/*
* call-seq:
* int.to_i -> integer
- * int.to_int -> integer
*
* As <i>int</i> is already an <code>Integer</code>, all these
* methods simply return the receiver.
@@ -677,31 +1008,59 @@ int_to_i(mrb_state *mrb, mrb_value num)
return num;
}
-/*tests if N*N would overflow*/
-#define SQRT_INT_MAX ((mrb_int)1<<((MRB_INT_BIT-1-MRB_FIXNUM_SHIFT)/2))
-#define FIT_SQRT_INT(n) (((n)<SQRT_INT_MAX)&&((n)>=-SQRT_INT_MAX))
-
-mrb_value
-mrb_fixnum_mul(mrb_state *mrb, mrb_value x, mrb_value y)
+static mrb_value
+fixnum_mul(mrb_state *mrb, mrb_value x, mrb_value y)
{
mrb_int a;
- a = mrb_fixnum(x);
- if (mrb_fixnum_p(y)) {
- mrb_float c;
- mrb_int b;
+ a = mrb_integer(x);
+ if (mrb_integer_p(y)) {
+ mrb_int b, c;
if (a == 0) return x;
- b = mrb_fixnum(y);
- if (FIT_SQRT_INT(a) && FIT_SQRT_INT(b))
- return mrb_fixnum_value(a*b);
- c = a * b;
- if ((a != 0 && c/a != b) || !FIXABLE(c)) {
- return mrb_float_value(mrb, (mrb_float)a*(mrb_float)b);
+ b = mrb_integer(y);
+ if (mrb_int_mul_overflow(a, b, &c)) {
+ int_overflow(mrb, "multiplication");
}
- return mrb_fixnum_value((mrb_int)c);
+ return mrb_int_value(mrb, c);
+ }
+ switch (mrb_type(y)) {
+#if defined(MRB_USE_RATIONAL) || defined(MRB_USE_COMPLEX)
+ case MRB_TT_RATIONAL:
+ case MRB_TT_COMPLEX:
+ return mrb_funcall_id(mrb, y, MRB_OPSYM(mul), 1, x);
+#endif
+ default:
+#ifdef MRB_NO_FLOAT
+ mrb_raise(mrb, E_TYPE_ERROR, "non integer multiplication");
+#else
+ return mrb_float_value(mrb, (mrb_float)a * mrb_as_float(mrb, y));
+#endif
}
- return mrb_float_value(mrb, (mrb_float)a * mrb_to_flo(mrb, y));
+}
+
+MRB_API mrb_value
+mrb_num_mul(mrb_state *mrb, mrb_value x, mrb_value y)
+{
+ if (mrb_integer_p(x)) {
+ return fixnum_mul(mrb, x, y);
+ }
+#ifndef MRB_NO_FLOAT
+ if (mrb_float_p(x)) {
+ return mrb_float_value(mrb, mrb_float(x) * mrb_as_float(mrb, y));
+ }
+#endif
+#if defined(MRB_USE_RATIONAL) || defined(MRB_USE_COMPLEX)
+ switch (mrb_type(x)) {
+ case MRB_TT_RATIONAL:
+ case MRB_TT_COMPLEX:
+ return mrb_funcall_id(mrb, x, MRB_OPSYM(mul), 1, y);
+ default:
+ break;
+ }
+#endif
+ mrb_raise(mrb, E_TYPE_ERROR, "no number multiply");
+ return mrb_nil_value(); /* not reached */
}
/* 15.2.8.3.3 */
@@ -715,40 +1074,33 @@ mrb_fixnum_mul(mrb_state *mrb, mrb_value x, mrb_value y)
*/
static mrb_value
-fix_mul(mrb_state *mrb, mrb_value x)
+int_mul(mrb_state *mrb, mrb_value x)
{
- mrb_value y;
+ mrb_value y = mrb_get_arg1(mrb);
- mrb_get_args(mrb, "o", &y);
- return mrb_fixnum_mul(mrb, x, y);
+ return fixnum_mul(mrb, x, y);
}
static void
fixdivmod(mrb_state *mrb, mrb_int x, mrb_int y, mrb_int *divp, mrb_int *modp)
{
- mrb_int div, mod;
-
- /* TODO: add mrb_assert(y != 0) to make sure */
-
- if (y < 0) {
- if (x < 0)
- div = -x / -y;
- else
- div = - (x / -y);
+ if (y == 0) {
+ int_zerodiv(mrb);
}
- else {
- if (x < 0)
- div = - (-x / y);
- else
- div = x / y;
+ else if(x == MRB_INT_MIN && y == -1) {
+ int_overflow(mrb, "division");
}
- mod = x - div*y;
- if ((mod < 0 && y > 0) || (mod > 0 && y < 0)) {
- mod += y;
- div -= 1;
+ else {
+ mrb_int div = x / y;
+ mrb_int mod = x - div * y;
+
+ if ((x ^ y) < 0 && x != div * y) {
+ mod += y;
+ div -= 1;
+ }
+ if (divp) *divp = div;
+ if (modp) *modp = mod;
}
- if (divp) *divp = div;
- if (modp) *modp = mod;
}
/* 15.2.8.3.5 */
@@ -762,28 +1114,28 @@ fixdivmod(mrb_state *mrb, mrb_int x, mrb_int y, mrb_int *divp, mrb_int *modp)
*/
static mrb_value
-fix_mod(mrb_state *mrb, mrb_value x)
+int_mod(mrb_state *mrb, mrb_value x)
{
- mrb_value y;
- mrb_int a;
+ mrb_value y = mrb_get_arg1(mrb);
+ mrb_int a, b;
- mrb_get_args(mrb, "o", &y);
- a = mrb_fixnum(x);
- if (mrb_fixnum_p(y)) {
- mrb_int b, mod;
+ a = mrb_integer(x);
+ if (mrb_integer_p(y) && a != MRB_INT_MIN && (b=mrb_integer(y)) != MRB_INT_MIN) {
+ mrb_int mod;
- if ((b=mrb_fixnum(y)) == 0) {
- return mrb_float_value(mrb, NAN);
- }
- fixdivmod(mrb, a, b, 0, &mod);
+ fixdivmod(mrb, a, b, NULL, &mod);
return mrb_fixnum_value(mod);
}
+#ifdef MRB_NO_FLOAT
+ mrb_raise(mrb, E_TYPE_ERROR, "non integer modulo");
+#else
else {
mrb_float mod;
- flodivmod(mrb, (mrb_float)a, mrb_to_flo(mrb, y), 0, &mod);
+ flodivmod(mrb, (mrb_float)a, mrb_as_float(mrb, y), NULL, &mod);
return mrb_float_value(mrb, mod);
}
+#endif
}
/*
@@ -793,47 +1145,48 @@ fix_mod(mrb_state *mrb, mrb_value x)
* See <code>Numeric#divmod</code>.
*/
static mrb_value
-fix_divmod(mrb_state *mrb, mrb_value x)
+int_divmod(mrb_state *mrb, mrb_value x)
{
- mrb_value y;
+ mrb_value y = mrb_get_arg1(mrb);
- mrb_get_args(mrb, "o", &y);
-
- if (mrb_fixnum_p(y)) {
+ if (mrb_integer_p(y)) {
mrb_int div, mod;
- if (mrb_fixnum(y) == 0) {
- return mrb_assoc_new(mrb, mrb_float_value(mrb, INFINITY),
- mrb_float_value(mrb, NAN));
- }
- fixdivmod(mrb, mrb_fixnum(x), mrb_fixnum(y), &div, &mod);
- return mrb_assoc_new(mrb, mrb_fixnum_value(div), mrb_fixnum_value(mod));
+ fixdivmod(mrb, mrb_integer(x), mrb_integer(y), &div, &mod);
+ return mrb_assoc_new(mrb, mrb_int_value(mrb, div), mrb_int_value(mrb, mod));
}
+#ifdef MRB_NO_FLOAT
+ mrb_raise(mrb, E_TYPE_ERROR, "non integer divmod");
+#else
else {
mrb_float div, mod;
mrb_value a, b;
- flodivmod(mrb, (mrb_float)mrb_fixnum(x), mrb_to_flo(mrb, y), &div, &mod);
- a = mrb_float_value(mrb, (mrb_int)div);
+ flodivmod(mrb, (mrb_float)mrb_integer(x), mrb_as_float(mrb, y), &div, &mod);
+ a = mrb_int_value(mrb, (mrb_int)div);
b = mrb_float_value(mrb, mod);
return mrb_assoc_new(mrb, a, b);
}
+#endif
}
+#ifndef MRB_NO_FLOAT
static mrb_value
flo_divmod(mrb_state *mrb, mrb_value x)
{
- mrb_value y;
+ mrb_value y = mrb_get_arg1(mrb);
mrb_float div, mod;
mrb_value a, b;
- mrb_get_args(mrb, "o", &y);
-
- flodivmod(mrb, mrb_float(x), mrb_to_flo(mrb, y), &div, &mod);
- a = mrb_float_value(mrb, (mrb_int)div);
+ flodivmod(mrb, mrb_float(x), mrb_as_float(mrb, y), &div, &mod);
+ if (!FIXABLE_FLOAT(div))
+ a = mrb_float_value(mrb, div);
+ else
+ a = mrb_int_value(mrb, (mrb_int)div);
b = mrb_float_value(mrb, mod);
return mrb_assoc_new(mrb, a, b);
}
+#endif
/* 15.2.8.3.7 */
/*
@@ -848,16 +1201,25 @@ flo_divmod(mrb_state *mrb, mrb_value x)
*/
static mrb_value
-fix_equal(mrb_state *mrb, mrb_value x)
+int_equal(mrb_state *mrb, mrb_value x)
{
- mrb_value y;
+ mrb_value y = mrb_get_arg1(mrb);
- mrb_get_args(mrb, "o", &y);
switch (mrb_type(y)) {
- case MRB_TT_FIXNUM:
- return mrb_bool_value(mrb_fixnum(x) == mrb_fixnum(y));
+ case MRB_TT_INTEGER:
+ return mrb_bool_value(mrb_integer(x) == mrb_integer(y));
+#ifndef MRB_NO_FLOAT
case MRB_TT_FLOAT:
- return mrb_bool_value((mrb_float)mrb_fixnum(x) == mrb_float(y));
+ return mrb_bool_value((mrb_float)mrb_integer(x) == mrb_float(y));
+#endif
+#ifdef MRB_USE_RATIONAL
+ case MRB_TT_RATIONAL:
+ return mrb_bool_value(mrb_equal(mrb, y, x));
+#endif
+#ifdef MRB_USE_COMPLEX
+ case MRB_TT_COMPLEX:
+ return mrb_bool_value(mrb_equal(mrb, y, x));
+#endif
default:
return mrb_false_value();
}
@@ -875,24 +1237,26 @@ fix_equal(mrb_state *mrb, mrb_value x)
*/
static mrb_value
-fix_rev(mrb_state *mrb, mrb_value num)
+int_rev(mrb_state *mrb, mrb_value num)
{
- mrb_int val = mrb_fixnum(num);
+ mrb_int val = mrb_integer(num);
- return mrb_fixnum_value(~val);
+ return mrb_int_value(mrb, ~val);
}
-static mrb_value
-bit_coerce(mrb_state *mrb, mrb_value x)
-{
- while (!mrb_fixnum_p(x)) {
- if (mrb_float_p(x)) {
- mrb_raise(mrb, E_TYPE_ERROR, "can't convert Float into Integer");
- }
- x = mrb_to_int(mrb, x);
- }
- return x;
-}
+#ifdef MRB_NO_FLOAT
+#define bit_op(x,y,op1,op2) do {\
+ return mrb_int_value(mrb, (mrb_integer(x) op2 mrb_integer(y)));\
+} while(0)
+#else
+static mrb_value flo_and(mrb_state *mrb, mrb_value x);
+static mrb_value flo_or(mrb_state *mrb, mrb_value x);
+static mrb_value flo_xor(mrb_state *mrb, mrb_value x);
+#define bit_op(x,y,op1,op2) do {\
+ if (mrb_integer_p(y)) return mrb_int_value(mrb, (mrb_integer(x) op2 mrb_integer(y))); \
+ return flo_ ## op1(mrb, mrb_float_value(mrb, (mrb_float)mrb_integer(x)));\
+} while(0)
+#endif
/* 15.2.8.3.9 */
/*
@@ -903,14 +1267,11 @@ bit_coerce(mrb_state *mrb, mrb_value x)
*/
static mrb_value
-fix_and(mrb_state *mrb, mrb_value x)
+int_and(mrb_state *mrb, mrb_value x)
{
- mrb_value y;
-
- mrb_get_args(mrb, "o", &y);
+ mrb_value y = mrb_get_arg1(mrb);
- y = bit_coerce(mrb, y);
- return mrb_fixnum_value(mrb_fixnum(x) & mrb_fixnum(y));
+ bit_op(x, y, and, &);
}
/* 15.2.8.3.10 */
@@ -922,14 +1283,11 @@ fix_and(mrb_state *mrb, mrb_value x)
*/
static mrb_value
-fix_or(mrb_state *mrb, mrb_value x)
+int_or(mrb_state *mrb, mrb_value x)
{
- mrb_value y;
+ mrb_value y = mrb_get_arg1(mrb);
- mrb_get_args(mrb, "o", &y);
-
- y = bit_coerce(mrb, y);
- return mrb_fixnum_value(mrb_fixnum(x) | mrb_fixnum(y));
+ bit_op(x, y, or, |);
}
/* 15.2.8.3.11 */
@@ -941,14 +1299,11 @@ fix_or(mrb_state *mrb, mrb_value x)
*/
static mrb_value
-fix_xor(mrb_state *mrb, mrb_value x)
+int_xor(mrb_state *mrb, mrb_value x)
{
- mrb_value y;
-
- mrb_get_args(mrb, "o", &y);
+ mrb_value y = mrb_get_arg1(mrb);
- y = bit_coerce(mrb, y);
- return mrb_fixnum_value(mrb_fixnum(x) ^ mrb_fixnum(y));
+ bit_op(x, y, or, ^);
}
#define NUMERIC_SHIFT_WIDTH_MAX (MRB_INT_BIT-1)
@@ -957,16 +1312,24 @@ static mrb_value
lshift(mrb_state *mrb, mrb_int val, mrb_int width)
{
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));
+ if (val > 0) {
+ if ((width > NUMERIC_SHIFT_WIDTH_MAX) ||
+ (val > (MRB_INT_MAX >> width))) {
+ int_overflow(mrb, "bit shift");
+ }
+ return mrb_int_value(mrb, val << width);
+ }
+ else {
+ if ((width > NUMERIC_SHIFT_WIDTH_MAX) ||
+ (val <= (MRB_INT_MIN >> width))) {
+ int_overflow(mrb, "bit shift");
+ }
+ return mrb_int_value(mrb, (val * ((mrb_int)1 << width)));
}
- return mrb_fixnum_value(val << width);
}
static mrb_value
-rshift(mrb_int val, mrb_int width)
+rshift(mrb_state *mrb, mrb_int val, mrb_int width)
{
mrb_assert(width >= 0);
if (width >= NUMERIC_SHIFT_WIDTH_MAX) {
@@ -975,39 +1338,31 @@ rshift(mrb_int val, mrb_int width)
}
return mrb_fixnum_value(0);
}
- return mrb_fixnum_value(val >> width);
-}
-
-static inline void
-fix_shift_get_width(mrb_state *mrb, mrb_int *width)
-{
- mrb_value y;
-
- mrb_get_args(mrb, "o", &y);
- *width = mrb_fixnum(bit_coerce(mrb, y));
+ return mrb_int_value(mrb, val >> width);
}
/* 15.2.8.3.12 */
/*
* call-seq:
- * fix << count -> integer
+ * fix << count -> integer or float
*
* Shifts _fix_ left _count_ positions (right if _count_ is negative).
*/
static mrb_value
-fix_lshift(mrb_state *mrb, mrb_value x)
+int_lshift(mrb_state *mrb, mrb_value x)
{
mrb_int width, val;
- fix_shift_get_width(mrb, &width);
-
+ mrb_get_args(mrb, "i", &width);
if (width == 0) {
return x;
}
- val = mrb_fixnum(x);
+ val = mrb_integer(x);
+ if (val == 0) return x;
if (width < 0) {
- return rshift(val, -width);
+ if (width == MRB_INT_MIN) return rshift(mrb, val, MRB_INT_BIT);
+ return rshift(mrb, val, -width);
}
return lshift(mrb, val, width);
}
@@ -1015,26 +1370,27 @@ fix_lshift(mrb_state *mrb, mrb_value x)
/* 15.2.8.3.13 */
/*
* call-seq:
- * fix >> count -> integer
+ * fix >> count -> integer or float
*
* Shifts _fix_ right _count_ positions (left if _count_ is negative).
*/
static mrb_value
-fix_rshift(mrb_state *mrb, mrb_value x)
+int_rshift(mrb_state *mrb, mrb_value x)
{
mrb_int width, val;
- fix_shift_get_width(mrb, &width);
-
+ mrb_get_args(mrb, "i", &width);
if (width == 0) {
return x;
}
- val = mrb_fixnum(x);
+ val = mrb_integer(x);
+ if (val == 0) return x;
if (width < 0) {
+ if (width == MRB_INT_MIN) int_overflow(mrb, "bit shift");
return lshift(mrb, val, -width);
}
- return rshift(val, width);
+ return rshift(mrb, val, width);
}
/* 15.2.8.3.23 */
@@ -1046,10 +1402,11 @@ fix_rshift(mrb_state *mrb, mrb_value x)
*
*/
+#ifndef MRB_NO_FLOAT
static mrb_value
-fix_to_f(mrb_state *mrb, mrb_value num)
+int_to_f(mrb_state *mrb, mrb_value num)
{
- return mrb_float_value(mrb, (mrb_float)mrb_fixnum(num));
+ return mrb_float_value(mrb, (mrb_float)mrb_integer(num));
}
/*
@@ -1059,7 +1416,7 @@ fix_to_f(mrb_state *mrb, mrb_value num)
* (in particular infinite or NaN)
* to numerical classes which don't support them.
*
- * Float::INFINITY.to_r
+ * Float::INFINITY.to_i
*
* <em>raises the exception:</em>
*
@@ -1067,45 +1424,81 @@ fix_to_f(mrb_state *mrb, mrb_value num)
*/
/* ------------------------------------------------------------------------*/
MRB_API mrb_value
-mrb_flo_to_fixnum(mrb_state *mrb, mrb_value x)
+mrb_float_to_integer(mrb_state *mrb, mrb_value x)
{
- mrb_int z;
+ mrb_int z = 0;
if (!mrb_float_p(x)) {
mrb_raise(mrb, E_TYPE_ERROR, "non float value");
- z = 0; /* not reached. just suppress warnings. */
}
else {
mrb_float d = mrb_float(x);
- if (isinf(d)) {
- mrb_raise(mrb, E_FLOATDOMAIN_ERROR, d < 0 ? "-Infinity" : "Infinity");
+ mrb_check_num_exact(mrb, d);
+ if (FIXABLE_FLOAT(d)) {
+ z = (mrb_int)d;
}
- if (isnan(d)) {
- mrb_raise(mrb, E_FLOATDOMAIN_ERROR, "NaN");
+ else {
+ mrb_raisef(mrb, E_RANGE_ERROR, "number (%v) too big for integer", x);
}
- z = (mrb_int)d;
}
- return mrb_fixnum_value(z);
+ return mrb_int_value(mrb, z);
}
+#endif
-mrb_value
-mrb_fixnum_plus(mrb_state *mrb, mrb_value x, mrb_value y)
+static mrb_value
+int_plus(mrb_state *mrb, mrb_value x, mrb_value y)
{
mrb_int a;
- a = mrb_fixnum(x);
- if (mrb_fixnum_p(y)) {
+ a = mrb_integer(x);
+ if (mrb_integer_p(y)) {
mrb_int b, c;
if (a == 0) return y;
- b = mrb_fixnum(y);
+ b = mrb_integer(y);
if (mrb_int_add_overflow(a, b, &c)) {
- return mrb_float_value(mrb, (mrb_float)a + (mrb_float)b);
+ int_overflow(mrb, "addition");
}
- return mrb_fixnum_value(c);
+ return mrb_int_value(mrb, c);
+ }
+ switch (mrb_type(y)) {
+#if defined(MRB_USE_RATIONAL) || defined(MRB_USE_COMPLEX)
+ case MRB_TT_RATIONAL:
+ case MRB_TT_COMPLEX:
+ return mrb_funcall_id(mrb, y, MRB_OPSYM(add), 1, x);
+#endif
+ default:
+#ifdef MRB_NO_FLOAT
+ mrb_raise(mrb, E_TYPE_ERROR, "non integer addition");
+#else
+ return mrb_float_value(mrb, (mrb_float)a + mrb_as_float(mrb, y));
+#endif
+ }
+}
+
+MRB_API mrb_value
+mrb_num_plus(mrb_state *mrb, mrb_value x, mrb_value y)
+{
+ if (mrb_integer_p(x)) {
+ return int_plus(mrb, x, y);
+ }
+#ifndef MRB_NO_FLOAT
+ if (mrb_float_p(x)) {
+ return mrb_float_value(mrb, mrb_float(x) + mrb_as_float(mrb, y));
+ }
+#endif
+#if defined(MRB_USE_RATIONAL) || defined(MRB_USE_COMPLEX)
+ switch (mrb_type(x)) {
+ case MRB_TT_RATIONAL:
+ case MRB_TT_COMPLEX:
+ return mrb_funcall_id(mrb, x, MRB_OPSYM(add), 1, y);
+ default:
+ break;
}
- return mrb_float_value(mrb, (mrb_float)a + mrb_to_flo(mrb, y));
+#endif
+ mrb_raise(mrb, E_TYPE_ERROR, "no number addition");
+ return mrb_nil_value(); /* not reached */
}
/* 15.2.8.3.1 */
@@ -1118,30 +1511,66 @@ mrb_fixnum_plus(mrb_state *mrb, mrb_value x, mrb_value y)
* result.
*/
static mrb_value
-fix_plus(mrb_state *mrb, mrb_value self)
+int_add(mrb_state *mrb, mrb_value self)
{
- mrb_value other;
+ mrb_value other = mrb_get_arg1(mrb);
- mrb_get_args(mrb, "o", &other);
- return mrb_fixnum_plus(mrb, self, other);
+ return int_plus(mrb, self, other);
}
-mrb_value
-mrb_fixnum_minus(mrb_state *mrb, mrb_value x, mrb_value y)
+static mrb_value
+int_minus(mrb_state *mrb, mrb_value x, mrb_value y)
{
mrb_int a;
- a = mrb_fixnum(x);
- if (mrb_fixnum_p(y)) {
+ a = mrb_integer(x);
+ if (mrb_integer_p(y)) {
mrb_int b, c;
- b = mrb_fixnum(y);
+ b = mrb_integer(y);
if (mrb_int_sub_overflow(a, b, &c)) {
- return mrb_float_value(mrb, (mrb_float)a - (mrb_float)b);
+ int_overflow(mrb, "subtraction");
}
- return mrb_fixnum_value(c);
+ return mrb_int_value(mrb, c);
+ }
+ switch (mrb_type(y)) {
+#if defined(MRB_USE_RATIONAL) || defined(MRB_USE_COMPLEX)
+ case MRB_TT_RATIONAL:
+ case MRB_TT_COMPLEX:
+ x = mrb_funcall_id(mrb, y, MRB_OPSYM(sub), 1, x);
+ return mrb_funcall_id(mrb, x, MRB_OPSYM(minus), 0);
+#endif
+ default:
+#ifdef MRB_NO_FLOAT
+ mrb_raise(mrb, E_TYPE_ERROR, "non integer subtraction");
+#else
+ return mrb_float_value(mrb, (mrb_float)a - mrb_as_float(mrb, y));
+#endif
}
- return mrb_float_value(mrb, (mrb_float)a - mrb_to_flo(mrb, y));
+}
+
+MRB_API mrb_value
+mrb_num_minus(mrb_state *mrb, mrb_value x, mrb_value y)
+{
+ if (mrb_integer_p(x)) {
+ return int_minus(mrb, x, y);
+ }
+#ifndef MRB_NO_FLOAT
+ if (mrb_float_p(x)) {
+ return mrb_float_value(mrb, mrb_float(x) - mrb_as_float(mrb, y));
+ }
+#endif
+#if defined(MRB_USE_RATIONAL) || defined(MRB_USE_COMPLEX)
+ switch (mrb_type(x)) {
+ case MRB_TT_RATIONAL:
+ case MRB_TT_COMPLEX:
+ return mrb_funcall_id(mrb, x, MRB_OPSYM(sub), 1, y);
+ default:
+ break;
+ }
+#endif
+ mrb_raise(mrb, E_TYPE_ERROR, "no number subtraction");
+ return mrb_nil_value(); /* not reached */
}
/* 15.2.8.3.2 */
@@ -1155,42 +1584,60 @@ mrb_fixnum_minus(mrb_state *mrb, mrb_value x, mrb_value y)
* result.
*/
static mrb_value
-fix_minus(mrb_state *mrb, mrb_value self)
+int_sub(mrb_state *mrb, mrb_value self)
{
- mrb_value other;
+ mrb_value other = mrb_get_arg1(mrb);
- mrb_get_args(mrb, "o", &other);
- return mrb_fixnum_minus(mrb, self, other);
+ return int_minus(mrb, self, other);
}
-
-MRB_API mrb_value
-mrb_fixnum_to_str(mrb_state *mrb, mrb_value x, int base)
+MRB_API char*
+mrb_int_to_cstr(char *buf, size_t len, mrb_int n, mrb_int base)
{
- char buf[MRB_INT_BIT+1];
- char *b = buf + sizeof buf;
- mrb_int val = mrb_fixnum(x);
+ char *bufend = buf + len;
+ char *b = bufend-1;
- if (base < 2 || 36 < base) {
- mrb_raisef(mrb, E_ARGUMENT_ERROR, "invalid radix %S", mrb_fixnum_value(base));
- }
+ if (base < 2 || 36 < base) return NULL;
+ if (len < 2) return NULL;
- if (val == 0) {
- *--b = '0';
+ if (n == 0) {
+ buf[0] = '0';
+ buf[1] = '\0';
+ return buf;
}
- else if (val < 0) {
+
+ *b = '\0';
+ if (n < 0) {
do {
- *--b = mrb_digitmap[-(val % base)];
- } while (val /= base);
- *--b = '-';
+ if (b-- == buf) return NULL;
+ *b = mrb_digitmap[-(n % base)];
+ } while (n /= base);
+ if (b-- == buf) return NULL;
+ *b = '-';
}
else {
do {
- *--b = mrb_digitmap[(int)(val % base)];
- } while (val /= base);
+ if (b-- == buf) return NULL;
+ *b = mrb_digitmap[(int)(n % base)];
+ } while (n /= base);
}
+ return b;
+}
- return mrb_str_new(mrb, b, buf + sizeof(buf) - b);
+MRB_API mrb_value
+mrb_integer_to_str(mrb_state *mrb, mrb_value x, mrb_int base)
+{
+ char buf[MRB_INT_BIT+1];
+ mrb_int val = mrb_integer(x);
+
+ if (base < 2 || 36 < base) {
+ mrb_raisef(mrb, E_ARGUMENT_ERROR, "invalid radix %i", base);
+ }
+ const char *p = mrb_int_to_cstr(buf, sizeof(buf), val, base);
+ mrb_assert(p != NULL);
+ mrb_value str = mrb_str_new_cstr(mrb, p);
+ RSTR_SET_ASCII_FLAG(mrb_str_ptr(str));
+ return str;
}
/* 15.2.8.3.25 */
@@ -1210,138 +1657,268 @@ mrb_fixnum_to_str(mrb_state *mrb, mrb_value x, int base)
*
*/
static mrb_value
-fix_to_s(mrb_state *mrb, mrb_value self)
+int_to_s(mrb_state *mrb, mrb_value self)
{
mrb_int base = 10;
mrb_get_args(mrb, "|i", &base);
- return mrb_fixnum_to_str(mrb, self, base);
+ return mrb_integer_to_str(mrb, self, base);
+}
+
+/* compare two numbers: (1:0:-1; -2 for error) */
+static mrb_int
+cmpnum(mrb_state *mrb, mrb_value v1, mrb_value v2)
+{
+#ifdef MRB_NO_FLOAT
+ mrb_int x, y;
+#else
+ mrb_float x, y;
+#endif
+
+#ifdef MRB_NO_FLOAT
+ x = mrb_integer(v1);
+#else
+ x = mrb_as_float(mrb, v1);
+#endif
+ switch (mrb_type(v2)) {
+ case MRB_TT_INTEGER:
+#ifdef MRB_NO_FLOAT
+ y = mrb_integer(v2);
+#else
+ y = (mrb_float)mrb_integer(v2);
+#endif
+ break;
+#ifndef MRB_NO_FLOAT
+ case MRB_TT_FLOAT:
+ y = mrb_float(v2);
+ break;
+#ifdef MRB_USE_RATIONAL
+ case MRB_TT_RATIONAL:
+ y = mrb_as_float(mrb, v2);
+ break;
+#endif
+#endif
+ default:
+ return -2;
+ }
+ if (x > y)
+ return 1;
+ else {
+ if (x < y)
+ return -1;
+ return 0;
+ }
}
/* 15.2.9.3.6 */
/*
* call-seq:
- * self.f <=> other.f => -1, 0, +1
+ * self.f <=> other.f => -1, 0, +1, or nil
* < => -1
* = => 0
* > => +1
* Comparison---Returns -1, 0, or +1 depending on whether <i>fix</i> is
* less than, equal to, or greater than <i>numeric</i>. This is the
- * basis for the tests in <code>Comparable</code>.
+ * basis for the tests in <code>Comparable</code>. When the operands are
+ * not comparable, it returns nil instead of raising an exception.
*/
static mrb_value
num_cmp(mrb_state *mrb, mrb_value self)
{
- mrb_value other;
- mrb_float x, y;
+ mrb_value other = mrb_get_arg1(mrb);
+ mrb_int n;
- mrb_get_args(mrb, "o", &other);
+ n = cmpnum(mrb, self, other);
+ if (n == -2) return mrb_nil_value();
+ return mrb_fixnum_value(n);
+}
- x = mrb_to_flo(mrb, self);
- switch (mrb_type(other)) {
- case MRB_TT_FIXNUM:
- y = (mrb_float)mrb_fixnum(other);
- break;
+static mrb_noreturn void
+cmperr(mrb_state *mrb, mrb_value v1, mrb_value v2)
+{
+ mrb_raisef(mrb, E_ARGUMENT_ERROR, "comparison of %t with %t failed", v1, v2);
+}
+
+static mrb_value
+num_lt(mrb_state *mrb, mrb_value self)
+{
+ mrb_value other = mrb_get_arg1(mrb);
+ mrb_int n;
+
+ n = cmpnum(mrb, self, other);
+ if (n == -2) cmperr(mrb, self, other);
+ if (n < 0) return mrb_true_value();
+ return mrb_false_value();
+}
+
+static mrb_value
+num_le(mrb_state *mrb, mrb_value self)
+{
+ mrb_value other = mrb_get_arg1(mrb);
+ mrb_int n;
+
+ n = cmpnum(mrb, self, other);
+ if (n == -2) cmperr(mrb, self, other);
+ if (n <= 0) return mrb_true_value();
+ return mrb_false_value();
+}
+
+static mrb_value
+num_gt(mrb_state *mrb, mrb_value self)
+{
+ mrb_value other = mrb_get_arg1(mrb);
+ mrb_int n;
+
+ n = cmpnum(mrb, self, other);
+ if (n == -2) cmperr(mrb, self, other);
+ if (n > 0) return mrb_true_value();
+ return mrb_false_value();
+}
+
+static mrb_value
+num_ge(mrb_state *mrb, mrb_value self)
+{
+ mrb_value other = mrb_get_arg1(mrb);
+ mrb_int n;
+
+ n = cmpnum(mrb, self, other);
+ if (n == -2) cmperr(mrb, self, other);
+ if (n >= 0) return mrb_true_value();
+ return mrb_false_value();
+}
+
+MRB_API mrb_int
+mrb_cmp(mrb_state *mrb, mrb_value obj1, mrb_value obj2)
+{
+ mrb_value v;
+
+ switch (mrb_type(obj1)) {
+ case MRB_TT_INTEGER:
case MRB_TT_FLOAT:
- y = mrb_float(other);
- break;
+ return cmpnum(mrb, obj1, obj2);
+ case MRB_TT_STRING:
+ if (!mrb_string_p(obj2))
+ return -2;
+ return mrb_str_cmp(mrb, obj1, obj2);
default:
- return mrb_nil_value();
- }
- if (x > y)
- return mrb_fixnum_value(1);
- else {
- if (x < y)
- return mrb_fixnum_value(-1);
- return mrb_fixnum_value(0);
+ v = mrb_funcall_id(mrb, obj1, MRB_OPSYM(cmp), 1, obj2);
+ if (mrb_nil_p(v) || !mrb_integer_p(v))
+ return -2;
+ return mrb_integer(v);
}
}
-/* 15.2.9.3.1 */
-/*
- * call-seq:
- * float + other -> float
- *
- * Returns a new float which is the sum of <code>float</code>
- * and <code>other</code>.
- */
static mrb_value
-flo_plus(mrb_state *mrb, mrb_value x)
+num_finite_p(mrb_state *mrb, mrb_value self)
{
- mrb_value y;
+ return mrb_true_value();
+}
- mrb_get_args(mrb, "o", &y);
- return mrb_float_value(mrb, mrb_float(x) + mrb_to_flo(mrb, y));
+static mrb_value
+num_infinite_p(mrb_state *mrb, mrb_value self)
+{
+ return mrb_false_value();
}
/* ------------------------------------------------------------------------*/
void
mrb_init_numeric(mrb_state *mrb)
{
- struct RClass *numeric, *integer, *fixnum, *fl;
+ struct RClass *numeric, *integer;
+#ifndef MRB_NO_FLOAT
+ struct RClass *fl;
+#endif
/* Numeric Class */
numeric = mrb_define_class(mrb, "Numeric", mrb->object_class); /* 15.2.7 */
-
- mrb_define_method(mrb, numeric, "**", num_pow, MRB_ARGS_REQ(1));
- mrb_define_method(mrb, numeric, "/", num_div, MRB_ARGS_REQ(1)); /* 15.2.8.3.4 */
- mrb_define_method(mrb, numeric, "quo", num_div, MRB_ARGS_REQ(1)); /* 15.2.7.4.5 (x) */
- mrb_define_method(mrb, numeric, "<=>", num_cmp, MRB_ARGS_REQ(1)); /* 15.2.9.3.6 */
+ mrb_define_method(mrb, numeric, "finite?", num_finite_p, MRB_ARGS_NONE());
+ mrb_define_method(mrb, numeric, "infinite?",num_infinite_p, MRB_ARGS_NONE());
/* Integer Class */
- integer = mrb_define_class(mrb, "Integer", numeric); /* 15.2.8 */
+ mrb->integer_class = integer = mrb_define_class(mrb, "Integer", numeric); /* 15.2.8 */
+ MRB_SET_INSTANCE_TT(integer, MRB_TT_INTEGER);
mrb_undef_class_method(mrb, integer, "new");
- mrb_define_method(mrb, integer, "to_i", int_to_i, MRB_ARGS_NONE()); /* 15.2.8.3.24 */
- mrb_define_method(mrb, integer, "to_int", int_to_i, MRB_ARGS_NONE());
-
- /* Fixnum Class */
- fixnum = mrb->fixnum_class = mrb_define_class(mrb, "Fixnum", integer);
- mrb_define_method(mrb, fixnum, "+", fix_plus, MRB_ARGS_REQ(1)); /* 15.2.8.3.1 */
- mrb_define_method(mrb, fixnum, "-", fix_minus, MRB_ARGS_REQ(1)); /* 15.2.8.3.2 */
- mrb_define_method(mrb, fixnum, "*", fix_mul, MRB_ARGS_REQ(1)); /* 15.2.8.3.3 */
- mrb_define_method(mrb, fixnum, "%", fix_mod, MRB_ARGS_REQ(1)); /* 15.2.8.3.5 */
- mrb_define_method(mrb, fixnum, "==", fix_equal, MRB_ARGS_REQ(1)); /* 15.2.8.3.7 */
- mrb_define_method(mrb, fixnum, "~", fix_rev, MRB_ARGS_NONE()); /* 15.2.8.3.8 */
- mrb_define_method(mrb, fixnum, "&", fix_and, MRB_ARGS_REQ(1)); /* 15.2.8.3.9 */
- mrb_define_method(mrb, fixnum, "|", fix_or, MRB_ARGS_REQ(1)); /* 15.2.8.3.10 */
- mrb_define_method(mrb, fixnum, "^", fix_xor, MRB_ARGS_REQ(1)); /* 15.2.8.3.11 */
- mrb_define_method(mrb, fixnum, "<<", fix_lshift, MRB_ARGS_REQ(1)); /* 15.2.8.3.12 */
- mrb_define_method(mrb, fixnum, ">>", fix_rshift, MRB_ARGS_REQ(1)); /* 15.2.8.3.13 */
- mrb_define_method(mrb, fixnum, "eql?", fix_eql, MRB_ARGS_REQ(1)); /* 15.2.8.3.16 */
- mrb_define_method(mrb, fixnum, "hash", flo_hash, MRB_ARGS_NONE()); /* 15.2.8.3.18 */
- mrb_define_method(mrb, fixnum, "to_f", fix_to_f, MRB_ARGS_NONE()); /* 15.2.8.3.23 */
- mrb_define_method(mrb, fixnum, "to_s", fix_to_s, MRB_ARGS_NONE()); /* 15.2.8.3.25 */
- mrb_define_method(mrb, fixnum, "inspect", fix_to_s, MRB_ARGS_NONE());
- mrb_define_method(mrb, fixnum, "divmod", fix_divmod, MRB_ARGS_REQ(1)); /* 15.2.8.3.30 (x) */
+ mrb_define_method(mrb, integer, "**", int_pow, MRB_ARGS_REQ(1));
+ mrb_define_method(mrb, integer, "<=>", num_cmp, MRB_ARGS_REQ(1)); /* 15.2.8.3.1 */
+ mrb_define_method(mrb, integer, "<", num_lt, MRB_ARGS_REQ(1));
+ mrb_define_method(mrb, integer, "<=", num_le, MRB_ARGS_REQ(1));
+ mrb_define_method(mrb, integer, ">", num_gt, MRB_ARGS_REQ(1));
+ mrb_define_method(mrb, integer, ">=", num_ge, MRB_ARGS_REQ(1));
+
+ mrb_define_method(mrb, integer, "to_i", int_to_i, MRB_ARGS_NONE()); /* 15.2.8.3.24 */
+ mrb_define_method(mrb, integer, "to_int", int_to_i, MRB_ARGS_NONE());
+
+ mrb_define_method(mrb, integer, "+", int_add, MRB_ARGS_REQ(1)); /* 15.2.8.3.1 */
+ mrb_define_method(mrb, integer, "-", int_sub, MRB_ARGS_REQ(1)); /* 15.2.8.3.2 */
+ mrb_define_method(mrb, integer, "*", int_mul, MRB_ARGS_REQ(1)); /* 15.2.8.3.3 */
+ mrb_define_method(mrb, integer, "%", int_mod, MRB_ARGS_REQ(1)); /* 15.2.8.3.5 */
+ mrb_define_method(mrb, integer, "/", int_div, MRB_ARGS_REQ(1)); /* 15.2.8.3.6 */
+ mrb_define_method(mrb, integer, "quo", int_quo, MRB_ARGS_REQ(1)); /* 15.2.7.4.5 (x) */
+ mrb_define_method(mrb, integer, "div", int_idiv, MRB_ARGS_REQ(1));
+ mrb_define_method(mrb, integer, "==", int_equal, MRB_ARGS_REQ(1)); /* 15.2.8.3.7 */
+ mrb_define_method(mrb, integer, "~", int_rev, MRB_ARGS_NONE()); /* 15.2.8.3.8 */
+ mrb_define_method(mrb, integer, "&", int_and, MRB_ARGS_REQ(1)); /* 15.2.8.3.9 */
+ mrb_define_method(mrb, integer, "|", int_or, MRB_ARGS_REQ(1)); /* 15.2.8.3.10 */
+ mrb_define_method(mrb, integer, "^", int_xor, MRB_ARGS_REQ(1)); /* 15.2.8.3.11 */
+ mrb_define_method(mrb, integer, "<<", int_lshift, MRB_ARGS_REQ(1)); /* 15.2.8.3.12 */
+ mrb_define_method(mrb, integer, ">>", int_rshift, MRB_ARGS_REQ(1)); /* 15.2.8.3.13 */
+ mrb_define_method(mrb, integer, "eql?", int_eql, MRB_ARGS_REQ(1)); /* 15.2.8.3.16 */
+#ifndef MRB_NO_FLOAT
+ mrb_define_method(mrb, integer, "to_f", int_to_f, MRB_ARGS_NONE()); /* 15.2.8.3.23 */
+#endif
+ mrb_define_method(mrb, integer, "to_s", int_to_s, MRB_ARGS_OPT(1)); /* 15.2.8.3.25 */
+ mrb_define_method(mrb, integer, "inspect", int_to_s, MRB_ARGS_OPT(1));
+ mrb_define_method(mrb, integer, "divmod", int_divmod, MRB_ARGS_REQ(1)); /* 15.2.8.3.30 (x) */
+ mrb_define_method(mrb, integer, "__coerce_step_counter", coerce_step_counter, MRB_ARGS_REQ(2));
+
+ /* Fixnum Class for compatibility */
+ mrb_define_const(mrb, mrb->object_class, "Fixnum", mrb_obj_value(integer));
+#ifndef MRB_NO_FLOAT
/* Float Class */
- fl = mrb->float_class = mrb_define_class(mrb, "Float", numeric); /* 15.2.9 */
+ mrb->float_class = fl = mrb_define_class(mrb, "Float", numeric); /* 15.2.9 */
+ MRB_SET_INSTANCE_TT(fl, MRB_TT_FLOAT);
mrb_undef_class_method(mrb, fl, "new");
- mrb_define_method(mrb, fl, "+", flo_plus, MRB_ARGS_REQ(1)); /* 15.2.9.3.1 */
- mrb_define_method(mrb, fl, "-", flo_minus, MRB_ARGS_REQ(1)); /* 15.2.9.3.2 */
- mrb_define_method(mrb, fl, "*", flo_mul, MRB_ARGS_REQ(1)); /* 15.2.9.3.3 */
- mrb_define_method(mrb, fl, "%", flo_mod, MRB_ARGS_REQ(1)); /* 15.2.9.3.5 */
- mrb_define_method(mrb, fl, "==", flo_eq, MRB_ARGS_REQ(1)); /* 15.2.9.3.7 */
- mrb_define_method(mrb, fl, "ceil", flo_ceil, MRB_ARGS_NONE()); /* 15.2.9.3.8 */
- mrb_define_method(mrb, fl, "finite?", flo_finite_p, MRB_ARGS_NONE()); /* 15.2.9.3.9 */
- mrb_define_method(mrb, fl, "floor", flo_floor, MRB_ARGS_NONE()); /* 15.2.9.3.10 */
- mrb_define_method(mrb, fl, "infinite?", flo_infinite_p, MRB_ARGS_NONE()); /* 15.2.9.3.11 */
- mrb_define_method(mrb, fl, "round", flo_round, MRB_ARGS_OPT(1)); /* 15.2.9.3.12 */
- mrb_define_method(mrb, fl, "to_f", flo_to_f, MRB_ARGS_NONE()); /* 15.2.9.3.13 */
- mrb_define_method(mrb, fl, "to_i", flo_truncate, MRB_ARGS_NONE()); /* 15.2.9.3.14 */
- mrb_define_method(mrb, fl, "to_int", flo_truncate, MRB_ARGS_NONE());
- mrb_define_method(mrb, fl, "truncate", flo_truncate, MRB_ARGS_NONE()); /* 15.2.9.3.15 */
- mrb_define_method(mrb, fl, "divmod", flo_divmod, MRB_ARGS_REQ(1));
- mrb_define_method(mrb, fl, "eql?", flo_eql, MRB_ARGS_REQ(1)); /* 15.2.8.3.16 */
-
- mrb_define_method(mrb, fl, "to_s", flo_to_s, MRB_ARGS_NONE()); /* 15.2.9.3.16(x) */
- mrb_define_method(mrb, fl, "inspect", flo_to_s, MRB_ARGS_NONE());
- mrb_define_method(mrb, fl, "nan?", flo_nan_p, MRB_ARGS_NONE());
+ mrb_define_method(mrb, fl, "**", flo_pow, MRB_ARGS_REQ(1));
+ mrb_define_method(mrb, fl, "/", flo_div, MRB_ARGS_REQ(1)); /* 15.2.9.3.6 */
+ mrb_define_method(mrb, fl, "quo", flo_div, MRB_ARGS_REQ(1)); /* 15.2.7.4.5 (x) */
+ mrb_define_method(mrb, fl, "div", flo_idiv, MRB_ARGS_REQ(1));
+ mrb_define_method(mrb, fl, "+", flo_add, MRB_ARGS_REQ(1)); /* 15.2.9.3.3 */
+ mrb_define_method(mrb, fl, "-", flo_sub, MRB_ARGS_REQ(1)); /* 15.2.9.3.4 */
+ mrb_define_method(mrb, fl, "*", flo_mul, MRB_ARGS_REQ(1)); /* 15.2.9.3.5 */
+ mrb_define_method(mrb, fl, "%", flo_mod, MRB_ARGS_REQ(1)); /* 15.2.9.3.7 */
+ mrb_define_method(mrb, fl, "<=>", num_cmp, MRB_ARGS_REQ(1)); /* 15.2.9.3.1 */
+ mrb_define_method(mrb, fl, "<", num_lt, MRB_ARGS_REQ(1));
+ mrb_define_method(mrb, fl, "<=", num_le, MRB_ARGS_REQ(1));
+ mrb_define_method(mrb, fl, ">", num_gt, MRB_ARGS_REQ(1));
+ mrb_define_method(mrb, fl, ">=", num_ge, MRB_ARGS_REQ(1));
+ mrb_define_method(mrb, fl, "==", flo_eq, MRB_ARGS_REQ(1)); /* 15.2.9.3.2 */
+ mrb_define_method(mrb, fl, "~", flo_rev, MRB_ARGS_NONE());
+ mrb_define_method(mrb, fl, "&", flo_and, MRB_ARGS_REQ(1));
+ mrb_define_method(mrb, fl, "|", flo_or, MRB_ARGS_REQ(1));
+ mrb_define_method(mrb, fl, "^", flo_xor, MRB_ARGS_REQ(1));
+ mrb_define_method(mrb, fl, ">>", flo_rshift, MRB_ARGS_REQ(1));
+ mrb_define_method(mrb, fl, "<<", flo_lshift, MRB_ARGS_REQ(1));
+ mrb_define_method(mrb, fl, "ceil", flo_ceil, MRB_ARGS_OPT(1)); /* 15.2.9.3.8 */
+ mrb_define_method(mrb, fl, "finite?", flo_finite_p, MRB_ARGS_NONE()); /* 15.2.9.3.9 */
+ mrb_define_method(mrb, fl, "floor", flo_floor, MRB_ARGS_OPT(1)); /* 15.2.9.3.10 */
+ mrb_define_method(mrb, fl, "infinite?", flo_infinite_p, MRB_ARGS_NONE()); /* 15.2.9.3.11 */
+ mrb_define_method(mrb, fl, "round", flo_round, MRB_ARGS_OPT(1)); /* 15.2.9.3.12 */
+ mrb_define_method(mrb, fl, "to_f", flo_to_f, MRB_ARGS_NONE()); /* 15.2.9.3.13 */
+ mrb_define_method(mrb, fl, "to_i", flo_to_i, MRB_ARGS_NONE()); /* 15.2.9.3.14 */
+ mrb_define_method(mrb, fl, "truncate", flo_truncate, MRB_ARGS_OPT(1)); /* 15.2.9.3.15 */
+ mrb_define_method(mrb, fl, "divmod", flo_divmod, MRB_ARGS_REQ(1));
+ mrb_define_method(mrb, fl, "eql?", flo_eql, MRB_ARGS_REQ(1)); /* 15.2.8.3.16 */
+
+ mrb_define_method(mrb, fl, "to_s", flo_to_s, MRB_ARGS_NONE()); /* 15.2.9.3.16(x) */
+ mrb_define_method(mrb, fl, "inspect", flo_to_s, MRB_ARGS_NONE());
+ mrb_define_method(mrb, fl, "nan?", flo_nan_p, MRB_ARGS_NONE());
#ifdef INFINITY
- mrb_define_const(mrb, fl, "INFINITY", mrb_float_value(mrb, INFINITY));
+ mrb_define_const_id(mrb, fl, MRB_SYM(INFINITY), mrb_float_value(mrb, INFINITY));
#endif
#ifdef NAN
- mrb_define_const(mrb, fl, "NAN", mrb_float_value(mrb, NAN));
+ mrb_define_const_id(mrb, fl, MRB_SYM(NAN), mrb_float_value(mrb, NAN));
+#endif
#endif
}
diff --git a/src/object.c b/src/object.c
index c5fb74575..8fe4688ac 100644
--- a/src/object.c
+++ b/src/object.c
@@ -4,10 +4,12 @@
** See Copyright Notice in mruby.h
*/
-#include "mruby.h"
-#include "mruby/class.h"
-#include "mruby/numeric.h"
-#include "mruby/string.h"
+#include <mruby.h>
+#include <mruby/class.h>
+#include <mruby/numeric.h>
+#include <mruby/string.h>
+#include <mruby/class.h>
+#include <mruby/presym.h>
MRB_API mrb_bool
mrb_obj_eq(mrb_state *mrb, mrb_value v1, mrb_value v2)
@@ -18,13 +20,15 @@ mrb_obj_eq(mrb_state *mrb, mrb_value v1, mrb_value v2)
return TRUE;
case MRB_TT_FALSE:
- case MRB_TT_FIXNUM:
- return (mrb_fixnum(v1) == mrb_fixnum(v2));
+ case MRB_TT_INTEGER:
+ return (mrb_integer(v1) == mrb_integer(v2));
case MRB_TT_SYMBOL:
return (mrb_symbol(v1) == mrb_symbol(v2));
+#ifndef MRB_NO_FLOAT
case MRB_TT_FLOAT:
return (mrb_float(v1) == mrb_float(v2));
+#endif
default:
return (mrb_ptr(v1) == mrb_ptr(v2));
@@ -44,7 +48,18 @@ mrb_equal(mrb_state *mrb, mrb_value obj1, mrb_value obj2)
mrb_value result;
if (mrb_obj_eq(mrb, obj1, obj2)) return TRUE;
- result = mrb_funcall(mrb, obj1, "==", 1, obj2);
+#ifndef MRB_NO_FLOAT
+ /* value mixing with integer and float */
+ if (mrb_integer_p(obj1)) {
+ if (mrb_float_p(obj2) && (mrb_float)mrb_integer(obj1) == mrb_float(obj2))
+ return TRUE;
+ }
+ else if (mrb_float_p(obj1)) {
+ if (mrb_integer_p(obj2) && mrb_float(obj1) == (mrb_float)mrb_integer(obj2))
+ return TRUE;
+ }
+#endif
+ result = mrb_funcall_id(mrb, obj1, MRB_OPSYM(eq), 1, obj2);
if (mrb_test(result)) return TRUE;
return FALSE;
}
@@ -80,13 +95,17 @@ mrb_true(mrb_state *mrb, mrb_value obj)
static mrb_value
nil_to_s(mrb_state *mrb, mrb_value obj)
{
- return mrb_str_new(mrb, 0, 0);
+ mrb_value str = mrb_str_new_frozen(mrb, NULL, 0);
+ RSTR_SET_ASCII_FLAG(mrb_str_ptr(str));
+ return str;
}
static mrb_value
nil_inspect(mrb_state *mrb, mrb_value obj)
{
- return mrb_str_new_lit(mrb, "nil");
+ mrb_value str = mrb_str_new_lit_frozen(mrb, "nil");
+ RSTR_SET_ASCII_FLAG(mrb_str_ptr(str));
+ return str;
}
/***********************************************************************
@@ -147,7 +166,9 @@ true_xor(mrb_state *mrb, mrb_value obj)
static mrb_value
true_to_s(mrb_state *mrb, mrb_value obj)
{
- return mrb_str_new_lit(mrb, "true");
+ mrb_value str = mrb_str_new_lit_frozen(mrb, "true");
+ RSTR_SET_ASCII_FLAG(mrb_str_ptr(str));
+ return str;
}
/* 15.2.5.3.4 */
@@ -254,7 +275,9 @@ false_or(mrb_state *mrb, mrb_value obj)
static mrb_value
false_to_s(mrb_state *mrb, mrb_value obj)
{
- return mrb_str_new_lit(mrb, "false");
+ mrb_value str = mrb_str_new_lit_frozen(mrb, "false");
+ RSTR_SET_ASCII_FLAG(mrb_str_ptr(str));
+ return str;
}
void
@@ -264,7 +287,8 @@ mrb_init_object(mrb_state *mrb)
struct RClass *t;
struct RClass *f;
- n = mrb->nil_class = mrb_define_class(mrb, "NilClass", mrb->object_class);
+ mrb->nil_class = n = mrb_define_class(mrb, "NilClass", mrb->object_class);
+ MRB_SET_INSTANCE_TT(n, MRB_TT_FALSE);
mrb_undef_class_method(mrb, n, "new");
mrb_define_method(mrb, n, "&", false_and, MRB_ARGS_REQ(1)); /* 15.2.4.3.1 */
mrb_define_method(mrb, n, "^", false_xor, MRB_ARGS_REQ(1)); /* 15.2.4.3.2 */
@@ -273,7 +297,8 @@ mrb_init_object(mrb_state *mrb)
mrb_define_method(mrb, n, "to_s", nil_to_s, MRB_ARGS_NONE()); /* 15.2.4.3.5 */
mrb_define_method(mrb, n, "inspect", nil_inspect, MRB_ARGS_NONE());
- t = mrb->true_class = mrb_define_class(mrb, "TrueClass", mrb->object_class);
+ mrb->true_class = t = mrb_define_class(mrb, "TrueClass", mrb->object_class);
+ MRB_SET_INSTANCE_TT(t, MRB_TT_TRUE);
mrb_undef_class_method(mrb, t, "new");
mrb_define_method(mrb, t, "&", true_and, MRB_ARGS_REQ(1)); /* 15.2.5.3.1 */
mrb_define_method(mrb, t, "^", true_xor, MRB_ARGS_REQ(1)); /* 15.2.5.3.2 */
@@ -281,7 +306,8 @@ mrb_init_object(mrb_state *mrb)
mrb_define_method(mrb, t, "|", true_or, MRB_ARGS_REQ(1)); /* 15.2.5.3.4 */
mrb_define_method(mrb, t, "inspect", true_to_s, MRB_ARGS_NONE());
- f = mrb->false_class = mrb_define_class(mrb, "FalseClass", mrb->object_class);
+ mrb->false_class = f = mrb_define_class(mrb, "FalseClass", mrb->object_class);
+ MRB_SET_INSTANCE_TT(f, MRB_TT_FALSE);
mrb_undef_class_method(mrb, f, "new");
mrb_define_method(mrb, f, "&", false_and, MRB_ARGS_REQ(1)); /* 15.2.6.3.1 */
mrb_define_method(mrb, f, "^", false_xor, MRB_ARGS_REQ(1)); /* 15.2.6.3.2 */
@@ -290,134 +316,86 @@ mrb_init_object(mrb_state *mrb)
mrb_define_method(mrb, f, "inspect", false_to_s, MRB_ARGS_NONE());
}
-static mrb_value
-inspect_type(mrb_state *mrb, mrb_value val)
+static const char*
+type_name(enum mrb_vtype t)
{
- if (mrb_type(val) == MRB_TT_FALSE || mrb_type(val) == MRB_TT_TRUE) {
- return mrb_inspect(mrb, val);
- }
- else {
- return mrb_str_new_cstr(mrb, mrb_obj_classname(mrb, val));
+ switch (t) {
+#define MRB_VTYPE_NAME(tt, type, name) case tt: return name;
+ MRB_VTYPE_FOREACH(MRB_VTYPE_NAME)
+#undef MRB_VTYPE_NAME
+ default: return NULL;
}
}
static mrb_value
-convert_type(mrb_state *mrb, mrb_value val, const char *tname, const char *method, mrb_bool raise)
+convert_type(mrb_state *mrb, mrb_value val, const char *tname, mrb_sym method, mrb_bool raise)
{
- mrb_sym m = 0;
-
- m = mrb_intern_cstr(mrb, method);
- if (!mrb_respond_to(mrb, val, m)) {
+ if (!mrb_respond_to(mrb, val, method)) {
if (raise) {
- mrb_raisef(mrb, E_TYPE_ERROR, "can't convert %S into %S", inspect_type(mrb, val), mrb_str_new_cstr(mrb, tname));
+ if (tname) mrb_raisef(mrb, E_TYPE_ERROR, "can't convert %Y into %s", val, tname);
+ mrb_raisef(mrb, E_TYPE_ERROR, "can't convert %Y", val);
}
return mrb_nil_value();
}
- return mrb_funcall_argv(mrb, val, m, 0, 0);
+ return mrb_funcall_argv(mrb, val, method, 0, 0);
}
MRB_API mrb_value
-mrb_check_to_integer(mrb_state *mrb, mrb_value val, const char *method)
-{
- mrb_value v;
-
- if (mrb_fixnum_p(val)) return val;
- v = convert_type(mrb, val, "Integer", method, FALSE);
- if (mrb_nil_p(v) || !mrb_fixnum_p(v)) {
- return mrb_nil_value();
- }
- return v;
-}
-
-MRB_API mrb_value
-mrb_convert_type(mrb_state *mrb, mrb_value val, enum mrb_vtype type, const char *tname, const char *method)
+mrb_type_convert(mrb_state *mrb, mrb_value val, enum mrb_vtype type, mrb_sym method)
{
mrb_value v;
+ const char *tname;
if (mrb_type(val) == type) return val;
+ tname = type_name(type);
v = convert_type(mrb, val, tname, method, TRUE);
if (mrb_type(v) != type) {
- mrb_raisef(mrb, E_TYPE_ERROR, "%S cannot be converted to %S by #%S", val,
- mrb_str_new_cstr(mrb, tname), mrb_str_new_cstr(mrb, method));
+ if (type == MRB_TT_STRING) return mrb_any_to_s(mrb, val);
+ mrb_raisef(mrb, E_TYPE_ERROR, "%v cannot be converted to %s by #%n", val, tname, method);
}
return v;
}
MRB_API mrb_value
-mrb_check_convert_type(mrb_state *mrb, mrb_value val, enum mrb_vtype type, const char *tname, const char *method)
+mrb_type_convert_check(mrb_state *mrb, mrb_value val, enum mrb_vtype type, mrb_sym method)
{
mrb_value v;
- if (mrb_type(val) == type && type != MRB_TT_DATA) return val;
- v = convert_type(mrb, val, tname, method, FALSE);
+ if (mrb_type(val) == type && type != MRB_TT_DATA && type != MRB_TT_ISTRUCT) return val;
+ v = convert_type(mrb, val, type_name(type), method, FALSE);
if (mrb_nil_p(v) || mrb_type(v) != type) return mrb_nil_value();
return v;
}
-static const struct types {
- unsigned char type;
- const char *name;
-} builtin_types[] = {
-/* {MRB_TT_NIL, "nil"}, */
- {MRB_TT_FALSE, "false"},
- {MRB_TT_TRUE, "true"},
- {MRB_TT_FIXNUM, "Fixnum"},
- {MRB_TT_SYMBOL, "Symbol"}, /* :symbol */
- {MRB_TT_MODULE, "Module"},
- {MRB_TT_OBJECT, "Object"},
- {MRB_TT_CLASS, "Class"},
- {MRB_TT_ICLASS, "iClass"}, /* internal use: mixed-in module holder */
- {MRB_TT_SCLASS, "SClass"},
- {MRB_TT_PROC, "Proc"},
- {MRB_TT_FLOAT, "Float"},
- {MRB_TT_ARRAY, "Array"},
- {MRB_TT_HASH, "Hash"},
- {MRB_TT_STRING, "String"},
- {MRB_TT_RANGE, "Range"},
-/* {MRB_TT_BIGNUM, "Bignum"}, */
- {MRB_TT_FILE, "File"},
- {MRB_TT_DATA, "Data"}, /* internal use: wrapped C pointers */
-/* {MRB_TT_VARMAP, "Varmap"}, */ /* internal use: dynamic variables */
-/* {MRB_TT_NODE, "Node"}, */ /* internal use: syntax tree node */
-/* {MRB_TT_UNDEF, "undef"}, */ /* internal use: #undef; should not happen */
- {-1, 0}
-};
-
MRB_API void
mrb_check_type(mrb_state *mrb, mrb_value x, enum mrb_vtype t)
{
- const struct types *type = builtin_types;
- enum mrb_vtype xt;
-
- xt = mrb_type(x);
- if ((xt != t) || (xt == MRB_TT_DATA)) {
- while (type->type < MRB_TT_MAXDEFINE) {
- if (type->type == t) {
- const char *etype;
-
- if (mrb_nil_p(x)) {
- etype = "nil";
- }
- else if (mrb_fixnum_p(x)) {
- etype = "Fixnum";
- }
- else if (mrb_type(x) == MRB_TT_SYMBOL) {
- etype = "Symbol";
- }
- else if (mrb_immediate_p(x)) {
- etype = RSTRING_PTR(mrb_obj_as_string(mrb, x));
- }
- else {
- etype = mrb_obj_classname(mrb, x);
- }
- mrb_raisef(mrb, E_TYPE_ERROR, "wrong argument type %S (expected %S)",
- mrb_str_new_cstr(mrb, etype), mrb_str_new_cstr(mrb, type->name));
- }
- type++;
- }
- mrb_raisef(mrb, E_TYPE_ERROR, "unknown type %S (%S given)",
- mrb_fixnum_value(t), mrb_fixnum_value(mrb_type(x)));
+ enum mrb_vtype xt = mrb_type(x);
+ const char *tname, *ename;
+
+ if (t == xt) return;
+
+ tname = type_name(t);
+ if (mrb_nil_p(x)) {
+ ename = "nil";
+ }
+ else if (mrb_integer_p(x)) {
+ ename = "Integer";
+ }
+ else if (mrb_symbol_p(x)) {
+ ename = "Symbol";
+ }
+ else if (mrb_immediate_p(x)) {
+ ename = RSTRING_PTR(mrb_obj_as_string(mrb, x));
}
+ else {
+ ename = mrb_obj_classname(mrb, x);
+ }
+ if (tname) {
+ mrb_raisef(mrb, E_TYPE_ERROR, "wrong argument type %s (expected %s)",
+ ename, tname);
+ }
+ mrb_raisef(mrb, E_TYPE_ERROR, "unknown type %d (%s given)", t, ename);
}
/* 15.3.1.3.46 */
@@ -428,19 +406,21 @@ mrb_check_type(mrb_state *mrb, mrb_value x, enum mrb_vtype t)
* Returns a string representing <i>obj</i>. The default
* <code>to_s</code> prints the object's class and an encoding of the
* object id. As a special case, the top-level object that is the
- * initial execution context of Ruby programs returns ``main.''
+ * initial execution context of Ruby programs returns "main."
*/
MRB_API mrb_value
mrb_any_to_s(mrb_state *mrb, mrb_value obj)
{
- mrb_value str = mrb_str_buf_new(mrb, 20);
+ mrb_value str = mrb_str_new_capa(mrb, 20);
const char *cname = mrb_obj_classname(mrb, 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)));
+ if (!mrb_immediate_p(obj)) {
+ mrb_str_cat_lit(mrb, str, ":");
+ mrb_str_cat_str(mrb, str, mrb_ptr_to_str(mrb, mrb_ptr(obj)));
+ }
mrb_str_cat_lit(mrb, str, ">");
return str;
@@ -481,12 +461,14 @@ mrb_obj_is_kind_of(mrb_state *mrb, mrb_value obj, struct RClass *c)
case MRB_TT_MODULE:
case MRB_TT_CLASS:
case MRB_TT_ICLASS:
+ case MRB_TT_SCLASS:
break;
default:
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;
@@ -495,45 +477,41 @@ mrb_obj_is_kind_of(mrb_state *mrb, mrb_value obj, struct RClass *c)
return FALSE;
}
-static mrb_value
-mrb_to_integer(mrb_state *mrb, mrb_value val, const char *method)
-{
- mrb_value v;
-
- if (mrb_fixnum_p(val)) return val;
- v = convert_type(mrb, val, "Integer", method, TRUE);
- if (!mrb_obj_is_kind_of(mrb, v, mrb->fixnum_class)) {
- mrb_value type = inspect_type(mrb, val);
- mrb_raisef(mrb, E_TYPE_ERROR, "can't convert %S to Integer (%S#%S gives %S)",
- type, type, mrb_str_new_cstr(mrb, method), inspect_type(mrb, v));
- }
- return v;
-}
-
MRB_API mrb_value
mrb_to_int(mrb_state *mrb, mrb_value val)
{
- return mrb_to_integer(mrb, val, "to_int");
+
+ if (!mrb_integer_p(val)) {
+#ifndef MRB_NO_FLOAT
+ if (mrb_float_p(val)) {
+ return mrb_float_to_integer(mrb, val);
+ }
+#endif
+ if (mrb_string_p(val)) {
+ mrb_raise(mrb, E_TYPE_ERROR, "can't convert String to Integer");
+ }
+ return mrb_type_convert(mrb, val, MRB_TT_INTEGER, MRB_SYM(to_i));
+ }
+ return val;
}
MRB_API mrb_value
-mrb_convert_to_integer(mrb_state *mrb, mrb_value val, int base)
+mrb_convert_to_integer(mrb_state *mrb, mrb_value val, mrb_int base)
{
mrb_value tmp;
if (mrb_nil_p(val)) {
if (base != 0) goto arg_error;
- mrb_raise(mrb, E_TYPE_ERROR, "can't convert nil into Integer");
+ mrb_raise(mrb, E_TYPE_ERROR, "can't convert nil into Integer");
}
switch (mrb_type(val)) {
+#ifndef MRB_NO_FLOAT
case MRB_TT_FLOAT:
if (base != 0) goto arg_error;
- if (FIXABLE(mrb_float(val))) {
- break;
- }
- return mrb_flo_to_fixnum(mrb, val);
+ return mrb_float_to_integer(mrb, val);
+#endif
- case MRB_TT_FIXNUM:
+ case MRB_TT_INTEGER:
if (base != 0) goto arg_error;
return val;
@@ -547,33 +525,26 @@ mrb_convert_to_integer(mrb_state *mrb, mrb_value val, int base)
if (base != 0) {
tmp = mrb_check_string_type(mrb, val);
if (!mrb_nil_p(tmp)) {
+ val = tmp;
goto string_conv;
}
arg_error:
mrb_raise(mrb, E_ARGUMENT_ERROR, "base specified for non string value");
}
- tmp = convert_type(mrb, val, "Integer", "to_int", FALSE);
- if (mrb_nil_p(tmp)) {
- return mrb_to_integer(mrb, val, "to_i");
- }
- return tmp;
-}
-
-MRB_API mrb_value
-mrb_Integer(mrb_state *mrb, mrb_value val)
-{
- return mrb_convert_to_integer(mrb, val, 0);
+ /* to raise TypeError */
+ return mrb_to_int(mrb, val);
}
+#ifndef MRB_NO_FLOAT
MRB_API mrb_value
-mrb_Float(mrb_state *mrb, mrb_value val)
+mrb_to_float(mrb_state *mrb, mrb_value val)
{
if (mrb_nil_p(val)) {
mrb_raise(mrb, E_TYPE_ERROR, "can't convert nil into Float");
}
switch (mrb_type(val)) {
- case MRB_TT_FIXNUM:
- return mrb_float_value(mrb, (mrb_float)mrb_fixnum(val));
+ case MRB_TT_INTEGER:
+ return mrb_float_value(mrb, (mrb_float)mrb_integer(val));
case MRB_TT_FLOAT:
return val;
@@ -582,19 +553,81 @@ mrb_Float(mrb_state *mrb, mrb_value val)
return mrb_float_value(mrb, mrb_str_to_dbl(mrb, val, TRUE));
default:
- return mrb_convert_type(mrb, val, MRB_TT_FLOAT, "Float", "to_f");
+ return mrb_type_convert(mrb, val, MRB_TT_FLOAT, MRB_SYM(to_f));
+ }
+}
+#endif
+
+MRB_API mrb_value
+mrb_to_str(mrb_state *mrb, mrb_value val)
+{
+ return mrb_ensure_string_type(mrb, val);
+}
+
+/* obsolete: use mrb_ensure_string_type() instead */
+MRB_API mrb_value
+mrb_string_type(mrb_state *mrb, mrb_value str)
+{
+ return mrb_ensure_string_type(mrb, str);
+}
+
+MRB_API mrb_value
+mrb_ensure_string_type(mrb_state *mrb, mrb_value str)
+{
+ if (!mrb_string_p(str)) {
+ mrb_raisef(mrb, E_TYPE_ERROR, "%Y cannot be converted to String", str);
}
+ return str;
+}
+
+MRB_API mrb_value
+mrb_check_string_type(mrb_state *mrb, mrb_value str)
+{
+ if (!mrb_string_p(str)) return mrb_nil_value();
+ return str;
+}
+
+MRB_API mrb_value
+mrb_ensure_array_type(mrb_state *mrb, mrb_value ary)
+{
+ if (!mrb_array_p(ary)) {
+ mrb_raisef(mrb, E_TYPE_ERROR, "%Y cannot be converted to Array", ary);
+ }
+ return ary;
+}
+
+MRB_API mrb_value
+mrb_check_array_type(mrb_state *mrb, mrb_value ary)
+{
+ if (!mrb_array_p(ary)) return mrb_nil_value();
+ return ary;
+}
+
+MRB_API mrb_value
+mrb_ensure_hash_type(mrb_state *mrb, mrb_value hash)
+{
+ if (!mrb_hash_p(hash)) {
+ mrb_raisef(mrb, E_TYPE_ERROR, "%Y cannot be converted to Hash", hash);
+ }
+ return hash;
+}
+
+MRB_API mrb_value
+mrb_check_hash_type(mrb_state *mrb, mrb_value hash)
+{
+ if (!mrb_hash_p(hash)) return mrb_nil_value();
+ return hash;
}
MRB_API mrb_value
mrb_inspect(mrb_state *mrb, mrb_value obj)
{
- return mrb_obj_as_string(mrb, mrb_funcall(mrb, obj, "inspect", 0));
+ return mrb_obj_as_string(mrb, mrb_funcall_id(mrb, obj, MRB_SYM(inspect), 0));
}
MRB_API mrb_bool
mrb_eql(mrb_state *mrb, mrb_value obj1, mrb_value obj2)
{
if (mrb_obj_eq(mrb, obj1, obj2)) return TRUE;
- return mrb_test(mrb_funcall(mrb, obj1, "eql?", 1, obj2));
+ return mrb_test(mrb_funcall_id(mrb, obj1, MRB_SYM_Q(eql), 1, obj2));
}
diff --git a/src/opcode.h b/src/opcode.h
index 2446f92ed..fe4d17a21 100644
--- a/src/opcode.h
+++ b/src/opcode.h
@@ -1,2 +1,2 @@
/* this header file is to be removed soon. */
-#include "mruby/opcode.h"
+#include <mruby/opcode.h>
diff --git a/src/parse.y b/src/parse.y
deleted file mode 100644
index f8e1ff9aa..000000000
--- a/src/parse.y
+++ /dev/null
@@ -1,6407 +0,0 @@
-/*
-** parse.y - mruby parser
-**
-** See Copyright Notice in mruby.h
-*/
-
-%{
-#undef PARSER_DEBUG
-
-#define YYDEBUG 1
-#define YYERROR_VERBOSE 1
-/*
- * Force yacc to use our memory management. This is a little evil because
- * the macros assume that "parser_state *p" is in scope
- */
-#define YYMALLOC(n) mrb_malloc(p->mrb, (n))
-#define YYFREE(o) mrb_free(p->mrb, (o))
-#define YYSTACK_USE_ALLOCA 0
-
-#include <ctype.h>
-#include <errno.h>
-#include <stdlib.h>
-#include <string.h>
-#include "mruby.h"
-#include "mruby/compile.h"
-#include "mruby/proc.h"
-#include "mruby/error.h"
-#include "node.h"
-#include "mrb_throw.h"
-
-#define YYLEX_PARAM p
-
-typedef mrb_ast_node node;
-typedef struct mrb_parser_state parser_state;
-typedef struct mrb_parser_heredoc_info parser_heredoc_info;
-
-static int yyparse(parser_state *p);
-static int yylex(void *lval, parser_state *p);
-static void yyerror(parser_state *p, const char *s);
-static void yywarn(parser_state *p, const char *s);
-static void yywarning(parser_state *p, const char *s);
-static void backref_error(parser_state *p, node *n);
-static void tokadd(parser_state *p, int32_t c);
-
-#define identchar(c) (ISALNUM(c) || (c) == '_' || !ISASCII(c))
-
-typedef unsigned int stack_type;
-
-#define BITSTACK_PUSH(stack, n) ((stack) = ((stack)<<1)|((n)&1))
-#define BITSTACK_POP(stack) ((stack) = (stack) >> 1)
-#define BITSTACK_LEXPOP(stack) ((stack) = ((stack) >> 1) | ((stack) & 1))
-#define BITSTACK_SET_P(stack) ((stack)&1)
-
-#define COND_PUSH(n) BITSTACK_PUSH(p->cond_stack, (n))
-#define COND_POP() BITSTACK_POP(p->cond_stack)
-#define COND_LEXPOP() BITSTACK_LEXPOP(p->cond_stack)
-#define COND_P() BITSTACK_SET_P(p->cond_stack)
-
-#define CMDARG_PUSH(n) BITSTACK_PUSH(p->cmdarg_stack, (n))
-#define CMDARG_POP() BITSTACK_POP(p->cmdarg_stack)
-#define CMDARG_LEXPOP() BITSTACK_LEXPOP(p->cmdarg_stack)
-#define CMDARG_P() BITSTACK_SET_P(p->cmdarg_stack)
-
-#define SET_LINENO(c,n) ((c)->lineno = (n))
-#define NODE_LINENO(c,n) do {\
- if (n) {\
- (c)->filename_index = (n)->filename_index;\
- (c)->lineno = (n)->lineno;\
- }\
-} while (0)
-
-#define sym(x) ((mrb_sym)(intptr_t)(x))
-#define nsym(x) ((node*)(intptr_t)(x))
-
-static inline mrb_sym
-intern_cstr_gen(parser_state *p, const char *s)
-{
- return mrb_intern_cstr(p->mrb, s);
-}
-#define intern_cstr(s) intern_cstr_gen(p,(s))
-
-static inline mrb_sym
-intern_gen(parser_state *p, const char *s, size_t len)
-{
- return mrb_intern(p->mrb, s, len);
-}
-#define intern(s,len) intern_gen(p,(s),(len))
-
-static inline mrb_sym
-intern_gen_c(parser_state *p, const char c)
-{
- return mrb_intern(p->mrb, &c, 1);
-}
-#define intern_c(c) intern_gen_c(p,(c))
-
-static void
-cons_free_gen(parser_state *p, node *cons)
-{
- cons->cdr = p->cells;
- p->cells = cons;
-}
-#define cons_free(c) cons_free_gen(p, (c))
-
-static void*
-parser_palloc(parser_state *p, size_t size)
-{
- void *m = mrb_pool_alloc(p->pool, size);
-
- if (!m) {
- MRB_THROW(p->jmp);
- }
- return m;
-}
-
-static node*
-cons_gen(parser_state *p, node *car, node *cdr)
-{
- node *c;
-
- if (p->cells) {
- c = p->cells;
- p->cells = p->cells->cdr;
- }
- else {
- c = (node *)parser_palloc(p, sizeof(mrb_ast_node));
- }
-
- c->car = car;
- c->cdr = cdr;
- c->lineno = p->lineno;
- c->filename_index = p->current_filename_index;
- return c;
-}
-#define cons(a,b) cons_gen(p,(a),(b))
-
-static node*
-list1_gen(parser_state *p, node *a)
-{
- return cons(a, 0);
-}
-#define list1(a) list1_gen(p, (a))
-
-static node*
-list2_gen(parser_state *p, node *a, node *b)
-{
- return cons(a, cons(b,0));
-}
-#define list2(a,b) list2_gen(p, (a),(b))
-
-static node*
-list3_gen(parser_state *p, node *a, node *b, node *c)
-{
- return cons(a, cons(b, cons(c,0)));
-}
-#define list3(a,b,c) list3_gen(p, (a),(b),(c))
-
-static node*
-list4_gen(parser_state *p, node *a, node *b, node *c, node *d)
-{
- return cons(a, cons(b, cons(c, cons(d, 0))));
-}
-#define list4(a,b,c,d) list4_gen(p, (a),(b),(c),(d))
-
-static node*
-list5_gen(parser_state *p, node *a, node *b, node *c, node *d, node *e)
-{
- return cons(a, cons(b, cons(c, cons(d, cons(e, 0)))));
-}
-#define list5(a,b,c,d,e) list5_gen(p, (a),(b),(c),(d),(e))
-
-static node*
-list6_gen(parser_state *p, node *a, node *b, node *c, node *d, node *e, node *f)
-{
- return cons(a, cons(b, cons(c, cons(d, cons(e, cons(f, 0))))));
-}
-#define list6(a,b,c,d,e,f) list6_gen(p, (a),(b),(c),(d),(e),(f))
-
-static node*
-append_gen(parser_state *p, node *a, node *b)
-{
- node *c = a;
-
- if (!a) return b;
- while (c->cdr) {
- c = c->cdr;
- }
- if (b) {
- c->cdr = b;
- }
- return a;
-}
-#define append(a,b) append_gen(p,(a),(b))
-#define push(a,b) append_gen(p,(a),list1(b))
-
-static char*
-parser_strndup(parser_state *p, const char *s, size_t len)
-{
- char *b = (char *)parser_palloc(p, len+1);
-
- memcpy(b, s, len);
- b[len] = '\0';
- return b;
-}
-#define strndup(s,len) parser_strndup(p, s, len)
-
-static char*
-parser_strdup(parser_state *p, const char *s)
-{
- return parser_strndup(p, s, strlen(s));
-}
-#undef strdup
-#define strdup(s) parser_strdup(p, s)
-
-/* xxx ----------------------------- */
-
-static node*
-local_switch(parser_state *p)
-{
- node *prev = p->locals;
-
- p->locals = cons(0, 0);
- return prev;
-}
-
-static void
-local_resume(parser_state *p, node *prev)
-{
- p->locals = prev;
-}
-
-static void
-local_nest(parser_state *p)
-{
- p->locals = cons(0, p->locals);
-}
-
-static void
-local_unnest(parser_state *p)
-{
- p->locals = p->locals->cdr;
-}
-
-static mrb_bool
-local_var_p(parser_state *p, mrb_sym sym)
-{
- node *l = p->locals;
-
- while (l) {
- node *n = l->car;
- while (n) {
- if (sym(n->car) == sym) return TRUE;
- n = n->cdr;
- }
- l = l->cdr;
- }
- return FALSE;
-}
-
-static void
-local_add_f(parser_state *p, mrb_sym sym)
-{
- p->locals->car = push(p->locals->car, nsym(sym));
-}
-
-static void
-local_add(parser_state *p, mrb_sym sym)
-{
- if (!local_var_p(p, sym)) {
- local_add_f(p, sym);
- }
-}
-
-/* (:scope (vars..) (prog...)) */
-static node*
-new_scope(parser_state *p, node *body)
-{
- return cons((node*)NODE_SCOPE, cons(p->locals->car, body));
-}
-
-/* (:begin prog...) */
-static node*
-new_begin(parser_state *p, node *body)
-{
- if (body) {
- return list2((node*)NODE_BEGIN, body);
- }
- return cons((node*)NODE_BEGIN, 0);
-}
-
-#define newline_node(n) (n)
-
-/* (:rescue body rescue else) */
-static node*
-new_rescue(parser_state *p, node *body, node *resq, node *els)
-{
- return list4((node*)NODE_RESCUE, body, resq, els);
-}
-
-/* (:ensure body ensure) */
-static node*
-new_ensure(parser_state *p, node *a, node *b)
-{
- return cons((node*)NODE_ENSURE, cons(a, cons(0, b)));
-}
-
-/* (:nil) */
-static node*
-new_nil(parser_state *p)
-{
- return list1((node*)NODE_NIL);
-}
-
-/* (:true) */
-static node*
-new_true(parser_state *p)
-{
- return list1((node*)NODE_TRUE);
-}
-
-/* (:false) */
-static node*
-new_false(parser_state *p)
-{
- return list1((node*)NODE_FALSE);
-}
-
-/* (:alias new old) */
-static node*
-new_alias(parser_state *p, mrb_sym a, mrb_sym b)
-{
- return cons((node*)NODE_ALIAS, cons(nsym(a), nsym(b)));
-}
-
-/* (:if cond then else) */
-static node*
-new_if(parser_state *p, node *a, node *b, node *c)
-{
- return list4((node*)NODE_IF, a, b, c);
-}
-
-/* (:unless cond then else) */
-static node*
-new_unless(parser_state *p, node *a, node *b, node *c)
-{
- return list4((node*)NODE_IF, a, c, b);
-}
-
-/* (:while cond body) */
-static node*
-new_while(parser_state *p, node *a, node *b)
-{
- return cons((node*)NODE_WHILE, cons(a, b));
-}
-
-/* (:until cond body) */
-static node*
-new_until(parser_state *p, node *a, node *b)
-{
- return cons((node*)NODE_UNTIL, cons(a, b));
-}
-
-/* (:for var obj body) */
-static node*
-new_for(parser_state *p, node *v, node *o, node *b)
-{
- return list4((node*)NODE_FOR, v, o, b);
-}
-
-/* (:case a ((when ...) body) ((when...) body)) */
-static node*
-new_case(parser_state *p, node *a, node *b)
-{
- node *n = list2((node*)NODE_CASE, a);
- node *n2 = n;
-
- while (n2->cdr) {
- n2 = n2->cdr;
- }
- n2->cdr = b;
- return n;
-}
-
-/* (:postexe a) */
-static node*
-new_postexe(parser_state *p, node *a)
-{
- return cons((node*)NODE_POSTEXE, a);
-}
-
-/* (:self) */
-static node*
-new_self(parser_state *p)
-{
- return list1((node*)NODE_SELF);
-}
-
-/* (:call a b c) */
-static node*
-new_call(parser_state *p, node *a, mrb_sym b, node *c)
-{
- node *n = list4((node*)NODE_CALL, a, nsym(b), c);
- NODE_LINENO(n, a);
- return n;
-}
-
-/* (:fcall self mid args) */
-static node*
-new_fcall(parser_state *p, mrb_sym b, node *c)
-{
- node *n = new_self(p);
- NODE_LINENO(n, c);
- n = list4((node*)NODE_FCALL, n, nsym(b), c);
- NODE_LINENO(n, c);
- return n;
-}
-
-/* (:super . c) */
-static node*
-new_super(parser_state *p, node *c)
-{
- return cons((node*)NODE_SUPER, c);
-}
-
-/* (:zsuper) */
-static node*
-new_zsuper(parser_state *p)
-{
- return list1((node*)NODE_ZSUPER);
-}
-
-/* (:yield . c) */
-static node*
-new_yield(parser_state *p, node *c)
-{
- if (c) {
- if (c->cdr) {
- yyerror(p, "both block arg and actual block given");
- }
- return cons((node*)NODE_YIELD, c->car);
- }
- return cons((node*)NODE_YIELD, 0);
-}
-
-/* (:return . c) */
-static node*
-new_return(parser_state *p, node *c)
-{
- return cons((node*)NODE_RETURN, c);
-}
-
-/* (:break . c) */
-static node*
-new_break(parser_state *p, node *c)
-{
- return cons((node*)NODE_BREAK, c);
-}
-
-/* (:next . c) */
-static node*
-new_next(parser_state *p, node *c)
-{
- return cons((node*)NODE_NEXT, c);
-}
-
-/* (:redo) */
-static node*
-new_redo(parser_state *p)
-{
- return list1((node*)NODE_REDO);
-}
-
-/* (:retry) */
-static node*
-new_retry(parser_state *p)
-{
- return list1((node*)NODE_RETRY);
-}
-
-/* (:dot2 a b) */
-static node*
-new_dot2(parser_state *p, node *a, node *b)
-{
- return cons((node*)NODE_DOT2, cons(a, b));
-}
-
-/* (:dot3 a b) */
-static node*
-new_dot3(parser_state *p, node *a, node *b)
-{
- return cons((node*)NODE_DOT3, cons(a, b));
-}
-
-/* (:colon2 b c) */
-static node*
-new_colon2(parser_state *p, node *b, mrb_sym c)
-{
- return cons((node*)NODE_COLON2, cons(b, nsym(c)));
-}
-
-/* (:colon3 . c) */
-static node*
-new_colon3(parser_state *p, mrb_sym c)
-{
- return cons((node*)NODE_COLON3, nsym(c));
-}
-
-/* (:and a b) */
-static node*
-new_and(parser_state *p, node *a, node *b)
-{
- return cons((node*)NODE_AND, cons(a, b));
-}
-
-/* (:or a b) */
-static node*
-new_or(parser_state *p, node *a, node *b)
-{
- return cons((node*)NODE_OR, cons(a, b));
-}
-
-/* (:array a...) */
-static node*
-new_array(parser_state *p, node *a)
-{
- return cons((node*)NODE_ARRAY, a);
-}
-
-/* (:splat . a) */
-static node*
-new_splat(parser_state *p, node *a)
-{
- return cons((node*)NODE_SPLAT, a);
-}
-
-/* (:hash (k . v) (k . v)...) */
-static node*
-new_hash(parser_state *p, node *a)
-{
- return cons((node*)NODE_HASH, a);
-}
-
-/* (:sym . a) */
-static node*
-new_sym(parser_state *p, mrb_sym sym)
-{
- return cons((node*)NODE_SYM, nsym(sym));
-}
-
-static mrb_sym
-new_strsym(parser_state *p, node* str)
-{
- const char *s = (const char*)str->cdr->car;
- size_t len = (size_t)str->cdr->cdr;
-
- return mrb_intern(p->mrb, s, len);
-}
-
-/* (:lvar . a) */
-static node*
-new_lvar(parser_state *p, mrb_sym sym)
-{
- return cons((node*)NODE_LVAR, nsym(sym));
-}
-
-/* (:gvar . a) */
-static node*
-new_gvar(parser_state *p, mrb_sym sym)
-{
- return cons((node*)NODE_GVAR, nsym(sym));
-}
-
-/* (:ivar . a) */
-static node*
-new_ivar(parser_state *p, mrb_sym sym)
-{
- return cons((node*)NODE_IVAR, nsym(sym));
-}
-
-/* (:cvar . a) */
-static node*
-new_cvar(parser_state *p, mrb_sym sym)
-{
- return cons((node*)NODE_CVAR, nsym(sym));
-}
-
-/* (:const . a) */
-static node*
-new_const(parser_state *p, mrb_sym sym)
-{
- return cons((node*)NODE_CONST, nsym(sym));
-}
-
-/* (:undef a...) */
-static node*
-new_undef(parser_state *p, mrb_sym sym)
-{
- return list2((node*)NODE_UNDEF, nsym(sym));
-}
-
-/* (:class class super body) */
-static node*
-new_class(parser_state *p, node *c, node *s, node *b)
-{
- return list4((node*)NODE_CLASS, c, s, cons(p->locals->car, b));
-}
-
-/* (:sclass obj body) */
-static node*
-new_sclass(parser_state *p, node *o, node *b)
-{
- return list3((node*)NODE_SCLASS, o, cons(p->locals->car, b));
-}
-
-/* (:module module body) */
-static node*
-new_module(parser_state *p, node *m, node *b)
-{
- return list3((node*)NODE_MODULE, m, cons(p->locals->car, b));
-}
-
-/* (:def m lv (arg . body)) */
-static node*
-new_def(parser_state *p, mrb_sym m, node *a, node *b)
-{
- return list5((node*)NODE_DEF, nsym(m), p->locals->car, a, b);
-}
-
-/* (:sdef obj m lv (arg . body)) */
-static node*
-new_sdef(parser_state *p, node *o, mrb_sym m, node *a, node *b)
-{
- return list6((node*)NODE_SDEF, o, nsym(m), p->locals->car, a, b);
-}
-
-/* (:arg . sym) */
-static node*
-new_arg(parser_state *p, mrb_sym sym)
-{
- return cons((node*)NODE_ARG, nsym(sym));
-}
-
-/* (m o r m2 b) */
-/* m: (a b c) */
-/* o: ((a . e1) (b . e2)) */
-/* r: a */
-/* m2: (a b c) */
-/* b: a */
-static node*
-new_args(parser_state *p, node *m, node *opt, mrb_sym rest, node *m2, mrb_sym blk)
-{
- node *n;
-
- n = cons(m2, nsym(blk));
- n = cons(nsym(rest), n);
- n = cons(opt, n);
- return cons(m, n);
-}
-
-/* (:block_arg . a) */
-static node*
-new_block_arg(parser_state *p, node *a)
-{
- return cons((node*)NODE_BLOCK_ARG, a);
-}
-
-/* (:block arg body) */
-static node*
-new_block(parser_state *p, node *a, node *b)
-{
- return list4((node*)NODE_BLOCK, p->locals->car, a, b);
-}
-
-/* (:lambda arg body) */
-static node*
-new_lambda(parser_state *p, node *a, node *b)
-{
- return list4((node*)NODE_LAMBDA, p->locals->car, a, b);
-}
-
-/* (:asgn lhs rhs) */
-static node*
-new_asgn(parser_state *p, node *a, node *b)
-{
- return cons((node*)NODE_ASGN, cons(a, b));
-}
-
-/* (:masgn mlhs=(pre rest post) mrhs) */
-static node*
-new_masgn(parser_state *p, node *a, node *b)
-{
- return cons((node*)NODE_MASGN, cons(a, b));
-}
-
-/* (:asgn lhs rhs) */
-static node*
-new_op_asgn(parser_state *p, node *a, mrb_sym op, node *b)
-{
- return list4((node*)NODE_OP_ASGN, a, nsym(op), b);
-}
-
-/* (:int . i) */
-static node*
-new_int(parser_state *p, const char *s, int base)
-{
- return list3((node*)NODE_INT, (node*)strdup(s), (node*)(intptr_t)base);
-}
-
-/* (:float . i) */
-static node*
-new_float(parser_state *p, const char *s)
-{
- return cons((node*)NODE_FLOAT, (node*)strdup(s));
-}
-
-/* (:str . (s . len)) */
-static node*
-new_str(parser_state *p, const char *s, int len)
-{
- return cons((node*)NODE_STR, cons((node*)strndup(s, len), (node*)(intptr_t)len));
-}
-
-/* (:dstr . a) */
-static node*
-new_dstr(parser_state *p, node *a)
-{
- return cons((node*)NODE_DSTR, a);
-}
-
-/* (:str . (s . len)) */
-static node*
-new_xstr(parser_state *p, const char *s, int len)
-{
- return cons((node*)NODE_XSTR, cons((node*)strndup(s, len), (node*)(intptr_t)len));
-}
-
-/* (:xstr . a) */
-static node*
-new_dxstr(parser_state *p, node *a)
-{
- return cons((node*)NODE_DXSTR, a);
-}
-
-/* (:dsym . a) */
-static node*
-new_dsym(parser_state *p, node *a)
-{
- return cons((node*)NODE_DSYM, new_dstr(p, a));
-}
-
-/* (:str . (a . a)) */
-static node*
-new_regx(parser_state *p, const char *p1, const char* p2)
-{
- return cons((node*)NODE_REGX, cons((node*)p1, (node*)p2));
-}
-
-/* (:dregx . a) */
-static node*
-new_dregx(parser_state *p, node *a, node *b)
-{
- return cons((node*)NODE_DREGX, cons(a, b));
-}
-
-/* (:backref . n) */
-static node*
-new_back_ref(parser_state *p, int n)
-{
- return cons((node*)NODE_BACK_REF, (node*)(intptr_t)n);
-}
-
-/* (:nthref . n) */
-static node*
-new_nth_ref(parser_state *p, int n)
-{
- return cons((node*)NODE_NTH_REF, (node*)(intptr_t)n);
-}
-
-/* (:heredoc . a) */
-static node*
-new_heredoc(parser_state *p)
-{
- parser_heredoc_info *inf = (parser_heredoc_info *)parser_palloc(p, sizeof(parser_heredoc_info));
- return cons((node*)NODE_HEREDOC, (node*)inf);
-}
-
-static void
-new_bv(parser_state *p, mrb_sym id)
-{
-}
-
-static node*
-new_literal_delim(parser_state *p)
-{
- return cons((node*)NODE_LITERAL_DELIM, 0);
-}
-
-/* (:words . a) */
-static node*
-new_words(parser_state *p, node *a)
-{
- return cons((node*)NODE_WORDS, a);
-}
-
-/* (:symbols . a) */
-static node*
-new_symbols(parser_state *p, node *a)
-{
- return cons((node*)NODE_SYMBOLS, a);
-}
-
-/* xxx ----------------------------- */
-
-/* (:call a op) */
-static node*
-call_uni_op(parser_state *p, node *recv, const char *m)
-{
- return new_call(p, recv, intern_cstr(m), 0);
-}
-
-/* (:call a op b) */
-static node*
-call_bin_op(parser_state *p, node *recv, const char *m, node *arg1)
-{
- return new_call(p, recv, intern_cstr(m), list1(list1(arg1)));
-}
-
-static void
-args_with_block(parser_state *p, node *a, node *b)
-{
- if (b) {
- if (a->cdr) {
- yyerror(p, "both block arg and actual block given");
- }
- a->cdr = b;
- }
-}
-
-static void
-call_with_block(parser_state *p, node *a, node *b)
-{
- node *n;
-
- if (a->car == (node*)NODE_SUPER ||
- a->car == (node*)NODE_ZSUPER) {
- if (!a->cdr) a->cdr = cons(0, b);
- else {
- args_with_block(p, a->cdr, b);
- }
- }
- else {
- n = a->cdr->cdr->cdr;
- if (!n->car) n->car = cons(0, b);
- else {
- args_with_block(p, n->car, b);
- }
- }
-}
-
-static node*
-negate_lit(parser_state *p, node *n)
-{
- return cons((node*)NODE_NEGATE, n);
-}
-
-static node*
-cond(node *n)
-{
- return n;
-}
-
-static node*
-ret_args(parser_state *p, node *n)
-{
- if (n->cdr) {
- yyerror(p, "block argument should not be given");
- return NULL;
- }
- if (!n->car->cdr) return n->car->car;
- return new_array(p, n->car);
-}
-
-static void
-assignable(parser_state *p, node *lhs)
-{
- if ((int)(intptr_t)lhs->car == NODE_LVAR) {
- local_add(p, sym(lhs->cdr));
- }
-}
-
-static node*
-var_reference(parser_state *p, node *lhs)
-{
- node *n;
-
- if ((int)(intptr_t)lhs->car == NODE_LVAR) {
- if (!local_var_p(p, sym(lhs->cdr))) {
- n = new_fcall(p, sym(lhs->cdr), 0);
- cons_free(lhs);
- return n;
- }
- }
-
- return lhs;
-}
-
-typedef enum mrb_string_type string_type;
-
-static node*
-new_strterm(parser_state *p, string_type type, int term, int paren)
-{
- return cons((node*)(intptr_t)type, cons((node*)0, cons((node*)(intptr_t)paren, (node*)(intptr_t)term)));
-}
-
-static void
-end_strterm(parser_state *p)
-{
- cons_free(p->lex_strterm->cdr->cdr);
- cons_free(p->lex_strterm->cdr);
- cons_free(p->lex_strterm);
- p->lex_strterm = NULL;
-}
-
-static parser_heredoc_info *
-parsing_heredoc_inf(parser_state *p)
-{
- node *nd = p->parsing_heredoc;
- if (nd == NULL)
- return NULL;
- /* mrb_assert(nd->car->car == NODE_HEREDOC); */
- return (parser_heredoc_info*)nd->car->cdr;
-}
-
-static void
-heredoc_treat_nextline(parser_state *p)
-{
- if (p->heredocs_from_nextline == NULL)
- return;
- if (p->parsing_heredoc == NULL) {
- node *n;
- p->parsing_heredoc = p->heredocs_from_nextline;
- p->lex_strterm_before_heredoc = p->lex_strterm;
- p->lex_strterm = new_strterm(p, parsing_heredoc_inf(p)->type, 0, 0);
- n = p->all_heredocs;
- if (n) {
- while (n->cdr)
- n = n->cdr;
- n->cdr = p->parsing_heredoc;
- }
- else {
- p->all_heredocs = p->parsing_heredoc;
- }
- }
- else {
- node *n, *m;
- m = p->heredocs_from_nextline;
- while (m->cdr)
- m = m->cdr;
- n = p->all_heredocs;
- mrb_assert(n != NULL);
- if (n == p->parsing_heredoc) {
- m->cdr = n;
- p->all_heredocs = p->heredocs_from_nextline;
- p->parsing_heredoc = p->heredocs_from_nextline;
- }
- else {
- while (n->cdr != p->parsing_heredoc) {
- n = n->cdr;
- mrb_assert(n != NULL);
- }
- m->cdr = n->cdr;
- n->cdr = p->heredocs_from_nextline;
- p->parsing_heredoc = p->heredocs_from_nextline;
- }
- }
- p->heredocs_from_nextline = NULL;
-}
-
-static void
-heredoc_end(parser_state *p)
-{
- p->parsing_heredoc = p->parsing_heredoc->cdr;
- if (p->parsing_heredoc == NULL) {
- p->lstate = EXPR_BEG;
- p->cmd_start = TRUE;
- end_strterm(p);
- p->lex_strterm = p->lex_strterm_before_heredoc;
- p->lex_strterm_before_heredoc = NULL;
- p->heredoc_end_now = TRUE;
- }
- else {
- /* next heredoc */
- p->lex_strterm->car = (node*)(intptr_t)parsing_heredoc_inf(p)->type;
- }
-}
-#define is_strterm_type(p,str_func) ((int)(intptr_t)((p)->lex_strterm->car) & (str_func))
-
-/* xxx ----------------------------- */
-
-%}
-
-%pure-parser
-%parse-param {parser_state *p}
-%lex-param {parser_state *p}
-
-%union {
- node *nd;
- mrb_sym id;
- int num;
- stack_type stack;
- const struct vtable *vars;
-}
-
-%token <num>
- keyword_class
- keyword_module
- keyword_def
- keyword_begin
- keyword_if
- keyword_unless
- keyword_while
- keyword_until
- keyword_for
-
-%token
- keyword_undef
- keyword_rescue
- keyword_ensure
- keyword_end
- keyword_then
- keyword_elsif
- keyword_else
- keyword_case
- keyword_when
- keyword_break
- keyword_next
- keyword_redo
- keyword_retry
- keyword_in
- keyword_do
- keyword_do_cond
- keyword_do_block
- keyword_do_LAMBDA
- keyword_return
- keyword_yield
- keyword_super
- keyword_self
- keyword_nil
- keyword_true
- keyword_false
- keyword_and
- keyword_or
- keyword_not
- modifier_if
- modifier_unless
- modifier_while
- modifier_until
- modifier_rescue
- keyword_alias
- keyword_BEGIN
- keyword_END
- keyword__LINE__
- keyword__FILE__
- keyword__ENCODING__
-
-%token <id> tIDENTIFIER tFID tGVAR tIVAR tCONSTANT tCVAR tLABEL
-%token <nd> tINTEGER tFLOAT tCHAR tXSTRING tREGEXP
-%token <nd> tSTRING tSTRING_PART tSTRING_MID
-%token <nd> tNTH_REF tBACK_REF
-%token <num> tREGEXP_END
-
-%type <nd> singleton string string_rep string_interp xstring regexp
-%type <nd> literal numeric cpath symbol
-%type <nd> top_compstmt top_stmts top_stmt
-%type <nd> bodystmt compstmt stmts stmt expr arg primary command command_call method_call
-%type <nd> expr_value arg_value primary_value
-%type <nd> if_tail opt_else case_body cases opt_rescue exc_list exc_var opt_ensure
-%type <nd> args call_args opt_call_args
-%type <nd> paren_args opt_paren_args variable
-%type <nd> command_args aref_args opt_block_arg block_arg var_ref var_lhs
-%type <nd> command_asgn mrhs superclass block_call block_command
-%type <nd> f_block_optarg f_block_opt
-%type <nd> f_arglist f_args f_arg f_arg_item f_optarg f_marg f_marg_list f_margs
-%type <nd> assoc_list assocs assoc undef_list backref for_var
-%type <nd> block_param opt_block_param block_param_def f_opt
-%type <nd> bv_decls opt_bv_decl bvar f_larglist lambda_body
-%type <nd> brace_block cmd_brace_block do_block lhs none f_bad_arg
-%type <nd> mlhs mlhs_list mlhs_post mlhs_basic mlhs_item mlhs_node mlhs_inner
-%type <id> fsym sym basic_symbol operation operation2 operation3
-%type <id> cname fname op f_rest_arg f_block_arg opt_f_block_arg f_norm_arg f_opt_asgn
-%type <nd> heredoc words symbols
-
-%token tUPLUS /* unary+ */
-%token tUMINUS /* unary- */
-%token tPOW /* ** */
-%token tCMP /* <=> */
-%token tEQ /* == */
-%token tEQQ /* === */
-%token tNEQ /* != */
-%token tGEQ /* >= */
-%token tLEQ /* <= */
-%token tANDOP tOROP /* && and || */
-%token tMATCH tNMATCH /* =~ and !~ */
-%token tDOT2 tDOT3 /* .. and ... */
-%token tAREF tASET /* [] and []= */
-%token tLSHFT tRSHFT /* << and >> */
-%token tCOLON2 /* :: */
-%token tCOLON3 /* :: at EXPR_BEG */
-%token <id> tOP_ASGN /* +=, -= etc. */
-%token tASSOC /* => */
-%token tLPAREN /* ( */
-%token tLPAREN_ARG /* ( */
-%token tRPAREN /* ) */
-%token tLBRACK /* [ */
-%token tLBRACE /* { */
-%token tLBRACE_ARG /* { */
-%token tSTAR /* * */
-%token tAMPER /* & */
-%token tLAMBDA /* -> */
-%token tSYMBEG tREGEXP_BEG tWORDS_BEG tSYMBOLS_BEG
-%token tSTRING_BEG tXSTRING_BEG tSTRING_DVAR tLAMBEG
-%token <nd> tHEREDOC_BEG /* <<, <<- */
-%token tHEREDOC_END tLITERAL_DELIM tHD_LITERAL_DELIM
-%token <nd> tHD_STRING_PART tHD_STRING_MID
-
-/*
- * precedence table
- */
-
-%nonassoc tLOWEST
-%nonassoc tLBRACE_ARG
-
-%nonassoc modifier_if modifier_unless modifier_while modifier_until
-%left keyword_or keyword_and
-%right keyword_not
-%right '=' tOP_ASGN
-%left modifier_rescue
-%right '?' ':'
-%nonassoc tDOT2 tDOT3
-%left tOROP
-%left tANDOP
-%nonassoc tCMP tEQ tEQQ tNEQ tMATCH tNMATCH
-%left '>' tGEQ '<' tLEQ
-%left '|' '^'
-%left '&'
-%left tLSHFT tRSHFT
-%left '+' '-'
-%left '*' '/' '%'
-%right tUMINUS_NUM tUMINUS
-%right tPOW
-%right '!' '~' tUPLUS
-
-%nonassoc idNULL
-%nonassoc idRespond_to
-%nonassoc idIFUNC
-%nonassoc idCFUNC
-%nonassoc id_core_set_method_alias
-%nonassoc id_core_set_variable_alias
-%nonassoc id_core_undef_method
-%nonassoc id_core_define_method
-%nonassoc id_core_define_singleton_method
-%nonassoc id_core_set_postexe
-
-%token tLAST_TOKEN
-
-%%
-program : {
- p->lstate = EXPR_BEG;
- if (!p->locals) p->locals = cons(0,0);
- }
- top_compstmt
- {
- p->tree = new_scope(p, $2);
- NODE_LINENO(p->tree, $2);
- }
- ;
-
-top_compstmt : top_stmts opt_terms
- {
- $$ = $1;
- }
- ;
-
-top_stmts : none
- {
- $$ = new_begin(p, 0);
- }
- | top_stmt
- {
- $$ = new_begin(p, $1);
- NODE_LINENO($$, $1);
- }
- | top_stmts terms top_stmt
- {
- $$ = push($1, newline_node($3));
- }
- | error top_stmt
- {
- $$ = new_begin(p, 0);
- }
- ;
-
-top_stmt : stmt
- | keyword_BEGIN
- {
- $<nd>$ = local_switch(p);
- }
- '{' top_compstmt '}'
- {
- yyerror(p, "BEGIN not supported");
- local_resume(p, $<nd>2);
- $$ = 0;
- }
- ;
-
-bodystmt : compstmt
- opt_rescue
- opt_else
- opt_ensure
- {
- if ($2) {
- $$ = new_rescue(p, $1, $2, $3);
- NODE_LINENO($$, $1);
- }
- else if ($3) {
- yywarn(p, "else without rescue is useless");
- $$ = push($1, $3);
- }
- else {
- $$ = $1;
- }
- if ($4) {
- if ($$) {
- $$ = new_ensure(p, $$, $4);
- }
- else {
- $$ = push($4, new_nil(p));
- }
- }
- }
- ;
-
-compstmt : stmts opt_terms
- {
- $$ = $1;
- }
- ;
-
-stmts : none
- {
- $$ = new_begin(p, 0);
- }
- | stmt
- {
- $$ = new_begin(p, $1);
- NODE_LINENO($$, $1);
- }
- | stmts terms stmt
- {
- $$ = push($1, newline_node($3));
- }
- | error stmt
- {
- $$ = new_begin(p, $2);
- }
- ;
-
-stmt : keyword_alias fsym {p->lstate = EXPR_FNAME;} fsym
- {
- $$ = new_alias(p, $2, $4);
- }
- | keyword_undef undef_list
- {
- $$ = $2;
- }
- | stmt modifier_if expr_value
- {
- $$ = new_if(p, cond($3), $1, 0);
- }
- | stmt modifier_unless expr_value
- {
- $$ = new_unless(p, cond($3), $1, 0);
- }
- | stmt modifier_while expr_value
- {
- $$ = new_while(p, cond($3), $1);
- }
- | stmt modifier_until expr_value
- {
- $$ = new_until(p, cond($3), $1);
- }
- | stmt modifier_rescue stmt
- {
- $$ = new_rescue(p, $1, list1(list3(0, 0, $3)), 0);
- }
- | keyword_END '{' compstmt '}'
- {
- yyerror(p, "END not suported");
- $$ = new_postexe(p, $3);
- }
- | command_asgn
- | mlhs '=' command_call
- {
- $$ = new_masgn(p, $1, $3);
- }
- | var_lhs tOP_ASGN command_call
- {
- $$ = new_op_asgn(p, $1, $2, $3);
- }
- | primary_value '[' opt_call_args rbracket tOP_ASGN command_call
- {
- $$ = new_op_asgn(p, new_call(p, $1, intern("[]",2), $3), $5, $6);
- }
- | primary_value '.' tIDENTIFIER tOP_ASGN command_call
- {
- $$ = new_op_asgn(p, new_call(p, $1, $3, 0), $4, $5);
- }
- | primary_value '.' tCONSTANT tOP_ASGN command_call
- {
- $$ = new_op_asgn(p, new_call(p, $1, $3, 0), $4, $5);
- }
- | primary_value tCOLON2 tCONSTANT tOP_ASGN command_call
- {
- yyerror(p, "constant re-assignment");
- $$ = 0;
- }
- | primary_value tCOLON2 tIDENTIFIER tOP_ASGN command_call
- {
- $$ = new_op_asgn(p, new_call(p, $1, $3, 0), $4, $5);
- }
- | backref tOP_ASGN command_call
- {
- backref_error(p, $1);
- $$ = new_begin(p, 0);
- }
- | lhs '=' mrhs
- {
- $$ = new_asgn(p, $1, new_array(p, $3));
- }
- | mlhs '=' arg_value
- {
- $$ = new_masgn(p, $1, $3);
- }
- | mlhs '=' mrhs
- {
- $$ = new_masgn(p, $1, new_array(p, $3));
- }
- | expr
- ;
-
-command_asgn : lhs '=' command_call
- {
- $$ = new_asgn(p, $1, $3);
- }
- | lhs '=' command_asgn
- {
- $$ = new_asgn(p, $1, $3);
- }
- ;
-
-
-expr : command_call
- | expr keyword_and expr
- {
- $$ = new_and(p, $1, $3);
- }
- | expr keyword_or expr
- {
- $$ = new_or(p, $1, $3);
- }
- | keyword_not opt_nl expr
- {
- $$ = call_uni_op(p, cond($3), "!");
- }
- | '!' command_call
- {
- $$ = call_uni_op(p, cond($2), "!");
- }
- | arg
- ;
-
-expr_value : expr
- {
- if (!$1) $$ = new_nil(p);
- else $$ = $1;
- }
- ;
-
-command_call : command
- | block_command
- ;
-
-block_command : block_call
- | block_call dot_or_colon operation2 command_args
- ;
-
-cmd_brace_block : tLBRACE_ARG
- {
- local_nest(p);
- }
- opt_block_param
- compstmt
- '}'
- {
- $$ = new_block(p, $3, $4);
- local_unnest(p);
- }
- ;
-
-command : operation command_args %prec tLOWEST
- {
- $$ = new_fcall(p, $1, $2);
- }
- | operation command_args cmd_brace_block
- {
- args_with_block(p, $2, $3);
- $$ = new_fcall(p, $1, $2);
- }
- | primary_value '.' operation2 command_args %prec tLOWEST
- {
- $$ = new_call(p, $1, $3, $4);
- }
- | primary_value '.' operation2 command_args cmd_brace_block
- {
- args_with_block(p, $4, $5);
- $$ = new_call(p, $1, $3, $4);
- }
- | primary_value tCOLON2 operation2 command_args %prec tLOWEST
- {
- $$ = new_call(p, $1, $3, $4);
- }
- | primary_value tCOLON2 operation2 command_args cmd_brace_block
- {
- args_with_block(p, $4, $5);
- $$ = new_call(p, $1, $3, $4);
- }
- | keyword_super command_args
- {
- $$ = new_super(p, $2);
- }
- | keyword_yield command_args
- {
- $$ = new_yield(p, $2);
- }
- | keyword_return call_args
- {
- $$ = new_return(p, ret_args(p, $2));
- }
- | keyword_break call_args
- {
- $$ = new_break(p, ret_args(p, $2));
- }
- | keyword_next call_args
- {
- $$ = new_next(p, ret_args(p, $2));
- }
- ;
-
-mlhs : mlhs_basic
- {
- $$ = $1;
- }
- | tLPAREN mlhs_inner rparen
- {
- $$ = $2;
- }
- ;
-
-mlhs_inner : mlhs_basic
- | tLPAREN mlhs_inner rparen
- {
- $$ = $2;
- }
- ;
-
-mlhs_basic : mlhs_list
- {
- $$ = list1($1);
- }
- | mlhs_list mlhs_item
- {
- $$ = list1(push($1,$2));
- }
- | mlhs_list tSTAR mlhs_node
- {
- $$ = list2($1, $3);
- }
- | mlhs_list tSTAR mlhs_node ',' mlhs_post
- {
- $$ = list3($1, $3, $5);
- }
- | mlhs_list tSTAR
- {
- $$ = list2($1, new_nil(p));
- }
- | mlhs_list tSTAR ',' mlhs_post
- {
- $$ = list3($1, new_nil(p), $4);
- }
- | tSTAR mlhs_node
- {
- $$ = list2(0, $2);
- }
- | tSTAR mlhs_node ',' mlhs_post
- {
- $$ = list3(0, $2, $4);
- }
- | tSTAR
- {
- $$ = list2(0, new_nil(p));
- }
- | tSTAR ',' mlhs_post
- {
- $$ = list3(0, new_nil(p), $3);
- }
- ;
-
-mlhs_item : mlhs_node
- | tLPAREN mlhs_inner rparen
- {
- $$ = new_masgn(p, $2, NULL);
- }
- ;
-
-mlhs_list : mlhs_item ','
- {
- $$ = list1($1);
- }
- | mlhs_list mlhs_item ','
- {
- $$ = push($1, $2);
- }
- ;
-
-mlhs_post : mlhs_item
- {
- $$ = list1($1);
- }
- | mlhs_list mlhs_item
- {
- $$ = push($1, $2);
- }
- ;
-
-mlhs_node : variable
- {
- assignable(p, $1);
- }
- | primary_value '[' opt_call_args rbracket
- {
- $$ = new_call(p, $1, intern("[]",2), $3);
- }
- | primary_value '.' tIDENTIFIER
- {
- $$ = new_call(p, $1, $3, 0);
- }
- | primary_value tCOLON2 tIDENTIFIER
- {
- $$ = new_call(p, $1, $3, 0);
- }
- | primary_value '.' tCONSTANT
- {
- $$ = new_call(p, $1, $3, 0);
- }
- | primary_value tCOLON2 tCONSTANT
- {
- if (p->in_def || p->in_single)
- yyerror(p, "dynamic constant assignment");
- $$ = new_colon2(p, $1, $3);
- }
- | tCOLON3 tCONSTANT
- {
- if (p->in_def || p->in_single)
- yyerror(p, "dynamic constant assignment");
- $$ = new_colon3(p, $2);
- }
- | backref
- {
- backref_error(p, $1);
- $$ = 0;
- }
- ;
-
-lhs : variable
- {
- assignable(p, $1);
- }
- | primary_value '[' opt_call_args rbracket
- {
- $$ = new_call(p, $1, intern("[]",2), $3);
- }
- | primary_value '.' tIDENTIFIER
- {
- $$ = new_call(p, $1, $3, 0);
- }
- | primary_value tCOLON2 tIDENTIFIER
- {
- $$ = new_call(p, $1, $3, 0);
- }
- | primary_value '.' tCONSTANT
- {
- $$ = new_call(p, $1, $3, 0);
- }
- | primary_value tCOLON2 tCONSTANT
- {
- if (p->in_def || p->in_single)
- yyerror(p, "dynamic constant assignment");
- $$ = new_colon2(p, $1, $3);
- }
- | tCOLON3 tCONSTANT
- {
- if (p->in_def || p->in_single)
- yyerror(p, "dynamic constant assignment");
- $$ = new_colon3(p, $2);
- }
- | backref
- {
- backref_error(p, $1);
- $$ = 0;
- }
- ;
-
-cname : tIDENTIFIER
- {
- yyerror(p, "class/module name must be CONSTANT");
- }
- | tCONSTANT
- ;
-
-cpath : tCOLON3 cname
- {
- $$ = cons((node*)1, nsym($2));
- }
- | cname
- {
- $$ = cons((node*)0, nsym($1));
- }
- | primary_value tCOLON2 cname
- {
- $$ = cons($1, nsym($3));
- }
- ;
-
-fname : tIDENTIFIER
- | tCONSTANT
- | tFID
- | op
- {
- p->lstate = EXPR_ENDFN;
- $$ = $1;
- }
- | reswords
- {
- p->lstate = EXPR_ENDFN;
- $$ = $<id>1;
- }
- ;
-
-fsym : fname
- | basic_symbol
- ;
-
-undef_list : fsym
- {
- $$ = new_undef(p, $1);
- }
- | undef_list ',' {p->lstate = EXPR_FNAME;} fsym
- {
- $$ = push($1, nsym($4));
- }
- ;
-
-op : '|' { $$ = intern_c('|'); }
- | '^' { $$ = intern_c('^'); }
- | '&' { $$ = intern_c('&'); }
- | tCMP { $$ = intern("<=>",3); }
- | tEQ { $$ = intern("==",2); }
- | tEQQ { $$ = intern("===",3); }
- | tMATCH { $$ = intern("=~",2); }
- | tNMATCH { $$ = intern("!~",2); }
- | '>' { $$ = intern_c('>'); }
- | tGEQ { $$ = intern(">=",2); }
- | '<' { $$ = intern_c('<'); }
- | tLEQ { $$ = intern("<=",2); }
- | tNEQ { $$ = intern("!=",2); }
- | tLSHFT { $$ = intern("<<",2); }
- | tRSHFT { $$ = intern(">>",2); }
- | '+' { $$ = intern_c('+'); }
- | '-' { $$ = intern_c('-'); }
- | '*' { $$ = intern_c('*'); }
- | tSTAR { $$ = intern_c('*'); }
- | '/' { $$ = intern_c('/'); }
- | '%' { $$ = intern_c('%'); }
- | tPOW { $$ = intern("**",2); }
- | '!' { $$ = intern_c('!'); }
- | '~' { $$ = intern_c('~'); }
- | tUPLUS { $$ = intern("+@",2); }
- | tUMINUS { $$ = intern("-@",2); }
- | tAREF { $$ = intern("[]",2); }
- | tASET { $$ = intern("[]=",3); }
- | '`' { $$ = intern_c('`'); }
- ;
-
-reswords : keyword__LINE__ | keyword__FILE__ | keyword__ENCODING__
- | keyword_BEGIN | keyword_END
- | keyword_alias | keyword_and | keyword_begin
- | keyword_break | keyword_case | keyword_class | keyword_def
- | keyword_do | keyword_else | keyword_elsif
- | keyword_end | keyword_ensure | keyword_false
- | keyword_for | keyword_in | keyword_module | keyword_next
- | keyword_nil | keyword_not | keyword_or | keyword_redo
- | keyword_rescue | keyword_retry | keyword_return | keyword_self
- | keyword_super | keyword_then | keyword_true | keyword_undef
- | keyword_when | keyword_yield | keyword_if | keyword_unless
- | keyword_while | keyword_until
- ;
-
-arg : lhs '=' arg
- {
- $$ = new_asgn(p, $1, $3);
- }
- | lhs '=' arg modifier_rescue arg
- {
- $$ = new_asgn(p, $1, new_rescue(p, $3, list1(list3(0, 0, $5)), 0));
- }
- | var_lhs tOP_ASGN arg
- {
- $$ = new_op_asgn(p, $1, $2, $3);
- }
- | var_lhs tOP_ASGN arg modifier_rescue arg
- {
- $$ = new_op_asgn(p, $1, $2, new_rescue(p, $3, list1(list3(0, 0, $5)), 0));
- }
- | primary_value '[' opt_call_args rbracket tOP_ASGN arg
- {
- $$ = new_op_asgn(p, new_call(p, $1, intern("[]",2), $3), $5, $6);
- }
- | primary_value '.' tIDENTIFIER tOP_ASGN arg
- {
- $$ = new_op_asgn(p, new_call(p, $1, $3, 0), $4, $5);
- }
- | primary_value '.' tCONSTANT tOP_ASGN arg
- {
- $$ = new_op_asgn(p, new_call(p, $1, $3, 0), $4, $5);
- }
- | primary_value tCOLON2 tIDENTIFIER tOP_ASGN arg
- {
- $$ = new_op_asgn(p, new_call(p, $1, $3, 0), $4, $5);
- }
- | primary_value tCOLON2 tCONSTANT tOP_ASGN arg
- {
- yyerror(p, "constant re-assignment");
- $$ = new_begin(p, 0);
- }
- | tCOLON3 tCONSTANT tOP_ASGN arg
- {
- yyerror(p, "constant re-assignment");
- $$ = new_begin(p, 0);
- }
- | backref tOP_ASGN arg
- {
- backref_error(p, $1);
- $$ = new_begin(p, 0);
- }
- | arg tDOT2 arg
- {
- $$ = new_dot2(p, $1, $3);
- }
- | arg tDOT3 arg
- {
- $$ = new_dot3(p, $1, $3);
- }
- | arg '+' arg
- {
- $$ = call_bin_op(p, $1, "+", $3);
- }
- | arg '-' arg
- {
- $$ = call_bin_op(p, $1, "-", $3);
- }
- | arg '*' arg
- {
- $$ = call_bin_op(p, $1, "*", $3);
- }
- | arg '/' arg
- {
- $$ = call_bin_op(p, $1, "/", $3);
- }
- | arg '%' arg
- {
- $$ = call_bin_op(p, $1, "%", $3);
- }
- | arg tPOW arg
- {
- $$ = call_bin_op(p, $1, "**", $3);
- }
- | tUMINUS_NUM tINTEGER tPOW arg
- {
- $$ = call_uni_op(p, call_bin_op(p, $2, "**", $4), "-@");
- }
- | tUMINUS_NUM tFLOAT tPOW arg
- {
- $$ = call_uni_op(p, call_bin_op(p, $2, "**", $4), "-@");
- }
- | tUPLUS arg
- {
- $$ = call_uni_op(p, $2, "+@");
- }
- | tUMINUS arg
- {
- $$ = call_uni_op(p, $2, "-@");
- }
- | arg '|' arg
- {
- $$ = call_bin_op(p, $1, "|", $3);
- }
- | arg '^' arg
- {
- $$ = call_bin_op(p, $1, "^", $3);
- }
- | arg '&' arg
- {
- $$ = call_bin_op(p, $1, "&", $3);
- }
- | arg tCMP arg
- {
- $$ = call_bin_op(p, $1, "<=>", $3);
- }
- | arg '>' arg
- {
- $$ = call_bin_op(p, $1, ">", $3);
- }
- | arg tGEQ arg
- {
- $$ = call_bin_op(p, $1, ">=", $3);
- }
- | arg '<' arg
- {
- $$ = call_bin_op(p, $1, "<", $3);
- }
- | arg tLEQ arg
- {
- $$ = call_bin_op(p, $1, "<=", $3);
- }
- | arg tEQ arg
- {
- $$ = call_bin_op(p, $1, "==", $3);
- }
- | arg tEQQ arg
- {
- $$ = call_bin_op(p, $1, "===", $3);
- }
- | arg tNEQ arg
- {
- $$ = call_bin_op(p, $1, "!=", $3);
- }
- | arg tMATCH arg
- {
- $$ = call_bin_op(p, $1, "=~", $3);
- }
- | arg tNMATCH arg
- {
- $$ = call_bin_op(p, $1, "!~", $3);
- }
- | '!' arg
- {
- $$ = call_uni_op(p, cond($2), "!");
- }
- | '~' arg
- {
- $$ = call_uni_op(p, cond($2), "~");
- }
- | arg tLSHFT arg
- {
- $$ = call_bin_op(p, $1, "<<", $3);
- }
- | arg tRSHFT arg
- {
- $$ = call_bin_op(p, $1, ">>", $3);
- }
- | arg tANDOP arg
- {
- $$ = new_and(p, $1, $3);
- }
- | arg tOROP arg
- {
- $$ = new_or(p, $1, $3);
- }
- | arg '?' arg opt_nl ':' arg
- {
- $$ = new_if(p, cond($1), $3, $6);
- }
- | primary
- {
- $$ = $1;
- }
- ;
-
-arg_value : arg
- {
- $$ = $1;
- if (!$$) $$ = new_nil(p);
- }
- ;
-
-aref_args : none
- | args trailer
- {
- $$ = $1;
- NODE_LINENO($$, $1);
- }
- | args ',' assocs trailer
- {
- $$ = push($1, new_hash(p, $3));
- }
- | assocs trailer
- {
- $$ = cons(new_hash(p, $1), 0);
- NODE_LINENO($$, $1);
- }
- ;
-
-paren_args : '(' opt_call_args rparen
- {
- $$ = $2;
- }
- ;
-
-opt_paren_args : none
- | paren_args
- ;
-
-opt_call_args : none
- | call_args
- | args ','
- {
- $$ = cons($1,0);
- NODE_LINENO($$, $1);
- }
- | args ',' assocs ','
- {
- $$ = cons(push($1, new_hash(p, $3)), 0);
- NODE_LINENO($$, $1);
- }
- | assocs ','
- {
- $$ = cons(list1(new_hash(p, $1)), 0);
- NODE_LINENO($$, $1);
- }
- ;
-
-call_args : command
- {
- $$ = cons(list1($1), 0);
- NODE_LINENO($$, $1);
- }
- | args opt_block_arg
- {
- $$ = cons($1, $2);
- NODE_LINENO($$, $1);
- }
- | assocs opt_block_arg
- {
- $$ = cons(list1(new_hash(p, $1)), $2);
- NODE_LINENO($$, $1);
- }
- | args ',' assocs opt_block_arg
- {
- $$ = cons(push($1, new_hash(p, $3)), $4);
- NODE_LINENO($$, $1);
- }
- | block_arg
- {
- $$ = cons(0, $1);
- NODE_LINENO($$, $1);
- }
- ;
-
-command_args : {
- $<stack>$ = p->cmdarg_stack;
- CMDARG_PUSH(1);
- }
- call_args
- {
- p->cmdarg_stack = $<stack>1;
- $$ = $2;
- }
- ;
-
-block_arg : tAMPER arg_value
- {
- $$ = new_block_arg(p, $2);
- }
- ;
-
-opt_block_arg : ',' block_arg
- {
- $$ = $2;
- }
- | none
- {
- $$ = 0;
- }
- ;
-
-args : arg_value
- {
- $$ = cons($1, 0);
- NODE_LINENO($$, $1);
- }
- | tSTAR arg_value
- {
- $$ = cons(new_splat(p, $2), 0);
- NODE_LINENO($$, $2);
- }
- | args ',' arg_value
- {
- $$ = push($1, $3);
- }
- | args ',' tSTAR arg_value
- {
- $$ = push($1, new_splat(p, $4));
- }
- | args ',' heredoc_bodies arg_value
- {
- $$ = push($1, $4);
- }
- | args ',' heredoc_bodies tSTAR arg_value
- {
- $$ = push($1, new_splat(p, $5));
- }
- ;
-
-mrhs : args ',' arg_value
- {
- $$ = push($1, $3);
- }
- | args ',' tSTAR arg_value
- {
- $$ = push($1, new_splat(p, $4));
- }
- | tSTAR arg_value
- {
- $$ = list1(new_splat(p, $2));
- }
- ;
-
-primary : literal
- | string
- | xstring
- | regexp
- | heredoc
- | var_ref
- | backref
- | tFID
- {
- $$ = new_fcall(p, $1, 0);
- }
- | keyword_begin
- {
- $<stack>$ = p->cmdarg_stack;
- p->cmdarg_stack = 0;
- }
- bodystmt
- keyword_end
- {
- p->cmdarg_stack = $<stack>2;
- $$ = $3;
- }
- | tLPAREN_ARG
- {
- $<stack>$ = p->cmdarg_stack;
- p->cmdarg_stack = 0;
- }
- expr {p->lstate = EXPR_ENDARG;} rparen
- {
- p->cmdarg_stack = $<stack>2;
- $$ = $3;
- }
- | tLPAREN_ARG {p->lstate = EXPR_ENDARG;} rparen
- {
- $$ = 0;
- }
- | tLPAREN compstmt ')'
- {
- $$ = $2;
- }
- | primary_value tCOLON2 tCONSTANT
- {
- $$ = new_colon2(p, $1, $3);
- }
- | tCOLON3 tCONSTANT
- {
- $$ = new_colon3(p, $2);
- }
- | tLBRACK aref_args ']'
- {
- $$ = new_array(p, $2);
- NODE_LINENO($$, $2);
- }
- | tLBRACE assoc_list '}'
- {
- $$ = new_hash(p, $2);
- NODE_LINENO($$, $2);
- }
- | keyword_return
- {
- $$ = new_return(p, 0);
- }
- | keyword_yield '(' call_args rparen
- {
- $$ = new_yield(p, $3);
- }
- | keyword_yield '(' rparen
- {
- $$ = new_yield(p, 0);
- }
- | keyword_yield
- {
- $$ = new_yield(p, 0);
- }
- | keyword_not '(' expr rparen
- {
- $$ = call_uni_op(p, cond($3), "!");
- }
- | keyword_not '(' rparen
- {
- $$ = call_uni_op(p, new_nil(p), "!");
- }
- | operation brace_block
- {
- $$ = new_fcall(p, $1, cons(0, $2));
- }
- | method_call
- | method_call brace_block
- {
- call_with_block(p, $1, $2);
- $$ = $1;
- }
- | tLAMBDA
- {
- local_nest(p);
- $<num>$ = p->lpar_beg;
- p->lpar_beg = ++p->paren_nest;
- }
- f_larglist
- lambda_body
- {
- p->lpar_beg = $<num>2;
- $$ = new_lambda(p, $3, $4);
- local_unnest(p);
- }
- | keyword_if expr_value then
- compstmt
- if_tail
- keyword_end
- {
- $$ = new_if(p, cond($2), $4, $5);
- SET_LINENO($$, $1);
- }
- | keyword_unless expr_value then
- compstmt
- opt_else
- keyword_end
- {
- $$ = new_unless(p, cond($2), $4, $5);
- SET_LINENO($$, $1);
- }
- | keyword_while {COND_PUSH(1);} expr_value do {COND_POP();}
- compstmt
- keyword_end
- {
- $$ = new_while(p, cond($3), $6);
- SET_LINENO($$, $1);
- }
- | keyword_until {COND_PUSH(1);} expr_value do {COND_POP();}
- compstmt
- keyword_end
- {
- $$ = new_until(p, cond($3), $6);
- SET_LINENO($$, $1);
- }
- | keyword_case expr_value opt_terms
- case_body
- keyword_end
- {
- $$ = new_case(p, $2, $4);
- }
- | keyword_case opt_terms case_body keyword_end
- {
- $$ = new_case(p, 0, $3);
- }
- | keyword_for for_var keyword_in
- {COND_PUSH(1);}
- expr_value do
- {COND_POP();}
- compstmt
- keyword_end
- {
- $$ = new_for(p, $2, $5, $8);
- SET_LINENO($$, $1);
- }
- | keyword_class
- cpath superclass
- {
- if (p->in_def || p->in_single)
- yyerror(p, "class definition in method body");
- $<nd>$ = local_switch(p);
- }
- bodystmt
- keyword_end
- {
- $$ = new_class(p, $2, $3, $5);
- SET_LINENO($$, $1);
- local_resume(p, $<nd>4);
- }
- | keyword_class
- tLSHFT expr
- {
- $<num>$ = p->in_def;
- p->in_def = 0;
- }
- term
- {
- $<nd>$ = cons(local_switch(p), (node*)(intptr_t)p->in_single);
- p->in_single = 0;
- }
- bodystmt
- keyword_end
- {
- $$ = new_sclass(p, $3, $7);
- SET_LINENO($$, $1);
- local_resume(p, $<nd>6->car);
- p->in_def = $<num>4;
- p->in_single = (int)(intptr_t)$<nd>6->cdr;
- }
- | keyword_module
- cpath
- {
- if (p->in_def || p->in_single)
- yyerror(p, "module definition in method body");
- $<nd>$ = local_switch(p);
- }
- bodystmt
- keyword_end
- {
- $$ = new_module(p, $2, $4);
- SET_LINENO($$, $1);
- local_resume(p, $<nd>3);
- }
- | keyword_def fname
- {
- $<stack>$ = p->cmdarg_stack;
- p->cmdarg_stack = 0;
- }
- {
- p->in_def++;
- $<nd>$ = local_switch(p);
- }
- f_arglist
- bodystmt
- keyword_end
- {
- $$ = new_def(p, $2, $5, $6);
- SET_LINENO($$, $1);
- local_resume(p, $<nd>4);
- p->in_def--;
- p->cmdarg_stack = $<stack>3;
- }
- | keyword_def singleton dot_or_colon
- {
- p->lstate = EXPR_FNAME;
- $<stack>$ = p->cmdarg_stack;
- p->cmdarg_stack = 0;
- }
- fname
- {
- p->in_single++;
- p->lstate = EXPR_ENDFN; /* force for args */
- $<nd>$ = local_switch(p);
- }
- f_arglist
- bodystmt
- keyword_end
- {
- $$ = new_sdef(p, $2, $5, $7, $8);
- SET_LINENO($$, $1);
- local_resume(p, $<nd>6);
- p->in_single--;
- p->cmdarg_stack = $<stack>4;
- }
- | keyword_break
- {
- $$ = new_break(p, 0);
- }
- | keyword_next
- {
- $$ = new_next(p, 0);
- }
- | keyword_redo
- {
- $$ = new_redo(p);
- }
- | keyword_retry
- {
- $$ = new_retry(p);
- }
- ;
-
-primary_value : primary
- {
- $$ = $1;
- if (!$$) $$ = new_nil(p);
- }
- ;
-
-then : term
- | keyword_then
- | term keyword_then
- ;
-
-do : term
- | keyword_do_cond
- ;
-
-if_tail : opt_else
- | keyword_elsif expr_value then
- compstmt
- if_tail
- {
- $$ = new_if(p, cond($2), $4, $5);
- }
- ;
-
-opt_else : none
- | keyword_else compstmt
- {
- $$ = $2;
- }
- ;
-
-for_var : lhs
- {
- $$ = list1(list1($1));
- }
- | mlhs
- ;
-
-f_marg : f_norm_arg
- {
- $$ = new_arg(p, $1);
- }
- | tLPAREN f_margs rparen
- {
- $$ = new_masgn(p, $2, 0);
- }
- ;
-
-f_marg_list : f_marg
- {
- $$ = list1($1);
- }
- | f_marg_list ',' f_marg
- {
- $$ = push($1, $3);
- }
- ;
-
-f_margs : f_marg_list
- {
- $$ = list3($1,0,0);
- }
- | f_marg_list ',' tSTAR f_norm_arg
- {
- $$ = list3($1, new_arg(p, $4), 0);
- }
- | f_marg_list ',' tSTAR f_norm_arg ',' f_marg_list
- {
- $$ = list3($1, new_arg(p, $4), $6);
- }
- | f_marg_list ',' tSTAR
- {
- $$ = list3($1, (node*)-1, 0);
- }
- | f_marg_list ',' tSTAR ',' f_marg_list
- {
- $$ = list3($1, (node*)-1, $5);
- }
- | tSTAR f_norm_arg
- {
- $$ = list3(0, new_arg(p, $2), 0);
- }
- | tSTAR f_norm_arg ',' f_marg_list
- {
- $$ = list3(0, new_arg(p, $2), $4);
- }
- | tSTAR
- {
- $$ = list3(0, (node*)-1, 0);
- }
- | tSTAR ',' f_marg_list
- {
- $$ = list3(0, (node*)-1, $3);
- }
- ;
-
-block_param : f_arg ',' f_block_optarg ',' f_rest_arg opt_f_block_arg
- {
- $$ = new_args(p, $1, $3, $5, 0, $6);
- }
- | f_arg ',' f_block_optarg ',' f_rest_arg ',' f_arg opt_f_block_arg
- {
- $$ = new_args(p, $1, $3, $5, $7, $8);
- }
- | f_arg ',' f_block_optarg opt_f_block_arg
- {
- $$ = new_args(p, $1, $3, 0, 0, $4);
- }
- | f_arg ',' f_block_optarg ',' f_arg opt_f_block_arg
- {
- $$ = new_args(p, $1, $3, 0, $5, $6);
- }
- | f_arg ',' f_rest_arg opt_f_block_arg
- {
- $$ = new_args(p, $1, 0, $3, 0, $4);
- }
- | f_arg ','
- {
- $$ = new_args(p, $1, 0, 1, 0, 0);
- }
- | f_arg ',' f_rest_arg ',' f_arg opt_f_block_arg
- {
- $$ = new_args(p, $1, 0, $3, $5, $6);
- }
- | f_arg opt_f_block_arg
- {
- $$ = new_args(p, $1, 0, 0, 0, $2);
- }
- | f_block_optarg ',' f_rest_arg opt_f_block_arg
- {
- $$ = new_args(p, 0, $1, $3, 0, $4);
- }
- | f_block_optarg ',' f_rest_arg ',' f_arg opt_f_block_arg
- {
- $$ = new_args(p, 0, $1, $3, $5, $6);
- }
- | f_block_optarg opt_f_block_arg
- {
- $$ = new_args(p, 0, $1, 0, 0, $2);
- }
- | f_block_optarg ',' f_arg opt_f_block_arg
- {
- $$ = new_args(p, 0, $1, 0, $3, $4);
- }
- | f_rest_arg opt_f_block_arg
- {
- $$ = new_args(p, 0, 0, $1, 0, $2);
- }
- | f_rest_arg ',' f_arg opt_f_block_arg
- {
- $$ = new_args(p, 0, 0, $1, $3, $4);
- }
- | f_block_arg
- {
- $$ = new_args(p, 0, 0, 0, 0, $1);
- }
- ;
-
-opt_block_param : none
- | block_param_def
- {
- p->cmd_start = TRUE;
- $$ = $1;
- }
- ;
-
-block_param_def : '|' opt_bv_decl '|'
- {
- $$ = 0;
- }
- | tOROP
- {
- $$ = 0;
- }
- | '|' block_param opt_bv_decl '|'
- {
- $$ = $2;
- }
- ;
-
-
-opt_bv_decl : opt_nl
- {
- $$ = 0;
- }
- | opt_nl ';' bv_decls opt_nl
- {
- $$ = 0;
- }
- ;
-
-bv_decls : bvar
- | bv_decls ',' bvar
- ;
-
-bvar : tIDENTIFIER
- {
- local_add_f(p, $1);
- new_bv(p, $1);
- }
- | f_bad_arg
- ;
-
-f_larglist : '(' f_args opt_bv_decl ')'
- {
- $$ = $2;
- }
- | f_args
- {
- $$ = $1;
- }
- ;
-
-lambda_body : tLAMBEG compstmt '}'
- {
- $$ = $2;
- }
- | keyword_do_LAMBDA compstmt keyword_end
- {
- $$ = $2;
- }
- ;
-
-do_block : keyword_do_block
- {
- local_nest(p);
- }
- opt_block_param
- compstmt
- keyword_end
- {
- $$ = new_block(p,$3,$4);
- local_unnest(p);
- }
- ;
-
-block_call : command do_block
- {
- if ($1->car == (node*)NODE_YIELD) {
- yyerror(p, "block given to yield");
- }
- else {
- call_with_block(p, $1, $2);
- }
- $$ = $1;
- }
- | block_call dot_or_colon operation2 opt_paren_args
- {
- $$ = new_call(p, $1, $3, $4);
- }
- | block_call dot_or_colon operation2 opt_paren_args brace_block
- {
- $$ = new_call(p, $1, $3, $4);
- call_with_block(p, $$, $5);
- }
- | block_call dot_or_colon operation2 command_args do_block
- {
- $$ = new_call(p, $1, $3, $4);
- call_with_block(p, $$, $5);
- }
- ;
-
-method_call : operation paren_args
- {
- $$ = new_fcall(p, $1, $2);
- }
- | primary_value '.' operation2 opt_paren_args
- {
- $$ = new_call(p, $1, $3, $4);
- }
- | primary_value tCOLON2 operation2 paren_args
- {
- $$ = new_call(p, $1, $3, $4);
- }
- | primary_value tCOLON2 operation3
- {
- $$ = new_call(p, $1, $3, 0);
- }
- | primary_value '.' paren_args
- {
- $$ = new_call(p, $1, intern("call",4), $3);
- }
- | primary_value tCOLON2 paren_args
- {
- $$ = new_call(p, $1, intern("call",4), $3);
- }
- | keyword_super paren_args
- {
- $$ = new_super(p, $2);
- }
- | keyword_super
- {
- $$ = new_zsuper(p);
- }
- | primary_value '[' opt_call_args rbracket
- {
- $$ = new_call(p, $1, intern("[]",2), $3);
- }
- ;
-
-brace_block : '{'
- {
- local_nest(p);
- $<num>$ = p->lineno;
- }
- opt_block_param
- compstmt '}'
- {
- $$ = new_block(p,$3,$4);
- SET_LINENO($$, $<num>2);
- local_unnest(p);
- }
- | keyword_do
- {
- local_nest(p);
- $<num>$ = p->lineno;
- }
- opt_block_param
- compstmt keyword_end
- {
- $$ = new_block(p,$3,$4);
- SET_LINENO($$, $<num>2);
- local_unnest(p);
- }
- ;
-
-case_body : keyword_when args then
- compstmt
- cases
- {
- $$ = cons(cons($2, $4), $5);
- }
- ;
-
-cases : opt_else
- {
- if ($1) {
- $$ = cons(cons(0, $1), 0);
- }
- else {
- $$ = 0;
- }
- }
- | case_body
- ;
-
-opt_rescue : keyword_rescue exc_list exc_var then
- compstmt
- opt_rescue
- {
- $$ = list1(list3($2, $3, $5));
- if ($6) $$ = append($$, $6);
- }
- | none
- ;
-
-exc_list : arg_value
- {
- $$ = list1($1);
- }
- | mrhs
- | none
- ;
-
-exc_var : tASSOC lhs
- {
- $$ = $2;
- }
- | none
- ;
-
-opt_ensure : keyword_ensure compstmt
- {
- $$ = $2;
- }
- | none
- ;
-
-literal : numeric
- | symbol
- | words
- | symbols
- ;
-
-string : tCHAR
- | tSTRING
- | tSTRING_BEG tSTRING
- {
- $$ = $2;
- }
- | tSTRING_BEG string_rep tSTRING
- {
- $$ = new_dstr(p, push($2, $3));
- }
- ;
-
-string_rep : string_interp
- | string_rep string_interp
- {
- $$ = append($1, $2);
- }
- ;
-
-string_interp : tSTRING_MID
- {
- $$ = list1($1);
- }
- | tSTRING_PART
- {
- $<nd>$ = p->lex_strterm;
- p->lex_strterm = NULL;
- }
- compstmt
- '}'
- {
- p->lex_strterm = $<nd>2;
- $$ = list2($1, $3);
- }
- | tLITERAL_DELIM
- {
- $$ = list1(new_literal_delim(p));
- }
- | tHD_LITERAL_DELIM heredoc_bodies
- {
- $$ = list1(new_literal_delim(p));
- }
- ;
-
-xstring : tXSTRING_BEG tXSTRING
- {
- $$ = $2;
- }
- | tXSTRING_BEG string_rep tXSTRING
- {
- $$ = new_dxstr(p, push($2, $3));
- }
- ;
-
-regexp : tREGEXP_BEG tREGEXP
- {
- $$ = $2;
- }
- | tREGEXP_BEG string_rep tREGEXP
- {
- $$ = new_dregx(p, $2, $3);
- }
- ;
-
-heredoc : tHEREDOC_BEG
- ;
-
-opt_heredoc_bodies : /* none */
- | heredoc_bodies
- ;
-
-heredoc_bodies : heredoc_body
- | heredoc_bodies heredoc_body
- ;
-
-heredoc_body : tHEREDOC_END
- {
- parser_heredoc_info * inf = parsing_heredoc_inf(p);
- inf->doc = push(inf->doc, new_str(p, "", 0));
- heredoc_end(p);
- }
- | heredoc_string_rep tHEREDOC_END
- {
- heredoc_end(p);
- }
- ;
-
-heredoc_string_rep : heredoc_string_interp
- | heredoc_string_rep heredoc_string_interp
- ;
-
-heredoc_string_interp : tHD_STRING_MID
- {
- parser_heredoc_info * inf = parsing_heredoc_inf(p);
- inf->doc = push(inf->doc, $1);
- heredoc_treat_nextline(p);
- }
- | tHD_STRING_PART
- {
- $<nd>$ = p->lex_strterm;
- p->lex_strterm = NULL;
- }
- compstmt
- '}'
- {
- parser_heredoc_info * inf = parsing_heredoc_inf(p);
- p->lex_strterm = $<nd>2;
- inf->doc = push(push(inf->doc, $1), $3);
- }
- ;
-
-words : tWORDS_BEG tSTRING
- {
- $$ = new_words(p, list1($2));
- }
- | tWORDS_BEG string_rep tSTRING
- {
- $$ = new_words(p, push($2, $3));
- }
- ;
-
-
-symbol : basic_symbol
- {
- $$ = new_sym(p, $1);
- }
- | tSYMBEG tSTRING_BEG string_interp tSTRING
- {
- p->lstate = EXPR_END;
- $$ = new_dsym(p, push($3, $4));
- }
- ;
-
-basic_symbol : tSYMBEG sym
- {
- p->lstate = EXPR_END;
- $$ = $2;
- }
- ;
-
-sym : fname
- | tIVAR
- | tGVAR
- | tCVAR
- | tSTRING
- {
- $$ = new_strsym(p, $1);
- }
- | tSTRING_BEG tSTRING
- {
- $$ = new_strsym(p, $2);
- }
- ;
-
-symbols : tSYMBOLS_BEG tSTRING
- {
- $$ = new_symbols(p, list1($2));
- }
- | tSYMBOLS_BEG string_rep tSTRING
- {
- $$ = new_symbols(p, push($2, $3));
- }
- ;
-
-numeric : tINTEGER
- | tFLOAT
- | tUMINUS_NUM tINTEGER %prec tLOWEST
- {
- $$ = negate_lit(p, $2);
- }
- | tUMINUS_NUM tFLOAT %prec tLOWEST
- {
- $$ = negate_lit(p, $2);
- }
- ;
-
-variable : tIDENTIFIER
- {
- $$ = new_lvar(p, $1);
- }
- | tIVAR
- {
- $$ = new_ivar(p, $1);
- }
- | tGVAR
- {
- $$ = new_gvar(p, $1);
- }
- | tCVAR
- {
- $$ = new_cvar(p, $1);
- }
- | tCONSTANT
- {
- $$ = new_const(p, $1);
- }
- ;
-
-var_lhs : variable
- {
- assignable(p, $1);
- }
- ;
-
-var_ref : variable
- {
- $$ = var_reference(p, $1);
- }
- | keyword_nil
- {
- $$ = new_nil(p);
- }
- | keyword_self
- {
- $$ = new_self(p);
- }
- | keyword_true
- {
- $$ = new_true(p);
- }
- | keyword_false
- {
- $$ = new_false(p);
- }
- | keyword__FILE__
- {
- if (!p->filename) {
- p->filename = "(null)";
- }
- $$ = new_str(p, p->filename, strlen(p->filename));
- }
- | keyword__LINE__
- {
- char buf[16];
-
- snprintf(buf, sizeof(buf), "%d", p->lineno);
- $$ = new_int(p, buf, 10);
- }
- ;
-
-backref : tNTH_REF
- | tBACK_REF
- ;
-
-superclass : term
- {
- $$ = 0;
- }
- | '<'
- {
- p->lstate = EXPR_BEG;
- p->cmd_start = TRUE;
- }
- expr_value term
- {
- $$ = $3;
- }
- | error term
- {
- yyerrok;
- $$ = 0;
- }
- ;
-
-f_arglist : '(' f_args rparen
- {
- $$ = $2;
- p->lstate = EXPR_BEG;
- p->cmd_start = TRUE;
- }
- | f_args term
- {
- $$ = $1;
- }
- ;
-
-f_args : f_arg ',' f_optarg ',' f_rest_arg opt_f_block_arg
- {
- $$ = new_args(p, $1, $3, $5, 0, $6);
- }
- | f_arg ',' f_optarg ',' f_rest_arg ',' f_arg opt_f_block_arg
- {
- $$ = new_args(p, $1, $3, $5, $7, $8);
- }
- | f_arg ',' f_optarg opt_f_block_arg
- {
- $$ = new_args(p, $1, $3, 0, 0, $4);
- }
- | f_arg ',' f_optarg ',' f_arg opt_f_block_arg
- {
- $$ = new_args(p, $1, $3, 0, $5, $6);
- }
- | f_arg ',' f_rest_arg opt_f_block_arg
- {
- $$ = new_args(p, $1, 0, $3, 0, $4);
- }
- | f_arg ',' f_rest_arg ',' f_arg opt_f_block_arg
- {
- $$ = new_args(p, $1, 0, $3, $5, $6);
- }
- | f_arg opt_f_block_arg
- {
- $$ = new_args(p, $1, 0, 0, 0, $2);
- }
- | f_optarg ',' f_rest_arg opt_f_block_arg
- {
- $$ = new_args(p, 0, $1, $3, 0, $4);
- }
- | f_optarg ',' f_rest_arg ',' f_arg opt_f_block_arg
- {
- $$ = new_args(p, 0, $1, $3, $5, $6);
- }
- | f_optarg opt_f_block_arg
- {
- $$ = new_args(p, 0, $1, 0, 0, $2);
- }
- | f_optarg ',' f_arg opt_f_block_arg
- {
- $$ = new_args(p, 0, $1, 0, $3, $4);
- }
- | f_rest_arg opt_f_block_arg
- {
- $$ = new_args(p, 0, 0, $1, 0, $2);
- }
- | f_rest_arg ',' f_arg opt_f_block_arg
- {
- $$ = new_args(p, 0, 0, $1, $3, $4);
- }
- | f_block_arg
- {
- $$ = new_args(p, 0, 0, 0, 0, $1);
- }
- | /* none */
- {
- local_add_f(p, 0);
- $$ = new_args(p, 0, 0, 0, 0, 0);
- }
- ;
-
-f_bad_arg : tCONSTANT
- {
- yyerror(p, "formal argument cannot be a constant");
- $$ = 0;
- }
- | tIVAR
- {
- yyerror(p, "formal argument cannot be an instance variable");
- $$ = 0;
- }
- | tGVAR
- {
- yyerror(p, "formal argument cannot be a global variable");
- $$ = 0;
- }
- | tCVAR
- {
- yyerror(p, "formal argument cannot be a class variable");
- $$ = 0;
- }
- ;
-
-f_norm_arg : f_bad_arg
- {
- $$ = 0;
- }
- | tIDENTIFIER
- {
- local_add_f(p, $1);
- $$ = $1;
- }
- ;
-
-f_arg_item : f_norm_arg
- {
- $$ = new_arg(p, $1);
- }
- | tLPAREN f_margs rparen
- {
- $$ = new_masgn(p, $2, 0);
- }
- ;
-
-f_arg : f_arg_item
- {
- $$ = list1($1);
- }
- | f_arg ',' f_arg_item
- {
- $$ = push($1, $3);
- }
- ;
-
-f_opt_asgn : tIDENTIFIER '='
- {
- local_add_f(p, $1);
- $$ = $1;
- }
- ;
-
-f_opt : f_opt_asgn arg_value
- {
- $$ = cons(nsym($1), $2);
- }
- ;
-
-f_block_opt : f_opt_asgn primary_value
- {
- $$ = cons(nsym($1), $2);
- }
- ;
-
-f_block_optarg : f_block_opt
- {
- $$ = list1($1);
- }
- | f_block_optarg ',' f_block_opt
- {
- $$ = push($1, $3);
- }
- ;
-
-f_optarg : f_opt
- {
- $$ = list1($1);
- }
- | f_optarg ',' f_opt
- {
- $$ = push($1, $3);
- }
- ;
-
-restarg_mark : '*'
- | tSTAR
- ;
-
-f_rest_arg : restarg_mark tIDENTIFIER
- {
- local_add_f(p, $2);
- $$ = $2;
- }
- | restarg_mark
- {
- local_add_f(p, 0);
- $$ = -1;
- }
- ;
-
-blkarg_mark : '&'
- | tAMPER
- ;
-
-f_block_arg : blkarg_mark tIDENTIFIER
- {
- local_add_f(p, $2);
- $$ = $2;
- }
- ;
-
-opt_f_block_arg : ',' f_block_arg
- {
- $$ = $2;
- }
- | none
- {
- local_add_f(p, 0);
- $$ = 0;
- }
- ;
-
-singleton : var_ref
- {
- $$ = $1;
- if (!$$) $$ = new_nil(p);
- }
- | '(' {p->lstate = EXPR_BEG;} expr rparen
- {
- if ($3 == 0) {
- yyerror(p, "can't define singleton method for ().");
- }
- else {
- switch ((enum node_type)(int)(intptr_t)$3->car) {
- case NODE_STR:
- case NODE_DSTR:
- case NODE_XSTR:
- case NODE_DXSTR:
- case NODE_DREGX:
- case NODE_MATCH:
- case NODE_FLOAT:
- case NODE_ARRAY:
- case NODE_HEREDOC:
- yyerror(p, "can't define singleton method for literals");
- default:
- break;
- }
- }
- $$ = $3;
- }
- ;
-
-assoc_list : none
- | assocs trailer
- {
- $$ = $1;
- }
- ;
-
-assocs : assoc
- {
- $$ = list1($1);
- NODE_LINENO($$, $1);
- }
- | assocs ',' assoc
- {
- $$ = push($1, $3);
- }
- ;
-
-assoc : arg_value tASSOC arg_value
- {
- $$ = cons($1, $3);
- }
- | tLABEL arg_value
- {
- $$ = cons(new_sym(p, $1), $2);
- }
- ;
-
-operation : tIDENTIFIER
- | tCONSTANT
- | tFID
- ;
-
-operation2 : tIDENTIFIER
- | tCONSTANT
- | tFID
- | op
- ;
-
-operation3 : tIDENTIFIER
- | tFID
- | op
- ;
-
-dot_or_colon : '.'
- | tCOLON2
- ;
-
-opt_terms : /* none */
- | terms
- ;
-
-opt_nl : /* none */
- | nl
- ;
-
-rparen : opt_nl ')'
- ;
-
-rbracket : opt_nl ']'
- ;
-
-trailer : /* none */
- | nl
- | ','
- ;
-
-term : ';' {yyerrok;}
- | nl
- ;
-
-nl : '\n'
- {
- p->lineno++;
- p->column = 0;
- }
- opt_heredoc_bodies
-
-terms : term
- | terms ';' {yyerrok;}
- ;
-
-none : /* none */
- {
- $$ = 0;
- }
- ;
-%%
-#define yylval (*((YYSTYPE*)(p->ylval)))
-
-static void
-yyerror(parser_state *p, const char *s)
-{
- char* c;
- int n;
-
- if (! p->capture_errors) {
-#ifdef ENABLE_STDIO
- if (p->filename) {
- fprintf(stderr, "%s:%d:%d: %s\n", p->filename, p->lineno, p->column, s);
- }
- else {
- fprintf(stderr, "line %d:%d: %s\n", p->lineno, p->column, s);
- }
-#endif
- }
- else if (p->nerr < sizeof(p->error_buffer) / sizeof(p->error_buffer[0])) {
- n = strlen(s);
- c = (char *)parser_palloc(p, n + 1);
- memcpy(c, s, n + 1);
- p->error_buffer[p->nerr].message = c;
- p->error_buffer[p->nerr].lineno = p->lineno;
- p->error_buffer[p->nerr].column = p->column;
- }
- p->nerr++;
-}
-
-static void
-yyerror_i(parser_state *p, const char *fmt, int i)
-{
- char buf[256];
-
- snprintf(buf, sizeof(buf), fmt, i);
- yyerror(p, buf);
-}
-
-static void
-yywarn(parser_state *p, const char *s)
-{
- char* c;
- int n;
-
- if (! p->capture_errors) {
-#ifdef ENABLE_STDIO
- if (p->filename) {
- fprintf(stderr, "%s:%d:%d: %s\n", p->filename, p->lineno, p->column, s);
- }
- else {
- fprintf(stderr, "line %d:%d: %s\n", p->lineno, p->column, s);
- }
-#endif
- }
- else if (p->nwarn < sizeof(p->warn_buffer) / sizeof(p->warn_buffer[0])) {
- n = strlen(s);
- c = (char *)parser_palloc(p, n + 1);
- memcpy(c, s, n + 1);
- p->warn_buffer[p->nwarn].message = c;
- p->warn_buffer[p->nwarn].lineno = p->lineno;
- p->warn_buffer[p->nwarn].column = p->column;
- }
- p->nwarn++;
-}
-
-static void
-yywarning(parser_state *p, const char *s)
-{
- yywarn(p, s);
-}
-
-static void
-yywarning_s(parser_state *p, const char *fmt, const char *s)
-{
- char buf[256];
-
- snprintf(buf, sizeof(buf), fmt, s);
- yywarning(p, buf);
-}
-
-static void
-backref_error(parser_state *p, node *n)
-{
- int c;
-
- c = (int)(intptr_t)n->car;
-
- if (c == NODE_NTH_REF) {
- yyerror_i(p, "can't set variable $%d", (int)(intptr_t)n->cdr);
- }
- else if (c == NODE_BACK_REF) {
- yyerror_i(p, "can't set variable $%c", (int)(intptr_t)n->cdr);
- }
- else {
- mrb_bug(p->mrb, "Internal error in backref_error() : n=>car == %S", mrb_fixnum_value(c));
- }
-}
-
-static void pushback(parser_state *p, int c);
-static mrb_bool peeks(parser_state *p, const char *s);
-static mrb_bool skips(parser_state *p, const char *s);
-
-static inline int
-nextc(parser_state *p)
-{
- int c;
-
- if (p->pb) {
- node *tmp;
-
- c = (int)(intptr_t)p->pb->car;
- tmp = p->pb;
- p->pb = p->pb->cdr;
- cons_free(tmp);
- }
- else {
-#ifdef ENABLE_STDIO
- if (p->f) {
- if (feof(p->f)) goto eof;
- c = fgetc(p->f);
- if (c == EOF) goto eof;
- }
- else
-#endif
- if (!p->s || p->s >= p->send) {
- goto eof;
- }
- else {
- c = (unsigned char)*p->s++;
- }
- }
- if (c >= 0) {
- p->column++;
- }
- if (c == '\r') {
- c = nextc(p);
- if (c != '\n') {
- pushback(p, c);
- return '\r';
- }
- return c;
- }
- return c;
-
- eof:
- if (!p->cxt) return -1;
- else {
- if (p->cxt->partial_hook(p) < 0)
- return -1; /* end of program(s) */
- return -2; /* end of a file in the program files */
- }
-}
-
-static void
-pushback(parser_state *p, int c)
-{
- if (c >= 0) {
- p->column--;
- }
- p->pb = cons((node*)(intptr_t)c, p->pb);
-}
-
-static void
-skip(parser_state *p, char term)
-{
- int c;
-
- for (;;) {
- c = nextc(p);
- if (c < 0) break;
- if (c == term) break;
- }
-}
-
-static int
-peekc_n(parser_state *p, int n)
-{
- node *list = 0;
- int c0;
-
- do {
- c0 = nextc(p);
- if (c0 == -1) return c0; /* do not skip partial EOF */
- list = push(list, (node*)(intptr_t)c0);
- } while(n--);
- if (p->pb) {
- p->pb = append((node*)list, p->pb);
- }
- else {
- p->pb = list;
- }
- return c0;
-}
-
-static mrb_bool
-peek_n(parser_state *p, int c, int n)
-{
- return peekc_n(p, n) == c && c >= 0;
-}
-#define peek(p,c) peek_n((p), (c), 0)
-
-static mrb_bool
-peeks(parser_state *p, const char *s)
-{
- int len = strlen(s);
-
-#ifdef ENABLE_STDIO
- if (p->f) {
- int n = 0;
- while (*s) {
- if (!peek_n(p, *s++, n++)) return FALSE;
- }
- return TRUE;
- }
- else
-#endif
- if (p->s && p->s + len <= p->send) {
- if (memcmp(p->s, s, len) == 0) return TRUE;
- }
- return FALSE;
-}
-
-static mrb_bool
-skips(parser_state *p, const char *s)
-{
- int c;
-
- for (;;) {
- /* skip until first char */
- for (;;) {
- c = nextc(p);
- if (c < 0) return c;
- if (c == '\n') {
- p->lineno++;
- p->column = 0;
- }
- if (c == *s) break;
- }
- s++;
- if (peeks(p, s)) {
- int len = strlen(s);
-
- while (len--) {
- if (nextc(p) == '\n') {
- p->lineno++;
- p->column = 0;
- }
- }
- return TRUE;
- }
- else{
- s--;
- }
- }
- return FALSE;
-}
-
-
-static int
-newtok(parser_state *p)
-{
- p->bidx = 0;
- return p->column - 1;
-}
-
-static void
-tokadd(parser_state *p, int32_t c)
-{
- char utf8[4];
- unsigned len;
-
- /* mrb_assert(-0x10FFFF <= c && c <= 0xFF); */
- if (c >= 0) {
- /* Single byte from source or non-Unicode escape */
- utf8[0] = (char)c;
- len = 1;
- }
- else {
- /* Unicode character */
- c = -c;
- if (c < 0x80) {
- utf8[0] = (char)c;
- len = 1;
- }
- else if (c < 0x800) {
- utf8[0] = (char)(0xC0 | (c >> 6));
- utf8[1] = (char)(0x80 | (c & 0x3F));
- len = 2;
- }
- else if (c < 0x10000) {
- utf8[0] = (char)(0xE0 | (c >> 12) );
- utf8[1] = (char)(0x80 | ((c >> 6) & 0x3F));
- utf8[2] = (char)(0x80 | ( c & 0x3F));
- len = 3;
- }
- else {
- utf8[0] = (char)(0xF0 | (c >> 18) );
- utf8[1] = (char)(0x80 | ((c >> 12) & 0x3F));
- utf8[2] = (char)(0x80 | ((c >> 6) & 0x3F));
- utf8[3] = (char)(0x80 | ( c & 0x3F));
- len = 4;
- }
- }
- if (p->bidx+len <= MRB_PARSER_BUF_SIZE) {
- unsigned i;
- for (i = 0; i < len; i++) {
- p->buf[p->bidx++] = utf8[i];
- }
- }
-}
-
-static int
-toklast(parser_state *p)
-{
- return p->buf[p->bidx-1];
-}
-
-static void
-tokfix(parser_state *p)
-{
- if (p->bidx >= MRB_PARSER_BUF_SIZE) {
- yyerror(p, "string too long (truncated)");
- }
- p->buf[p->bidx] = '\0';
-}
-
-static const char*
-tok(parser_state *p)
-{
- return p->buf;
-}
-
-static int
-toklen(parser_state *p)
-{
- return p->bidx;
-}
-
-#define IS_ARG() (p->lstate == EXPR_ARG || p->lstate == EXPR_CMDARG)
-#define IS_END() (p->lstate == EXPR_END || p->lstate == EXPR_ENDARG || p->lstate == EXPR_ENDFN)
-#define IS_BEG() (p->lstate == EXPR_BEG || p->lstate == EXPR_MID || p->lstate == EXPR_VALUE || p->lstate == EXPR_CLASS)
-#define IS_SPCARG(c) (IS_ARG() && space_seen && !ISSPACE(c))
-#define IS_LABEL_POSSIBLE() ((p->lstate == EXPR_BEG && !cmd_state) || IS_ARG())
-#define IS_LABEL_SUFFIX(n) (peek_n(p, ':',(n)) && !peek_n(p, ':', (n)+1))
-
-static int
-scan_oct(const int *start, int len, int *retlen)
-{
- const int *s = start;
- int retval = 0;
-
- /* mrb_assert(len <= 3) */
- while (len-- && *s >= '0' && *s <= '7') {
- retval <<= 3;
- retval |= *s++ - '0';
- }
- *retlen = s - start;
-
- return retval;
-}
-
-static int32_t
-scan_hex(const int *start, int len, int *retlen)
-{
- static const char hexdigit[] = "0123456789abcdef0123456789ABCDEF";
- const int *s = start;
- int32_t retval = 0;
- char *tmp;
-
- /* mrb_assert(len <= 8) */
- while (len-- && *s && (tmp = (char*)strchr(hexdigit, *s))) {
- retval <<= 4;
- retval |= (tmp - hexdigit) & 15;
- s++;
- }
- *retlen = s - start;
-
- return retval;
-}
-
-/* Return negative to indicate Unicode code point */
-static int32_t
-read_escape(parser_state *p)
-{
- int32_t c;
-
- switch (c = nextc(p)) {
- case '\\':/* Backslash */
- return c;
-
- case 'n':/* newline */
- return '\n';
-
- case 't':/* horizontal tab */
- return '\t';
-
- case 'r':/* carriage-return */
- return '\r';
-
- case 'f':/* form-feed */
- return '\f';
-
- case 'v':/* vertical tab */
- return '\13';
-
- case 'a':/* alarm(bell) */
- return '\007';
-
- case 'e':/* escape */
- return 033;
-
- case '0': case '1': case '2': case '3': /* octal constant */
- case '4': case '5': case '6': case '7':
- {
- int buf[3];
- int i;
-
- buf[0] = c;
- for (i=1; i<3; i++) {
- buf[i] = nextc(p);
- if (buf[i] < 0) goto eof;
- if (buf[i] < '0' || '7' < buf[i]) {
- pushback(p, buf[i]);
- break;
- }
- }
- c = scan_oct(buf, i, &i);
- }
- return c;
-
- case 'x': /* hex constant */
- {
- int buf[2];
- int i;
-
- for (i=0; i<2; i++) {
- buf[i] = nextc(p);
- if (buf[i] < 0) goto eof;
- if (!ISXDIGIT(buf[i])) {
- pushback(p, buf[i]);
- break;
- }
- }
- c = scan_hex(buf, i, &i);
- if (i == 0) {
- yyerror(p, "Invalid escape character syntax");
- return 0;
- }
- }
- return c;
-
- case 'u': /* Unicode */
- {
- int buf[9];
- int i;
-
- /* Look for opening brace */
- i = 0;
- buf[0] = nextc(p);
- if (buf[0] < 0) goto eof;
- if (buf[0] == '{') {
- /* \u{xxxxxxxx} form */
- for (i=0; i<9; i++) {
- buf[i] = nextc(p);
- if (buf[i] < 0) goto eof;
- if (buf[i] == '}') {
- break;
- }
- else if (!ISXDIGIT(buf[i])) {
- yyerror(p, "Invalid escape character syntax");
- pushback(p, buf[i]);
- return 0;
- }
- }
- }
- else if (ISXDIGIT(buf[0])) {
- /* \uxxxx form */
- for (i=1; i<4; i++) {
- buf[i] = nextc(p);
- if (buf[i] < 0) goto eof;
- if (!ISXDIGIT(buf[i])) {
- pushback(p, buf[i]);
- break;
- }
- }
- }
- else {
- pushback(p, buf[0]);
- }
- c = scan_hex(buf, i, &i);
- if (i == 0) {
- yyerror(p, "Invalid escape character syntax");
- return 0;
- }
- if (c < 0 || c > 0x10FFFF || (c & 0xFFFFF800) == 0xD800) {
- yyerror(p, "Invalid Unicode code point");
- return 0;
- }
- }
- return -c;
-
- case 'b':/* backspace */
- return '\010';
-
- case 's':/* space */
- return ' ';
-
- case 'M':
- if ((c = nextc(p)) != '-') {
- yyerror(p, "Invalid escape character syntax");
- pushback(p, c);
- return '\0';
- }
- if ((c = nextc(p)) == '\\') {
- return read_escape(p) | 0x80;
- }
- else if (c < 0) goto eof;
- else {
- return ((c & 0xff) | 0x80);
- }
-
- case 'C':
- if ((c = nextc(p)) != '-') {
- yyerror(p, "Invalid escape character syntax");
- pushback(p, c);
- return '\0';
- }
- case 'c':
- if ((c = nextc(p))== '\\') {
- c = read_escape(p);
- }
- else if (c == '?')
- return 0177;
- else if (c < 0) goto eof;
- return c & 0x9f;
-
- eof:
- case -1:
- case -2: /* end of a file */
- yyerror(p, "Invalid escape character syntax");
- return '\0';
-
- default:
- return c;
- }
-}
-
-static int
-parse_string(parser_state *p)
-{
- int c;
- string_type type = (string_type)(intptr_t)p->lex_strterm->car;
- int nest_level = (intptr_t)p->lex_strterm->cdr->car;
- int beg = (intptr_t)p->lex_strterm->cdr->cdr->car;
- int end = (intptr_t)p->lex_strterm->cdr->cdr->cdr;
- parser_heredoc_info *hinf = (type & STR_FUNC_HEREDOC) ? parsing_heredoc_inf(p) : NULL;
-
- newtok(p);
- while ((c = nextc(p)) != end || nest_level != 0) {
- if (hinf && (c == '\n' || c < 0)) {
- mrb_bool line_head;
- tokadd(p, '\n');
- tokfix(p);
- p->lineno++;
- p->column = 0;
- line_head = hinf->line_head;
- hinf->line_head = TRUE;
- if (line_head) {
- /* check whether end of heredoc */
- const char *s = tok(p);
- int len = toklen(p);
- if (hinf->allow_indent) {
- while (ISSPACE(*s) && len > 0) {
- ++s;
- --len;
- }
- }
- if ((len-1 == hinf->term_len) && (strncmp(s, hinf->term, len-1) == 0)) {
- return tHEREDOC_END;
- }
- }
- if (c < 0) {
- char buf[256];
- snprintf(buf, sizeof(buf), "can't find heredoc delimiter \"%s\" anywhere before EOF", hinf->term);
- yyerror(p, buf);
- return 0;
- }
- yylval.nd = new_str(p, tok(p), toklen(p));
- return tHD_STRING_MID;
- }
- if (c < 0) {
- yyerror(p, "unterminated string meets end of file");
- return 0;
- }
- else if (c == beg) {
- nest_level++;
- p->lex_strterm->cdr->car = (node*)(intptr_t)nest_level;
- }
- else if (c == end) {
- nest_level--;
- p->lex_strterm->cdr->car = (node*)(intptr_t)nest_level;
- }
- else if (c == '\\') {
- c = nextc(p);
- if (type & STR_FUNC_EXPAND) {
- if (c == end || c == beg) {
- tokadd(p, c);
- }
- else if (c == '\n') {
- p->lineno++;
- p->column = 0;
- if (type & STR_FUNC_ARRAY) {
- tokadd(p, '\n');
- }
- }
- else if (type & STR_FUNC_REGEXP) {
- tokadd(p, '\\');
- tokadd(p, c);
- }
- else {
- pushback(p, c);
- tokadd(p, read_escape(p));
- if (hinf)
- hinf->line_head = FALSE;
- }
- }
- else {
- if (c != beg && c != end) {
- if (c == '\n') {
- p->lineno++;
- p->column = 0;
- }
- if (!(c == '\\' || ((type & STR_FUNC_ARRAY) && ISSPACE(c)))) {
- tokadd(p, '\\');
- }
- }
- tokadd(p, c);
- }
- continue;
- }
- else if ((c == '#') && (type & STR_FUNC_EXPAND)) {
- c = nextc(p);
- if (c == '{') {
- tokfix(p);
- p->lstate = EXPR_BEG;
- p->cmd_start = TRUE;
- yylval.nd = new_str(p, tok(p), toklen(p));
- if (hinf) {
- hinf->line_head = FALSE;
- return tHD_STRING_PART;
- }
- return tSTRING_PART;
- }
- tokadd(p, '#');
- pushback(p, c);
- continue;
- }
- if ((type & STR_FUNC_ARRAY) && ISSPACE(c)) {
- if (toklen(p) == 0) {
- do {
- if (c == '\n') {
- p->lineno++;
- p->column = 0;
- heredoc_treat_nextline(p);
- if (p->parsing_heredoc != NULL) {
- return tHD_LITERAL_DELIM;
- }
- }
- c = nextc(p);
- } while (ISSPACE(c));
- pushback(p, c);
- return tLITERAL_DELIM;
- }
- else {
- pushback(p, c);
- tokfix(p);
- yylval.nd = new_str(p, tok(p), toklen(p));
- return tSTRING_MID;
- }
- }
- tokadd(p, c);
- }
-
- tokfix(p);
- p->lstate = EXPR_END;
- end_strterm(p);
-
- if (type & STR_FUNC_XQUOTE) {
- yylval.nd = new_xstr(p, tok(p), toklen(p));
- return tXSTRING;
- }
-
- if (type & STR_FUNC_REGEXP) {
- int f = 0;
- int re_opt;
- char *s = strndup(tok(p), toklen(p));
- char flags[3];
- char *flag = flags;
- char *dup;
-
- newtok(p);
- while (re_opt = nextc(p), re_opt >= 0 && ISALPHA(re_opt)) {
- switch (re_opt) {
- case 'i': f |= 1; break;
- case 'x': f |= 2; break;
- case 'm': f |= 4; break;
- default: tokadd(p, re_opt); break;
- }
- }
- pushback(p, re_opt);
- if (toklen(p)) {
- char msg[128];
- tokfix(p);
- snprintf(msg, sizeof(msg), "unknown regexp option%s - %s",
- toklen(p) > 1 ? "s" : "", tok(p));
- yyerror(p, msg);
- }
- if (f != 0) {
- if (f & 1) *flag++ = 'i';
- if (f & 2) *flag++ = 'x';
- if (f & 4) *flag++ = 'm';
- dup = strndup(flags, (size_t)(flag - flags));
- }
- else {
- dup = NULL;
- }
- yylval.nd = new_regx(p, s, dup);
-
- return tREGEXP;
- }
-
- yylval.nd = new_str(p, tok(p), toklen(p));
- return tSTRING;
-}
-
-
-static int
-heredoc_identifier(parser_state *p)
-{
- int c;
- int type = str_heredoc;
- mrb_bool indent = FALSE;
- mrb_bool quote = FALSE;
- node *newnode;
- parser_heredoc_info *info;
-
- c = nextc(p);
- if (ISSPACE(c) || c == '=') {
- pushback(p, c);
- return 0;
- }
- if (c == '-') {
- indent = TRUE;
- c = nextc(p);
- }
- if (c == '\'' || c == '"') {
- int term = c;
- if (c == '\'')
- quote = TRUE;
- newtok(p);
- while ((c = nextc(p)) >= 0 && c != term) {
- if (c == '\n') {
- c = -1;
- break;
- }
- tokadd(p, c);
- }
- if (c < 0) {
- yyerror(p, "unterminated here document identifier");
- return 0;
- }
- }
- else {
- if (c < 0) {
- return 0; /* missing here document identifier */
- }
- if (! identchar(c)) {
- pushback(p, c);
- if (indent) pushback(p, '-');
- return 0;
- }
- newtok(p);
- do {
- tokadd(p, c);
- } while ((c = nextc(p)) >= 0 && identchar(c));
- pushback(p, c);
- }
- tokfix(p);
- newnode = new_heredoc(p);
- info = (parser_heredoc_info*)newnode->cdr;
- info->term = strndup(tok(p), toklen(p));
- info->term_len = toklen(p);
- if (! quote)
- type |= STR_FUNC_EXPAND;
- info->type = (string_type)type;
- info->allow_indent = indent;
- info->line_head = TRUE;
- info->doc = NULL;
- p->heredocs_from_nextline = push(p->heredocs_from_nextline, newnode);
- p->lstate = EXPR_END;
-
- yylval.nd = newnode;
- return tHEREDOC_BEG;
-}
-
-static int
-arg_ambiguous(parser_state *p)
-{
- yywarning(p, "ambiguous first argument; put parentheses or even spaces");
- return 1;
-}
-
-#include "lex.def"
-
-static int
-parser_yylex(parser_state *p)
-{
- int32_t c;
- int space_seen = 0;
- int cmd_state;
- enum mrb_lex_state_enum last_state;
- int token_column;
-
- if (p->lex_strterm) {
- if (is_strterm_type(p, STR_FUNC_HEREDOC)) {
- if (p->parsing_heredoc != NULL)
- return parse_string(p);
- }
- else
- return parse_string(p);
- }
- cmd_state = p->cmd_start;
- p->cmd_start = FALSE;
- retry:
- last_state = p->lstate;
- switch (c = nextc(p)) {
- case '\0': /* NUL */
- case '\004': /* ^D */
- case '\032': /* ^Z */
- return 0;
- case -1: /* end of script. */
- if (p->heredocs_from_nextline)
- goto maybe_heredoc;
- return 0;
-
- /* white spaces */
- case ' ': case '\t': case '\f': case '\r':
- case '\13': /* '\v' */
- space_seen = 1;
- goto retry;
-
- case '#': /* it's a comment */
- skip(p, '\n');
- /* fall through */
- case -2: /* end of a file */
- case '\n':
- maybe_heredoc:
- heredoc_treat_nextline(p);
- switch (p->lstate) {
- case EXPR_BEG:
- case EXPR_FNAME:
- case EXPR_DOT:
- case EXPR_CLASS:
- case EXPR_VALUE:
- p->lineno++;
- p->column = 0;
- if (p->parsing_heredoc != NULL) {
- return parse_string(p);
- }
- goto retry;
- default:
- break;
- }
- if (p->parsing_heredoc != NULL) {
- return '\n';
- }
- while ((c = nextc(p))) {
- switch (c) {
- case ' ': case '\t': case '\f': case '\r':
- case '\13': /* '\v' */
- space_seen = 1;
- break;
- case '.':
- if ((c = nextc(p)) != '.') {
- pushback(p, c);
- pushback(p, '.');
- goto retry;
- }
- case -1: /* EOF */
- case -2: /* end of a file */
- goto normal_newline;
- default:
- pushback(p, c);
- goto normal_newline;
- }
- }
- normal_newline:
- p->cmd_start = TRUE;
- p->lstate = EXPR_BEG;
- return '\n';
-
- case '*':
- if ((c = nextc(p)) == '*') {
- if ((c = nextc(p)) == '=') {
- yylval.id = intern("**",2);
- p->lstate = EXPR_BEG;
- return tOP_ASGN;
- }
- pushback(p, c);
- c = tPOW;
- }
- else {
- if (c == '=') {
- yylval.id = intern_c('*');
- p->lstate = EXPR_BEG;
- return tOP_ASGN;
- }
- pushback(p, c);
- if (IS_SPCARG(c)) {
- yywarning(p, "`*' interpreted as argument prefix");
- c = tSTAR;
- }
- else if (IS_BEG()) {
- c = tSTAR;
- }
- else {
- c = '*';
- }
- }
- if (p->lstate == EXPR_FNAME || p->lstate == EXPR_DOT) {
- p->lstate = EXPR_ARG;
- }
- else {
- p->lstate = EXPR_BEG;
- }
- return c;
-
- case '!':
- c = nextc(p);
- if (p->lstate == EXPR_FNAME || p->lstate == EXPR_DOT) {
- p->lstate = EXPR_ARG;
- if (c == '@') {
- return '!';
- }
- }
- else {
- p->lstate = EXPR_BEG;
- }
- if (c == '=') {
- return tNEQ;
- }
- if (c == '~') {
- return tNMATCH;
- }
- pushback(p, c);
- return '!';
-
- case '=':
- if (p->column == 1) {
- static const char begin[] = "begin";
- static const char end[] = "\n=end";
- if (peeks(p, begin)) {
- c = peekc_n(p, sizeof(begin)-1);
- if (c < 0 || ISSPACE(c)) {
- do {
- if (!skips(p, end)) {
- yyerror(p, "embedded document meets end of file");
- return 0;
- }
- c = nextc(p);
- } while (!(c < 0 || ISSPACE(c)));
- if (c != '\n') skip(p, '\n');
- p->lineno++;
- p->column = 0;
- goto retry;
- }
- }
- }
- if (p->lstate == EXPR_FNAME || p->lstate == EXPR_DOT) {
- p->lstate = EXPR_ARG;
- }
- else {
- p->lstate = EXPR_BEG;
- }
- if ((c = nextc(p)) == '=') {
- if ((c = nextc(p)) == '=') {
- return tEQQ;
- }
- pushback(p, c);
- return tEQ;
- }
- if (c == '~') {
- return tMATCH;
- }
- else if (c == '>') {
- return tASSOC;
- }
- pushback(p, c);
- return '=';
-
- case '<':
- c = nextc(p);
- if (c == '<' &&
- p->lstate != EXPR_DOT &&
- p->lstate != EXPR_CLASS &&
- !IS_END() &&
- (!IS_ARG() || space_seen)) {
- int token = heredoc_identifier(p);
- if (token)
- return token;
- }
- if (p->lstate == EXPR_FNAME || p->lstate == EXPR_DOT) {
- p->lstate = EXPR_ARG;
- }
- else {
- p->lstate = EXPR_BEG;
- if (p->lstate == EXPR_CLASS) {
- p->cmd_start = TRUE;
- }
- }
- if (c == '=') {
- if ((c = nextc(p)) == '>') {
- return tCMP;
- }
- pushback(p, c);
- return tLEQ;
- }
- if (c == '<') {
- if ((c = nextc(p)) == '=') {
- yylval.id = intern("<<",2);
- p->lstate = EXPR_BEG;
- return tOP_ASGN;
- }
- pushback(p, c);
- return tLSHFT;
- }
- pushback(p, c);
- return '<';
-
- case '>':
- if (p->lstate == EXPR_FNAME || p->lstate == EXPR_DOT) {
- p->lstate = EXPR_ARG;
- }
- else {
- p->lstate = EXPR_BEG;
- }
- if ((c = nextc(p)) == '=') {
- return tGEQ;
- }
- if (c == '>') {
- if ((c = nextc(p)) == '=') {
- yylval.id = intern(">>",2);
- p->lstate = EXPR_BEG;
- return tOP_ASGN;
- }
- pushback(p, c);
- return tRSHFT;
- }
- pushback(p, c);
- return '>';
-
- case '"':
- p->lex_strterm = new_strterm(p, str_dquote, '"', 0);
- return tSTRING_BEG;
-
- case '\'':
- p->lex_strterm = new_strterm(p, str_squote, '\'', 0);
- return parse_string(p);
-
- case '`':
- if (p->lstate == EXPR_FNAME) {
- p->lstate = EXPR_ENDFN;
- return '`';
- }
- if (p->lstate == EXPR_DOT) {
- if (cmd_state)
- p->lstate = EXPR_CMDARG;
- else
- p->lstate = EXPR_ARG;
- return '`';
- }
- p->lex_strterm = new_strterm(p, str_xquote, '`', 0);
- return tXSTRING_BEG;
-
- case '?':
- if (IS_END()) {
- p->lstate = EXPR_VALUE;
- return '?';
- }
- c = nextc(p);
- if (c < 0) {
- yyerror(p, "incomplete character syntax");
- return 0;
- }
- if (ISSPACE(c)) {
- if (!IS_ARG()) {
- int c2;
- switch (c) {
- case ' ':
- c2 = 's';
- break;
- case '\n':
- c2 = 'n';
- break;
- case '\t':
- c2 = 't';
- break;
- case '\v':
- c2 = 'v';
- break;
- case '\r':
- c2 = 'r';
- break;
- case '\f':
- c2 = 'f';
- break;
- default:
- c2 = 0;
- break;
- }
- if (c2) {
- char buf[256];
- snprintf(buf, sizeof(buf), "invalid character syntax; use ?\\%c", c2);
- yyerror(p, buf);
- }
- }
- ternary:
- pushback(p, c);
- p->lstate = EXPR_VALUE;
- return '?';
- }
- newtok(p);
- /* need support UTF-8 if configured */
- if ((isalnum(c) || c == '_')) {
- int c2 = nextc(p);
- pushback(p, c2);
- if ((isalnum(c2) || c2 == '_')) {
- goto ternary;
- }
- }
- if (c == '\\') {
- c = read_escape(p);
- tokadd(p, c);
- }
- else {
- tokadd(p, c);
- }
- tokfix(p);
- yylval.nd = new_str(p, tok(p), toklen(p));
- p->lstate = EXPR_END;
- return tCHAR;
-
- case '&':
- if ((c = nextc(p)) == '&') {
- p->lstate = EXPR_BEG;
- if ((c = nextc(p)) == '=') {
- yylval.id = intern("&&",2);
- p->lstate = EXPR_BEG;
- return tOP_ASGN;
- }
- pushback(p, c);
- return tANDOP;
- }
- else if (c == '=') {
- yylval.id = intern_c('&');
- p->lstate = EXPR_BEG;
- return tOP_ASGN;
- }
- pushback(p, c);
- if (IS_SPCARG(c)) {
- yywarning(p, "`&' interpreted as argument prefix");
- c = tAMPER;
- }
- else if (IS_BEG()) {
- c = tAMPER;
- }
- else {
- c = '&';
- }
- if (p->lstate == EXPR_FNAME || p->lstate == EXPR_DOT) {
- p->lstate = EXPR_ARG;
- }
- else {
- p->lstate = EXPR_BEG;
- }
- return c;
-
- case '|':
- if ((c = nextc(p)) == '|') {
- p->lstate = EXPR_BEG;
- if ((c = nextc(p)) == '=') {
- yylval.id = intern("||",2);
- p->lstate = EXPR_BEG;
- return tOP_ASGN;
- }
- pushback(p, c);
- return tOROP;
- }
- if (c == '=') {
- yylval.id = intern_c('|');
- p->lstate = EXPR_BEG;
- return tOP_ASGN;
- }
- if (p->lstate == EXPR_FNAME || p->lstate == EXPR_DOT) {
- p->lstate = EXPR_ARG;
- }
- else {
- p->lstate = EXPR_BEG;
- }
- pushback(p, c);
- return '|';
-
- case '+':
- c = nextc(p);
- if (p->lstate == EXPR_FNAME || p->lstate == EXPR_DOT) {
- p->lstate = EXPR_ARG;
- if (c == '@') {
- return tUPLUS;
- }
- pushback(p, c);
- return '+';
- }
- if (c == '=') {
- yylval.id = intern_c('+');
- p->lstate = EXPR_BEG;
- return tOP_ASGN;
- }
- if (IS_BEG() || (IS_SPCARG(c) && arg_ambiguous(p))) {
- p->lstate = EXPR_BEG;
- pushback(p, c);
- if (c >= 0 && ISDIGIT(c)) {
- c = '+';
- goto start_num;
- }
- return tUPLUS;
- }
- p->lstate = EXPR_BEG;
- pushback(p, c);
- return '+';
-
- case '-':
- c = nextc(p);
- if (p->lstate == EXPR_FNAME || p->lstate == EXPR_DOT) {
- p->lstate = EXPR_ARG;
- if (c == '@') {
- return tUMINUS;
- }
- pushback(p, c);
- return '-';
- }
- if (c == '=') {
- yylval.id = intern_c('-');
- p->lstate = EXPR_BEG;
- return tOP_ASGN;
- }
- if (c == '>') {
- p->lstate = EXPR_ENDFN;
- return tLAMBDA;
- }
- if (IS_BEG() || (IS_SPCARG(c) && arg_ambiguous(p))) {
- p->lstate = EXPR_BEG;
- pushback(p, c);
- if (c >= 0 && ISDIGIT(c)) {
- return tUMINUS_NUM;
- }
- return tUMINUS;
- }
- p->lstate = EXPR_BEG;
- pushback(p, c);
- return '-';
-
- case '.':
- p->lstate = EXPR_BEG;
- if ((c = nextc(p)) == '.') {
- if ((c = nextc(p)) == '.') {
- return tDOT3;
- }
- pushback(p, c);
- return tDOT2;
- }
- pushback(p, c);
- if (c >= 0 && ISDIGIT(c)) {
- yyerror(p, "no .<digit> floating literal anymore; put 0 before dot");
- }
- p->lstate = EXPR_DOT;
- return '.';
-
- start_num:
- case '0': case '1': case '2': case '3': case '4':
- case '5': case '6': case '7': case '8': case '9':
- {
- int is_float, seen_point, seen_e, nondigit;
-
- is_float = seen_point = seen_e = nondigit = 0;
- p->lstate = EXPR_END;
- newtok(p);
- if (c == '-' || c == '+') {
- tokadd(p, c);
- c = nextc(p);
- }
- if (c == '0') {
-#define no_digits() do {yyerror(p,"numeric literal without digits"); return 0;} while (0)
- int start = toklen(p);
- c = nextc(p);
- if (c == 'x' || c == 'X') {
- /* hexadecimal */
- c = nextc(p);
- if (c >= 0 && ISXDIGIT(c)) {
- do {
- if (c == '_') {
- if (nondigit) break;
- nondigit = c;
- continue;
- }
- if (!ISXDIGIT(c)) break;
- nondigit = 0;
- tokadd(p, tolower(c));
- } while ((c = nextc(p)) >= 0);
- }
- pushback(p, c);
- tokfix(p);
- if (toklen(p) == start) {
- no_digits();
- }
- else if (nondigit) goto trailing_uc;
- yylval.nd = new_int(p, tok(p), 16);
- return tINTEGER;
- }
- if (c == 'b' || c == 'B') {
- /* binary */
- c = nextc(p);
- if (c == '0' || c == '1') {
- do {
- if (c == '_') {
- if (nondigit) break;
- nondigit = c;
- continue;
- }
- if (c != '0' && c != '1') break;
- nondigit = 0;
- tokadd(p, c);
- } while ((c = nextc(p)) >= 0);
- }
- pushback(p, c);
- tokfix(p);
- if (toklen(p) == start) {
- no_digits();
- }
- else if (nondigit) goto trailing_uc;
- yylval.nd = new_int(p, tok(p), 2);
- return tINTEGER;
- }
- if (c == 'd' || c == 'D') {
- /* decimal */
- c = nextc(p);
- if (c >= 0 && ISDIGIT(c)) {
- do {
- if (c == '_') {
- if (nondigit) break;
- nondigit = c;
- continue;
- }
- if (!ISDIGIT(c)) break;
- nondigit = 0;
- tokadd(p, c);
- } while ((c = nextc(p)) >= 0);
- }
- pushback(p, c);
- tokfix(p);
- if (toklen(p) == start) {
- no_digits();
- }
- else if (nondigit) goto trailing_uc;
- yylval.nd = new_int(p, tok(p), 10);
- return tINTEGER;
- }
- if (c == '_') {
- /* 0_0 */
- goto octal_number;
- }
- if (c == 'o' || c == 'O') {
- /* prefixed octal */
- c = nextc(p);
- if (c < 0 || c == '_' || !ISDIGIT(c)) {
- no_digits();
- }
- }
- if (c >= '0' && c <= '7') {
- /* octal */
- octal_number:
- do {
- if (c == '_') {
- if (nondigit) break;
- nondigit = c;
- continue;
- }
- if (c < '0' || c > '9') break;
- if (c > '7') goto invalid_octal;
- nondigit = 0;
- tokadd(p, c);
- } while ((c = nextc(p)) >= 0);
-
- if (toklen(p) > start) {
- pushback(p, c);
- tokfix(p);
- if (nondigit) goto trailing_uc;
- yylval.nd = new_int(p, tok(p), 8);
- return tINTEGER;
- }
- if (nondigit) {
- pushback(p, c);
- goto trailing_uc;
- }
- }
- if (c > '7' && c <= '9') {
- invalid_octal:
- yyerror(p, "Invalid octal digit");
- }
- else if (c == '.' || c == 'e' || c == 'E') {
- tokadd(p, '0');
- }
- else {
- pushback(p, c);
- yylval.nd = new_int(p, "0", 10);
- return tINTEGER;
- }
- }
-
- for (;;) {
- switch (c) {
- case '0': case '1': case '2': case '3': case '4':
- case '5': case '6': case '7': case '8': case '9':
- nondigit = 0;
- tokadd(p, c);
- break;
-
- case '.':
- if (nondigit) goto trailing_uc;
- if (seen_point || seen_e) {
- goto decode_num;
- }
- else {
- int c0 = nextc(p);
- if (c0 < 0 || !ISDIGIT(c0)) {
- pushback(p, c0);
- goto decode_num;
- }
- c = c0;
- }
- tokadd(p, '.');
- tokadd(p, c);
- is_float++;
- seen_point++;
- nondigit = 0;
- break;
-
- case 'e':
- case 'E':
- if (nondigit) {
- pushback(p, c);
- c = nondigit;
- goto decode_num;
- }
- if (seen_e) {
- goto decode_num;
- }
- tokadd(p, c);
- seen_e++;
- is_float++;
- nondigit = c;
- c = nextc(p);
- if (c != '-' && c != '+') continue;
- tokadd(p, c);
- nondigit = c;
- break;
-
- case '_': /* `_' in number just ignored */
- if (nondigit) goto decode_num;
- nondigit = c;
- break;
-
- default:
- goto decode_num;
- }
- c = nextc(p);
- }
-
- decode_num:
- pushback(p, c);
- if (nondigit) {
- trailing_uc:
- yyerror_i(p, "trailing `%c' in number", nondigit);
- }
- tokfix(p);
- if (is_float) {
- double d;
- char *endp;
-
- errno = 0;
- d = strtod(tok(p), &endp);
- if (d == 0 && endp == tok(p)) {
- yywarning_s(p, "corrupted float value %s", tok(p));
- }
- else if (errno == ERANGE) {
- yywarning_s(p, "float %s out of range", tok(p));
- errno = 0;
- }
- yylval.nd = new_float(p, tok(p));
- return tFLOAT;
- }
- yylval.nd = new_int(p, tok(p), 10);
- return tINTEGER;
- }
-
- case ')':
- case ']':
- p->paren_nest--;
- case '}':
- COND_LEXPOP();
- CMDARG_LEXPOP();
- if (c == ')')
- p->lstate = EXPR_ENDFN;
- else
- p->lstate = EXPR_ENDARG;
- return c;
-
- case ':':
- c = nextc(p);
- if (c == ':') {
- if (IS_BEG() || p->lstate == EXPR_CLASS || IS_SPCARG(-1)) {
- p->lstate = EXPR_BEG;
- return tCOLON3;
- }
- p->lstate = EXPR_DOT;
- return tCOLON2;
- }
- if (IS_END() || ISSPACE(c)) {
- pushback(p, c);
- p->lstate = EXPR_BEG;
- return ':';
- }
- pushback(p, c);
- p->lstate = EXPR_FNAME;
- return tSYMBEG;
-
- case '/':
- if (IS_BEG()) {
- p->lex_strterm = new_strterm(p, str_regexp, '/', 0);
- return tREGEXP_BEG;
- }
- if ((c = nextc(p)) == '=') {
- yylval.id = intern_c('/');
- p->lstate = EXPR_BEG;
- return tOP_ASGN;
- }
- pushback(p, c);
- if (IS_SPCARG(c)) {
- p->lex_strterm = new_strterm(p, str_regexp, '/', 0);
- return tREGEXP_BEG;
- }
- if (p->lstate == EXPR_FNAME || p->lstate == EXPR_DOT) {
- p->lstate = EXPR_ARG;
- }
- else {
- p->lstate = EXPR_BEG;
- }
- return '/';
-
- case '^':
- if ((c = nextc(p)) == '=') {
- yylval.id = intern_c('^');
- p->lstate = EXPR_BEG;
- return tOP_ASGN;
- }
- if (p->lstate == EXPR_FNAME || p->lstate == EXPR_DOT) {
- p->lstate = EXPR_ARG;
- }
- else {
- p->lstate = EXPR_BEG;
- }
- pushback(p, c);
- return '^';
-
- case ';':
- p->lstate = EXPR_BEG;
- return ';';
-
- case ',':
- p->lstate = EXPR_BEG;
- return ',';
-
- case '~':
- if (p->lstate == EXPR_FNAME || p->lstate == EXPR_DOT) {
- if ((c = nextc(p)) != '@') {
- pushback(p, c);
- }
- p->lstate = EXPR_ARG;
- }
- else {
- p->lstate = EXPR_BEG;
- }
- return '~';
-
- case '(':
- if (IS_BEG()) {
- c = tLPAREN;
- }
- else if (IS_SPCARG(-1)) {
- c = tLPAREN_ARG;
- }
- p->paren_nest++;
- COND_PUSH(0);
- CMDARG_PUSH(0);
- p->lstate = EXPR_BEG;
- return c;
-
- case '[':
- p->paren_nest++;
- if (p->lstate == EXPR_FNAME || p->lstate == EXPR_DOT) {
- p->lstate = EXPR_ARG;
- if ((c = nextc(p)) == ']') {
- if ((c = nextc(p)) == '=') {
- return tASET;
- }
- pushback(p, c);
- return tAREF;
- }
- pushback(p, c);
- return '[';
- }
- else if (IS_BEG()) {
- c = tLBRACK;
- }
- else if (IS_ARG() && space_seen) {
- c = tLBRACK;
- }
- p->lstate = EXPR_BEG;
- COND_PUSH(0);
- CMDARG_PUSH(0);
- return c;
-
- case '{':
- if (p->lpar_beg && p->lpar_beg == p->paren_nest) {
- p->lstate = EXPR_BEG;
- p->lpar_beg = 0;
- p->paren_nest--;
- COND_PUSH(0);
- CMDARG_PUSH(0);
- return tLAMBEG;
- }
- if (IS_ARG() || p->lstate == EXPR_END || p->lstate == EXPR_ENDFN)
- c = '{'; /* block (primary) */
- else if (p->lstate == EXPR_ENDARG)
- c = tLBRACE_ARG; /* block (expr) */
- else
- c = tLBRACE; /* hash */
- COND_PUSH(0);
- CMDARG_PUSH(0);
- p->lstate = EXPR_BEG;
- return c;
-
- case '\\':
- c = nextc(p);
- if (c == '\n') {
- p->lineno++;
- p->column = 0;
- space_seen = 1;
- goto retry; /* skip \\n */
- }
- pushback(p, c);
- return '\\';
-
- case '%':
- if (IS_BEG()) {
- int term;
- int paren;
-
- c = nextc(p);
- quotation:
- if (c < 0 || !ISALNUM(c)) {
- term = c;
- c = 'Q';
- }
- else {
- term = nextc(p);
- if (isalnum(term)) {
- yyerror(p, "unknown type of %string");
- return 0;
- }
- }
- if (c < 0 || term < 0) {
- yyerror(p, "unterminated quoted string meets end of file");
- return 0;
- }
- paren = term;
- if (term == '(') term = ')';
- else if (term == '[') term = ']';
- else if (term == '{') term = '}';
- else if (term == '<') term = '>';
- else paren = 0;
-
- switch (c) {
- case 'Q':
- p->lex_strterm = new_strterm(p, str_dquote, term, paren);
- return tSTRING_BEG;
-
- case 'q':
- p->lex_strterm = new_strterm(p, str_squote, term, paren);
- return parse_string(p);
-
- case 'W':
- p->lex_strterm = new_strterm(p, str_dword, term, paren);
- return tWORDS_BEG;
-
- case 'w':
- p->lex_strterm = new_strterm(p, str_sword, term, paren);
- return tWORDS_BEG;
-
- case 'x':
- p->lex_strterm = new_strterm(p, str_xquote, term, paren);
- return tXSTRING_BEG;
-
- case 'r':
- p->lex_strterm = new_strterm(p, str_regexp, term, paren);
- return tREGEXP_BEG;
-
- case 's':
- p->lex_strterm = new_strterm(p, str_ssym, term, paren);
- return tSYMBEG;
-
- case 'I':
- p->lex_strterm = new_strterm(p, str_dsymbols, term, paren);
- return tSYMBOLS_BEG;
-
- case 'i':
- p->lex_strterm = new_strterm(p, str_ssymbols, term, paren);
- return tSYMBOLS_BEG;
-
- default:
- yyerror(p, "unknown type of %string");
- return 0;
- }
- }
- if ((c = nextc(p)) == '=') {
- yylval.id = intern_c('%');
- p->lstate = EXPR_BEG;
- return tOP_ASGN;
- }
- if (IS_SPCARG(c)) {
- goto quotation;
- }
- if (p->lstate == EXPR_FNAME || p->lstate == EXPR_DOT) {
- p->lstate = EXPR_ARG;
- }
- else {
- p->lstate = EXPR_BEG;
- }
- pushback(p, c);
- return '%';
-
- case '$':
- p->lstate = EXPR_END;
- token_column = newtok(p);
- c = nextc(p);
- if (c < 0) {
- yyerror(p, "incomplete global variable syntax");
- return 0;
- }
- switch (c) {
- case '_': /* $_: last read line string */
- c = nextc(p);
- if (c >= 0 && identchar(c)) { /* if there is more after _ it is a variable */
- tokadd(p, '$');
- tokadd(p, c);
- break;
- }
- pushback(p, c);
- c = '_';
- /* fall through */
- case '~': /* $~: match-data */
- case '*': /* $*: argv */
- case '$': /* $$: pid */
- case '?': /* $?: last status */
- case '!': /* $!: error string */
- case '@': /* $@: error position */
- case '/': /* $/: input record separator */
- case '\\': /* $\: output record separator */
- case ';': /* $;: field separator */
- case ',': /* $,: output field separator */
- case '.': /* $.: last read line number */
- case '=': /* $=: ignorecase */
- case ':': /* $:: load path */
- case '<': /* $<: reading filename */
- case '>': /* $>: default output handle */
- case '\"': /* $": already loaded files */
- tokadd(p, '$');
- tokadd(p, c);
- tokfix(p);
- yylval.id = intern_cstr(tok(p));
- return tGVAR;
-
- case '-':
- tokadd(p, '$');
- tokadd(p, c);
- c = nextc(p);
- pushback(p, c);
- gvar:
- tokfix(p);
- yylval.id = intern_cstr(tok(p));
- return tGVAR;
-
- case '&': /* $&: last match */
- case '`': /* $`: string before last match */
- case '\'': /* $': string after last match */
- case '+': /* $+: string matches last pattern */
- if (last_state == EXPR_FNAME) {
- tokadd(p, '$');
- tokadd(p, c);
- goto gvar;
- }
- yylval.nd = new_back_ref(p, c);
- return tBACK_REF;
-
- case '1': case '2': case '3':
- case '4': case '5': case '6':
- case '7': case '8': case '9':
- do {
- tokadd(p, c);
- c = nextc(p);
- } while (c >= 0 && isdigit(c));
- pushback(p, c);
- if (last_state == EXPR_FNAME) goto gvar;
- tokfix(p);
- yylval.nd = new_nth_ref(p, atoi(tok(p)));
- return tNTH_REF;
-
- default:
- if (!identchar(c)) {
- pushback(p, c);
- return '$';
- }
- case '0':
- tokadd(p, '$');
- }
- break;
-
- case '@':
- c = nextc(p);
- token_column = newtok(p);
- tokadd(p, '@');
- if (c == '@') {
- tokadd(p, '@');
- c = nextc(p);
- }
- if (c < 0) {
- if (p->bidx == 1) {
- yyerror(p, "incomplete instance variable syntax");
- }
- else {
- yyerror(p, "incomplete class variable syntax");
- }
- return 0;
- }
- else if (isdigit(c)) {
- if (p->bidx == 1) {
- yyerror_i(p, "`@%c' is not allowed as an instance variable name", c);
- }
- else {
- yyerror_i(p, "`@@%c' is not allowed as a class variable name", c);
- }
- return 0;
- }
- if (!identchar(c)) {
- pushback(p, c);
- return '@';
- }
- break;
-
- case '_':
- token_column = newtok(p);
- break;
-
- default:
- if (!identchar(c)) {
- yyerror_i(p, "Invalid char `\\x%02X' in expression", c);
- goto retry;
- }
-
- token_column = newtok(p);
- break;
- }
-
- do {
- tokadd(p, c);
- c = nextc(p);
- if (c < 0) break;
- } while (identchar(c));
- if (token_column == 0 && toklen(p) == 7 && (c < 0 || c == '\n') &&
- strncmp(tok(p), "__END__", toklen(p)) == 0)
- return -1;
-
- switch (tok(p)[0]) {
- case '@': case '$':
- pushback(p, c);
- break;
- default:
- if ((c == '!' || c == '?') && !peek(p, '=')) {
- tokadd(p, c);
- }
- else {
- pushback(p, c);
- }
- }
- tokfix(p);
- {
- int result = 0;
-
- switch (tok(p)[0]) {
- case '$':
- p->lstate = EXPR_END;
- result = tGVAR;
- break;
- case '@':
- p->lstate = EXPR_END;
- if (tok(p)[1] == '@')
- result = tCVAR;
- else
- result = tIVAR;
- break;
-
- default:
- if (toklast(p) == '!' || toklast(p) == '?') {
- result = tFID;
- }
- else {
- if (p->lstate == EXPR_FNAME) {
- if ((c = nextc(p)) == '=' && !peek(p, '~') && !peek(p, '>') &&
- (!peek(p, '=') || (peek_n(p, '>', 1)))) {
- result = tIDENTIFIER;
- tokadd(p, c);
- tokfix(p);
- }
- else {
- pushback(p, c);
- }
- }
- if (result == 0 && ISUPPER(tok(p)[0])) {
- result = tCONSTANT;
- }
- else {
- result = tIDENTIFIER;
- }
- }
-
- if (IS_LABEL_POSSIBLE()) {
- if (IS_LABEL_SUFFIX(0)) {
- p->lstate = EXPR_BEG;
- nextc(p);
- tokfix(p);
- yylval.id = intern_cstr(tok(p));
- return tLABEL;
- }
- }
- if (p->lstate != EXPR_DOT) {
- const struct kwtable *kw;
-
- /* See if it is a reserved word. */
- kw = mrb_reserved_word(tok(p), toklen(p));
- if (kw) {
- enum mrb_lex_state_enum state = p->lstate;
- yylval.num = p->lineno;
- p->lstate = kw->state;
- if (state == EXPR_FNAME) {
- yylval.id = intern_cstr(kw->name);
- return kw->id[0];
- }
- if (p->lstate == EXPR_BEG) {
- p->cmd_start = TRUE;
- }
- if (kw->id[0] == keyword_do) {
- if (p->lpar_beg && p->lpar_beg == p->paren_nest) {
- p->lpar_beg = 0;
- p->paren_nest--;
- return keyword_do_LAMBDA;
- }
- if (COND_P()) return keyword_do_cond;
- if (CMDARG_P() && state != EXPR_CMDARG)
- return keyword_do_block;
- if (state == EXPR_ENDARG || state == EXPR_BEG)
- return keyword_do_block;
- return keyword_do;
- }
- if (state == EXPR_BEG || state == EXPR_VALUE)
- return kw->id[0];
- else {
- if (kw->id[0] != kw->id[1])
- p->lstate = EXPR_BEG;
- return kw->id[1];
- }
- }
- }
-
- if (IS_BEG() || p->lstate == EXPR_DOT || IS_ARG()) {
- if (cmd_state) {
- p->lstate = EXPR_CMDARG;
- }
- else {
- p->lstate = EXPR_ARG;
- }
- }
- else if (p->lstate == EXPR_FNAME) {
- p->lstate = EXPR_ENDFN;
- }
- else {
- p->lstate = EXPR_END;
- }
- }
- {
- mrb_sym ident = intern_cstr(tok(p));
-
- yylval.id = ident;
-#if 0
- if (last_state != EXPR_DOT && islower(tok(p)[0]) && lvar_defined(ident)) {
- p->lstate = EXPR_END;
- }
-#endif
- }
- return result;
- }
-}
-
-static int
-yylex(void *lval, parser_state *p)
-{
- p->ylval = lval;
- return parser_yylex(p);
-}
-
-static void
-parser_init_cxt(parser_state *p, mrbc_context *cxt)
-{
- if (!cxt) return;
- if (cxt->filename) mrb_parser_set_filename(p, cxt->filename);
- if (cxt->lineno) p->lineno = cxt->lineno;
- if (cxt->syms) {
- int i;
-
- p->locals = cons(0,0);
- for (i=0; i<cxt->slen; i++) {
- local_add_f(p, cxt->syms[i]);
- }
- }
- p->capture_errors = cxt->capture_errors;
- p->no_optimize = cxt->no_optimize;
- if (cxt->partial_hook) {
- p->cxt = cxt;
- }
-}
-
-static void
-parser_update_cxt(parser_state *p, mrbc_context *cxt)
-{
- node *n, *n0;
- int i = 0;
-
- if (!cxt) return;
- if ((int)(intptr_t)p->tree->car != NODE_SCOPE) return;
- n0 = n = p->tree->cdr->car;
- while (n) {
- i++;
- n = n->cdr;
- }
- cxt->syms = (mrb_sym *)mrb_realloc(p->mrb, cxt->syms, i*sizeof(mrb_sym));
- cxt->slen = i;
- for (i=0, n=n0; n; i++,n=n->cdr) {
- cxt->syms[i] = sym(n->car);
- }
-}
-
-void mrb_codedump_all(mrb_state*, struct RProc*);
-void mrb_parser_dump(mrb_state *mrb, node *tree, int offset);
-
-MRB_API void
-mrb_parser_parse(parser_state *p, mrbc_context *c)
-{
- struct mrb_jmpbuf buf;
- p->jmp = &buf;
-
- MRB_TRY(p->jmp) {
-
- p->cmd_start = TRUE;
- p->in_def = p->in_single = 0;
- p->nerr = p->nwarn = 0;
- p->lex_strterm = NULL;
-
- parser_init_cxt(p, c);
- yyparse(p);
- if (!p->tree) {
- p->tree = new_nil(p);
- }
- parser_update_cxt(p, c);
- if (c && c->dump_result) {
- mrb_parser_dump(p->mrb, p->tree, 0);
- }
-
- }
- MRB_CATCH(p->jmp) {
- yyerror(p, "memory allocation error");
- p->nerr++;
- p->tree = 0;
- return;
- }
- MRB_END_EXC(p->jmp);
-}
-
-MRB_API parser_state*
-mrb_parser_new(mrb_state *mrb)
-{
- mrb_pool *pool;
- parser_state *p;
- static const parser_state parser_state_zero = { 0 };
-
- pool = mrb_pool_open(mrb);
- if (!pool) return NULL;
- p = (parser_state *)mrb_pool_alloc(pool, sizeof(parser_state));
- if (!p) return NULL;
-
- *p = parser_state_zero;
- p->mrb = mrb;
- p->pool = pool;
-
- p->s = p->send = NULL;
-#ifdef ENABLE_STDIO
- p->f = NULL;
-#endif
-
- p->cmd_start = TRUE;
- p->in_def = p->in_single = 0;
-
- p->capture_errors = FALSE;
- p->lineno = 1;
- p->column = 0;
-#if defined(PARSER_TEST) || defined(PARSER_DEBUG)
- yydebug = 1;
-#endif
-
- p->lex_strterm = NULL;
- p->all_heredocs = p->parsing_heredoc = NULL;
- p->lex_strterm_before_heredoc = NULL;
-
- p->current_filename_index = -1;
- p->filename_table = NULL;
- p->filename_table_length = 0;
-
- return p;
-}
-
-MRB_API void
-mrb_parser_free(parser_state *p) {
- mrb_pool_close(p->pool);
-}
-
-MRB_API mrbc_context*
-mrbc_context_new(mrb_state *mrb)
-{
- return (mrbc_context *)mrb_calloc(mrb, 1, sizeof(mrbc_context));
-}
-
-MRB_API void
-mrbc_context_free(mrb_state *mrb, mrbc_context *cxt)
-{
- mrb_free(mrb, cxt->syms);
- mrb_free(mrb, cxt);
-}
-
-MRB_API const char*
-mrbc_filename(mrb_state *mrb, mrbc_context *c, const char *s)
-{
- if (s) {
- int len = strlen(s);
- char *p = (char *)mrb_alloca(mrb, len + 1);
-
- memcpy(p, s, len + 1);
- c->filename = p;
- }
- return c->filename;
-}
-
-MRB_API void
-mrbc_partial_hook(mrb_state *mrb, mrbc_context *c, int (*func)(struct mrb_parser_state*), void *data)
-{
- c->partial_hook = func;
- c->partial_data = data;
-}
-
-MRB_API void
-mrb_parser_set_filename(struct mrb_parser_state *p, const char *f)
-{
- mrb_sym sym;
- size_t i;
- mrb_sym* new_table;
-
- sym = mrb_intern_cstr(p->mrb, f);
- p->filename = mrb_sym2name_len(p->mrb, sym, NULL);
- p->lineno = (p->filename_table_length > 0)? 0 : 1;
-
- for (i = 0; i < p->filename_table_length; ++i) {
- if (p->filename_table[i] == sym) {
- p->current_filename_index = i;
- return;
- }
- }
-
- p->current_filename_index = p->filename_table_length++;
-
- new_table = (mrb_sym*)parser_palloc(p, sizeof(mrb_sym) * p->filename_table_length);
- if (p->filename_table) {
- memmove(new_table, p->filename_table, sizeof(mrb_sym) * p->filename_table_length);
- }
- p->filename_table = new_table;
- p->filename_table[p->filename_table_length - 1] = sym;
-}
-
-MRB_API char const*
-mrb_parser_get_filename(struct mrb_parser_state* p, uint16_t idx) {
- if (idx >= p->filename_table_length) { return NULL; }
- else {
- return mrb_sym2name_len(p->mrb, p->filename_table[idx], NULL);
- }
-}
-
-#ifdef ENABLE_STDIO
-MRB_API parser_state*
-mrb_parse_file(mrb_state *mrb, FILE *f, mrbc_context *c)
-{
- parser_state *p;
-
- p = mrb_parser_new(mrb);
- if (!p) return NULL;
- p->s = p->send = NULL;
- p->f = f;
-
- mrb_parser_parse(p, c);
- return p;
-}
-#endif
-
-MRB_API parser_state*
-mrb_parse_nstring(mrb_state *mrb, const char *s, int len, mrbc_context *c)
-{
- parser_state *p;
-
- p = mrb_parser_new(mrb);
- if (!p) return NULL;
- p->s = s;
- p->send = s + len;
-
- mrb_parser_parse(p, c);
- return p;
-}
-
-MRB_API parser_state*
-mrb_parse_string(mrb_state *mrb, const char *s, mrbc_context *c)
-{
- return mrb_parse_nstring(mrb, s, strlen(s), c);
-}
-
-static mrb_value
-load_exec(mrb_state *mrb, parser_state *p, mrbc_context *c)
-{
- struct RClass *target = mrb->object_class;
- struct RProc *proc;
- mrb_value v;
- unsigned int keep = 0;
-
- if (!p) {
- return mrb_undef_value();
- }
- if (!p->tree || p->nerr) {
- if (p->capture_errors) {
- char buf[256];
- int n;
-
- n = snprintf(buf, sizeof(buf), "line %d: %s\n",
- p->error_buffer[0].lineno, p->error_buffer[0].message);
- mrb->exc = mrb_obj_ptr(mrb_exc_new(mrb, E_SYNTAX_ERROR, buf, n));
- mrb_parser_free(p);
- return mrb_undef_value();
- }
- else {
- mrb->exc = mrb_obj_ptr(mrb_exc_new_str_lit(mrb, E_SYNTAX_ERROR, "syntax error"));
- mrb_parser_free(p);
- return mrb_undef_value();
- }
- }
- proc = mrb_generate_code(mrb, p);
- mrb_parser_free(p);
- if (proc == NULL) {
- mrb->exc = mrb_obj_ptr(mrb_exc_new_str_lit(mrb, E_SCRIPT_ERROR, "codegen error"));
- return mrb_undef_value();
- }
- if (c) {
- if (c->dump_result) mrb_codedump_all(mrb, proc);
- if (c->no_exec) return mrb_obj_value(proc);
- if (c->target_class) {
- target = c->target_class;
- }
- if (c->keep_lv) {
- keep = c->slen + 1;
- }
- else {
- c->keep_lv = TRUE;
- }
- }
- proc->target_class = target;
- if (mrb->c->ci) {
- mrb->c->ci->target_class = target;
- }
- v = mrb_toplevel_run_keep(mrb, proc, keep);
- if (mrb->exc) return mrb_nil_value();
- return v;
-}
-
-#ifdef ENABLE_STDIO
-MRB_API mrb_value
-mrb_load_file_cxt(mrb_state *mrb, FILE *f, mrbc_context *c)
-{
- return load_exec(mrb, mrb_parse_file(mrb, f, c), c);
-}
-
-MRB_API mrb_value
-mrb_load_file(mrb_state *mrb, FILE *f)
-{
- return mrb_load_file_cxt(mrb, f, NULL);
-}
-#endif
-
-MRB_API mrb_value
-mrb_load_nstring_cxt(mrb_state *mrb, const char *s, int len, mrbc_context *c)
-{
- return load_exec(mrb, mrb_parse_nstring(mrb, s, len, c), c);
-}
-
-MRB_API mrb_value
-mrb_load_nstring(mrb_state *mrb, const char *s, int len)
-{
- return mrb_load_nstring_cxt(mrb, s, len, NULL);
-}
-
-MRB_API mrb_value
-mrb_load_string_cxt(mrb_state *mrb, const char *s, mrbc_context *c)
-{
- return mrb_load_nstring_cxt(mrb, s, strlen(s), c);
-}
-
-MRB_API mrb_value
-mrb_load_string(mrb_state *mrb, const char *s)
-{
- return mrb_load_string_cxt(mrb, s, NULL);
-}
-
-#ifdef ENABLE_STDIO
-
-static void
-dump_prefix(node *tree, int offset)
-{
- printf("%05d ", tree->lineno);
- while (offset--) {
- putc(' ', stdout);
- putc(' ', stdout);
- }
-}
-
-static void
-dump_recur(mrb_state *mrb, node *tree, int offset)
-{
- while (tree) {
- mrb_parser_dump(mrb, tree->car, offset);
- tree = tree->cdr;
- }
-}
-
-#endif
-
-void
-mrb_parser_dump(mrb_state *mrb, node *tree, int offset)
-{
-#ifdef ENABLE_STDIO
- int nodetype;
-
- if (!tree) return;
- again:
- dump_prefix(tree, offset);
- nodetype = (int)(intptr_t)tree->car;
- tree = tree->cdr;
- switch (nodetype) {
- case NODE_BEGIN:
- printf("NODE_BEGIN:\n");
- dump_recur(mrb, tree, offset+1);
- break;
-
- case NODE_RESCUE:
- printf("NODE_RESCUE:\n");
- if (tree->car) {
- dump_prefix(tree, offset+1);
- printf("body:\n");
- mrb_parser_dump(mrb, tree->car, offset+2);
- }
- tree = tree->cdr;
- if (tree->car) {
- node *n2 = tree->car;
-
- dump_prefix(n2, offset+1);
- printf("rescue:\n");
- while (n2) {
- node *n3 = n2->car;
- if (n3->car) {
- dump_prefix(n2, offset+2);
- printf("handle classes:\n");
- dump_recur(mrb, n3->car, offset+3);
- }
- if (n3->cdr->car) {
- dump_prefix(n3, offset+2);
- printf("exc_var:\n");
- mrb_parser_dump(mrb, n3->cdr->car, offset+3);
- }
- if (n3->cdr->cdr->car) {
- dump_prefix(n3, offset+2);
- printf("rescue body:\n");
- mrb_parser_dump(mrb, n3->cdr->cdr->car, offset+3);
- }
- n2 = n2->cdr;
- }
- }
- tree = tree->cdr;
- if (tree->car) {
- dump_prefix(tree, offset+1);
- printf("else:\n");
- mrb_parser_dump(mrb, tree->car, offset+2);
- }
- break;
-
- case NODE_ENSURE:
- printf("NODE_ENSURE:\n");
- dump_prefix(tree, offset+1);
- printf("body:\n");
- mrb_parser_dump(mrb, tree->car, offset+2);
- dump_prefix(tree, offset+1);
- printf("ensure:\n");
- mrb_parser_dump(mrb, tree->cdr->cdr, offset+2);
- break;
-
- case NODE_LAMBDA:
- printf("NODE_BLOCK:\n");
- goto block;
-
- case NODE_BLOCK:
- block:
- printf("NODE_BLOCK:\n");
- tree = tree->cdr;
- if (tree->car) {
- node *n = tree->car;
-
- if (n->car) {
- dump_prefix(n, offset+1);
- printf("mandatory args:\n");
- dump_recur(mrb, n->car, offset+2);
- }
- n = n->cdr;
- if (n->car) {
- dump_prefix(n, offset+1);
- printf("optional args:\n");
- {
- node *n2 = n->car;
-
- while (n2) {
- dump_prefix(n2, offset+2);
- printf("%s=", mrb_sym2name(mrb, sym(n2->car->car)));
- mrb_parser_dump(mrb, n2->car->cdr, 0);
- n2 = n2->cdr;
- }
- }
- }
- n = n->cdr;
- if (n->car) {
- dump_prefix(n, offset+1);
- printf("rest=*%s\n", mrb_sym2name(mrb, sym(n->car)));
- }
- n = n->cdr;
- if (n->car) {
- dump_prefix(n, offset+1);
- printf("post mandatory args:\n");
- dump_recur(mrb, n->car, offset+2);
- }
- n = n->cdr;
- if (n) {
- dump_prefix(n, offset+1);
- printf("blk=&%s\n", mrb_sym2name(mrb, sym(n)));
- }
- }
- dump_prefix(tree, offset+1);
- printf("body:\n");
- mrb_parser_dump(mrb, tree->cdr->car, offset+2);
- break;
-
- case NODE_IF:
- printf("NODE_IF:\n");
- dump_prefix(tree, offset+1);
- printf("cond:\n");
- mrb_parser_dump(mrb, tree->car, offset+2);
- dump_prefix(tree, offset+1);
- printf("then:\n");
- mrb_parser_dump(mrb, tree->cdr->car, offset+2);
- if (tree->cdr->cdr->car) {
- dump_prefix(tree, offset+1);
- printf("else:\n");
- mrb_parser_dump(mrb, tree->cdr->cdr->car, offset+2);
- }
- break;
-
- case NODE_AND:
- printf("NODE_AND:\n");
- mrb_parser_dump(mrb, tree->car, offset+1);
- mrb_parser_dump(mrb, tree->cdr, offset+1);
- break;
-
- case NODE_OR:
- printf("NODE_OR:\n");
- mrb_parser_dump(mrb, tree->car, offset+1);
- mrb_parser_dump(mrb, tree->cdr, offset+1);
- break;
-
- case NODE_CASE:
- printf("NODE_CASE:\n");
- if (tree->car) {
- mrb_parser_dump(mrb, tree->car, offset+1);
- }
- tree = tree->cdr;
- while (tree) {
- dump_prefix(tree, offset+1);
- printf("case:\n");
- dump_recur(mrb, tree->car->car, offset+2);
- dump_prefix(tree, offset+1);
- printf("body:\n");
- mrb_parser_dump(mrb, tree->car->cdr, offset+2);
- tree = tree->cdr;
- }
- break;
-
- case NODE_WHILE:
- printf("NODE_WHILE:\n");
- dump_prefix(tree, offset+1);
- printf("cond:\n");
- mrb_parser_dump(mrb, tree->car, offset+2);
- dump_prefix(tree, offset+1);
- printf("body:\n");
- mrb_parser_dump(mrb, tree->cdr, offset+2);
- break;
-
- case NODE_UNTIL:
- printf("NODE_UNTIL:\n");
- dump_prefix(tree, offset+1);
- printf("cond:\n");
- mrb_parser_dump(mrb, tree->car, offset+2);
- dump_prefix(tree, offset+1);
- printf("body:\n");
- mrb_parser_dump(mrb, tree->cdr, offset+2);
- break;
-
- case NODE_FOR:
- printf("NODE_FOR:\n");
- dump_prefix(tree, offset+1);
- printf("var:\n");
- {
- node *n2 = tree->car;
-
- if (n2->car) {
- dump_prefix(n2, offset+2);
- printf("pre:\n");
- dump_recur(mrb, n2->car, offset+3);
- }
- n2 = n2->cdr;
- if (n2) {
- if (n2->car) {
- dump_prefix(n2, offset+2);
- printf("rest:\n");
- mrb_parser_dump(mrb, n2->car, offset+3);
- }
- n2 = n2->cdr;
- if (n2) {
- if (n2->car) {
- dump_prefix(n2, offset+2);
- printf("post:\n");
- dump_recur(mrb, n2->car, offset+3);
- }
- }
- }
- }
- tree = tree->cdr;
- dump_prefix(tree, offset+1);
- printf("in:\n");
- mrb_parser_dump(mrb, tree->car, offset+2);
- tree = tree->cdr;
- dump_prefix(tree, offset+1);
- printf("do:\n");
- mrb_parser_dump(mrb, tree->car, offset+2);
- break;
-
- case NODE_SCOPE:
- printf("NODE_SCOPE:\n");
- {
- node *n2 = tree->car;
- mrb_bool first_lval = TRUE;
-
- if (n2 && (n2->car || n2->cdr)) {
- dump_prefix(n2, offset+1);
- printf("local variables:\n");
- dump_prefix(n2, offset+2);
- while (n2) {
- if (n2->car) {
- if (!first_lval) printf(", ");
- printf("%s", mrb_sym2name(mrb, sym(n2->car)));
- first_lval = FALSE;
- }
- n2 = n2->cdr;
- }
- printf("\n");
- }
- }
- tree = tree->cdr;
- offset++;
- goto again;
-
- case NODE_FCALL:
- case NODE_CALL:
- printf("NODE_CALL:\n");
- mrb_parser_dump(mrb, tree->car, offset+1);
- dump_prefix(tree, offset+1);
- printf("method='%s' (%d)\n",
- mrb_sym2name(mrb, sym(tree->cdr->car)),
- (int)(intptr_t)tree->cdr->car);
- tree = tree->cdr->cdr->car;
- if (tree) {
- dump_prefix(tree, offset+1);
- printf("args:\n");
- dump_recur(mrb, tree->car, offset+2);
- if (tree->cdr) {
- dump_prefix(tree, offset+1);
- printf("block:\n");
- mrb_parser_dump(mrb, tree->cdr, offset+2);
- }
- }
- break;
-
- case NODE_DOT2:
- printf("NODE_DOT2:\n");
- mrb_parser_dump(mrb, tree->car, offset+1);
- mrb_parser_dump(mrb, tree->cdr, offset+1);
- break;
-
- case NODE_DOT3:
- printf("NODE_DOT3:\n");
- mrb_parser_dump(mrb, tree->car, offset+1);
- mrb_parser_dump(mrb, tree->cdr, offset+1);
- break;
-
- case NODE_COLON2:
- printf("NODE_COLON2:\n");
- mrb_parser_dump(mrb, tree->car, offset+1);
- dump_prefix(tree, offset+1);
- printf("::%s\n", mrb_sym2name(mrb, sym(tree->cdr)));
- break;
-
- case NODE_COLON3:
- printf("NODE_COLON3:\n");
- dump_prefix(tree, offset+1);
- printf("::%s\n", mrb_sym2name(mrb, sym(tree)));
- break;
-
- case NODE_ARRAY:
- printf("NODE_ARRAY:\n");
- dump_recur(mrb, tree, offset+1);
- break;
-
- case NODE_HASH:
- printf("NODE_HASH:\n");
- while (tree) {
- dump_prefix(tree, offset+1);
- printf("key:\n");
- mrb_parser_dump(mrb, tree->car->car, offset+2);
- dump_prefix(tree, offset+1);
- printf("value:\n");
- mrb_parser_dump(mrb, tree->car->cdr, offset+2);
- tree = tree->cdr;
- }
- break;
-
- case NODE_SPLAT:
- printf("NODE_SPLAT:\n");
- mrb_parser_dump(mrb, tree, offset+1);
- break;
-
- case NODE_ASGN:
- printf("NODE_ASGN:\n");
- dump_prefix(tree, offset+1);
- printf("lhs:\n");
- mrb_parser_dump(mrb, tree->car, offset+2);
- dump_prefix(tree, offset+1);
- printf("rhs:\n");
- mrb_parser_dump(mrb, tree->cdr, offset+2);
- break;
-
- case NODE_MASGN:
- printf("NODE_MASGN:\n");
- dump_prefix(tree, offset+1);
- printf("mlhs:\n");
- {
- node *n2 = tree->car;
-
- if (n2->car) {
- dump_prefix(tree, offset+2);
- printf("pre:\n");
- dump_recur(mrb, n2->car, offset+3);
- }
- n2 = n2->cdr;
- if (n2) {
- if (n2->car) {
- dump_prefix(n2, offset+2);
- printf("rest:\n");
- if (n2->car == (node*)-1) {
- dump_prefix(n2, offset+2);
- printf("(empty)\n");
- }
- else {
- mrb_parser_dump(mrb, n2->car, offset+3);
- }
- }
- n2 = n2->cdr;
- if (n2) {
- if (n2->car) {
- dump_prefix(n2, offset+2);
- printf("post:\n");
- dump_recur(mrb, n2->car, offset+3);
- }
- }
- }
- }
- dump_prefix(tree, offset+1);
- printf("rhs:\n");
- mrb_parser_dump(mrb, tree->cdr, offset+2);
- break;
-
- case NODE_OP_ASGN:
- printf("NODE_OP_ASGN:\n");
- dump_prefix(tree, offset+1);
- printf("lhs:\n");
- mrb_parser_dump(mrb, tree->car, offset+2);
- tree = tree->cdr;
- dump_prefix(tree, offset+1);
- printf("op='%s' (%d)\n", mrb_sym2name(mrb, sym(tree->car)), (int)(intptr_t)tree->car);
- tree = tree->cdr;
- mrb_parser_dump(mrb, tree->car, offset+1);
- break;
-
- case NODE_SUPER:
- printf("NODE_SUPER:\n");
- if (tree) {
- dump_prefix(tree, offset+1);
- printf("args:\n");
- dump_recur(mrb, tree->car, offset+2);
- if (tree->cdr) {
- dump_prefix(tree, offset+1);
- printf("block:\n");
- mrb_parser_dump(mrb, tree->cdr, offset+2);
- }
- }
- break;
-
- case NODE_ZSUPER:
- printf("NODE_ZSUPER\n");
- break;
-
- case NODE_RETURN:
- printf("NODE_RETURN:\n");
- mrb_parser_dump(mrb, tree, offset+1);
- break;
-
- case NODE_YIELD:
- printf("NODE_YIELD:\n");
- dump_recur(mrb, tree, offset+1);
- break;
-
- case NODE_BREAK:
- printf("NODE_BREAK:\n");
- mrb_parser_dump(mrb, tree, offset+1);
- break;
-
- case NODE_NEXT:
- printf("NODE_NEXT:\n");
- mrb_parser_dump(mrb, tree, offset+1);
- break;
-
- case NODE_REDO:
- printf("NODE_REDO\n");
- break;
-
- case NODE_RETRY:
- printf("NODE_RETRY\n");
- break;
-
- case NODE_LVAR:
- printf("NODE_LVAR %s\n", mrb_sym2name(mrb, sym(tree)));
- break;
-
- case NODE_GVAR:
- printf("NODE_GVAR %s\n", mrb_sym2name(mrb, sym(tree)));
- break;
-
- case NODE_IVAR:
- printf("NODE_IVAR %s\n", mrb_sym2name(mrb, sym(tree)));
- break;
-
- case NODE_CVAR:
- printf("NODE_CVAR %s\n", mrb_sym2name(mrb, sym(tree)));
- break;
-
- case NODE_CONST:
- printf("NODE_CONST %s\n", mrb_sym2name(mrb, sym(tree)));
- break;
-
- case NODE_MATCH:
- printf("NODE_MATCH:\n");
- dump_prefix(tree, offset + 1);
- printf("lhs:\n");
- mrb_parser_dump(mrb, tree->car, offset + 2);
- dump_prefix(tree, offset + 1);
- printf("rhs:\n");
- mrb_parser_dump(mrb, tree->cdr, offset + 2);
- break;
-
- case NODE_BACK_REF:
- printf("NODE_BACK_REF: $%c\n", (int)(intptr_t)tree);
- break;
-
- case NODE_NTH_REF:
- printf("NODE_NTH_REF: $%d\n", (int)(intptr_t)tree);
- break;
-
- case NODE_ARG:
- printf("NODE_ARG %s\n", mrb_sym2name(mrb, sym(tree)));
- break;
-
- case NODE_BLOCK_ARG:
- printf("NODE_BLOCK_ARG:\n");
- mrb_parser_dump(mrb, tree, offset+1);
- break;
-
- case NODE_INT:
- printf("NODE_INT %s base %d\n", (char*)tree->car, (int)(intptr_t)tree->cdr->car);
- break;
-
- case NODE_FLOAT:
- printf("NODE_FLOAT %s\n", (char*)tree);
- break;
-
- case NODE_NEGATE:
- printf("NODE_NEGATE\n");
- mrb_parser_dump(mrb, tree, offset+1);
- break;
-
- case NODE_STR:
- printf("NODE_STR \"%s\" len %d\n", (char*)tree->car, (int)(intptr_t)tree->cdr);
- break;
-
- case NODE_DSTR:
- printf("NODE_DSTR\n");
- dump_recur(mrb, tree, offset+1);
- break;
-
- case NODE_XSTR:
- printf("NODE_XSTR \"%s\" len %d\n", (char*)tree->car, (int)(intptr_t)tree->cdr);
- break;
-
- case NODE_DXSTR:
- printf("NODE_DXSTR\n");
- dump_recur(mrb, tree, offset+1);
- break;
-
- case NODE_REGX:
- printf("NODE_REGX /%s/%s\n", (char*)tree->car, (char*)tree->cdr);
- break;
-
- case NODE_DREGX:
- printf("NODE_DREGX\n");
- dump_recur(mrb, tree->car, offset+1);
- dump_prefix(tree, offset);
- printf("tail: %s\n", (char*)tree->cdr->cdr->car);
- dump_prefix(tree, offset);
- printf("opt: %s\n", (char*)tree->cdr->cdr->cdr);
- break;
-
- case NODE_SYM:
- printf("NODE_SYM :%s\n", mrb_sym2name(mrb, sym(tree)));
- break;
-
- case NODE_SELF:
- printf("NODE_SELF\n");
- break;
-
- case NODE_NIL:
- printf("NODE_NIL\n");
- break;
-
- case NODE_TRUE:
- printf("NODE_TRUE\n");
- break;
-
- case NODE_FALSE:
- printf("NODE_FALSE\n");
- break;
-
- case NODE_ALIAS:
- printf("NODE_ALIAS %s %s:\n",
- mrb_sym2name(mrb, sym(tree->car)),
- mrb_sym2name(mrb, sym(tree->cdr)));
- break;
-
- case NODE_UNDEF:
- printf("NODE_UNDEF");
- {
- node *t = tree;
- while (t) {
- printf(" %s", mrb_sym2name(mrb, sym(t->car)));
- t = t->cdr;
- }
- }
- printf(":\n");
- break;
-
- case NODE_CLASS:
- printf("NODE_CLASS:\n");
- if (tree->car->car == (node*)0) {
- dump_prefix(tree, offset+1);
- printf(":%s\n", mrb_sym2name(mrb, sym(tree->car->cdr)));
- }
- else if (tree->car->car == (node*)1) {
- dump_prefix(tree, offset+1);
- printf("::%s\n", mrb_sym2name(mrb, sym(tree->car->cdr)));
- }
- else {
- mrb_parser_dump(mrb, tree->car->car, offset+1);
- dump_prefix(tree, offset+1);
- printf("::%s\n", mrb_sym2name(mrb, sym(tree->car->cdr)));
- }
- if (tree->cdr->car) {
- dump_prefix(tree, offset+1);
- printf("super:\n");
- mrb_parser_dump(mrb, tree->cdr->car, offset+2);
- }
- dump_prefix(tree, offset+1);
- printf("body:\n");
- mrb_parser_dump(mrb, tree->cdr->cdr->car->cdr, offset+2);
- break;
-
- case NODE_MODULE:
- printf("NODE_MODULE:\n");
- if (tree->car->car == (node*)0) {
- dump_prefix(tree, offset+1);
- printf(":%s\n", mrb_sym2name(mrb, sym(tree->car->cdr)));
- }
- else if (tree->car->car == (node*)1) {
- dump_prefix(tree, offset+1);
- printf("::%s\n", mrb_sym2name(mrb, sym(tree->car->cdr)));
- }
- else {
- mrb_parser_dump(mrb, tree->car->car, offset+1);
- dump_prefix(tree, offset+1);
- printf("::%s\n", mrb_sym2name(mrb, sym(tree->car->cdr)));
- }
- dump_prefix(tree, offset+1);
- printf("body:\n");
- mrb_parser_dump(mrb, tree->cdr->car->cdr, offset+2);
- break;
-
- case NODE_SCLASS:
- printf("NODE_SCLASS:\n");
- mrb_parser_dump(mrb, tree->car, offset+1);
- dump_prefix(tree, offset+1);
- printf("body:\n");
- mrb_parser_dump(mrb, tree->cdr->car->cdr, offset+2);
- break;
-
- case NODE_DEF:
- printf("NODE_DEF:\n");
- dump_prefix(tree, offset+1);
- printf("%s\n", mrb_sym2name(mrb, sym(tree->car)));
- tree = tree->cdr;
- {
- node *n2 = tree->car;
- mrb_bool first_lval = TRUE;
-
- if (n2 && (n2->car || n2->cdr)) {
- dump_prefix(n2, offset+1);
- printf("local variables:\n");
- dump_prefix(n2, offset+2);
- while (n2) {
- if (n2->car) {
- if (!first_lval) printf(", ");
- printf("%s", mrb_sym2name(mrb, sym(n2->car)));
- first_lval = FALSE;
- }
- n2 = n2->cdr;
- }
- printf("\n");
- }
- }
- tree = tree->cdr;
- if (tree->car) {
- node *n = tree->car;
-
- if (n->car) {
- dump_prefix(n, offset+1);
- printf("mandatory args:\n");
- dump_recur(mrb, n->car, offset+2);
- }
- n = n->cdr;
- if (n->car) {
- dump_prefix(n, offset+1);
- printf("optional args:\n");
- {
- node *n2 = n->car;
-
- while (n2) {
- dump_prefix(n2, offset+2);
- printf("%s=", mrb_sym2name(mrb, sym(n2->car->car)));
- mrb_parser_dump(mrb, n2->car->cdr, 0);
- n2 = n2->cdr;
- }
- }
- }
- n = n->cdr;
- if (n->car) {
- dump_prefix(n, offset+1);
- printf("rest=*%s\n", mrb_sym2name(mrb, sym(n->car)));
- }
- n = n->cdr;
- if (n->car) {
- dump_prefix(n, offset+1);
- printf("post mandatory args:\n");
- dump_recur(mrb, n->car, offset+2);
- }
- n = n->cdr;
- if (n) {
- dump_prefix(n, offset+1);
- printf("blk=&%s\n", mrb_sym2name(mrb, sym(n)));
- }
- }
- mrb_parser_dump(mrb, tree->cdr->car, offset+1);
- break;
-
- case NODE_SDEF:
- printf("NODE_SDEF:\n");
- mrb_parser_dump(mrb, tree->car, offset+1);
- tree = tree->cdr;
- dump_prefix(tree, offset+1);
- printf(":%s\n", mrb_sym2name(mrb, sym(tree->car)));
- tree = tree->cdr->cdr;
- if (tree->car) {
- node *n = tree->car;
-
- if (n->car) {
- dump_prefix(n, offset+1);
- printf("mandatory args:\n");
- dump_recur(mrb, n->car, offset+2);
- }
- n = n->cdr;
- if (n->car) {
- dump_prefix(n, offset+1);
- printf("optional args:\n");
- {
- node *n2 = n->car;
-
- while (n2) {
- dump_prefix(n2, offset+2);
- printf("%s=", mrb_sym2name(mrb, sym(n2->car->car)));
- mrb_parser_dump(mrb, n2->car->cdr, 0);
- n2 = n2->cdr;
- }
- }
- }
- n = n->cdr;
- if (n->car) {
- dump_prefix(n, offset+1);
- printf("rest=*%s\n", mrb_sym2name(mrb, sym(n->car)));
- }
- n = n->cdr;
- if (n->car) {
- dump_prefix(n, offset+1);
- printf("post mandatory args:\n");
- dump_recur(mrb, n->car, offset+2);
- }
- n = n->cdr;
- if (n) {
- dump_prefix(n, offset+1);
- printf("blk=&%s\n", mrb_sym2name(mrb, sym(n)));
- }
- }
- tree = tree->cdr;
- mrb_parser_dump(mrb, tree->car, offset+1);
- break;
-
- case NODE_POSTEXE:
- printf("NODE_POSTEXE:\n");
- mrb_parser_dump(mrb, tree, offset+1);
- break;
-
- case NODE_HEREDOC:
- printf("NODE_HEREDOC:\n");
- mrb_parser_dump(mrb, ((parser_heredoc_info*)tree)->doc, offset+1);
- break;
-
- default:
- printf("node type: %d (0x%x)\n", nodetype, (unsigned)nodetype);
- break;
- }
-#endif
-}
diff --git a/src/pool.c b/src/pool.c
index 285cca6c3..ab30be1d8 100644
--- a/src/pool.c
+++ b/src/pool.c
@@ -4,23 +4,32 @@
** See Copyright Notice in mruby.h
*/
-#include <stddef.h>
-#include <stdint.h>
#include <string.h>
-#include "mruby.h"
+#include <mruby.h>
/* configuration section */
/* allocated memory address should be multiple of POOL_ALIGNMENT */
/* or undef it if alignment does not matter */
#ifndef POOL_ALIGNMENT
+#if INTPTR_MAX == INT64_MAX
+#define POOL_ALIGNMENT 8
+#else
#define POOL_ALIGNMENT 4
#endif
+#endif
/* page size of memory pool */
#ifndef POOL_PAGE_SIZE
#define POOL_PAGE_SIZE 16000
#endif
/* end of configuration section */
+/* Disable MSVC warning "C4200: nonstandard extension used: zero-sized array
+ * in struct/union" when in C++ mode */
+#ifdef _MSC_VER
+#pragma warning(push)
+#pragma warning(disable : 4200)
+#endif
+
struct mrb_pool_page {
struct mrb_pool_page *next;
size_t offset;
@@ -29,6 +38,10 @@ struct mrb_pool_page {
char page[];
};
+#ifdef _MSC_VER
+#pragma warning(pop)
+#endif
+
struct mrb_pool {
mrb_state *mrb;
struct mrb_pool_page *pages;
diff --git a/src/print.c b/src/print.c
index 6c561100f..4af871b43 100644
--- a/src/print.c
+++ b/src/print.c
@@ -4,66 +4,92 @@
** See Copyright Notice in mruby.h
*/
-#include "mruby.h"
-#include "mruby/string.h"
-#include "mruby/variable.h"
+#include <mruby.h>
+#include <mruby/string.h>
+#include <mruby/variable.h>
+#include <mruby/error.h>
+#include <mruby/presym.h>
+#include <string.h>
+#ifndef MRB_NO_STDIO
static void
-printstr(mrb_state *mrb, mrb_value obj)
+printcstr(const char *str, size_t len, FILE *stream)
{
-#ifdef ENABLE_STDIO
- char *s;
- int len;
+ if (str) {
+ fwrite(str, len, 1, stream);
+ putc('\n', stream);
+ }
+}
+static void
+printstr(mrb_value obj, FILE *stream)
+{
if (mrb_string_p(obj)) {
- s = RSTRING_PTR(obj);
- len = RSTRING_LEN(obj);
- fwrite(s, len, 1, stdout);
+ printcstr(RSTRING_PTR(obj), RSTRING_LEN(obj), stream);
}
-#endif
+}
+
+void
+mrb_core_init_printabort(void)
+{
+ static const char *str = "Failed mruby core initialization";
+ printcstr(str, strlen(str), stdout);
}
MRB_API void
mrb_p(mrb_state *mrb, mrb_value obj)
{
-#ifdef ENABLE_STDIO
- obj = mrb_funcall(mrb, obj, "inspect", 0);
- printstr(mrb, obj);
- putc('\n', stdout);
-#endif
+ if (mrb_type(obj) == MRB_TT_EXCEPTION && mrb_obj_ptr(obj) == mrb->nomem_err) {
+ static const char *str = "Out of memory";
+ printcstr(str, strlen(str), stdout);
+ }
+ else {
+ printstr(mrb_inspect(mrb, obj), stdout);
+ }
}
+
MRB_API void
mrb_print_error(mrb_state *mrb)
{
-#ifdef ENABLE_STDIO
- mrb_value s;
-
mrb_print_backtrace(mrb);
- s = mrb_funcall(mrb, mrb_obj_value(mrb->exc), "inspect", 0);
- if (mrb_string_p(s)) {
- fwrite(RSTRING_PTR(s), RSTRING_LEN(s), 1, stderr);
- putc('\n', stderr);
- }
-#endif
}
MRB_API void
mrb_show_version(mrb_state *mrb)
{
- mrb_value msg;
-
- msg = mrb_const_get(mrb, mrb_obj_value(mrb->object_class), mrb_intern_lit(mrb, "MRUBY_DESCRIPTION"));
- printstr(mrb, msg);
- printstr(mrb, mrb_str_new_lit(mrb, "\n"));
+ printstr(mrb_const_get(mrb, mrb_obj_value(mrb->object_class), MRB_SYM(MRUBY_DESCRIPTION)), stdout);
}
MRB_API void
mrb_show_copyright(mrb_state *mrb)
{
- mrb_value msg;
+ printstr(mrb_const_get(mrb, mrb_obj_value(mrb->object_class), MRB_SYM(MRUBY_COPYRIGHT)), stdout);
+}
- msg = mrb_const_get(mrb, mrb_obj_value(mrb->object_class), mrb_intern_lit(mrb, "MRUBY_COPYRIGHT"));
- printstr(mrb, msg);
- printstr(mrb, mrb_str_new_lit(mrb, "\n"));
+#else
+void
+mrb_core_init_printabort(void)
+{
}
+
+MRB_API void
+mrb_p(mrb_state *mrb, mrb_value obj)
+{
+}
+
+MRB_API void
+mrb_print_error(mrb_state *mrb)
+{
+}
+
+MRB_API void
+mrb_show_version(mrb_state *mrb)
+{
+}
+
+MRB_API void
+mrb_show_copyright(mrb_state *mrb)
+{
+}
+#endif
diff --git a/src/proc.c b/src/proc.c
index e1d1d138d..1d5a4aa76 100644
--- a/src/proc.c
+++ b/src/proc.c
@@ -4,97 +4,160 @@
** See Copyright Notice in mruby.h
*/
-#include "mruby.h"
-#include "mruby/class.h"
-#include "mruby/proc.h"
-#include "mruby/opcode.h"
+#include <mruby.h>
+#include <mruby/class.h>
+#include <mruby/proc.h>
+#include <mruby/opcode.h>
+#include <mruby/data.h>
+#include <mruby/presym.h>
+#include <mruby/array.h>
+#include <mruby/hash.h>
+
+static const mrb_code call_iseq[] = {
+ OP_CALL,
+};
+
+static const mrb_irep call_irep = {
+ 0, /* nlocals */
+ 2, /* nregs */
+ 0, /* clen */
+ MRB_ISEQ_NO_FREE | MRB_IREP_NO_FREE, /* flags */
+ call_iseq, /* iseq */
+ NULL, /* pool */
+ NULL, /* syms */
+ NULL, /* reps */
+ NULL, /* lv */
+ NULL, /* debug_info */
+ 1, /* ilen */
+ 0, /* plen */
+ 0, /* slen */
+ 1, /* rlen */
+ 0, /* refcnt */
+};
-static mrb_code call_iseq[] = {
- MKOP_A(OP_CALL, 0),
+static const struct RProc call_proc = {
+ NULL, NULL, MRB_TT_PROC, MRB_GC_RED, MRB_FL_OBJ_IS_FROZEN | MRB_PROC_SCOPE | MRB_PROC_STRICT,
+ { &call_irep }, NULL, { NULL }
};
-struct RProc *
-mrb_proc_new(mrb_state *mrb, mrb_irep *irep)
+struct RProc*
+mrb_proc_new(mrb_state *mrb, const mrb_irep *irep)
{
struct RProc *p;
mrb_callinfo *ci = mrb->c->ci;
- p = (struct RProc*)mrb_obj_alloc(mrb, MRB_TT_PROC, mrb->proc_class);
- p->target_class = 0;
+ p = MRB_OBJ_ALLOC(mrb, MRB_TT_PROC, mrb->proc_class);
if (ci) {
- if (ci->proc)
- p->target_class = ci->proc->target_class;
- if (!p->target_class)
- p->target_class = ci->target_class;
+ struct RClass *tc = NULL;
+
+ if (ci->proc) {
+ if (ci->proc->color != MRB_GC_RED) {
+ tc = MRB_PROC_TARGET_CLASS(ci->proc);
+ }
+ else {
+ tc = mrb_vm_ci_target_class(ci);
+ if (tc && tc->tt == MRB_TT_ICLASS) {
+ tc = tc->c;
+ }
+ }
+ }
+ if (tc == NULL) {
+ tc = mrb_vm_ci_target_class(ci);
+ }
+ p->upper = ci->proc;
+ p->e.target_class = tc;
}
p->body.irep = irep;
- p->env = 0;
- mrb_irep_incref(mrb, irep);
+ if (irep) {
+ mrb_irep_incref(mrb, (mrb_irep*)irep);
+ }
return p;
}
-static struct REnv*
-env_new(mrb_state *mrb, int nlocals)
+struct REnv*
+mrb_env_new(mrb_state *mrb, struct mrb_context *c, mrb_callinfo *ci, int nstacks, mrb_value *stack, struct RClass *tc)
{
struct REnv *e;
-
- e = (struct REnv*)mrb_obj_alloc(mrb, MRB_TT_ENV, (struct RClass*)mrb->c->ci->proc->env);
- MRB_SET_ENV_STACK_LEN(e, nlocals);
- e->mid = mrb->c->ci->mid;
- e->cioff = mrb->c->ci - mrb->c->cibase;
- e->stack = mrb->c->stack;
+ mrb_int bidx;
+
+ e = MRB_OBJ_ALLOC(mrb, MRB_TT_ENV, tc);
+ MRB_ENV_SET_LEN(e, nstacks);
+ bidx = ci->argc;
+ if (bidx < 0) bidx = 2;
+ else bidx += 1;
+ MRB_ENV_SET_BIDX(e, bidx);
+ e->mid = ci->mid;
+ e->stack = stack;
+ e->cxt = c;
return e;
}
static void
-closure_setup(mrb_state *mrb, struct RProc *p, int nlocals)
+closure_setup(mrb_state *mrb, struct RProc *p)
{
- struct REnv *e;
+ mrb_callinfo *ci = mrb->c->ci;
+ const struct RProc *up = p->upper;
+ struct REnv *e = NULL;
- if (!mrb->c->ci->env) {
- e = env_new(mrb, nlocals);
- mrb->c->ci->env = e;
+ if (ci && (e = mrb_vm_ci_env(ci)) != NULL) {
+ /* do nothing, because e is assigned already */
}
- else {
- e = mrb->c->ci->env;
+ else if (up) {
+ struct RClass *tc = MRB_PROC_TARGET_CLASS(p);
+
+ e = mrb_env_new(mrb, mrb->c, ci, up->body.irep->nlocals, ci->stack, tc);
+ ci->u.env = e;
+ if (MRB_PROC_ENV_P(up) && MRB_PROC_ENV(up)->cxt == NULL) {
+ e->mid = MRB_PROC_ENV(up)->mid;
+ }
+ }
+ if (e) {
+ p->e.env = e;
+ p->flags |= MRB_PROC_ENVSET;
+ mrb_field_write_barrier(mrb, (struct RBasic*)p, (struct RBasic*)e);
}
- p->env = e;
}
-struct RProc *
-mrb_closure_new(mrb_state *mrb, mrb_irep *irep)
+struct RProc*
+mrb_closure_new(mrb_state *mrb, const mrb_irep *irep)
{
struct RProc *p = mrb_proc_new(mrb, irep);
- closure_setup(mrb, p, mrb->c->ci->proc->body.irep->nlocals);
+ closure_setup(mrb, p);
return p;
}
-MRB_API struct RProc *
+MRB_API struct RProc*
mrb_proc_new_cfunc(mrb_state *mrb, mrb_func_t func)
{
struct RProc *p;
- p = (struct RProc*)mrb_obj_alloc(mrb, MRB_TT_PROC, mrb->proc_class);
+ p = MRB_OBJ_ALLOC(mrb, MRB_TT_PROC, mrb->proc_class);
p->body.func = func;
- p->flags |= MRB_PROC_CFUNC;
- p->env = 0;
+ p->flags |= MRB_PROC_CFUNC_FL;
+ p->upper = 0;
+ p->e.target_class = 0;
return p;
}
-MRB_API struct RProc *
+MRB_API struct RProc*
mrb_proc_new_cfunc_with_env(mrb_state *mrb, mrb_func_t func, mrb_int argc, const mrb_value *argv)
{
struct RProc *p = mrb_proc_new_cfunc(mrb, func);
struct REnv *e;
int i;
- p->env = e = env_new(mrb, argc);
- MRB_ENV_UNSHARE_STACK(e);
+ p->e.env = e = mrb_env_new(mrb, mrb->c, mrb->c->ci, 0, NULL, NULL);
+ p->flags |= MRB_PROC_ENVSET;
+ mrb_field_write_barrier(mrb, (struct RBasic*)p, (struct RBasic*)e);
+ MRB_ENV_CLOSE(e);
+
e->stack = (mrb_value*)mrb_malloc(mrb, sizeof(mrb_value) * argc);
+ MRB_ENV_SET_LEN(e, argc);
+
if (argv) {
for (i = 0; i < argc; ++i) {
e->stack[i] = argv[i];
@@ -108,7 +171,7 @@ mrb_proc_new_cfunc_with_env(mrb_state *mrb, mrb_func_t func, mrb_int argc, const
return p;
}
-MRB_API struct RProc *
+MRB_API struct RProc*
mrb_closure_new_cfunc(mrb_state *mrb, mrb_func_t func, int nlocals)
{
return mrb_proc_new_cfunc_with_env(mrb, func, nlocals, NULL);
@@ -117,108 +180,78 @@ mrb_closure_new_cfunc(mrb_state *mrb, mrb_func_t func, int nlocals)
MRB_API mrb_value
mrb_proc_cfunc_env_get(mrb_state *mrb, mrb_int idx)
{
- struct RProc *p = mrb->c->ci->proc;
- struct REnv *e = p->env;
+ const struct RProc *p = mrb->c->ci->proc;
+ struct REnv *e;
- if (!MRB_PROC_CFUNC_P(p)) {
- mrb_raise(mrb, E_TYPE_ERROR, "Can't get cfunc env from non-cfunc proc.");
+ if (!p || !MRB_PROC_CFUNC_P(p)) {
+ mrb_raise(mrb, E_TYPE_ERROR, "Can't get cfunc env from non-cfunc proc");
}
+ e = MRB_PROC_ENV(p);
if (!e) {
- mrb_raise(mrb, E_TYPE_ERROR, "Can't get cfunc env from cfunc Proc without REnv.");
+ mrb_raise(mrb, E_TYPE_ERROR, "Can't get cfunc env from cfunc Proc without REnv");
}
- if (idx < 0 || MRB_ENV_STACK_LEN(e) <= idx) {
- mrb_raisef(mrb, E_INDEX_ERROR, "Env index out of range: %S (expected: 0 <= index < %S)",
- mrb_fixnum_value(idx), mrb_fixnum_value(MRB_ENV_STACK_LEN(e)));
+ if (idx < 0 || MRB_ENV_LEN(e) <= idx) {
+ mrb_raisef(mrb, E_INDEX_ERROR, "Env index out of range: %i (expected: 0 <= index < %i)",
+ idx, MRB_ENV_LEN(e));
}
return e->stack[idx];
}
-MRB_API void
+void
mrb_proc_copy(struct RProc *a, struct RProc *b)
{
+ if (a->body.irep) {
+ /* already initialized proc */
+ return;
+ }
a->flags = b->flags;
a->body = b->body;
- if (!MRB_PROC_CFUNC_P(a)) {
- a->body.irep->refcnt++;
+ if (!MRB_PROC_CFUNC_P(a) && a->body.irep) {
+ mrb_irep_incref(NULL, (mrb_irep*)a->body.irep);
}
- a->target_class = b->target_class;
- a->env = b->env;
+ a->upper = b->upper;
+ a->e.env = b->e.env;
+ /* a->e.target_class = a->e.target_class; */
}
static mrb_value
-mrb_proc_initialize(mrb_state *mrb, mrb_value self)
+mrb_proc_s_new(mrb_state *mrb, mrb_value proc_class)
{
mrb_value blk;
+ mrb_value proc;
+ struct RProc *p;
- mrb_get_args(mrb, "&", &blk);
- if (mrb_nil_p(blk)) {
- /* Calling Proc.new without a block is not implemented yet */
- mrb_raise(mrb, E_ARGUMENT_ERROR, "tried to create Proc object without a block");
- }
- else {
- mrb_proc_copy(mrb_proc_ptr(self), mrb_proc_ptr(blk));
+ /* Calling Proc.new without a block is not implemented yet */
+ mrb_get_args(mrb, "&!", &blk);
+ p = MRB_OBJ_ALLOC(mrb, MRB_TT_PROC, mrb_class_ptr(proc_class));
+ mrb_proc_copy(p, mrb_proc_ptr(blk));
+ proc = mrb_obj_value(p);
+ mrb_funcall_with_block(mrb, proc, MRB_SYM(initialize), 0, NULL, proc);
+ if (!MRB_PROC_STRICT_P(p) &&
+ mrb->c->ci > mrb->c->cibase && MRB_PROC_ENV(p) == mrb->c->ci[-1].u.env) {
+ p->flags |= MRB_PROC_ORPHAN;
}
- return self;
+ return proc;
}
static mrb_value
mrb_proc_init_copy(mrb_state *mrb, mrb_value self)
{
- mrb_value proc;
+ mrb_value proc = mrb_get_arg1(mrb);
- mrb_get_args(mrb, "o", &proc);
- if (mrb_type(proc) != MRB_TT_PROC) {
+ if (!mrb_proc_p(proc)) {
mrb_raise(mrb, E_ARGUMENT_ERROR, "not a proc");
}
mrb_proc_copy(mrb_proc_ptr(self), mrb_proc_ptr(proc));
return self;
}
-int
-mrb_proc_cfunc_p(struct RProc *p)
-{
- return MRB_PROC_CFUNC_P(p);
-}
-
-mrb_value
-mrb_proc_call_cfunc(mrb_state *mrb, struct RProc *p, mrb_value self)
-{
- return (p->body.func)(mrb, self);
-}
-
-mrb_code*
-mrb_proc_iseq(mrb_state *mrb, struct RProc *p)
-{
- return p->body.irep->iseq;
-}
-
/* 15.2.17.4.2 */
static mrb_value
-mrb_proc_arity(mrb_state *mrb, mrb_value self)
+proc_arity(mrb_state *mrb, mrb_value self)
{
- struct RProc *p = mrb_proc_ptr(self);
- mrb_code *iseq = mrb_proc_iseq(mrb, p);
- mrb_aspec aspec;
- int ma, ra, pa, arity;
-
- if (MRB_PROC_CFUNC_P(p)) {
- /* TODO cfunc aspec not implemented yet */
- return mrb_fixnum_value(-1);
- }
-
- /* arity is depend on OP_ENTER */
- if (GET_OPCODE(*iseq) != OP_ENTER) {
- return mrb_fixnum_value(0);
- }
-
- aspec = GETARG_Ax(*iseq);
- ma = MRB_ASPEC_REQ(aspec);
- ra = MRB_ASPEC_REST(aspec);
- pa = MRB_ASPEC_POST(aspec);
- arity = ra ? -(ma + pa + 1) : ma + pa;
-
- return mrb_fixnum_value(arity);
+ return mrb_int_value(mrb, mrb_proc_arity(mrb_proc_ptr(self)));
}
/* 15.3.1.2.6 */
@@ -240,9 +273,12 @@ proc_lambda(mrb_state *mrb, mrb_value self)
if (mrb_nil_p(blk)) {
mrb_raise(mrb, E_ARGUMENT_ERROR, "tried to create Proc object without a block");
}
+ if (!mrb_proc_p(blk)) {
+ mrb_raise(mrb, E_ARGUMENT_ERROR, "not a proc");
+ }
p = mrb_proc_ptr(blk);
if (!MRB_PROC_STRICT_P(p)) {
- struct RProc *p2 = (struct RProc*)mrb_obj_alloc(mrb, MRB_TT_PROC, p->c);
+ struct RProc *p2 = MRB_OBJ_ALLOC(mrb, MRB_TT_PROC, p->c);
mrb_proc_copy(p2, p);
p2->flags |= MRB_PROC_STRICT;
return mrb_obj_value(p2);
@@ -250,26 +286,163 @@ proc_lambda(mrb_state *mrb, mrb_value self)
return blk;
}
+mrb_int
+mrb_proc_arity(const struct RProc *p)
+{
+ const mrb_irep *irep;
+ const mrb_code *pc;
+ mrb_aspec aspec;
+ int ma, op, ra, pa, arity;
+
+ if (MRB_PROC_CFUNC_P(p)) {
+ /* TODO cfunc aspec not implemented yet */
+ return -1;
+ }
+
+ irep = p->body.irep;
+ if (!irep) {
+ return 0;
+ }
+
+ pc = irep->iseq;
+ /* arity is depend on OP_ENTER */
+ if (*pc != OP_ENTER) {
+ return 0;
+ }
+
+ aspec = PEEK_W(pc+1);
+ ma = MRB_ASPEC_REQ(aspec);
+ op = MRB_ASPEC_OPT(aspec);
+ ra = MRB_ASPEC_REST(aspec);
+ pa = MRB_ASPEC_POST(aspec);
+ arity = ra || (MRB_PROC_STRICT_P(p) && op) ? -(ma + pa + 1) : ma + pa;
+
+ return arity;
+}
+
+mrb_value
+mrb_proc_local_variables(mrb_state *mrb, const struct RProc *proc)
+{
+ const mrb_irep *irep;
+ mrb_value vars;
+ size_t i;
+
+ if (proc == NULL || MRB_PROC_CFUNC_P(proc)) {
+ return mrb_ary_new(mrb);
+ }
+ vars = mrb_hash_new(mrb);
+ while (proc) {
+ if (MRB_PROC_CFUNC_P(proc)) break;
+ irep = proc->body.irep;
+ if (irep->lv) {
+ for (i = 0; i + 1 < irep->nlocals; ++i) {
+ if (irep->lv[i]) {
+ mrb_sym sym = irep->lv[i];
+ const char *name = mrb_sym_name(mrb, sym);
+ switch (name[0]) {
+ case '*': case '&':
+ break;
+ default:
+ mrb_hash_set(mrb, vars, mrb_symbol_value(sym), mrb_true_value());
+ break;
+ }
+ }
+ }
+ }
+ if (MRB_PROC_SCOPE_P(proc)) break;
+ proc = proc->upper;
+ }
+
+ return mrb_hash_keys(mrb, vars);
+}
+
+const struct RProc *
+mrb_proc_get_caller(mrb_state *mrb, struct REnv **envp)
+{
+ struct mrb_context *c = mrb->c;
+ mrb_callinfo *ci = (c->ci > c->cibase) ? c->ci - 1 : c->cibase;
+ const struct RProc *proc = ci->proc;
+
+ if (!proc || MRB_PROC_CFUNC_P(proc)) {
+ if (envp) *envp = NULL;
+ }
+ else {
+ struct RClass *tc = MRB_PROC_TARGET_CLASS(proc);
+ struct REnv *e = mrb_vm_ci_env(ci);
+
+ if (e == NULL) {
+ int nstacks = proc->body.irep->nlocals;
+ e = mrb_env_new(mrb, c, ci, nstacks, ci->stack, tc);
+ ci->u.env = e;
+ }
+ else if (tc) {
+ e->c = tc;
+ mrb_field_write_barrier(mrb, (struct RBasic*)e, (struct RBasic*)tc);
+ }
+ if (envp) *envp = e;
+ }
+
+ return proc;
+}
+
+#define IREP_LVAR_MERGE_DEFAULT 50
+#define IREP_LVAR_MERGE_MINIMUM 8
+#define IREP_LVAR_MERGE_MAXIMUM 240
+
+#ifdef MRB_IREP_LVAR_MERGE_LIMIT
+# define IREP_LVAR_MERGE_LIMIT \
+ ((MRB_IREP_LVAR_MERGE_LIMIT) < IREP_LVAR_MERGE_MINIMUM ? IREP_LVAR_MERGE_MINIMUM : \
+ (MRB_IREP_LVAR_MERGE_LIMIT) > IREP_LVAR_MERGE_MAXIMUM ? IREP_LVAR_MERGE_MAXIMUM : \
+ (MRB_IREP_LVAR_MERGE_LIMIT))
+#else
+# define IREP_LVAR_MERGE_LIMIT IREP_LVAR_MERGE_DEFAULT
+#endif
+
void
-mrb_init_proc(mrb_state *mrb)
+mrb_proc_merge_lvar(mrb_state *mrb, mrb_irep *irep, struct REnv *env, int num, const mrb_sym *lv, const mrb_value *stack)
{
- struct RProc *m;
- mrb_irep *call_irep = (mrb_irep *)mrb_malloc(mrb, sizeof(mrb_irep));
- static const mrb_irep mrb_irep_zero = { 0 };
+ mrb_assert(!(irep->flags & MRB_IREP_NO_FREE));
+
+ if ((irep->nlocals + num) > IREP_LVAR_MERGE_LIMIT) {
+ mrb_raise(mrb, E_RUNTIME_ERROR, "too many local variables for binding (mruby limitation)");
+ }
+
+ if (!lv) {
+ mrb_raise(mrb, E_RUNTIME_ERROR, "unavailable local variable names");
+ }
+
+ irep->lv = (mrb_sym*)mrb_realloc(mrb, (mrb_sym*)irep->lv, sizeof(mrb_sym) * (irep->nlocals + num));
+ env->stack = (mrb_value*)mrb_realloc(mrb, env->stack, sizeof(mrb_value) * (irep->nlocals + 1 /* self */ + num));
- *call_irep = mrb_irep_zero;
- call_irep->flags = MRB_ISEQ_NO_FREE;
- call_irep->iseq = call_iseq;
- call_irep->ilen = 1;
+ mrb_sym *destlv = (mrb_sym*)irep->lv + irep->nlocals - 1 /* self */;
+ mrb_value *destst = env->stack + irep->nlocals;
+ memmove(destlv, lv, sizeof(mrb_sym) * num);
+ if (stack) {
+ memmove(destst, stack, sizeof(mrb_value) * num);
+ }
+ else {
+ for (int i = num; i > 0; i--, destst++) {
+ *destst = mrb_nil_value();
+ }
+ }
+ irep->nlocals += num;
+ irep->nregs = irep->nlocals;
+ MRB_ENV_SET_LEN(env, irep->nlocals);
+}
+
+void
+mrb_init_proc(mrb_state *mrb)
+{
+ mrb_method_t m;
- mrb_define_method(mrb, mrb->proc_class, "initialize", mrb_proc_initialize, MRB_ARGS_NONE());
+ mrb_define_class_method(mrb, mrb->proc_class, "new", mrb_proc_s_new, MRB_ARGS_NONE()|MRB_ARGS_BLOCK());
mrb_define_method(mrb, mrb->proc_class, "initialize_copy", mrb_proc_init_copy, MRB_ARGS_REQ(1));
- mrb_define_method(mrb, mrb->proc_class, "arity", mrb_proc_arity, MRB_ARGS_NONE());
+ mrb_define_method(mrb, mrb->proc_class, "arity", proc_arity, MRB_ARGS_NONE());
- m = mrb_proc_new(mrb, call_irep);
- mrb_define_method_raw(mrb, mrb->proc_class, mrb_intern_lit(mrb, "call"), m);
- mrb_define_method_raw(mrb, mrb->proc_class, mrb_intern_lit(mrb, "[]"), m);
+ MRB_METHOD_FROM_PROC(m, &call_proc);
+ mrb_define_method_raw(mrb, mrb->proc_class, MRB_SYM(call), m);
+ mrb_define_method_raw(mrb, mrb->proc_class, MRB_OPSYM(aref), m);
- mrb_define_class_method(mrb, mrb->kernel_module, "lambda", proc_lambda, MRB_ARGS_NONE()); /* 15.3.1.2.6 */
- mrb_define_method(mrb, mrb->kernel_module, "lambda", proc_lambda, MRB_ARGS_NONE()); /* 15.3.1.3.27 */
+ mrb_define_class_method(mrb, mrb->kernel_module, "lambda", proc_lambda, MRB_ARGS_NONE()|MRB_ARGS_BLOCK()); /* 15.3.1.2.6 */
+ mrb_define_method(mrb, mrb->kernel_module, "lambda", proc_lambda, MRB_ARGS_NONE()|MRB_ARGS_BLOCK()); /* 15.3.1.3.27 */
}
diff --git a/src/range.c b/src/range.c
index b427dc1b7..ec764e39b 100644
--- a/src/range.c
+++ b/src/range.c
@@ -4,47 +4,107 @@
** See Copyright Notice in mruby.h
*/
-#include "mruby.h"
-#include "mruby/class.h"
-#include "mruby/range.h"
-#include "mruby/string.h"
-#include "mruby/array.h"
+#include <mruby.h>
+#include <mruby/class.h>
+#include <mruby/range.h>
+#include <mruby/string.h>
+#include <mruby/array.h>
+#include <mruby/presym.h>
-#define RANGE_CLASS (mrb_class_get(mrb, "Range"))
+#define RANGE_INITIALIZED_FLAG 1
+#define RANGE_INITIALIZED(p) ((p)->flags |= RANGE_INITIALIZED_FLAG)
+#define RANGE_INITIALIZED_P(p) ((p)->flags & RANGE_INITIALIZED_FLAG)
static void
-range_check(mrb_state *mrb, mrb_value a, mrb_value b)
+r_check(mrb_state *mrb, mrb_value a, mrb_value b)
{
- mrb_value ans;
enum mrb_vtype ta;
enum mrb_vtype tb;
+ mrb_int n;
ta = mrb_type(a);
tb = mrb_type(b);
- if ((ta == MRB_TT_FIXNUM || ta == MRB_TT_FLOAT) &&
- (tb == MRB_TT_FIXNUM || tb == MRB_TT_FLOAT)) {
+#ifdef MRB_NO_FLOAT
+ if (ta == MRB_TT_INTEGER && tb == MRB_TT_INTEGER ) return;
+#else
+ if ((ta == MRB_TT_INTEGER || ta == MRB_TT_FLOAT) &&
+ (tb == MRB_TT_INTEGER || tb == MRB_TT_FLOAT)) {
return;
}
+#endif
- ans = mrb_funcall(mrb, a, "<=>", 1, b);
- if (mrb_nil_p(ans)) {
- /* can not be compared */
+ if (mrb_nil_p(a) || mrb_nil_p(b)) return;
+
+ n = mrb_cmp(mrb, a, b);
+ if (n == -2) { /* can not be compared */
mrb_raise(mrb, E_ARGUMENT_ERROR, "bad value for range");
}
}
-MRB_API mrb_value
-mrb_range_new(mrb_state *mrb, mrb_value beg, mrb_value end, mrb_bool excl)
+static mrb_bool
+r_le(mrb_state *mrb, mrb_value a, mrb_value b)
{
- struct RRange *r;
+ mrb_int n = mrb_cmp(mrb, a, b);
- range_check(mrb, beg, end);
- r = (struct RRange*)mrb_obj_alloc(mrb, MRB_TT_RANGE, RANGE_CLASS);
+ if (n == 0 || n == -1) return TRUE;
+ return FALSE;
+}
+
+static mrb_bool
+r_gt(mrb_state *mrb, mrb_value a, mrb_value b)
+{
+ return mrb_cmp(mrb, a, b) == 1;
+}
+
+static mrb_bool
+r_ge(mrb_state *mrb, mrb_value a, mrb_value b)
+{
+ mrb_int n = mrb_cmp(mrb, a, b);
+
+ if (n == 0 || n == 1) return TRUE;
+ return FALSE;
+}
+
+static void
+range_ptr_alloc_edges(mrb_state *mrb, struct RRange *r)
+{
+#ifndef MRB_RANGE_EMBED
r->edges = (mrb_range_edges *)mrb_malloc(mrb, sizeof(mrb_range_edges));
- r->edges->beg = beg;
- r->edges->end = end;
- r->excl = excl;
- return mrb_range_value(r);
+#endif
+}
+
+static struct RRange *
+range_ptr_init(mrb_state *mrb, struct RRange *r, mrb_value beg, mrb_value end, mrb_bool excl)
+{
+ r_check(mrb, beg, end);
+
+ if (r) {
+ if (RANGE_INITIALIZED_P(r)) {
+ /* Ranges are immutable, so that they should be initialized only once. */
+ mrb_name_error(mrb, MRB_SYM(initialize), "'initialize' called twice");
+ }
+ else {
+ range_ptr_alloc_edges(mrb, r);
+ }
+ }
+ else {
+ r = MRB_OBJ_ALLOC(mrb, MRB_TT_RANGE, mrb->range_class);
+ range_ptr_alloc_edges(mrb, r);
+ }
+
+ RANGE_BEG(r) = beg;
+ RANGE_END(r) = end;
+ RANGE_EXCL(r) = excl;
+ RANGE_INITIALIZED(r);
+
+ return r;
+}
+
+static void
+range_ptr_replace(mrb_state *mrb, struct RRange *r, mrb_value beg, mrb_value end, mrb_bool excl)
+{
+ range_ptr_init(mrb, r, beg, end, excl);
+ mrb_write_barrier(mrb, (struct RBasic*)r);
}
/*
@@ -54,12 +114,10 @@ mrb_range_new(mrb_state *mrb, mrb_value beg, mrb_value end, mrb_bool excl)
*
* Returns the first object in <i>rng</i>.
*/
-mrb_value
-mrb_range_beg(mrb_state *mrb, mrb_value range)
+static mrb_value
+range_beg(mrb_state *mrb, mrb_value range)
{
- struct RRange *r = mrb_range_ptr(range);
-
- return r->edges->beg;
+ return mrb_range_beg(mrb, range);
}
/*
@@ -72,13 +130,10 @@ mrb_range_beg(mrb_state *mrb, mrb_value range)
* (1..10).end #=> 10
* (1...10).end #=> 10
*/
-
-mrb_value
-mrb_range_end(mrb_state *mrb, mrb_value range)
+static mrb_value
+range_end(mrb_state *mrb, mrb_value range)
{
- struct RRange *r = mrb_range_ptr(range);
-
- return r->edges->end;
+ return mrb_range_end(mrb, range);
}
/*
@@ -87,27 +142,12 @@ mrb_range_end(mrb_state *mrb, mrb_value range)
*
* Returns <code>true</code> if <i>range</i> excludes its end value.
*/
-mrb_value
-mrb_range_excl(mrb_state *mrb, mrb_value range)
+static mrb_value
+range_excl(mrb_state *mrb, mrb_value range)
{
- struct RRange *r = mrb_range_ptr(range);
-
- return mrb_bool_value(r->excl);
+ return mrb_bool_value(mrb_range_excl_p(mrb, 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);
-
- range_check(mrb, beg, end);
- r->excl = exclude_end;
- if (!r->edges) {
- r->edges = (mrb_range_edges *)mrb_malloc(mrb, sizeof(mrb_range_edges));
- }
- r->edges->beg = beg;
- r->edges->end = end;
-}
/*
* call-seq:
* Range.new(start, end, exclusive=false) => range
@@ -116,22 +156,18 @@ range_init(mrb_state *mrb, mrb_value range, mrb_value beg, mrb_value end, mrb_bo
* parameter is omitted or is <code>false</code>, the <i>range</i> will include
* the end object; otherwise, it will be excluded.
*/
-
-mrb_value
-mrb_range_initialize(mrb_state *mrb, mrb_value range)
+static mrb_value
+range_initialize(mrb_state *mrb, mrb_value range)
{
mrb_value beg, end;
- mrb_bool exclusive;
- int n;
+ mrb_bool exclusive = FALSE;
- n = mrb_get_args(mrb, "oo|b", &beg, &end, &exclusive);
- if (n != 3) {
- exclusive = FALSE;
- }
- /* Ranges are immutable, so that they should be initialized only once. */
- range_init(mrb, range, beg, end, exclusive);
+ mrb_get_args(mrb, "oo|b", &beg, &end, &exclusive);
+ range_ptr_replace(mrb, mrb_range_raw_ptr(range), beg, end, exclusive);
+ mrb_obj_freeze(mrb, range);
return range;
}
+
/*
* call-seq:
* range == obj => true or false
@@ -144,132 +180,61 @@ mrb_range_initialize(mrb_state *mrb, mrb_value range)
* (0..2) == (0..2) #=> true
* (0..2) == Range.new(0,2) #=> true
* (0..2) == (0...2) #=> false
- *
*/
-
-mrb_value
-mrb_range_eq(mrb_state *mrb, mrb_value range)
+static mrb_value
+range_eq(mrb_state *mrb, mrb_value range)
{
struct RRange *rr;
struct RRange *ro;
- mrb_value obj;
-
- mrb_get_args(mrb, "o", &obj);
+ mrb_value obj = mrb_get_arg1(mrb);
+ mrb_bool v1, v2;
if (mrb_obj_equal(mrb, range, obj)) return mrb_true_value();
if (!mrb_obj_is_instance_of(mrb, obj, mrb_obj_class(mrb, range))) { /* same class? */
return mrb_false_value();
}
- rr = mrb_range_ptr(range);
- ro = mrb_range_ptr(obj);
- if (!mrb_bool(mrb_funcall(mrb, rr->edges->beg, "==", 1, ro->edges->beg)) ||
- !mrb_bool(mrb_funcall(mrb, rr->edges->end, "==", 1, ro->edges->end)) ||
- rr->excl != ro->excl) {
+ rr = mrb_range_ptr(mrb, range);
+ ro = mrb_range_ptr(mrb, obj);
+ v1 = mrb_equal(mrb, RANGE_BEG(rr), RANGE_BEG(ro));
+ v2 = mrb_equal(mrb, RANGE_END(rr), RANGE_END(ro));
+ if (!v1 || !v2 || RANGE_EXCL(rr) != RANGE_EXCL(ro)) {
return mrb_false_value();
}
return mrb_true_value();
}
-static mrb_bool
-r_le(mrb_state *mrb, mrb_value a, mrb_value b)
-{
- mrb_value r = mrb_funcall(mrb, a, "<=>", 1, b); /* compare result */
- /* output :a < b => -1, a = b => 0, a > b => +1 */
-
- if (mrb_fixnum_p(r)) {
- mrb_int c = mrb_fixnum(r);
- if (c == 0 || c == -1) return TRUE;
- }
-
- return FALSE;
-}
-
-static mrb_bool
-r_gt(mrb_state *mrb, mrb_value a, mrb_value b)
-{
- mrb_value r = mrb_funcall(mrb, a, "<=>", 1, b);
- /* output :a < b => -1, a = b => 0, a > b => +1 */
-
- return mrb_fixnum_p(r) && mrb_fixnum(r) == 1;
-}
-
-static mrb_bool
-r_ge(mrb_state *mrb, mrb_value a, mrb_value b)
-{
- mrb_value r = mrb_funcall(mrb, a, "<=>", 1, b); /* compare result */
- /* output :a < b => -1, a = b => 0, a > b => +1 */
-
- if (mrb_fixnum_p(r)) {
- mrb_int c = mrb_fixnum(r);
- if (c == 0 || c == 1) return TRUE;
- }
-
- return FALSE;
-}
-
/*
* call-seq:
* range === obj => true or false
* range.member?(val) => true or false
* range.include?(val) => true or false
- *
*/
-mrb_value
-mrb_range_include(mrb_state *mrb, mrb_value range)
+static mrb_value
+range_include(mrb_state *mrb, mrb_value range)
{
- mrb_value val;
- struct RRange *r = mrb_range_ptr(range);
+ mrb_value val = mrb_get_arg1(mrb);
+ struct RRange *r = mrb_range_ptr(mrb, range);
mrb_value beg, end;
- mrb_bool include_p;
-
- mrb_get_args(mrb, "o", &val);
- beg = r->edges->beg;
- end = r->edges->end;
- include_p = r_le(mrb, beg, val) && /* beg <= val */
- ((r->excl && r_gt(mrb, end, val)) || /* end > val */
- (r_ge(mrb, end, val))); /* end >= val */
-
- return mrb_bool_value(include_p);
-}
-
-static mrb_bool
-range_beg_len(mrb_state *mrb, mrb_value range, mrb_int *begp, mrb_int *lenp, mrb_int len, mrb_bool trunc)
-{
- mrb_int beg, end;
- struct RRange *r = mrb_range_ptr(range);
-
- if (mrb_type(range) != MRB_TT_RANGE) return FALSE;
-
- beg = mrb_int(mrb, r->edges->beg);
- end = mrb_int(mrb, r->edges->end);
-
- if (beg < 0) {
- beg += len;
- if (beg < 0) return FALSE;
+ beg = RANGE_BEG(r);
+ end = RANGE_END(r);
+ if (mrb_nil_p(beg)) {
+ if (RANGE_EXCL(r) ? r_gt(mrb, end, val) /* end > val */
+ : r_ge(mrb, end, val)) { /* end >= val */
+ return mrb_true_value();
+ }
}
-
- if (trunc) {
- if (beg > len) return FALSE;
- if (end > len) end = len;
+ else if (r_le(mrb, beg, val)) { /* beg <= val */
+ if (mrb_nil_p(end)) {
+ return mrb_true_value();
+ }
+ if (RANGE_EXCL(r) ? r_gt(mrb, end, val) /* end > val */
+ : r_ge(mrb, end, val)) { /* end >= val */
+ return mrb_true_value();
+ }
}
-
- if (end < 0) end += len;
- if (!r->excl && (!trunc || end < len))
- end++; /* include end point */
- len = end - beg;
- if (len < 0) len = 0;
-
- *begp = beg;
- *lenp = len;
- return TRUE;
-}
-
-MRB_API mrb_bool
-mrb_range_beg_len(mrb_state *mrb, mrb_value range, mrb_int *begp, mrb_int *lenp, mrb_int len)
-{
- return range_beg_len(mrb, range, begp, lenp, len, TRUE);
+ return mrb_false_value();
}
/* 15.2.14.4.12(x) */
@@ -279,18 +244,17 @@ mrb_range_beg_len(mrb_state *mrb, mrb_value range, mrb_int *begp, mrb_int *lenp,
*
* Convert this range object to a printable form.
*/
-
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);
+ str = mrb_obj_as_string(mrb, RANGE_BEG(r));
+ str2 = mrb_obj_as_string(mrb, RANGE_END(r));
str = mrb_str_dup(mrb, str);
- mrb_str_cat(mrb, str, "...", r->excl ? 3 : 2);
- mrb_str_append(mrb, str, str2);
+ mrb_str_cat(mrb, str, "...", RANGE_EXCL(r) ? 3 : 2);
+ mrb_str_cat_str(mrb, str, str2);
return str;
}
@@ -304,18 +268,24 @@ range_to_s(mrb_state *mrb, mrb_value range)
* <code>inspect</code> to convert the start and end
* objects).
*/
-
static mrb_value
range_inspect(mrb_state *mrb, mrb_value range)
{
- mrb_value str, str2;
- struct RRange *r = mrb_range_ptr(range);
+ mrb_value str;
+ struct RRange *r = mrb_range_ptr(mrb, range);
- str = mrb_inspect(mrb, r->edges->beg);
- str2 = mrb_inspect(mrb, r->edges->end);
- str = mrb_str_dup(mrb, str);
- mrb_str_cat(mrb, str, "...", r->excl ? 3 : 2);
- mrb_str_append(mrb, str, str2);
+ if (!mrb_nil_p(RANGE_BEG(r))) {
+ str = mrb_inspect(mrb, RANGE_BEG(r));
+ str = mrb_str_dup(mrb, str);
+ mrb_str_cat(mrb, str, "...", RANGE_EXCL(r) ? 3 : 2);
+ }
+ else {
+ str = mrb_str_new(mrb, "...", RANGE_EXCL(r) ? 3 : 2);
+ }
+ if (!mrb_nil_p(RANGE_END(r))) {
+ mrb_value str2 = mrb_inspect(mrb, RANGE_END(r));
+ mrb_str_cat_str(mrb, str, str2);
+ }
return str;
}
@@ -332,28 +302,22 @@ range_inspect(mrb_state *mrb, mrb_value range)
* (0..2).eql?(0..2) #=> true
* (0..2).eql?(Range.new(0,2)) #=> true
* (0..2).eql?(0...2) #=> false
- *
*/
-
static mrb_value
range_eql(mrb_state *mrb, mrb_value range)
{
- mrb_value obj;
+ mrb_value obj = mrb_get_arg1(mrb);
struct RRange *r, *o;
- mrb_get_args(mrb, "o", &obj);
-
if (mrb_obj_equal(mrb, range, obj)) return mrb_true_value();
- if (!mrb_obj_is_kind_of(mrb, obj, RANGE_CLASS)) {
- return mrb_false_value();
- }
- if (mrb_type(obj) != MRB_TT_RANGE) return mrb_false_value();
-
- r = mrb_range_ptr(range);
- o = mrb_range_ptr(obj);
- if (!mrb_eql(mrb, r->edges->beg, o->edges->beg) ||
- !mrb_eql(mrb, r->edges->end, o->edges->end) ||
- (r->excl != o->excl)) {
+ if (!mrb_obj_is_kind_of(mrb, obj, mrb->range_class)) return mrb_false_value();
+ if (!mrb_range_p(obj)) return mrb_false_value();
+
+ r = mrb_range_ptr(mrb, range);
+ o = mrb_range_ptr(mrb, obj);
+ if (!mrb_eql(mrb, RANGE_BEG(r), RANGE_BEG(o)) ||
+ !mrb_eql(mrb, RANGE_END(r), RANGE_END(o)) ||
+ (RANGE_EXCL(r) != RANGE_EXCL(o))) {
return mrb_false_value();
}
return mrb_true_value();
@@ -363,22 +327,70 @@ range_eql(mrb_state *mrb, mrb_value range)
static mrb_value
range_initialize_copy(mrb_state *mrb, mrb_value copy)
{
- mrb_value src;
+ mrb_value src = mrb_get_arg1(mrb);
struct RRange *r;
- mrb_get_args(mrb, "o", &src);
-
if (mrb_obj_equal(mrb, copy, src)) return copy;
if (!mrb_obj_is_instance_of(mrb, src, mrb_obj_class(mrb, copy))) {
mrb_raise(mrb, E_TYPE_ERROR, "wrong argument class");
}
- r = mrb_range_ptr(src);
- range_init(mrb, copy, r->edges->beg, r->edges->end, r->excl);
+ r = mrb_range_ptr(mrb, src);
+ range_ptr_replace(mrb, mrb_range_raw_ptr(copy), RANGE_BEG(r), RANGE_END(r), RANGE_EXCL(r));
+ mrb_obj_freeze(mrb, copy);
return copy;
}
+static mrb_value
+range_num_to_a(mrb_state *mrb, mrb_value range)
+{
+ struct RRange *r = mrb_range_ptr(mrb, range);
+ mrb_value beg = RANGE_BEG(r);
+ mrb_value end = RANGE_END(r);
+ mrb_value ary;
+
+ if (mrb_nil_p(end)) {
+ mrb_raise(mrb, E_RANGE_ERROR, "cannot convert endless range to an array");
+ }
+ if (mrb_integer_p(beg)) {
+ if (mrb_integer_p(end)) {
+ mrb_int a = mrb_integer(beg);
+ mrb_int b = mrb_integer(end);
+ mrb_int len = b - a;
+
+ if (!RANGE_EXCL(r)) len++;
+ ary = mrb_ary_new_capa(mrb, len);
+ for (mrb_int i=0; i<len; i++) {
+ mrb_ary_push(mrb, ary, mrb_int_value(mrb, a+i));
+ }
+ return ary;
+ }
+#ifndef MRB_NO_FLOAT
+ if (mrb_float_p(end)) {
+ mrb_float a = (mrb_float)mrb_integer(beg);
+ mrb_float b = mrb_float(end);
+
+ ary = mrb_ary_new_capa(mrb, (mrb_int)(b - a) + 1);
+ if (RANGE_EXCL(r)) {
+ while (a < b) {
+ mrb_ary_push(mrb, ary, mrb_int_value(mrb, a));
+ a += 1.0;
+ }
+ }
+ else {
+ while (a <= b) {
+ mrb_ary_push(mrb, ary, mrb_int_value(mrb, a));
+ a += 1.0;
+ }
+ }
+ return ary;
+ }
+#endif
+ }
+ return mrb_nil_value();
+}
+
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))
{
@@ -387,10 +399,10 @@ mrb_get_values_at(mrb_state *mrb, mrb_value obj, mrb_int olen, mrb_int argc, con
result = mrb_ary_new(mrb);
for (i = 0; i < argc; ++i) {
- if (mrb_fixnum_p(argv[i])) {
- mrb_ary_push(mrb, result, func(mrb, obj, mrb_fixnum(argv[i])));
+ if (mrb_integer_p(argv[i])) {
+ mrb_ary_push(mrb, result, func(mrb, obj, mrb_integer(argv[i])));
}
- else if (range_beg_len(mrb, argv[i], &beg, &len, olen, FALSE)) {
+ else if (mrb_range_beg_len(mrb, argv[i], &beg, &len, olen, FALSE) == MRB_RANGE_OK) {
mrb_int const end = olen < beg + len ? olen : beg + len;
for (j = beg; j < end; ++j) {
mrb_ary_push(mrb, result, func(mrb, obj, j));
@@ -401,7 +413,7 @@ mrb_get_values_at(mrb_state *mrb, mrb_value obj, mrb_int olen, mrb_int argc, con
}
}
else {
- mrb_raisef(mrb, E_TYPE_ERROR, "invalid values selector: %S", argv[i]);
+ mrb_raisef(mrb, E_TYPE_ERROR, "invalid values selector: %v", argv[i]);
}
}
@@ -409,26 +421,89 @@ mrb_get_values_at(mrb_state *mrb, mrb_value obj, mrb_int olen, mrb_int argc, con
}
void
+mrb_gc_mark_range(mrb_state *mrb, struct RRange *r)
+{
+ if (RANGE_INITIALIZED_P(r)) {
+ mrb_gc_mark_value(mrb, RANGE_BEG(r));
+ mrb_gc_mark_value(mrb, RANGE_END(r));
+ }
+}
+
+MRB_API struct RRange*
+mrb_range_ptr(mrb_state *mrb, mrb_value range)
+{
+ struct RRange *r = mrb_range_raw_ptr(range);
+
+ /* check for if #initialize_copy was removed [#3320] */
+ if (!RANGE_INITIALIZED_P(r)) {
+ mrb_raise(mrb, E_ARGUMENT_ERROR, "uninitialized range");
+ }
+ return r;
+}
+
+MRB_API mrb_value
+mrb_range_new(mrb_state *mrb, mrb_value beg, mrb_value end, mrb_bool excl)
+{
+ struct RRange *r = range_ptr_init(mrb, NULL, beg, end, excl);
+ return mrb_range_value(r);
+}
+
+MRB_API enum mrb_range_beg_len
+mrb_range_beg_len(mrb_state *mrb, mrb_value range, mrb_int *begp, mrb_int *lenp, mrb_int len, mrb_bool trunc)
+{
+ mrb_int beg, end;
+ mrb_bool excl;
+ struct RRange *r;
+
+ if (!mrb_range_p(range)) return MRB_RANGE_TYPE_MISMATCH;
+ r = mrb_range_ptr(mrb, range);
+
+ beg = mrb_nil_p(RANGE_BEG(r)) ? 0 : mrb_as_int(mrb, RANGE_BEG(r));
+ end = mrb_nil_p(RANGE_END(r)) ? -1 : mrb_as_int(mrb, RANGE_END(r));
+ excl = mrb_nil_p(RANGE_END(r)) ? 0 : RANGE_EXCL(r);
+
+ if (beg < 0) {
+ beg += len;
+ if (beg < 0) return MRB_RANGE_OUT;
+ }
+
+ if (trunc) {
+ if (beg > len) return MRB_RANGE_OUT;
+ if (end > len) end = len;
+ }
+
+ if (end < 0) end += len;
+ if (!excl && (!trunc || end < len)) end++; /* include end point */
+ len = end - beg;
+ if (len < 0) len = 0;
+
+ *begp = beg;
+ *lenp = len;
+ return MRB_RANGE_OK;
+}
+
+void
mrb_init_range(mrb_state *mrb)
{
struct RClass *r;
r = mrb_define_class(mrb, "Range", mrb->object_class); /* 15.2.14 */
+ mrb->range_class = r;
MRB_SET_INSTANCE_TT(r, MRB_TT_RANGE);
- mrb_define_method(mrb, r, "begin", mrb_range_beg, MRB_ARGS_NONE()); /* 15.2.14.4.3 */
- mrb_define_method(mrb, r, "end", mrb_range_end, MRB_ARGS_NONE()); /* 15.2.14.4.5 */
- mrb_define_method(mrb, r, "==", mrb_range_eq, MRB_ARGS_REQ(1)); /* 15.2.14.4.1 */
- mrb_define_method(mrb, r, "===", mrb_range_include, MRB_ARGS_REQ(1)); /* 15.2.14.4.2 */
- mrb_define_method(mrb, r, "exclude_end?", mrb_range_excl, MRB_ARGS_NONE()); /* 15.2.14.4.6 */
- mrb_define_method(mrb, r, "first", mrb_range_beg, MRB_ARGS_NONE()); /* 15.2.14.4.7 */
- mrb_define_method(mrb, r, "include?", mrb_range_include, MRB_ARGS_REQ(1)); /* 15.2.14.4.8 */
- mrb_define_method(mrb, r, "initialize", mrb_range_initialize, MRB_ARGS_ANY()); /* 15.2.14.4.9 */
- mrb_define_method(mrb, r, "last", mrb_range_end, MRB_ARGS_NONE()); /* 15.2.14.4.10 */
- mrb_define_method(mrb, r, "member?", mrb_range_include, MRB_ARGS_REQ(1)); /* 15.2.14.4.11 */
-
+ mrb_define_method(mrb, r, "begin", range_beg, MRB_ARGS_NONE()); /* 15.2.14.4.3 */
+ mrb_define_method(mrb, r, "end", range_end, MRB_ARGS_NONE()); /* 15.2.14.4.5 */
+ mrb_define_method(mrb, r, "==", range_eq, MRB_ARGS_REQ(1)); /* 15.2.14.4.1 */
+ mrb_define_method(mrb, r, "===", range_include, MRB_ARGS_REQ(1)); /* 15.2.14.4.2 */
+ mrb_define_method(mrb, r, "exclude_end?", range_excl, MRB_ARGS_NONE()); /* 15.2.14.4.6 */
+ mrb_define_method(mrb, r, "first", range_beg, MRB_ARGS_NONE()); /* 15.2.14.4.7 */
+ mrb_define_method(mrb, r, "include?", range_include, MRB_ARGS_REQ(1)); /* 15.2.14.4.8 */
+ mrb_define_method(mrb, r, "initialize", range_initialize, MRB_ARGS_ANY()); /* 15.2.14.4.9 */
+ mrb_define_method(mrb, r, "last", range_end, MRB_ARGS_NONE()); /* 15.2.14.4.10 */
+ mrb_define_method(mrb, r, "member?", range_include, MRB_ARGS_REQ(1)); /* 15.2.14.4.11 */
mrb_define_method(mrb, r, "to_s", range_to_s, MRB_ARGS_NONE()); /* 15.2.14.4.12(x) */
mrb_define_method(mrb, r, "inspect", range_inspect, MRB_ARGS_NONE()); /* 15.2.14.4.13(x) */
mrb_define_method(mrb, r, "eql?", range_eql, MRB_ARGS_REQ(1)); /* 15.2.14.4.14(x) */
mrb_define_method(mrb, r, "initialize_copy", range_initialize_copy, MRB_ARGS_REQ(1)); /* 15.2.14.4.15(x) */
+ mrb_define_method(mrb, r, "__num_to_a", range_num_to_a, MRB_ARGS_NONE());
}
diff --git a/src/readflt.c b/src/readflt.c
new file mode 100644
index 000000000..19a8e8dc6
--- /dev/null
+++ b/src/readflt.c
@@ -0,0 +1,120 @@
+#include <mruby.h>
+
+#ifndef MRB_NO_FLOAT
+/*
+ * strtod implementation.
+ * author: Yasuhiro Matsumoto (@mattn)
+ * license: public domain
+ */
+
+/*
+The original code can be found in https://github.com/mattn/strtod
+
+I modified the routine for mruby:
+
+ * renamed the function `vim_strtod` -> `mrb_float_read`
+ * simplified the code
+
+My modifications in this file are also placed in the public domain.
+
+Matz (Yukihiro Matsumoto)
+*/
+
+#include <stdlib.h>
+#include <string.h>
+#include <math.h>
+#include <errno.h>
+
+MRB_API double
+mrb_float_read(const char *str, char **end)
+{
+ double d = 0.0;
+ int sign;
+ int n = 0;
+ const char *p, *a;
+
+ a = p = str;
+ while (ISSPACE(*p))
+ ++p;
+
+ /* decimal part */
+ sign = 1;
+ if (*p == '-') {
+ sign = -1;
+ ++p;
+ } else if (*p == '+')
+ ++p;
+ if (ISDIGIT(*p)) {
+ d = (double)(*p++ - '0');
+ while (*p && ISDIGIT(*p)) {
+ d = d * 10.0 + (double)(*p - '0');
+ ++p;
+ ++n;
+ }
+ a = p;
+ } else if (*p != '.')
+ goto done;
+ d *= sign;
+
+ /* fraction part */
+ if (*p == '.') {
+ double f = 0.0;
+ double base = 0.1;
+ ++p;
+
+ if (ISDIGIT(*p))
+ {
+ while (*p && ISDIGIT(*p)) {
+ f += base * (*p - '0') ;
+ base /= 10.0;
+ ++p;
+ ++n;
+ }
+ }
+ d += f * sign;
+ a = p;
+ }
+
+ /* exponential part */
+ if ((*p == 'E') || (*p == 'e')) {
+ int e = 0;
+ ++p;
+
+ sign = 1;
+ if (*p == '-') {
+ sign = -1;
+ ++p;
+ } else if (*p == '+')
+ ++p;
+
+ if (ISDIGIT(*p)) {
+ while (*p == '0')
+ ++p;
+ if (*p == '\0') --p;
+ e = (int)(*p++ - '0');
+ for (; *p && ISDIGIT(*p); p++) {
+ if (e < 10000)
+ e = e * 10 + (*p - '0');
+ }
+ e *= sign;
+ }
+ else if (!ISDIGIT(*(a-1))) {
+ a = str;
+ goto done;
+ }
+ else if (*p == 0)
+ goto done;
+ d *= pow(10.0, (double) e);
+ a = p;
+ }
+ else if (p > str && !ISDIGIT(*(p-1))) {
+ a = str;
+ goto done;
+ }
+
+done:
+ if (end)
+ *end = (char*)a;
+ return d;
+}
+#endif
diff --git a/src/readint.c b/src/readint.c
new file mode 100644
index 000000000..5fae222c2
--- /dev/null
+++ b/src/readint.c
@@ -0,0 +1,30 @@
+#include <mruby.h>
+#include <mruby/numeric.h>
+#include <errno.h>
+
+/* mrb_int_read(): read mrb_int from a string (base 10 only) */
+/* const char *p - string to read */
+/* const char *e - end of string */
+/* char **endp - end of parsed integer */
+
+/* if integer overflows, errno will be set to ERANGE */
+/* also endp will be set to NULL on overflow */
+MRB_API mrb_int
+mrb_int_read(const char *p, const char *e, char **endp)
+{
+ mrb_int n = 0;
+ int ch;
+
+ while ((e == NULL || p < e) && ISDIGIT(*p)) {
+ ch = *p - '0';
+ if (mrb_int_mul_overflow(n, 10, &n) ||
+ mrb_int_add_overflow(n, ch, &n)) {
+ if (endp) *endp = NULL;
+ errno = ERANGE;
+ return MRB_INT_MAX;
+ }
+ p++;
+ }
+ if (endp) *endp = (char*)p;
+ return n;
+}
diff --git a/src/state.c b/src/state.c
index 2efd34334..4dafc964a 100644
--- a/src/state.c
+++ b/src/state.c
@@ -6,49 +6,53 @@
#include <stdlib.h>
#include <string.h>
-#include "mruby.h"
-#include "mruby/irep.h"
-#include "mruby/variable.h"
-#include "mruby/debug.h"
-#include "mruby/string.h"
+#include <mruby.h>
+#include <mruby/irep.h>
+#include <mruby/variable.h>
+#include <mruby/debug.h>
+#include <mruby/string.h>
+#include <mruby/class.h>
-void mrb_init_heap(mrb_state*);
void mrb_init_core(mrb_state*);
void mrb_init_mrbgems(mrb_state*);
-static mrb_value
-inspect_main(mrb_state *mrb, mrb_value mod)
+void mrb_gc_init(mrb_state*, mrb_gc *gc);
+void mrb_gc_destroy(mrb_state*, mrb_gc *gc);
+
+int mrb_core_init_protect(mrb_state *mrb, void (*body)(mrb_state *, void *), void *opaque);
+
+static void
+init_gc_and_core(mrb_state *mrb, void *opaque)
{
- return mrb_str_new_lit(mrb, "main");
+ static const struct mrb_context mrb_context_zero = { 0 };
+
+ mrb_gc_init(mrb, &mrb->gc);
+ mrb->c = (struct mrb_context*)mrb_malloc(mrb, sizeof(struct mrb_context));
+ *mrb->c = mrb_context_zero;
+ mrb->root_c = mrb->c;
+
+ mrb_init_core(mrb);
}
MRB_API mrb_state*
mrb_open_core(mrb_allocf f, void *ud)
{
static const mrb_state mrb_state_zero = { 0 };
- static const struct mrb_context mrb_context_zero = { 0 };
mrb_state *mrb;
+ if (f == NULL) f = mrb_default_allocf;
mrb = (mrb_state *)(f)(NULL, NULL, sizeof(mrb_state), ud);
if (mrb == NULL) return NULL;
*mrb = mrb_state_zero;
mrb->allocf_ud = ud;
mrb->allocf = f;
- mrb->current_white_part = MRB_GC_WHITE_A;
mrb->atexit_stack_len = 0;
-#ifndef MRB_GC_FIXED_ARENA
- mrb->arena = (struct RBasic**)mrb_malloc(mrb, sizeof(struct RBasic*)*MRB_GC_ARENA_SIZE);
- mrb->arena_capa = MRB_GC_ARENA_SIZE;
-#endif
-
- mrb_init_heap(mrb);
- mrb->c = (struct mrb_context*)mrb_malloc(mrb, sizeof(struct mrb_context));
- *mrb->c = mrb_context_zero;
- mrb->root_c = mrb->c;
-
- mrb_init_core(mrb);
+ if (mrb_core_init_protect(mrb, init_gc_and_core, NULL)) {
+ mrb_close(mrb);
+ return NULL;
+ }
return mrb;
}
@@ -65,38 +69,6 @@ mrb_default_allocf(mrb_state *mrb, void *p, size_t size, void *ud)
}
}
-struct alloca_header {
- struct alloca_header *next;
- char buf[];
-};
-
-MRB_API void*
-mrb_alloca(mrb_state *mrb, size_t size)
-{
- struct alloca_header *p;
-
- p = (struct alloca_header*) mrb_malloc(mrb, sizeof(struct alloca_header)+size);
- p->next = mrb->mems;
- mrb->mems = p;
- return (void*)p->buf;
-}
-
-static void
-mrb_alloca_free(mrb_state *mrb)
-{
- struct alloca_header *p;
- struct alloca_header *tmp;
-
- if (mrb == NULL) return;
- p = mrb->mems;
-
- while (p) {
- tmp = p;
- p = p->next;
- mrb_free(mrb, tmp);
- }
-}
-
MRB_API mrb_state*
mrb_open(void)
{
@@ -105,6 +77,14 @@ mrb_open(void)
return mrb;
}
+#ifndef MRB_NO_GEMS
+static void
+init_mrbgems(mrb_state *mrb, void *opaque)
+{
+ mrb_init_mrbgems(mrb);
+}
+#endif
+
MRB_API mrb_state*
mrb_open_allocf(mrb_allocf f, void *ud)
{
@@ -114,25 +94,35 @@ mrb_open_allocf(mrb_allocf f, void *ud)
return NULL;
}
-#ifndef DISABLE_GEMS
- mrb_init_mrbgems(mrb);
+#ifndef MRB_NO_GEMS
+ if (mrb_core_init_protect(mrb, init_mrbgems, NULL)) {
+ mrb_close(mrb);
+ return NULL;
+ }
mrb_gc_arena_restore(mrb, 0);
#endif
return mrb;
}
void mrb_free_symtbl(mrb_state *mrb);
-void mrb_free_heap(mrb_state *mrb);
void
mrb_irep_incref(mrb_state *mrb, mrb_irep *irep)
{
+ if (irep->flags & MRB_IREP_NO_FREE) return;
+ if (irep->refcnt == UINT16_MAX) {
+ mrb_garbage_collect(mrb);
+ if (irep->refcnt == UINT16_MAX) {
+ mrb_raise(mrb, E_RUNTIME_ERROR, "too many irep references");
+ }
+ }
irep->refcnt++;
}
void
mrb_irep_decref(mrb_state *mrb, mrb_irep *irep)
{
+ if (irep->flags & MRB_IREP_NO_FREE) return;
irep->refcnt--;
if (irep->refcnt == 0) {
mrb_irep_free(mrb, irep);
@@ -140,84 +130,48 @@ mrb_irep_decref(mrb_state *mrb, mrb_irep *irep)
}
void
-mrb_irep_free(mrb_state *mrb, mrb_irep *irep)
+mrb_irep_cutref(mrb_state *mrb, mrb_irep *irep)
{
- size_t i;
+ mrb_irep **reps;
+ int i;
- if (!(irep->flags & MRB_ISEQ_NO_FREE))
- mrb_free(mrb, irep->iseq);
- for (i=0; i<irep->plen; i++) {
- if (mrb_type(irep->pool[i]) == MRB_TT_STRING) {
- mrb_gc_free_str(mrb, RSTRING(irep->pool[i]));
- mrb_free(mrb, mrb_obj_ptr(irep->pool[i]));
- }
-#ifdef MRB_WORD_BOXING
- else if (mrb_type(irep->pool[i]) == MRB_TT_FLOAT) {
- mrb_free(mrb, mrb_obj_ptr(irep->pool[i]));
- }
-#endif
- }
- mrb_free(mrb, irep->pool);
- mrb_free(mrb, irep->syms);
+ if (irep->flags & MRB_IREP_NO_FREE) return;
+ reps = (mrb_irep**)irep->reps;
for (i=0; i<irep->rlen; i++) {
- mrb_irep_decref(mrb, irep->reps[i]);
+ mrb_irep *tmp = reps[i];
+ reps[i] = NULL;
+ if (tmp) mrb_irep_decref(mrb, tmp);
}
- mrb_free(mrb, irep->reps);
- mrb_free(mrb, irep->lv);
- mrb_free(mrb, (void *)irep->filename);
- mrb_free(mrb, irep->lines);
- mrb_debug_info_free(mrb, irep->debug_info);
- mrb_free(mrb, irep);
}
-mrb_value
-mrb_str_pool(mrb_state *mrb, mrb_value str)
+void
+mrb_irep_free(mrb_state *mrb, mrb_irep *irep)
{
- struct RString *s = mrb_str_ptr(str);
- struct RString *ns;
- char *ptr;
- mrb_int len;
-
- ns = (struct RString *)mrb_malloc(mrb, sizeof(struct RString));
- ns->tt = MRB_TT_STRING;
- ns->c = mrb->string_class;
-
- if (RSTR_NOFREE_P(s)) {
- ns->flags = MRB_STR_NOFREE;
- ns->as.heap.ptr = s->as.heap.ptr;
- ns->as.heap.len = s->as.heap.len;
- ns->as.heap.aux.capa = 0;
- }
- else {
- ns->flags = 0;
- if (RSTR_EMBED_P(s)) {
- ptr = s->as.ary;
- len = RSTR_EMBED_LEN(s);
- }
- else {
- ptr = s->as.heap.ptr;
- len = s->as.heap.len;
- }
+ int i;
- if (len < RSTRING_EMBED_LEN_MAX) {
- RSTR_SET_EMBED_FLAG(ns);
- RSTR_SET_EMBED_LEN(ns, len);
- if (ptr) {
- memcpy(ns->as.ary, ptr, len);
+ if (irep->flags & MRB_IREP_NO_FREE) return;
+ if (!(irep->flags & MRB_ISEQ_NO_FREE))
+ mrb_free(mrb, (void*)irep->iseq);
+ if (irep->pool) {
+ for (i=0; i<irep->plen; i++) {
+ if ((irep->pool[i].tt & 3) == IREP_TT_STR ||
+ irep->pool[i].tt == IREP_TT_BIGINT) {
+ mrb_free(mrb, (void*)irep->pool[i].u.str);
}
- ns->as.ary[len] = '\0';
}
- else {
- ns->as.heap.ptr = (char *)mrb_malloc(mrb, (size_t)len+1);
- ns->as.heap.len = len;
- ns->as.heap.aux.capa = len;
- if (ptr) {
- memcpy(ns->as.heap.ptr, ptr, len);
- }
- ns->as.heap.ptr[len] = '\0';
+ mrb_free(mrb, (void*)irep->pool);
+ }
+ mrb_free(mrb, (void*)irep->syms);
+ if (irep->reps) {
+ for (i=0; i<irep->rlen; i++) {
+ if (irep->reps[i])
+ mrb_irep_decref(mrb, (mrb_irep*)irep->reps[i]);
}
+ mrb_free(mrb, (void*)irep->reps);
}
- return mrb_obj_value(ns);
+ mrb_free(mrb, (void*)irep->lv);
+ mrb_debug_info_free(mrb, irep->debug_info);
+ mrb_free(mrb, irep);
}
MRB_API void
@@ -226,33 +180,22 @@ mrb_free_context(mrb_state *mrb, struct mrb_context *c)
if (!c) return;
mrb_free(mrb, c->stbase);
mrb_free(mrb, c->cibase);
- mrb_free(mrb, c->rescue);
- mrb_free(mrb, c->ensure);
mrb_free(mrb, c);
}
-MRB_API void
+void mrb_protect_atexit(mrb_state *mrb);
+
+ MRB_API void
mrb_close(mrb_state *mrb)
{
- if (mrb->atexit_stack_len > 0) {
- mrb_int i;
- for (i = mrb->atexit_stack_len; i > 0; --i) {
- mrb->atexit_stack[i - 1](mrb);
- }
-#ifndef MRB_FIXED_STATE_ATEXIT_STACK
- mrb_free(mrb, mrb->atexit_stack);
-#endif
- }
+ if (!mrb) return;
+ mrb_protect_atexit(mrb);
/* free */
- mrb_gc_free_gv(mrb);
+ mrb_gc_destroy(mrb, &mrb->gc);
mrb_free_context(mrb, mrb->root_c);
+ mrb_gc_free_gv(mrb);
mrb_free_symtbl(mrb);
- mrb_free_heap(mrb);
- mrb_alloca_free(mrb);
-#ifndef MRB_GC_FIXED_ARENA
- mrb_free(mrb, mrb->arena);
-#endif
mrb_free(mrb, mrb);
}
@@ -272,11 +215,6 @@ mrb_add_irep(mrb_state *mrb)
MRB_API mrb_value
mrb_top_self(mrb_state *mrb)
{
- if (!mrb->top_self) {
- mrb->top_self = (struct RObject*)mrb_obj_alloc(mrb, MRB_TT_OBJECT, mrb->object_class);
- mrb_define_singleton_method(mrb, mrb->top_self, "inspect", inspect_main, MRB_ARGS_NONE());
- mrb_define_singleton_method(mrb, mrb->top_self, "to_s", inspect_main, MRB_ARGS_NONE());
- }
return mrb_obj_value(mrb->top_self);
}
@@ -293,7 +231,8 @@ mrb_state_atexit(mrb_state *mrb, mrb_atexit_func f)
stack_size = sizeof(mrb_atexit_func) * (mrb->atexit_stack_len + 1);
if (mrb->atexit_stack_len == 0) {
mrb->atexit_stack = (mrb_atexit_func*)mrb_malloc(mrb, stack_size);
- } else {
+ }
+ else {
mrb->atexit_stack = (mrb_atexit_func*)mrb_realloc(mrb, mrb->atexit_stack, stack_size);
}
#endif
diff --git a/src/string.c b/src/string.c
index 23cd76747..03d9c8a9c 100644
--- a/src/string.c
+++ b/src/string.c
@@ -4,65 +4,209 @@
** See Copyright Notice in mruby.h
*/
-#include <ctype.h>
+#ifdef _MSC_VER
+# define _CRT_NONSTDC_NO_DEPRECATE
+#endif
+
+#ifndef MRB_NO_FLOAT
#include <float.h>
+#include <math.h>
+#endif
#include <limits.h>
#include <stddef.h>
#include <stdlib.h>
#include <string.h>
-#include "mruby.h"
-#include "mruby/array.h"
-#include "mruby/class.h"
-#include "mruby/range.h"
-#include "mruby/string.h"
-#include "mruby/numeric.h"
-#include "mruby/re.h"
-
-const char mrb_digitmap[] = "0123456789abcdefghijklmnopqrstuvwxyz";
+#include <mruby.h>
+#include <mruby/array.h>
+#include <mruby/class.h>
+#include <mruby/range.h>
+#include <mruby/string.h>
+#include <mruby/numeric.h>
+#include <mruby/presym.h>
typedef struct mrb_shared_string {
- mrb_bool nofree : 1;
int refcnt;
+ mrb_ssize capa;
char *ptr;
- 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);
+const char mrb_digitmap[] = "0123456789abcdefghijklmnopqrstuvwxyz";
-MRB_API mrb_int
-mrb_str_strlen(mrb_state *mrb, struct RString *s)
+#define mrb_obj_alloc_string(mrb) MRB_OBJ_ALLOC((mrb), MRB_TT_STRING, (mrb)->string_class)
+
+static struct RString*
+str_init_normal_capa(mrb_state *mrb, struct RString *s,
+ const char *p, size_t len, size_t capa)
+{
+ char *dst = (char *)mrb_malloc(mrb, capa + 1);
+ if (p) memcpy(dst, p, len);
+ dst[len] = '\0';
+ s->as.heap.ptr = dst;
+ s->as.heap.len = (mrb_ssize)len;
+ s->as.heap.aux.capa = (mrb_ssize)capa;
+ RSTR_UNSET_TYPE_FLAG(s);
+ return s;
+}
+
+static struct RString*
+str_init_normal(mrb_state *mrb, struct RString *s, const char *p, size_t len)
{
- mrb_int i, max = RSTR_LEN(s);
- char *p = RSTR_PTR(s);
+ return str_init_normal_capa(mrb, s, p, len, len);
+}
- if (!p) return 0;
- for (i=0; i<max; i++) {
- if (p[i] == '\0') {
- mrb_raise(mrb, E_ARGUMENT_ERROR, "string contains null byte");
- }
+static struct RString*
+str_init_embed(struct RString *s, const char *p, size_t len)
+{
+ if (p) memcpy(RSTR_EMBED_PTR(s), p, len);
+ RSTR_EMBED_PTR(s)[len] = '\0';
+ RSTR_SET_TYPE_FLAG(s, EMBED);
+ RSTR_SET_EMBED_LEN(s, len);
+ return s;
+}
+
+static struct RString*
+str_init_nofree(struct RString *s, const char *p, size_t len)
+{
+ s->as.heap.ptr = (char *)p;
+ s->as.heap.len = (mrb_ssize)len;
+ s->as.heap.aux.capa = 0; /* nofree */
+ RSTR_SET_TYPE_FLAG(s, NOFREE);
+ return s;
+}
+
+static struct RString*
+str_init_shared(mrb_state *mrb, const struct RString *orig, struct RString *s, mrb_shared_string *shared)
+{
+ if (shared) {
+ shared->refcnt++;
}
- return max;
+ else {
+ shared = (mrb_shared_string *)mrb_malloc(mrb, sizeof(mrb_shared_string));
+ shared->refcnt = 1;
+ shared->ptr = orig->as.heap.ptr;
+ shared->capa = orig->as.heap.aux.capa;
+ }
+ s->as.heap.ptr = orig->as.heap.ptr;
+ s->as.heap.len = orig->as.heap.len;
+ s->as.heap.aux.shared = shared;
+ RSTR_SET_TYPE_FLAG(s, SHARED);
+ return s;
}
-static inline void
-resize_capa(mrb_state *mrb, struct RString *s, mrb_int capacity)
+static struct RString*
+str_init_fshared(const struct RString *orig, struct RString *s, struct RString *fshared)
+{
+ s->as.heap.ptr = orig->as.heap.ptr;
+ s->as.heap.len = orig->as.heap.len;
+ s->as.heap.aux.fshared = fshared;
+ RSTR_SET_TYPE_FLAG(s, FSHARED);
+ return s;
+}
+
+static struct RString*
+str_init_modifiable(mrb_state *mrb, struct RString *s, const char *p, size_t len)
+{
+ if (RSTR_EMBEDDABLE_P(len)) {
+ return str_init_embed(s, p, len);
+ }
+ else {
+ return str_init_normal(mrb, s, p, len);
+ }
+}
+
+static struct RString*
+str_new_static(mrb_state *mrb, const char *p, size_t len)
+{
+ if (RSTR_EMBEDDABLE_P(len)) {
+ return str_init_embed(mrb_obj_alloc_string(mrb), p, len);
+ }
+ if (len >= MRB_SSIZE_MAX) {
+ mrb_raise(mrb, E_ARGUMENT_ERROR, "string size too big");
+ }
+ return str_init_nofree(mrb_obj_alloc_string(mrb), p, len);
+}
+
+static struct RString*
+str_new(mrb_state *mrb, const char *p, size_t len)
+{
+ if (RSTR_EMBEDDABLE_P(len)) {
+ return str_init_embed(mrb_obj_alloc_string(mrb), p, len);
+ }
+ if (len >= MRB_SSIZE_MAX) {
+ mrb_raise(mrb, E_ARGUMENT_ERROR, "string size too big");
+ }
+ if (p && mrb_ro_data_p(p)) {
+ return str_init_nofree(mrb_obj_alloc_string(mrb), p, len);
+ }
+ return str_init_normal(mrb, mrb_obj_alloc_string(mrb), p, len);
+}
+
+MRB_API mrb_value
+mrb_str_new_capa(mrb_state *mrb, size_t capa)
+{
+ struct RString *s;
+
+ if (RSTR_EMBEDDABLE_P(capa)) {
+ s = str_init_embed(mrb_obj_alloc_string(mrb), NULL, 0);
+ }
+ else if (capa >= MRB_SSIZE_MAX) {
+ mrb_raise(mrb, E_ARGUMENT_ERROR, "string capacity size too big");
+ /* not reached */
+ s = NULL;
+ }
+ else {
+ s = str_init_normal_capa(mrb, mrb_obj_alloc_string(mrb), NULL, 0, capa);
+ }
+
+ return mrb_obj_value(s);
+}
+
+static void
+resize_capa(mrb_state *mrb, struct RString *s, size_t capacity)
{
+#if SIZE_MAX > MRB_SSIZE_MAX
+ mrb_assert(capacity < MRB_SSIZE_MAX);
+#endif
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;
+ if (!RSTR_EMBEDDABLE_P(capacity)) {
+ str_init_normal_capa(mrb, s, RSTR_EMBED_PTR(s), RSTR_EMBED_LEN(s), capacity);
}
}
else {
- s->as.heap.ptr = (char *)mrb_realloc(mrb, RSTR_PTR(s), capacity+1);
- s->as.heap.aux.capa = capacity;
+ s->as.heap.ptr = (char*)mrb_realloc(mrb, RSTR_PTR(s), capacity+1);
+ s->as.heap.aux.capa = (mrb_ssize)capacity;
+ }
+}
+
+MRB_API mrb_value
+mrb_str_new(mrb_state *mrb, const char *p, size_t len)
+{
+ return mrb_obj_value(str_new(mrb, p, len));
+}
+
+MRB_API mrb_value
+mrb_str_new_cstr(mrb_state *mrb, const char *p)
+{
+ struct RString *s;
+ size_t len;
+
+ if (p) {
+ len = strlen(p);
+ }
+ else {
+ len = 0;
}
+
+ s = str_new(mrb, p, len);
+
+ return mrb_obj_value(s);
+}
+
+MRB_API mrb_value
+mrb_str_new_static(mrb_state *mrb, const char *p, size_t len)
+{
+ struct RString *s = str_new_static(mrb, p, len);
+ return mrb_obj_value(s);
}
static void
@@ -70,341 +214,643 @@ 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->ptr);
mrb_free(mrb, shared);
}
}
-MRB_API void
-mrb_str_modify(mrb_state *mrb, struct RString *s)
+static void
+str_modify_keep_ascii(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';
+ s->as.heap.aux.capa = shared->capa;
+ s->as.heap.ptr[s->as.heap.len] = '\0';
+ RSTR_UNSET_SHARED_FLAG(s);
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_init_modifiable(mrb, s, s->as.heap.ptr, (size_t)s->as.heap.len);
str_decref(mrb, shared);
}
- RSTR_UNSET_SHARED_FLAG(s);
- return;
}
- if (RSTR_NOFREE_P(s)) {
- char *p = s->as.heap.ptr;
+ else if (RSTR_NOFREE_P(s) || RSTR_FSHARED_P(s)) {
+ str_init_modifiable(mrb, s, s->as.heap.ptr, (size_t)s->as.heap.len);
+ }
+}
- 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 void
+check_null_byte(mrb_state *mrb, mrb_value str)
+{
+ mrb_to_str(mrb, str);
+ if (memchr(RSTRING_PTR(str), '\0', RSTRING_LEN(str))) {
+ mrb_raise(mrb, E_ARGUMENT_ERROR, "string contains null byte");
}
}
-MRB_API mrb_value
-mrb_str_resize(mrb_state *mrb, mrb_value str, mrb_int len)
+void
+mrb_gc_free_str(mrb_state *mrb, struct RString *str)
+{
+ if (RSTR_EMBED_P(str))
+ /* no code */;
+ else if (RSTR_SHARED_P(str))
+ str_decref(mrb, str->as.heap.aux.shared);
+ else if (!RSTR_NOFREE_P(str) && !RSTR_FSHARED_P(str))
+ mrb_free(mrb, str->as.heap.ptr);
+}
+
+#ifdef MRB_UTF8_STRING
+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,
+ 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,
+ 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,
+};
+
+#define utf8_islead(c) ((unsigned char)((c)&0xc0) != 0x80)
+
+mrb_int
+mrb_utf8len(const char* p, const char* e)
+{
+ mrb_int len;
+ mrb_int i;
+
+ if ((unsigned char)*p < 0x80) return 1;
+ len = utf8len_codepage[(unsigned char)*p];
+ if (len == 1) return 1;
+ if (len > e - p) return 1;
+ for (i = 1; i < len; ++i)
+ if (utf8_islead(p[i]))
+ return 1;
+ return len;
+}
+
+mrb_int
+mrb_utf8_strlen(const char *str, mrb_int byte_len)
+{
+ mrb_int len = 0;
+ const char *p = str;
+ const char *e = p + byte_len;
+
+ while (p < e) {
+ p += mrb_utf8len(p, e);
+ len++;
+ }
+ return len;
+}
+
+static mrb_int
+utf8_strlen(mrb_value str)
{
- mrb_int slen;
struct RString *s = mrb_str_ptr(str);
+ mrb_int byte_len = RSTR_LEN(s);
- 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 */
+ if (RSTR_ASCII_P(s)) {
+ return byte_len;
+ }
+ else {
+ mrb_int utf8_len = mrb_utf8_strlen(RSTR_PTR(s), byte_len);
+ if (byte_len == utf8_len) RSTR_SET_ASCII_FLAG(s);
+ return utf8_len;
}
- return str;
}
-#define mrb_obj_alloc_string(mrb) ((struct RString*)mrb_obj_alloc((mrb), MRB_TT_STRING, (mrb)->string_class))
+#define RSTRING_CHAR_LEN(s) utf8_strlen(s)
-static struct RString*
-str_new_static(mrb_state *mrb, const char *p, size_t len)
+/* map character index to byte offset index */
+static mrb_int
+chars2bytes(mrb_value s, mrb_int off, mrb_int idx)
{
- struct RString *s;
-
- if (len >= MRB_INT_MAX) {
- mrb_raise(mrb, E_ARGUMENT_ERROR, "string size too big");
+ if (RSTR_ASCII_P(mrb_str_ptr(s))) {
+ return idx;
}
- s = mrb_obj_alloc_string(mrb);
- s->as.heap.len = len;
- s->as.heap.aux.capa = 0; /* nofree */
- s->as.heap.ptr = (char *)p;
- s->flags = MRB_STR_NOFREE;
+ else {
+ mrb_int i, b, n;
+ const char *p = RSTRING_PTR(s) + off;
+ const char *e = RSTRING_END(s);
- return s;
+ for (b=i=0; p<e && i<idx; i++) {
+ n = mrb_utf8len(p, e);
+ b += n;
+ p += n;
+ }
+ return b;
+ }
}
-static struct RString*
-str_new(mrb_state *mrb, const char *p, size_t len)
+/* map byte offset to character index */
+static mrb_int
+bytes2chars(char *p, mrb_int len, mrb_int bi)
{
- struct RString *s;
+ const char *e = p + (size_t)len;
+ const char *pivot = p + bi;
+ mrb_int i;
- if (mrb_ro_data_p(p)) {
- return str_new_static(mrb, p, len);
+ for (i = 0; p < pivot; i ++) {
+ p += mrb_utf8len(p, e);
}
- s = mrb_obj_alloc_string(mrb);
- if (len < RSTRING_EMBED_LEN_MAX) {
- RSTR_SET_EMBED_FLAG(s);
- RSTR_SET_EMBED_LEN(s, len);
- if (p) {
- memcpy(s->as.ary, p, len);
- }
- } else {
- if (len >= MRB_INT_MAX) {
- mrb_raise(mrb, E_ARGUMENT_ERROR, "string size too big");
+ if (p != pivot) return -1;
+ return i;
+}
+
+static const char *
+char_adjust(const char *beg, const char *end, const char *ptr)
+{
+ if ((ptr > beg || ptr < end) && (*ptr & 0xc0) == 0x80) {
+ const int utf8_adjust_max = 3;
+ const char *p;
+
+ if (ptr - beg > utf8_adjust_max) {
+ beg = ptr - utf8_adjust_max;
}
- s->as.heap.len = len;
- s->as.heap.aux.capa = len;
- s->as.heap.ptr = (char *)mrb_malloc(mrb, len+1);
- if (p) {
- memcpy(s->as.heap.ptr, p, len);
+
+ p = ptr;
+ while (p > beg) {
+ p --;
+ if ((*p & 0xc0) != 0x80) {
+ int clen = mrb_utf8len(p, end);
+ if (clen > ptr - p) return p;
+ break;
+ }
}
}
- RSTR_PTR(s)[len] = '\0';
- return s;
-}
-static inline void
-str_with_class(mrb_state *mrb, struct RString *s, mrb_value obj)
-{
- s->c = mrb_str_ptr(obj)->c;
+ return ptr;
}
-static mrb_value
-mrb_str_new_empty(mrb_state *mrb, mrb_value str)
+static const char *
+char_backtrack(const char *ptr, const char *end)
{
- struct RString *s = str_new(mrb, 0, 0);
+ if (ptr < end) {
+ const int utf8_bytelen_max = 4;
+ const char *p;
- str_with_class(mrb, s, str);
- return mrb_obj_value(s);
-}
+ if (end - ptr > utf8_bytelen_max) {
+ ptr = end - utf8_bytelen_max;
+ }
-#ifndef MRB_STR_BUF_MIN_SIZE
-# define MRB_STR_BUF_MIN_SIZE 128
-#endif
+ p = end;
+ while (p > ptr) {
+ p --;
+ if ((*p & 0xc0) != 0x80) {
+ int clen = utf8len_codepage[(unsigned char)*p];
+ if (clen == end - p) { return p; }
+ break;
+ }
+ }
+ }
-MRB_API mrb_value
-mrb_str_buf_new(mrb_state *mrb, size_t capa)
+ return end - 1;
+}
+
+static mrb_int
+str_index_str_by_char_search(mrb_state *mrb, const char *p, const char *pend, const char *s, const mrb_int slen, mrb_int off)
{
- struct RString *s;
+ /* Based on Quick Search algorithm (Boyer-Moore-Horspool algorithm) */
- s = mrb_obj_alloc_string(mrb);
+ ptrdiff_t qstable[1 << CHAR_BIT];
- if (capa >= MRB_INT_MAX) {
- mrb_raise(mrb, E_ARGUMENT_ERROR, "string capacity size too big");
+ /* Preprocessing */
+ {
+ mrb_int i;
+
+ for (i = 0; i < 1 << CHAR_BIT; i ++) {
+ qstable[i] = slen;
+ }
+ for (i = 0; i < slen; i ++) {
+ qstable[(unsigned char)s[i]] = slen - (i + 1);
+ }
}
- if (capa < MRB_STR_BUF_MIN_SIZE) {
- capa = MRB_STR_BUF_MIN_SIZE;
+
+ /* Searching */
+ while (p < pend && pend - p >= slen) {
+ const char *pivot;
+
+ if (memcmp(p, s, slen) == 0) {
+ return off;
+ }
+
+ pivot = p + qstable[(unsigned char)p[slen - 1]];
+ if (pivot >= pend || pivot < p /* overflowed */) { return -1; }
+
+ do {
+ p += mrb_utf8len(p, pend);
+ off ++;
+ } while (p < pivot);
}
- s->as.heap.len = 0;
- s->as.heap.aux.capa = capa;
- s->as.heap.ptr = (char *)mrb_malloc(mrb, capa+1);
- RSTR_PTR(s)[0] = '\0';
- return mrb_obj_value(s);
+ return -1;
}
-static void
-str_buf_cat(mrb_state *mrb, struct RString *s, const char *ptr, size_t len)
+static mrb_int
+str_index_str_by_char(mrb_state *mrb, mrb_value str, mrb_value sub, mrb_int pos)
{
- size_t capa;
- size_t total;
- ptrdiff_t off = -1;
+ const char *p = RSTRING_PTR(str);
+ const char *pend = p + RSTRING_LEN(str);
+ const char *s = RSTRING_PTR(sub);
+ const mrb_int slen = RSTRING_LEN(sub);
+ mrb_int off = pos;
- if (len == 0) return;
- mrb_str_modify(mrb, s);
- if (ptr >= RSTR_PTR(s) && ptr <= RSTR_PTR(s) + (size_t)RSTR_LEN(s)) {
- off = ptr - RSTR_PTR(s);
+ for (; pos > 0; pos --) {
+ if (pend - p < 1) { return -1; }
+ p += mrb_utf8len(p, pend);
}
- if (RSTR_EMBED_P(s))
- capa = RSTRING_EMBED_LEN_MAX;
- else
- capa = s->as.heap.aux.capa;
+ if (slen < 1) { return off; }
- if (RSTR_LEN(s) >= MRB_INT_MAX - (mrb_int)len) {
- mrb_raise(mrb, E_ARGUMENT_ERROR, "string size too big");
- }
- total = RSTR_LEN(s)+len;
- if (capa <= total) {
- while (total > capa) {
- if (capa + 1 >= MRB_INT_MAX / 2) {
- capa = (total + 4095) / 4096;
- break;
+ return str_index_str_by_char_search(mrb, p, pend, s, slen, off);
+}
+
+#define BYTES_ALIGN_CHECK(pos) if (pos < 0) return mrb_nil_value();
+#else
+#define RSTRING_CHAR_LEN(s) RSTRING_LEN(s)
+#define chars2bytes(p, off, ci) (ci)
+#define bytes2chars(p, end, bi) (bi)
+#define char_adjust(beg, end, ptr) (ptr)
+#define char_backtrack(ptr, end) ((end) - 1)
+#define BYTES_ALIGN_CHECK(pos)
+#define str_index_str_by_char(mrb, str, sub, pos) str_index_str(mrb, str, sub, pos)
+#endif
+
+#ifndef MRB_QS_SHORT_STRING_LENGTH
+#define MRB_QS_SHORT_STRING_LENGTH 2048
+#endif
+
+static inline mrb_int
+mrb_memsearch_qs(const unsigned char *xs, mrb_int m, const unsigned char *ys, mrb_int n)
+{
+ if (n + m < MRB_QS_SHORT_STRING_LENGTH) {
+ const unsigned char *y = ys;
+ const unsigned char *ye = ys+n-m+1;
+
+ for (;;) {
+ y = (const unsigned char*)memchr(y, xs[0], (size_t)(ye-y));
+ if (y == NULL) return -1;
+ if (memcmp(xs, y, m) == 0) {
+ return (mrb_int)(y - ys);
}
- capa = (capa + 1) * 2;
+ y++;
}
- resize_capa(mrb, s, capa);
+ return -1;
}
- if (off != -1) {
- ptr = RSTR_PTR(s) + off;
+ else {
+ const unsigned char *x = xs, *xe = xs + m;
+ const unsigned char *y = ys;
+ int i;
+ ptrdiff_t 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 (mrb_int)(y - ys);
+ }
+ return -1;
}
- memcpy(RSTR_PTR(s) + RSTR_LEN(s), ptr, len);
- mrb_assert_int_fit(size_t, total, mrb_int, MRB_INT_MAX);
- RSTR_SET_LEN(s, total);
- RSTR_PTR(s)[total] = '\0'; /* sentinel */
}
-MRB_API mrb_value
-mrb_str_new(mrb_state *mrb, const char *p, size_t len)
+static mrb_int
+mrb_memsearch(const void *x0, mrb_int m, const void *y0, mrb_int n)
{
- return mrb_obj_value(str_new(mrb, p, len));
-}
+ const unsigned char *x = (const unsigned char *)x0, *y = (const unsigned char *)y0;
-/*
- * call-seq: (Caution! NULL string)
- * String.new(str="") => new_str
- *
- * Returns a new string object containing a copy of <i>str</i>.
- */
+ 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 = (const unsigned char *)memchr(y, *x, n);
-MRB_API mrb_value
-mrb_str_new_cstr(mrb_state *mrb, const char *p)
+ if (ys)
+ return (mrb_int)(ys - y);
+ else
+ return -1;
+ }
+ return mrb_memsearch_qs((const unsigned char *)x0, m, (const unsigned char *)y0, n);
+}
+
+static void
+str_share(mrb_state *mrb, struct RString *orig, struct RString *s)
{
- struct RString *s;
- size_t len;
+ size_t len = (size_t)orig->as.heap.len;
- if (p) {
- len = strlen(p);
+ mrb_assert(!RSTR_EMBED_P(orig));
+ if (RSTR_NOFREE_P(orig)) {
+ str_init_nofree(s, orig->as.heap.ptr, len);
+ }
+ else if (RSTR_SHARED_P(orig)) {
+ str_init_shared(mrb, orig, s, orig->as.heap.aux.shared);
+ }
+ else if (RSTR_FSHARED_P(orig)) {
+ str_init_fshared(orig, s, orig->as.heap.aux.fshared);
}
else {
- len = 0;
+ if (orig->as.heap.aux.capa > orig->as.heap.len) {
+ orig->as.heap.ptr = (char *)mrb_realloc(mrb, orig->as.heap.ptr, len+1);
+ orig->as.heap.aux.capa = (mrb_ssize)len;
+ }
+ str_init_shared(mrb, orig, s, NULL);
+ str_init_shared(mrb, orig, orig, s->as.heap.aux.shared);
}
+}
- s = str_new(mrb, p, len);
+mrb_value
+mrb_str_byte_subseq(mrb_state *mrb, mrb_value str, mrb_int beg, mrb_int len)
+{
+ struct RString *orig, *s;
+ orig = mrb_str_ptr(str);
+ s = mrb_obj_alloc_string(mrb);
+ if (RSTR_EMBEDDABLE_P(len)) {
+ str_init_embed(s, RSTR_PTR(orig)+beg, len);
+ }
+ else {
+ str_share(mrb, orig, s);
+ s->as.heap.ptr += (mrb_ssize)beg;
+ s->as.heap.len = (mrb_ssize)len;
+ }
+ RSTR_COPY_ASCII_FLAG(s, orig);
return mrb_obj_value(s);
}
-MRB_API mrb_value
-mrb_str_new_static(mrb_state *mrb, const char *p, size_t len)
+static void
+str_range_to_bytes(mrb_value str, mrb_int *pos, mrb_int *len)
{
- struct RString *s = str_new_static(mrb, p, len);
- return mrb_obj_value(s);
+ *pos = chars2bytes(str, 0, *pos);
+ *len = chars2bytes(str, *pos, *len);
+}
+#ifdef MRB_UTF8_STRING
+static inline mrb_value
+str_subseq(mrb_state *mrb, mrb_value str, mrb_int beg, mrb_int len)
+{
+ str_range_to_bytes(str, &beg, &len);
+ return mrb_str_byte_subseq(mrb, str, beg, len);
}
+#else
+#define str_subseq(mrb, str, beg, len) mrb_str_byte_subseq(mrb, str, beg, len)
+#endif
-void
-mrb_gc_free_str(mrb_state *mrb, struct RString *str)
+mrb_bool
+mrb_str_beg_len(mrb_int str_len, mrb_int *begp, mrb_int *lenp)
{
- if (RSTR_EMBED_P(str))
- /* no code */;
- else if (RSTR_SHARED_P(str))
- str_decref(mrb, str->as.heap.aux.shared);
- else if (!RSTR_NOFREE_P(str))
- mrb_free(mrb, str->as.heap.ptr);
+ if (str_len < *begp || *lenp < 0) return FALSE;
+ if (*begp < 0) {
+ *begp += str_len;
+ if (*begp < 0) return FALSE;
+ }
+ if (*lenp > str_len - *begp)
+ *lenp = str_len - *begp;
+ if (*lenp <= 0) {
+ *lenp = 0;
+ }
+ return TRUE;
}
-MRB_API char*
-mrb_str_to_cstr(mrb_state *mrb, mrb_value str0)
+static mrb_value
+str_substr(mrb_state *mrb, mrb_value str, mrb_int beg, mrb_int len)
{
- struct RString *s;
+ return mrb_str_beg_len(RSTRING_CHAR_LEN(str), &beg, &len) ?
+ str_subseq(mrb, str, beg, len) : mrb_nil_value();
+}
- if (!mrb_string_p(str0)) {
- mrb_raise(mrb, E_TYPE_ERROR, "expected String");
+MRB_API mrb_int
+mrb_str_index(mrb_state *mrb, mrb_value str, const char *sptr, mrb_int slen, mrb_int offset)
+{
+ mrb_int pos;
+ char *s;
+ mrb_int len;
+
+ len = RSTRING_LEN(str);
+ 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 */
+ len = RSTRING_LEN(str) - offset;
+ pos = mrb_memsearch(sptr, slen, s, len);
+ if (pos < 0) return pos;
+ return pos + offset;
+}
+
+static mrb_int
+str_index_str(mrb_state *mrb, mrb_value str, mrb_value str2, mrb_int offset)
+{
+ const char *ptr;
+ mrb_int len;
+
+ ptr = RSTRING_PTR(str2);
+ len = RSTRING_LEN(str2);
+
+ return mrb_str_index(mrb, str, ptr, len, offset);
+}
+
+static mrb_value
+str_replace(mrb_state *mrb, struct RString *s1, struct RString *s2)
+{
+ size_t len;
+
+ mrb_check_frozen(mrb, s1);
+ if (s1 == s2) return mrb_obj_value(s1);
+ RSTR_COPY_ASCII_FLAG(s1, s2);
+ if (RSTR_SHARED_P(s1)) {
+ str_decref(mrb, s1->as.heap.aux.shared);
+ }
+ else if (!RSTR_EMBED_P(s1) && !RSTR_NOFREE_P(s1) && !RSTR_FSHARED_P(s1)
+ && s1->as.heap.ptr) {
+ mrb_free(mrb, s1->as.heap.ptr);
}
- 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");
+ len = (size_t)RSTR_LEN(s2);
+ if (RSTR_EMBEDDABLE_P(len)) {
+ str_init_embed(s1, RSTR_PTR(s2), len);
}
- return RSTR_PTR(s);
+ else {
+ str_share(mrb, s2, s1);
+ }
+
+ return mrb_obj_value(s1);
}
-static void
-str_make_shared(mrb_state *mrb, struct RString *s)
+static mrb_int
+str_rindex(mrb_state *mrb, mrb_value str, mrb_value sub, mrb_int pos)
{
- if (!RSTR_SHARED_P(s)) {
- mrb_shared_string *shared = (mrb_shared_string *)mrb_malloc(mrb, sizeof(mrb_shared_string));
+ const char *s, *sbeg, *t;
+ struct RString *ps = mrb_str_ptr(str);
+ mrb_int len = RSTRING_LEN(sub);
- shared->refcnt = 1;
- if (RSTR_EMBED_P(s)) {
- const mrb_int len = RSTR_EMBED_LEN(s);
- char *const tmp = (char *)mrb_malloc(mrb, len+1);
- memcpy(tmp, s->as.ary, len);
- tmp[len] = '\0';
- RSTR_UNSET_EMBED_FLAG(s);
- s->as.heap.ptr = tmp;
- s->as.heap.len = len;
- shared->nofree = FALSE;
- shared->ptr = s->as.heap.ptr;
- }
- else if (RSTR_NOFREE_P(s)) {
- shared->nofree = TRUE;
- shared->ptr = s->as.heap.ptr;
- RSTR_UNSET_NOFREE_FLAG(s);
- }
- else {
- shared->nofree = FALSE;
- if (s->as.heap.aux.capa > s->as.heap.len) {
- s->as.heap.ptr = shared->ptr = (char *)mrb_realloc(mrb, s->as.heap.ptr, s->as.heap.len+1);
- }
- else {
- shared->ptr = s->as.heap.ptr;
+ /* 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) {
+ s = char_adjust(sbeg, sbeg + RSTR_LEN(ps), s);
+ while (sbeg <= s) {
+ if (memcmp(s, t, len) == 0) {
+ return (mrb_int)(s - RSTR_PTR(ps));
}
+ s = char_backtrack(sbeg, s);
}
- shared->len = s->as.heap.len;
- s->as.heap.aux.shared = shared;
- RSTR_SET_SHARED_FLAG(s);
+ return -1;
+ }
+ else {
+ return pos;
}
}
-/*
- * call-seq: (Caution! String("abcd") change)
- * String("abcdefg") = String("abcd") + String("efg")
- *
- * Returns a new string object containing a copy of <i>str</i>.
- */
-MRB_API void
-mrb_str_concat(mrb_state *mrb, mrb_value self, mrb_value other)
+MRB_API mrb_int
+mrb_str_strlen(mrb_state *mrb, struct RString *s)
{
- struct RString *s1 = mrb_str_ptr(self), *s2;
- mrb_int len;
+ mrb_int i, max = RSTR_LEN(s);
+ char *p = RSTR_PTR(s);
- mrb_str_modify(mrb, s1);
- if (!mrb_string_p(other)) {
- other = mrb_str_to_str(mrb, other);
+ if (!p) return 0;
+ for (i=0; i<max; i++) {
+ if (p[i] == '\0') {
+ mrb_raise(mrb, E_ARGUMENT_ERROR, "string contains null byte");
+ }
}
- s2 = mrb_str_ptr(other);
- len = RSTR_LEN(s1) + RSTR_LEN(s2);
+ return max;
+}
+
+#ifdef _WIN32
+#include <windows.h>
+
+char*
+mrb_utf8_from_locale(const char *str, int len)
+{
+ wchar_t* wcsp;
+ char* mbsp;
+ int mbssize, wcssize;
+
+ if (len == 0)
+ return strdup("");
+ if (len == -1)
+ len = (int)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, int len)
+{
+ wchar_t* wcsp;
+ char* mbsp;
+ int mbssize, wcssize;
+
+ if (len == 0)
+ return strdup("");
+ if (len == -1)
+ len = (int)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_keep_ascii(mrb_state *mrb, struct RString *s)
+{
+ mrb_check_frozen(mrb, s);
+ str_modify_keep_ascii(mrb, s);
+}
- if (RSTRING_CAPA(self) < len) {
- resize_capa(mrb, s1, len);
+MRB_API void
+mrb_str_modify(mrb_state *mrb, struct RString *s)
+{
+ mrb_str_modify_keep_ascii(mrb, s);
+ RSTR_UNSET_ASCII_FLAG(s);
+}
+
+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);
+
+ if (len < 0) {
+ mrb_raise(mrb, E_ARGUMENT_ERROR, "negative (or overflowed) string size");
}
- memcpy(RSTR_PTR(s1)+RSTR_LEN(s1), RSTR_PTR(s2), RSTR_LEN(s2));
- RSTR_SET_LEN(s1, len);
- RSTR_PTR(s1)[len] = '\0';
+ 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;
+
+ check_null_byte(mrb, str0);
+ s = str_new(mrb, RSTRING_PTR(str0), RSTRING_LEN(str0));
+ return RSTR_PTR(s);
+}
+
+MRB_API void
+mrb_str_concat(mrb_state *mrb, mrb_value self, mrb_value other)
+{
+ other = mrb_obj_as_string(mrb, other);
+ mrb_str_cat_str(mrb, self, other);
}
-/*
- * call-seq: (Caution! String("abcd") remain)
- * String("abcdefg") = String("abcd") + String("efg")
- *
- * Returns a new string object containing a copy of <i>str</i>.
- */
MRB_API mrb_value
mrb_str_plus(mrb_state *mrb, mrb_value a, mrb_value b)
{
@@ -422,10 +868,13 @@ mrb_str_plus(mrb_state *mrb, mrb_value a, mrb_value b)
/* 15.2.10.5.2 */
/*
- * call-seq: (Caution! String("abcd") remain) for stack_argument
- * String("abcdefg") = String("abcd") + String("efg")
+ * call-seq:
+ * str + other_str -> new_str
*
- * Returns a new string object containing a copy of <i>str</i>.
+ * Concatenation---Returns a new <code>String</code> containing
+ * <i>other_str</i> concatenated to <i>str</i>.
+ *
+ * "Hello from " + self.to_s #=> "Hello from main"
*/
static mrb_value
mrb_str_plus_m(mrb_state *mrb, mrb_value self)
@@ -440,15 +889,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 */
@@ -472,13 +928,12 @@ mrb_str_times(mrb_state *mrb, mrb_value self)
if (times < 0) {
mrb_raise(mrb, E_ARGUMENT_ERROR, "negative argument");
}
- if (times && MRB_INT_MAX / times < RSTRING_LEN(self)) {
+ if (times && MRB_SSIZE_MAX / times < RSTRING_LEN(self)) {
mrb_raise(mrb, E_ARGUMENT_ERROR, "argument too big");
}
len = RSTRING_LEN(self)*times;
str2 = str_new(mrb, 0, len);
- str_with_class(mrb, str2, self);
p = RSTR_PTR(str2);
if (len > 0) {
n = RSTRING_LEN(self);
@@ -490,6 +945,7 @@ mrb_str_times(mrb_state *mrb, mrb_value self)
memcpy(p + n, p, len-n);
}
p[RSTR_LEN(str2)] = '\0';
+ RSTR_COPY_ASCII_FLAG(str2, mrb_str_ptr(self));
return mrb_obj_value(str2);
}
@@ -553,26 +1009,11 @@ mrb_str_cmp(mrb_state *mrb, mrb_value str1, mrb_value str2)
static mrb_value
mrb_str_cmp_m(mrb_state *mrb, mrb_value str1)
{
- mrb_value str2;
+ mrb_value str2 = mrb_get_arg1(mrb);
mrb_int result;
- mrb_get_args(mrb, "o", &str2);
if (!mrb_string_p(str2)) {
- if (!mrb_respond_to(mrb, str2, mrb_intern_lit(mrb, "to_s"))) {
- return mrb_nil_value();
- }
- else if (!mrb_respond_to(mrb, str2, mrb_intern_lit(mrb, "<=>"))) {
- return mrb_nil_value();
- }
- else {
- mrb_value tmp = mrb_funcall(mrb, str2, "<=>", 1, str1);
-
- if (mrb_nil_p(tmp)) return mrb_nil_value();
- if (!mrb_fixnum(tmp)) {
- return mrb_funcall(mrb, mrb_fixnum_value(0), "-", 1, tmp);
- }
- result = -mrb_fixnum(tmp);
- }
+ return mrb_nil_value();
}
else {
result = mrb_str_cmp(mrb, str1, str2);
@@ -594,15 +1035,7 @@ str_eql(mrb_state *mrb, const mrb_value str1, const mrb_value str2)
MRB_API mrb_bool
mrb_str_equal(mrb_state *mrb, mrb_value str1, mrb_value str2)
{
- if (mrb_immediate_p(str2)) return FALSE;
- if (!mrb_string_p(str2)) {
- if (mrb_nil_p(str2)) return FALSE;
- if (!mrb_respond_to(mrb, str2, mrb_intern_lit(mrb, "to_str"))) {
- return FALSE;
- }
- str2 = mrb_funcall(mrb, str2, "to_str", 0);
- return mrb_equal(mrb, str2, str1);
- }
+ if (!mrb_string_p(str2)) return FALSE;
return str_eql(mrb, str1, str2);
}
@@ -620,171 +1053,122 @@ mrb_str_equal(mrb_state *mrb, mrb_value str1, mrb_value str2)
static mrb_value
mrb_str_equal_m(mrb_state *mrb, mrb_value str1)
{
- mrb_value str2;
-
- mrb_get_args(mrb, "o", &str2);
+ mrb_value str2 = mrb_get_arg1(mrb);
return mrb_bool_value(mrb_str_equal(mrb, str1, str2));
}
/* ---------------------------------- */
-MRB_API mrb_value
-mrb_str_to_str(mrb_state *mrb, mrb_value str)
-{
- mrb_value s;
-
- if (!mrb_string_p(str)) {
- s = mrb_check_convert_type(mrb, str, MRB_TT_STRING, "String", "to_str");
- if (mrb_nil_p(s)) {
- s = mrb_convert_type(mrb, str, MRB_TT_STRING, "String", "to_s");
- }
- return s;
- }
- return str;
-}
+/* obslete: use RSTRING_PTR() */
MRB_API const char*
-mrb_string_value_ptr(mrb_state *mrb, mrb_value ptr)
+mrb_string_value_ptr(mrb_state *mrb, mrb_value str)
{
- mrb_value str = mrb_str_to_str(mrb, ptr);
+ str = mrb_obj_as_string(mrb, str);
return RSTRING_PTR(str);
}
-void
-mrb_noregexp(mrb_state *mrb, mrb_value self)
+/* obslete: use RSTRING_LEN() */
+MRB_API mrb_int
+mrb_string_value_len(mrb_state *mrb, mrb_value ptr)
{
- mrb_raise(mrb, E_NOTIMP_ERROR, "Regexp class not implemented");
+ mrb_to_str(mrb, ptr);
+ return RSTRING_LEN(ptr);
}
-void
-mrb_regexp_check(mrb_state *mrb, mrb_value obj)
+MRB_API mrb_value
+mrb_str_dup(mrb_state *mrb, mrb_value str)
{
- if (mrb_regexp_p(mrb, obj)) {
- mrb_noregexp(mrb, obj);
- }
+ struct RString *s = mrb_str_ptr(str);
+ struct RString *dup = str_new(mrb, 0, 0);
+
+ return str_replace(mrb, dup, s);
}
-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];
+enum str_convert_range {
+ /* `beg` and `len` are byte unit in `0 ... str.bytesize` */
+ STR_BYTE_RANGE_CORRECTED = 1,
- /* 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;
-}
+ /* `beg` and `len` are char unit in any range */
+ STR_CHAR_RANGE = 2,
-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;
+ /* `beg` and `len` are char unit in `0 ... str.size` */
+ STR_CHAR_RANGE_CORRECTED = 3,
- 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);
-}
+ /* `beg` is out of range */
+ STR_OUT_OF_RANGE = -1
+};
-static mrb_int
-mrb_str_index(mrb_state *mrb, mrb_value str, mrb_value sub, mrb_int offset)
+static enum str_convert_range
+str_convert_range(mrb_state *mrb, mrb_value str, mrb_value indx, mrb_value alen, mrb_int *beg, mrb_int *len)
{
- 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 (!mrb_undef_p(alen)) {
+ *beg = mrb_as_int(mrb, indx);
+ *len = mrb_as_int(mrb, alen);
+ return STR_CHAR_RANGE;
}
- 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;
-}
+ else {
+ switch (mrb_type(indx)) {
+ case MRB_TT_INTEGER:
+ *beg = mrb_integer(indx);
+ *len = 1;
+ return STR_CHAR_RANGE;
-MRB_API mrb_value
-mrb_str_dup(mrb_state *mrb, mrb_value str)
-{
- struct RString *s = mrb_str_ptr(str);
- struct RString *dup = str_new(mrb, 0, 0);
+ case MRB_TT_STRING:
+ *beg = str_index_str(mrb, str, indx, 0);
+ if (*beg < 0) { break; }
+ *len = RSTRING_LEN(indx);
+ return STR_BYTE_RANGE_CORRECTED;
- str_with_class(mrb, dup, str);
- return str_replace(mrb, dup, s);
+ case MRB_TT_RANGE:
+ goto range_arg;
+
+ default:
+ indx = mrb_to_int(mrb, indx);
+ if (mrb_integer_p(indx)) {
+ *beg = mrb_integer(indx);
+ *len = 1;
+ return STR_CHAR_RANGE;
+ }
+range_arg:
+ *len = RSTRING_CHAR_LEN(str);
+ switch (mrb_range_beg_len(mrb, indx, beg, len, *len, TRUE)) {
+ case MRB_RANGE_OK:
+ return STR_CHAR_RANGE_CORRECTED;
+ case MRB_RANGE_OUT:
+ return STR_OUT_OF_RANGE;
+ default:
+ break;
+ }
+
+ mrb_raise(mrb, E_TYPE_ERROR, "can't convert to Integer");
+ }
+ }
+ return STR_OUT_OF_RANGE;
}
static mrb_value
-mrb_str_aref(mrb_state *mrb, mrb_value str, mrb_value indx)
+mrb_str_aref(mrb_state *mrb, mrb_value str, mrb_value indx, mrb_value alen)
{
- 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);
+ mrb_int beg, len;
-num_index:
- str = mrb_str_substr(mrb, str, idx, 1);
- if (!mrb_nil_p(str) && RSTRING_LEN(str) == 0) return mrb_nil_value();
+ switch (str_convert_range(mrb, str, indx, alen, &beg, &len)) {
+ case STR_CHAR_RANGE_CORRECTED:
+ return str_subseq(mrb, str, beg, len);
+ case STR_CHAR_RANGE:
+ str = str_substr(mrb, str, beg, len);
+ if (mrb_undef_p(alen) && !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)
+ case STR_BYTE_RANGE_CORRECTED:
+ if (mrb_string_p(indx)) {
return mrb_str_dup(mrb, indx);
- return mrb_nil_value();
-
- case MRB_TT_RANGE:
- /* check if indx is Range */
- {
- mrb_int beg, len;
-
- len = RSTRING_LEN(str);
- if (mrb_range_beg_len(mrb, indx, &beg, &len, len)) {
- return mrb_str_subseq(mrb, str, beg, len);
- }
- else {
- return mrb_nil_value();
- }
}
+ else {
+ return mrb_str_byte_subseq(mrb, str, beg, len);
+ }
+ case STR_OUT_OF_RANGE:
default:
- idx = mrb_fixnum(indx);
- goto num_index;
+ return mrb_nil_value();
}
- return mrb_nil_value(); /* not reached */
}
/* 15.2.10.5.6 */
@@ -794,16 +1178,14 @@ num_index:
* str[fixnum] => fixnum or nil
* str[fixnum, fixnum] => new_str or nil
* str[range] => new_str or nil
- * str[regexp] => new_str or nil
- * str[regexp, fixnum] => new_str or nil
* str[other_str] => new_str or nil
* str.slice(fixnum) => fixnum or nil
* str.slice(fixnum, fixnum) => new_str or nil
* str.slice(range) => new_str or nil
* str.slice(other_str) => new_str or nil
*
- * Element Reference---If passed a single <code>Fixnum</code>, returns the code
- * of the character at that position. If passed two <code>Fixnum</code>
+ * Element Reference---If passed a single <code>Integer</code>, returns the code
+ * of the character at that position. If passed two <code>Integer</code>
* objects, returns a substring starting at the offset given by the first, and
* a length given by the second. If given a range, a substring containing
* characters at offsets given by the range is returned. In all three cases, if
@@ -831,17 +1213,198 @@ 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 mrb_str_substr(mrb, str, mrb_fixnum(a1), mrb_fixnum(a2));
+ if (mrb_get_args(mrb, "o|o", &a1, &a2) == 1) {
+ a2 = mrb_undef_value();
+ }
+
+ return mrb_str_aref(mrb, str, a1, a2);
+}
+
+static mrb_noreturn void
+str_out_of_index(mrb_state *mrb, mrb_value index)
+{
+ mrb_raisef(mrb, E_INDEX_ERROR, "index %v out of string", index);
+}
+
+static mrb_value
+str_replace_partial(mrb_state *mrb, mrb_value src, mrb_int pos, mrb_int end, mrb_value rep)
+{
+ const mrb_int shrink_threshold = 256;
+ struct RString *str = mrb_str_ptr(src);
+ mrb_int len = RSTR_LEN(str);
+ mrb_int replen, newlen;
+ char *strp;
+
+ if (end > len) { end = len; }
+
+ if (pos < 0 || pos > len) {
+ str_out_of_index(mrb, mrb_fixnum_value(pos));
+ }
+
+ replen = (mrb_nil_p(rep) ? 0 : RSTRING_LEN(rep));
+ newlen = replen + (len - (end - pos));
+
+ if (newlen >= MRB_SSIZE_MAX || newlen < replen /* overflowed */) {
+ mrb_raise(mrb, E_RUNTIME_ERROR, "string size too big");
+ }
+
+ mrb_str_modify(mrb, str);
+
+ if (len < newlen) {
+ resize_capa(mrb, str, newlen);
+ }
+
+ strp = RSTR_PTR(str);
+
+ memmove(strp + newlen - (len - end), strp + end, len - end);
+ if (!mrb_nil_p(rep)) {
+ memmove(strp + pos, RSTRING_PTR(rep), replen);
+ }
+ RSTR_SET_LEN(str, newlen);
+ strp[newlen] = '\0';
+
+ if (len - newlen >= shrink_threshold) {
+ resize_capa(mrb, str, newlen);
+ }
+
+ return src;
+}
+
+#define IS_EVSTR(p,e) ((p) < (e) && (*(p) == '$' || *(p) == '@' || *(p) == '{'))
+
+static mrb_value
+str_escape(mrb_state *mrb, mrb_value str, mrb_bool inspect)
+{
+ const char *p, *pend;
+ char buf[4]; /* `\x??` or UTF-8 character */
+ mrb_value result = mrb_str_new_lit(mrb, "\"");
+#ifdef MRB_UTF8_STRING
+ uint32_t ascii_flag = MRB_STR_ASCII;
+#endif
+
+ p = RSTRING_PTR(str); pend = RSTRING_END(str);
+ for (;p < pend; p++) {
+ unsigned char c, cc;
+#ifdef MRB_UTF8_STRING
+ if (inspect) {
+ mrb_int clen = mrb_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;
+ ascii_flag = 0;
+ continue;
+ }
+ }
+#endif
+ c = *p;
+ if (c == '"'|| c == '\\' || (c == '#' && IS_EVSTR(p+1, pend))) {
+ buf[0] = '\\'; buf[1] = c;
+ mrb_str_cat(mrb, result, buf, 2);
+ continue;
+ }
+ if (ISPRINT(c)) {
+ buf[0] = c;
+ mrb_str_cat(mrb, result, buf, 1);
+ continue;
+ }
+ switch (c) {
+ case '\n': cc = 'n'; break;
+ case '\r': cc = 'r'; break;
+ case '\t': cc = 't'; break;
+ case '\f': cc = 'f'; break;
+ case '\013': cc = 'v'; break;
+ case '\010': cc = 'b'; break;
+ case '\007': cc = 'a'; break;
+ case 033: cc = 'e'; break;
+ default: cc = 0; break;
+ }
+ if (cc) {
+ buf[0] = '\\';
+ buf[1] = (char)cc;
+ mrb_str_cat(mrb, result, buf, 2);
+ continue;
+ }
+ else {
+ buf[0] = '\\';
+ buf[1] = 'x';
+ buf[3] = mrb_digitmap[c % 16]; c /= 16;
+ buf[2] = mrb_digitmap[c % 16];
+ mrb_str_cat(mrb, result, buf, 4);
+ continue;
+ }
+ }
+ mrb_str_cat_lit(mrb, result, "\"");
+#ifdef MRB_UTF8_STRING
+ if (inspect) {
+ mrb_str_ptr(str)->flags |= ascii_flag;
+ mrb_str_ptr(result)->flags |= ascii_flag;
+ }
+ else {
+ RSTR_SET_ASCII_FLAG(mrb_str_ptr(result));
+ }
+#endif
+
+ return result;
+}
+
+static void
+mrb_str_aset(mrb_state *mrb, mrb_value str, mrb_value indx, mrb_value alen, mrb_value replace)
+{
+ mrb_int beg, len, charlen;
+
+ mrb_to_str(mrb, replace);
+
+ switch (str_convert_range(mrb, str, indx, alen, &beg, &len)) {
+ case STR_OUT_OF_RANGE:
+ default:
+ mrb_raise(mrb, E_INDEX_ERROR, "string not matched");
+ case STR_CHAR_RANGE:
+ if (len < 0) {
+ mrb_raisef(mrb, E_INDEX_ERROR, "negative length %v", alen);
+ }
+ charlen = RSTRING_CHAR_LEN(str);
+ if (beg < 0) { beg += charlen; }
+ if (beg < 0 || beg > charlen) { str_out_of_index(mrb, indx); }
+ /* fall through */
+ case STR_CHAR_RANGE_CORRECTED:
+ str_range_to_bytes(str, &beg, &len);
+ /* fall through */
+ case STR_BYTE_RANGE_CORRECTED:
+ str_replace_partial(mrb, str, beg, beg + len, replace);
}
- if (argc != 1) {
- mrb_raisef(mrb, E_ARGUMENT_ERROR, "wrong number of arguments (%S for 1)", mrb_fixnum_value(argc));
+}
+
+/*
+ * call-seq:
+ * str[fixnum] = replace
+ * str[fixnum, fixnum] = replace
+ * str[range] = replace
+ * str[other_str] = replace
+ *
+ * Modify +self+ by replacing the content of +self+.
+ * The portion of the string affected is determined using the same criteria as +String#[]+.
+ */
+static mrb_value
+mrb_str_aset_m(mrb_state *mrb, mrb_value str)
+{
+ mrb_value indx, alen, replace;
+
+ switch (mrb_get_args(mrb, "oo|S!", &indx, &alen, &replace)) {
+ case 2:
+ replace = alen;
+ alen = mrb_undef_value();
+ break;
+ case 3:
+ break;
}
- return mrb_str_aref(mrb, str, a1);
+ mrb_str_aset(mrb, str, indx, alen, replace);
+ return str;
}
/* 15.2.10.5.8 */
@@ -864,7 +1427,7 @@ mrb_str_capitalize_bang(mrb_state *mrb, mrb_value str)
mrb_bool modify = FALSE;
struct RString *s = mrb_str_ptr(str);
- mrb_str_modify(mrb, s);
+ mrb_str_modify_keep_ascii(mrb, s);
if (RSTR_LEN(s) == 0 || !RSTR_PTR(s)) return mrb_nil_value();
p = RSTR_PTR(s); pend = RSTR_PTR(s) + RSTR_LEN(s);
if (ISLOWER(*p)) {
@@ -906,7 +1469,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.
@@ -919,11 +1482,13 @@ mrb_str_chomp_bang(mrb_state *mrb, mrb_value str)
char *p, *pp;
mrb_int rslen;
mrb_int len;
+ mrb_int argc;
struct RString *s = mrb_str_ptr(str);
- mrb_str_modify(mrb, s);
+ argc = mrb_get_args(mrb, "|S", &rs);
+ mrb_str_modify_keep_ascii(mrb, s);
len = RSTR_LEN(s);
- if (mrb_get_args(mrb, "|S", &rs) == 0) {
+ if (argc == 0) {
if (len == 0) return mrb_nil_value();
smart_chomp:
if (RSTR_PTR(s)[len-1] == '\n') {
@@ -980,12 +1545,11 @@ 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
- * changed from the default Ruby record separator, then <code>chomp</code> also
- * removes carriage return characters (that is it will remove <code>\n</code>,
+ * from the end of <i>str</i> (if present). <code>chomp</code> also removes
+ * carriage return characters (that is it will remove <code>\n</code>,
* <code>\r</code>, and <code>\r\n</code>).
*
* "hello".chomp #=> "hello"
@@ -1020,10 +1584,21 @@ mrb_str_chop_bang(mrb_state *mrb, mrb_value str)
{
struct RString *s = mrb_str_ptr(str);
- mrb_str_modify(mrb, s);
+ mrb_str_modify_keep_ascii(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 = mrb_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') {
@@ -1078,7 +1653,7 @@ mrb_str_downcase_bang(mrb_state *mrb, mrb_value str)
mrb_bool modify = FALSE;
struct RString *s = mrb_str_ptr(str);
- mrb_str_modify(mrb, s);
+ mrb_str_modify_keep_ascii(mrb, s);
p = RSTR_PTR(s);
pend = RSTR_PTR(s) + RSTR_LEN(s);
while (p < pend) {
@@ -1100,7 +1675,7 @@ mrb_str_downcase_bang(mrb_state *mrb, mrb_value str)
*
* Returns a copy of <i>str</i> with all uppercase letters replaced with their
* lowercase counterparts. The operation is locale insensitive---only
- * characters ``A'' to ``Z'' are affected.
+ * characters 'A' to 'Z' are affected.
*
* "hEllO".downcase #=> "hello"
*/
@@ -1142,72 +1717,38 @@ mrb_str_empty_p(mrb_state *mrb, mrb_value self)
static mrb_value
mrb_str_eql(mrb_state *mrb, mrb_value self)
{
- mrb_value str2;
+ mrb_value str2 = mrb_get_arg1(mrb);
mrb_bool eql_p;
- mrb_get_args(mrb, "o", &str2);
- eql_p = (mrb_type(str2) == MRB_TT_STRING) && str_eql(mrb, self, str2);
+ eql_p = (mrb_string_p(str2)) && str_eql(mrb, self, str2);
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
+uint32_t
mrb_str_hash(mrb_state *mrb, mrb_value str)
{
/* 1-8-7 */
struct RString *s = mrb_str_ptr(str);
mrb_int len = RSTR_LEN(s);
char *p = RSTR_PTR(s);
- mrb_int key = 0;
+ uint32_t hash = 0;
- while (len--) {
- key = key*65599 + *p;
- p++;
+ for(int i = 0; i < len; ++i) {
+ hash += p[i];
+ hash += (hash << 10);
+ hash ^= (hash >> 6);
}
- return key + (key>>5);
+ hash += (hash << 3);
+ hash ^= (hash >> 11);
+ hash += (hash << 15);
+ return hash;
}
/* 15.2.10.5.20 */
@@ -1240,146 +1781,52 @@ mrb_str_hash_m(mrb_state *mrb, mrb_value self)
static mrb_value
mrb_str_include(mrb_state *mrb, mrb_value self)
{
- mrb_int i;
mrb_value str2;
- mrb_bool include_p;
-
- mrb_get_args(mrb, "o", &str2);
- if (mrb_fixnum_p(str2)) {
- include_p = (memchr(RSTRING_PTR(self), mrb_fixnum(str2), RSTRING_LEN(self)) != NULL);
- }
- else {
- str2 = mrb_str_to_str(mrb, str2);
- i = mrb_str_index(mrb, self, str2, 0);
- include_p = (i != -1);
- }
-
- return mrb_bool_value(include_p);
+ mrb_get_args(mrb, "S", &str2);
+ if (str_index_str(mrb, self, str2, 0) < 0)
+ return mrb_bool_value(FALSE);
+ return mrb_bool_value(TRUE);
}
/* 15.2.10.5.22 */
/*
* call-seq:
* str.index(substring [, offset]) => fixnum or nil
- * str.index(fixnum [, offset]) => fixnum or nil
- * str.index(regexp [, offset]) => fixnum or nil
*
* Returns the index of the first 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.
+ * <i>substring</i>. Returns <code>nil</code> if not found.
* If the second parameter is present, it
* specifies the position in the string to begin the search.
*
- * "hello".index('e') #=> 1
+ * "hello".index('l') #=> 2
* "hello".index('lo') #=> 3
* "hello".index('a') #=> nil
- * "hello".index(101) #=> 1(101=0x65='e')
- * "hello".index(/[aeiou]/, -3) #=> 4
+ * "hello".index('l', -2) #=> 3
*/
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 {
+ if (mrb_get_args(mrb, "S|i", &sub, &pos) == 1) {
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);
+ else if (pos < 0) {
+ mrb_int clen = RSTRING_CHAR_LEN(str);
+ pos += clen;
if (pos < 0) {
return mrb_nil_value();
}
}
-
- switch (mrb_type(sub)) {
- case MRB_TT_FIXNUM: {
- 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;
-
- 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 = mrb_str_index(mrb, str, sub, pos);
- break;
- }
+ pos = str_index_str_by_char(mrb, str, sub, pos);
if (pos == -1) return mrb_nil_value();
+ BYTES_ALIGN_CHECK(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 */
/*
@@ -1410,9 +1857,11 @@ mrb_str_init(mrb_state *mrb, mrb_value self)
{
mrb_value str2;
- if (mrb_get_args(mrb, "|S", &str2) == 1) {
- str_replace(mrb, mrb_str_ptr(self), mrb_str_ptr(str2));
+ if (mrb_get_args(mrb, "|S", &str2) == 0) {
+ struct RString *s = str_new(mrb, 0, 0);
+ str2 = mrb_obj_value(s);
}
+ str_replace(mrb, mrb_str_ptr(self), mrb_str_ptr(str2));
return self;
}
@@ -1424,7 +1873,7 @@ mrb_str_init(mrb_state *mrb, mrb_value self)
* str.to_sym => symbol
*
* Returns the <code>Symbol</code> corresponding to <i>str</i>, creating the
- * symbol if it did not previously exist. See <code>Symbol#id2name</code>.
+ * symbol if it did not previously exist.
*
* "Koala".intern #=> :Koala
* s = 'cat'.to_sym #=> :cat
@@ -1446,15 +1895,20 @@ mrb_str_intern(mrb_state *mrb, mrb_value self)
MRB_API mrb_value
mrb_obj_as_string(mrb_state *mrb, mrb_value obj)
{
- mrb_value str;
-
- if (mrb_string_p(obj)) {
+ switch (mrb_type(obj)) {
+ case MRB_TT_STRING:
return obj;
+ case MRB_TT_SYMBOL:
+ return mrb_sym_str(mrb, mrb_symbol(obj));
+ case MRB_TT_INTEGER:
+ return mrb_integer_to_str(mrb, obj, 10);
+ case MRB_TT_SCLASS:
+ case MRB_TT_CLASS:
+ case MRB_TT_MODULE:
+ return mrb_mod_to_s(mrb, obj);
+ default:
+ return mrb_type_convert(mrb, obj, MRB_TT_STRING, MRB_SYM(to_s));
}
- str = mrb_funcall(mrb, obj, "to_s", 0);
- if (!mrb_string_p(str))
- return mrb_any_to_s(mrb, obj);
- return str;
}
MRB_API mrb_value
@@ -1487,45 +1941,16 @@ mrb_ptr_to_str(mrb_state *mrb, void *p)
return mrb_obj_value(p_str);
}
-MRB_API mrb_value
-mrb_string_type(mrb_state *mrb, mrb_value str)
-{
- return mrb_convert_type(mrb, str, MRB_TT_STRING, "String", "to_str");
-}
-
-MRB_API mrb_value
-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 */
-/*
- * call-seq:
- * str.reverse => new_str
- *
- * Returns a new string with the characters from <i>str</i> in reverse order.
- *
- * "stressed".reverse #=> "desserts"
- */
-static mrb_value
-mrb_str_reverse(mrb_state *mrb, mrb_value str)
+static inline void
+str_reverse(char *p, char *e)
{
- struct RString *s2;
- char *s, *e, *p;
-
- if (RSTRING_LEN(str) <= 1) return mrb_str_dup(mrb, str);
-
- s2 = str_new(mrb, 0, RSTRING_LEN(str));
- str_with_class(mrb, s2, str);
- s = RSTRING_PTR(str); e = RSTRING_END(str) - 1;
- p = RSTR_PTR(s2);
+ char c;
- while (e >= s) {
- *p++ = *e--;
+ while (p < e) {
+ c = *p;
+ *p++ = *e;
+ *e-- = c;
}
- return mrb_obj_value(s2);
}
/* 15.2.10.5.30 */
@@ -1540,146 +1965,96 @@ mrb_str_reverse_bang(mrb_state *mrb, mrb_value str)
{
struct RString *s = mrb_str_ptr(str);
char *p, *e;
- char c;
- mrb_str_modify(mrb, s);
- if (RSTR_LEN(s) > 1) {
+#ifdef MRB_UTF8_STRING
+ mrb_int utf8_len = RSTRING_CHAR_LEN(str);
+ mrb_int len = RSTR_LEN(s);
+
+ if (utf8_len < 2) return str;
+ if (utf8_len < len) {
+ mrb_str_modify(mrb, s);
p = RSTR_PTR(s);
- e = p + RSTR_LEN(s) - 1;
- while (p < e) {
- c = *p;
- *p++ = *e;
- *e-- = c;
+ e = p + RSTR_LEN(s);
+ while (p<e) {
+ mrb_int clen = mrb_utf8len(p, e);
+ str_reverse(p, p + clen - 1);
+ p += clen;
}
+ goto bytes;
+ }
+#endif
+
+ if (RSTR_LEN(s) > 1) {
+ mrb_str_modify(mrb, s);
+ goto bytes;
}
return str;
+
+ bytes:
+ p = RSTR_PTR(s);
+ e = p + RSTR_LEN(s) - 1;
+ str_reverse(p, e);
+ 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 */
/*
* call-seq:
- * str.rindex(substring [, fixnum]) => fixnum or nil
- * str.rindex(fixnum [, fixnum]) => fixnum or nil
- * str.rindex(regexp [, fixnum]) => fixnum or nil
+ * str.rindex(substring [, offset]) => fixnum or nil
*
- * 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 the index of the last occurrence of the given <i>substring</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.
*
* "hello".rindex('e') #=> 1
* "hello".rindex('l') #=> 3
* "hello".rindex('a') #=> nil
- * "hello".rindex(101) #=> 1
- * "hello".rindex(/[aeiou]/, -2) #=> 1
+ * "hello".rindex('l', 2) #=> 2
*/
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) {
- sub = argv[0];
- vpos = argv[1];
- pos = mrb_fixnum(vpos);
+ if (mrb_get_args(mrb, "S|i", &sub, &pos) == 1) {
+ pos = len;
+ }
+ else {
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();
+ pos = chars2bytes(str, 0, pos);
+ pos = str_rindex(mrb, str, sub, pos);
+ if (pos >= 0) {
+ pos = bytes2chars(RSTRING_PTR(str), RSTRING_LEN(str), pos);
+ BYTES_ALIGN_CHECK(pos);
+ return mrb_fixnum_value(pos);
}
- mrb_regexp_check(mrb, sub);
-
- switch (mrb_type(sub)) {
- case MRB_TT_FIXNUM: {
- 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;
-
- 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 = mrb_str_rindex(mrb, str, sub, pos);
- if (pos >= 0) return mrb_fixnum_value(pos);
- break;
-
- } /* end of switch (TYPE(sub)) */
return mrb_nil_value();
}
@@ -1687,23 +2062,18 @@ mrb_str_rindex_m(mrb_state *mrb, mrb_value str)
/*
* call-seq:
- * str.split(pattern=$;, [limit]) => anArray
+ * str.split(separator=nil, [limit]) => anArray
*
* Divides <i>str</i> into substrings based on a delimiter, returning an array
* of these substrings.
*
- * If <i>pattern</i> is a <code>String</code>, then its contents are used as
- * the delimiter when splitting <i>str</i>. If <i>pattern</i> is a single
+ * If <i>separator</i> is a <code>String</code>, then its contents are used as
+ * the delimiter when splitting <i>str</i>. If <i>separator</i> is a single
* space, <i>str</i> is split on whitespace, with leading whitespace and runs
* of contiguous whitespace characters ignored.
*
- * If <i>pattern</i> is a <code>Regexp</code>, <i>str</i> is divided where the
- * pattern matches. Whenever the pattern matches a zero-length string,
- * <i>str</i> is split into individual characters.
- *
- * If <i>pattern</i> is omitted, the value of <code>$;</code> is used. If
- * <code>$;</code> is <code>nil</code> (which is the default), <i>str</i> is
- * split on whitespace as if ` ' were specified.
+ * If <i>separator</i> is omitted or <code>nil</code> (which is the default),
+ * <i>str</i> is split on whitespace as if ' ' were specified.
*
* If the <i>limit</i> parameter is omitted, trailing null fields are
* suppressed. If <i>limit</i> is a positive number, at most that number of
@@ -1714,11 +2084,6 @@ mrb_str_rindex_m(mrb_state *mrb, mrb_value str)
*
* " now's the time".split #=> ["now's", "the", "time"]
* " now's the time".split(' ') #=> ["now's", "the", "time"]
- * " now's the time".split(/ /) #=> ["", "now's", "", "the", "time"]
- * "1, 2.34,56, 7".split(%r{,\s*}) #=> ["1", "2.34", "56", "7"]
- * "hello".split(//) #=> ["h", "e", "l", "l", "o"]
- * "hello".split(//, 3) #=> ["h", "e", "llo"]
- * "hi mom".split(%r{\s*}) #=> ["h", "i", "m", "o", "m"]
*
* "mellow yellow".split("ello") #=> ["m", "w y", "w"]
* "1,2,,3,4,,".split(',') #=> ["1", "2", "", "3", "4"]
@@ -1729,13 +2094,14 @@ mrb_str_rindex_m(mrb_state *mrb, mrb_value str)
static mrb_value
mrb_str_split_m(mrb_state *mrb, mrb_value str)
{
- int argc;
+ mrb_int argc;
mrb_value spat = mrb_nil_value();
- enum {awk, string, regexp} split_type = string;
- long i = 0, lim_p;
+ enum {awk, string} split_type = string;
+ mrb_int i = 0;
mrb_int beg;
mrb_int end;
mrb_int lim = 0;
+ mrb_bool lim_p;
mrb_value result, tmp;
argc = mrb_get_args(mrb, "|oi", &spat, &lim);
@@ -1752,91 +2118,74 @@ mrb_str_split_m(mrb_state *mrb, mrb_value str)
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);
- }
+ else if (!mrb_string_p(spat)) {
+ mrb_raise(mrb, E_TYPE_ERROR, "expected String");
+ }
+ else if (RSTRING_LEN(spat) == 1 && RSTRING_PTR(spat)[0] == ' ') {
+ split_type = awk;
}
result = mrb_ary_new(mrb);
beg = 0;
if (split_type == awk) {
- char *ptr = RSTRING_PTR(str);
- char *eptr = RSTRING_END(str);
- char *bptr = ptr;
mrb_bool skip = TRUE;
+ mrb_int idx = 0;
+ mrb_int str_len = RSTRING_LEN(str);
unsigned int c;
+ int ai = mrb_gc_arena_save(mrb);
- end = beg;
- while (ptr < eptr) {
- int ai = mrb_gc_arena_save(mrb);
- c = (unsigned char)*ptr++;
+ idx = end = beg;
+ while (idx < str_len) {
+ c = (unsigned char)RSTRING_PTR(str)[idx++];
if (skip) {
if (ISSPACE(c)) {
- beg = ptr - bptr;
+ beg = idx;
}
else {
- end = ptr - bptr;
+ end = idx;
skip = FALSE;
if (lim_p && lim <= i) break;
}
}
else if (ISSPACE(c)) {
- mrb_ary_push(mrb, result, mrb_str_subseq(mrb, str, beg, end-beg));
+ mrb_ary_push(mrb, result, mrb_str_byte_subseq(mrb, str, beg, end-beg));
mrb_gc_arena_restore(mrb, ai);
skip = TRUE;
- beg = ptr - bptr;
+ beg = idx;
if (lim_p) ++i;
}
else {
- end = ptr - bptr;
+ end = idx;
}
}
}
- 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);
+ else { /* split_type == string */
+ mrb_int str_len = RSTRING_LEN(str);
+ mrb_int pat_len = RSTRING_LEN(spat);
+ mrb_int idx = 0;
+ int ai = mrb_gc_arena_save(mrb);
- if (slen == 0) {
- int ai = mrb_gc_arena_save(mrb);
- while (ptr < eptr) {
- mrb_ary_push(mrb, result, mrb_str_subseq(mrb, str, ptr-temp, 1));
- mrb_gc_arena_restore(mrb, ai);
- ptr++;
- if (lim_p && lim <= ++i) break;
+ while (idx < str_len) {
+ if (pat_len > 0) {
+ end = mrb_memsearch(RSTRING_PTR(spat), pat_len, RSTRING_PTR(str)+idx, str_len - idx);
+ if (end < 0) 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, mrb_str_subseq(mrb, str, ptr - temp, end));
- mrb_gc_arena_restore(mrb, ai);
- ptr += end + slen;
- if (lim_p && lim <= ++i) break;
+ else {
+ end = chars2bytes(str, idx, 1);
}
+ mrb_ary_push(mrb, result, mrb_str_byte_subseq(mrb, str, idx, end));
+ mrb_gc_arena_restore(mrb, ai);
+ idx += end + pat_len;
+ if (lim_p && lim <= ++i) break;
}
- beg = ptr - temp;
- }
- else {
- mrb_noregexp(mrb, str);
+ beg = idx;
}
if (RSTRING_LEN(str) > 0 && (lim_p || RSTRING_LEN(str) > beg || lim < 0)) {
if (RSTRING_LEN(str) == beg) {
- tmp = mrb_str_new_empty(mrb, str);
+ tmp = mrb_str_new(mrb, 0, 0);
}
else {
- tmp = mrb_str_subseq(mrb, str, beg, RSTRING_LEN(str)-beg);
+ tmp = mrb_str_byte_subseq(mrb, str, beg, RSTRING_LEN(str)-beg);
}
mrb_ary_push(mrb, result, tmp);
}
@@ -1850,13 +2199,14 @@ mrb_str_split_m(mrb_state *mrb, mrb_value str)
return result;
}
-MRB_API mrb_value
-mrb_cstr_to_inum(mrb_state *mrb, const char *str, int base, int badcheck)
+static mrb_value
+mrb_str_len_to_inum(mrb_state *mrb, const char *str, size_t len, mrb_int base, int badcheck)
{
- const char *p;
+ const char *p = str;
+ const char *pend = str + len;
char sign = 1;
- int c, uscore;
- unsigned long n = 0;
+ int c;
+ mrb_int n = 0;
mrb_int val;
#define conv_digit(c) \
@@ -1865,26 +2215,23 @@ mrb_cstr_to_inum(mrb_state *mrb, const char *str, int base, int badcheck)
ISUPPER(c) ? ((c) - 'A' + 10) : \
-1)
- if (!str) {
+ if (!p) {
if (badcheck) goto bad;
return mrb_fixnum_value(0);
}
- while (ISSPACE(*str)) str++;
+ while (p<pend && ISSPACE(*p))
+ p++;
- if (str[0] == '+') {
- str++;
+ if (p[0] == '+') {
+ p++;
}
- else if (str[0] == '-') {
- str++;
+ else if (p[0] == '-') {
+ p++;
sign = 0;
}
- if (str[0] == '+' || str[0] == '-') {
- if (badcheck) goto bad;
- return mrb_fixnum_value(0);
- }
if (base <= 0) {
- if (str[0] == '0') {
- switch (str[1]) {
+ if (p[0] == '0') {
+ switch (p[1]) {
case 'x': case 'X':
base = 16;
break;
@@ -1899,6 +2246,7 @@ mrb_cstr_to_inum(mrb_state *mrb, const char *str, int base, int badcheck)
break;
default:
base = 8;
+ break;
}
}
else if (base < -1) {
@@ -1910,102 +2258,140 @@ mrb_cstr_to_inum(mrb_state *mrb, const char *str, int base, int badcheck)
}
switch (base) {
case 2:
- if (str[0] == '0' && (str[1] == 'b'||str[1] == 'B')) {
- str += 2;
+ if (p[0] == '0' && (p[1] == 'b'||p[1] == 'B')) {
+ p += 2;
}
break;
case 3:
break;
case 8:
- if (str[0] == '0' && (str[1] == 'o'||str[1] == 'O')) {
- str += 2;
+ if (p[0] == '0' && (p[1] == 'o'||p[1] == 'O')) {
+ p += 2;
}
case 4: case 5: case 6: case 7:
break;
case 10:
- if (str[0] == '0' && (str[1] == 'd'||str[1] == 'D')) {
- str += 2;
+ if (p[0] == '0' && (p[1] == 'd'||p[1] == 'D')) {
+ p += 2;
}
case 9: case 11: case 12: case 13: case 14: case 15:
break;
case 16:
- if (str[0] == '0' && (str[1] == 'x'||str[1] == 'X')) {
- str += 2;
+ if (p[0] == '0' && (p[1] == 'x'||p[1] == 'X')) {
+ p += 2;
}
break;
default:
if (base < 2 || 36 < base) {
- mrb_raisef(mrb, E_ARGUMENT_ERROR, "illegal radix %S", mrb_fixnum_value(base));
+ mrb_raisef(mrb, E_ARGUMENT_ERROR, "illegal radix %i", base);
}
break;
} /* end of switch (base) { */
- if (*str == '0') { /* squeeze preceeding 0s */
- uscore = 0;
- while ((c = *++str) == '0' || c == '_') {
+ if (p>=pend) {
+ if (badcheck) goto bad;
+ return mrb_fixnum_value(0);
+ }
+ if (*p == '0') { /* squeeze preceding 0s */
+ p++;
+ while (p<pend) {
+ c = *p++;
if (c == '_') {
- if (++uscore >= 2)
+ if (p<pend && *p == '_') {
+ if (badcheck) goto bad;
break;
+ }
+ continue;
+ }
+ if (c != '0') {
+ p--;
+ break;
}
- else
- uscore = 0;
}
- if (!(c = *str) || ISSPACE(c)) --str;
+ if (*(p - 1) == '0')
+ p--;
}
- c = *str;
- c = conv_digit(c);
- if (c < 0 || c >= base) {
+ if (p == pend || *p == '_') {
if (badcheck) goto bad;
return mrb_fixnum_value(0);
}
-
- uscore = 0;
- for (p=str;*p;p++) {
+ for ( ;p<pend;p++) {
if (*p == '_') {
- if (uscore == 0) {
- uscore++;
+ p++;
+ if (p==pend) {
+ if (badcheck) goto bad;
continue;
}
- if (badcheck) goto bad;
- break;
+ if (*p == '_') {
+ if (badcheck) goto bad;
+ break;
+ }
+ }
+ if (badcheck && *p == '\0') {
+ goto nullbyte;
}
- uscore = 0;
c = conv_digit(*p);
if (c < 0 || c >= base) {
- if (badcheck) goto bad;
break;
}
- n *= base;
+ if (mrb_int_mul_overflow(n, base, &n)) goto overflow;
+ if (MRB_INT_MAX - c < n) {
+ if (sign == 0 && MRB_INT_MAX - n == c - 1) {
+ n = MRB_INT_MIN;
+ sign = 1;
+ break;
+ }
+ overflow:
+ mrb_raisef(mrb, E_RANGE_ERROR, "string (%l) too big for integer", str, pend-str);
+ }
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));
- }
- val = n;
+ val = (mrb_int)n;
if (badcheck) {
- if (p == str) goto bad; /* no number */
- while (*p && ISSPACE(*p)) p++;
- if (*p) goto bad; /* trailing garbage */
+ if (p == str) goto bad; /* no number */
+ if (*(p - 1) == '_') goto bad; /* trailing '_' */
+ while (p<pend && ISSPACE(*p)) p++;
+ if (p<pend) goto bad; /* trailing garbage */
}
- return mrb_fixnum_value(sign ? val : -val);
-bad:
- mrb_raisef(mrb, E_ARGUMENT_ERROR, "invalid string for number(%S)", mrb_str_new_cstr(mrb, str));
+ return mrb_int_value(mrb, sign ? val : -val);
+ nullbyte:
+ mrb_raise(mrb, E_ARGUMENT_ERROR, "string contains null byte");
+ /* not reached */
+ bad:
+ mrb_raisef(mrb, E_ARGUMENT_ERROR, "invalid string for number(%!l)", str, pend-str);
/* not reached */
return mrb_fixnum_value(0);
}
+/* obslete: use RSTRING_CSTR() or mrb_string_cstr() */
MRB_API const char*
mrb_string_value_cstr(mrb_state *mrb, mrb_value *ptr)
{
- struct RString *ps = mrb_str_ptr(*ptr);
- mrb_int len = mrb_str_strlen(mrb, ps);
- char *p = RSTR_PTR(ps);
+ struct RString *ps;
+ const char *p;
+ mrb_int len;
- if (!p || p[len] != '\0') {
- mrb_str_modify(mrb, ps);
- return RSTR_PTR(ps);
+ check_null_byte(mrb, *ptr);
+ ps = mrb_str_ptr(*ptr);
+ p = RSTR_PTR(ps);
+ len = RSTR_LEN(ps);
+ if (p[len] == '\0') {
+ return p;
}
- return p;
+
+ /*
+ * Even after str_modify_keep_ascii(), NULL termination is not ensured if
+ * RSTR_SET_LEN() is used explicitly (e.g. String#delete_suffix!).
+ */
+ str_modify_keep_ascii(mrb, ps);
+ RSTR_PTR(ps)[len] = '\0';
+ return RSTR_PTR(ps);
+}
+
+MRB_API const char*
+mrb_string_cstr(mrb_state *mrb, mrb_value str)
+{
+ return mrb_string_value_cstr(mrb, &str);
}
MRB_API mrb_value
@@ -2014,21 +2400,10 @@ mrb_str_to_inum(mrb_state *mrb, mrb_value str, mrb_int base, mrb_bool badcheck)
const char *s;
mrb_int len;
- str = mrb_str_to_str(mrb, str);
- if (badcheck) {
- s = mrb_string_value_cstr(mrb, &str);
- }
- else {
- s = RSTRING_PTR(str);
- }
- if (s) {
- len = RSTRING_LEN(str);
- if (s[len]) { /* no sentinel somehow */
- struct RString *temp_str = str_new(mrb, s, len);
- s = RSTR_PTR(temp_str);
- }
- }
- return mrb_cstr_to_inum(mrb, s, base, badcheck);
+ mrb_to_str(mrb, str);
+ s = RSTRING_PTR(str);
+ len = RSTRING_LEN(str);
+ return mrb_str_len_to_inum(mrb, s, len, base, badcheck);
}
/* 15.2.10.5.38 */
@@ -2058,71 +2433,98 @@ mrb_str_to_i(mrb_state *mrb, mrb_value self)
mrb_int base = 10;
mrb_get_args(mrb, "|i", &base);
- if (base < 0) {
- mrb_raisef(mrb, E_ARGUMENT_ERROR, "illegal radix %S", mrb_fixnum_value(base));
+ if (base < 0 || 36 < base) {
+ mrb_raisef(mrb, E_ARGUMENT_ERROR, "illegal radix %i", base);
}
return mrb_str_to_inum(mrb, self, base, FALSE);
}
-MRB_API double
-mrb_cstr_to_dbl(mrb_state *mrb, const char * p, mrb_bool badcheck)
+#ifndef MRB_NO_FLOAT
+static double
+mrb_str_len_to_dbl(mrb_state *mrb, const char *s, size_t len, mrb_bool badcheck)
{
+ char buf[DBL_DIG * 4 + 20];
+ const char *p = s, *p2;
+ const char *pend = p + len;
char *end;
+ char *n;
+ char prev = 0;
double d;
-
- enum {max_width = 20};
+ mrb_bool dot = FALSE;
if (!p) return 0.0;
- while (ISSPACE(*p)) p++;
-
- if (!badcheck && p[0] == '0' && (p[1] == 'x' || p[1] == 'X')) {
- return 0.0;
+ while (p<pend && ISSPACE(*p)) p++;
+ p2 = p;
+
+ if (pend - p > 2 && p[0] == '0' && (p[1] == 'x' || p[1] == 'X')) {
+ mrb_value x;
+
+ if (!badcheck) return 0.0;
+ x = mrb_str_len_to_inum(mrb, p, pend-p, 0, badcheck);
+ if (mrb_integer_p(x))
+ d = (double)mrb_integer(x);
+ else /* if (mrb_float_p(x)) */
+ d = mrb_float(x);
+ return d;
}
- d = strtod(p, &end);
- if (p == end) {
- if (badcheck) {
-bad:
- mrb_raisef(mrb, E_ARGUMENT_ERROR, "invalid string for float(%S)", mrb_str_new_cstr(mrb, p));
- /* not reached */
+ while (p < pend) {
+ if (!*p) {
+ if (badcheck) {
+ mrb_raise(mrb, E_ARGUMENT_ERROR, "string for Float contains null byte");
+ /* not reached */
+ }
+ pend = p;
+ p = p2;
+ goto nocopy;
}
- return d;
+ if (!badcheck && *p == ' ') {
+ pend = p;
+ p = p2;
+ goto nocopy;
+ }
+ if (*p == '_') break;
+ p++;
}
- if (*end) {
- char buf[DBL_DIG * 4 + 10];
- char *n = buf;
- char *e = buf + sizeof(buf) - 1;
- char prev = 0;
-
- while (p < end && n < e) prev = *n++ = *p++;
- while (*p) {
- if (*p == '_') {
- /* remove underscores between digits */
- if (badcheck) {
- if (n == buf || !ISDIGIT(prev)) goto bad;
- ++p;
- if (!ISDIGIT(*p)) goto bad;
- }
- else {
- while (*++p == '_');
- continue;
- }
+ p = p2;
+ n = buf;
+ while (p < pend) {
+ char c = *p++;
+ if (c == '.') dot = TRUE;
+ if (c == '_') {
+ /* remove an underscore between digits */
+ if (n == buf || !ISDIGIT(prev) || p == pend) {
+ if (badcheck) goto bad;
+ break;
}
- prev = *p++;
- if (n < e) *n++ = prev;
}
- *n = '\0';
- p = buf;
-
- if (!badcheck && p[0] == '0' && (p[1] == 'x' || p[1] == 'X')) {
- return 0.0;
+ else if (badcheck && prev == '_' && !ISDIGIT(c)) goto bad;
+ else {
+ const char *bend = buf+sizeof(buf)-1;
+ if (n==bend) { /* buffer overflow */
+ if (dot) break; /* cut off remaining fractions */
+ return INFINITY;
+ }
+ *n++ = c;
}
-
- d = strtod(p, &end);
+ prev = c;
+ }
+ *n = '\0';
+ p = buf;
+ pend = n;
+nocopy:
+ d = mrb_float_read(p, &end);
+ if (p == end) {
if (badcheck) {
- if (!end || p == end) goto bad;
- while (*end && ISSPACE(*end)) end++;
- if (*end) goto bad;
+bad:
+ mrb_raisef(mrb, E_ARGUMENT_ERROR, "invalid string for float(%!s)", s);
+ /* not reached */
}
+ return d;
+ }
+ if (badcheck) {
+ if (!end || p == end) goto bad;
+ while (end<pend && ISSPACE(*end)) end++;
+ if (end<pend) goto bad;
}
return d;
}
@@ -2130,22 +2532,7 @@ bad:
MRB_API double
mrb_str_to_dbl(mrb_state *mrb, mrb_value str, mrb_bool badcheck)
{
- char *s;
- mrb_int len;
-
- str = mrb_str_to_str(mrb, str);
- s = RSTRING_PTR(str);
- len = RSTRING_LEN(str);
- if (s) {
- if (badcheck && memchr(s, '\0', len)) {
- mrb_raise(mrb, E_ARGUMENT_ERROR, "string for Float contains null byte");
- }
- if (s[len]) { /* no sentinel somehow */
- struct RString *temp_str = str_new(mrb, s, len);
- s = RSTR_PTR(temp_str);
- }
- }
- return mrb_cstr_to_dbl(mrb, s, badcheck);
+ return mrb_str_len_to_dbl(mrb, RSTRING_PTR(str), RSTRING_LEN(str), badcheck);
}
/* 15.2.10.5.39 */
@@ -2154,7 +2541,7 @@ mrb_str_to_dbl(mrb_state *mrb, mrb_value str, mrb_bool badcheck)
* str.to_f => float
*
* Returns the result of interpreting leading characters in <i>str</i> as a
- * floating point number. Extraneous characters past the end of a valid number
+ * floating-point number. Extraneous characters past the end of a valid number
* are ignored. If there is not a valid number at the start of <i>str</i>,
* <code>0.0</code> is returned. This method never raises an exception.
*
@@ -2167,12 +2554,12 @@ mrb_str_to_f(mrb_state *mrb, mrb_value self)
{
return mrb_float_value(mrb, mrb_str_to_dbl(mrb, self, FALSE));
}
+#endif
/* 15.2.10.5.40 */
/*
* call-seq:
* str.to_s => str
- * str.to_str => str
*
* Returns the receiver.
*/
@@ -2200,7 +2587,7 @@ mrb_str_upcase_bang(mrb_state *mrb, mrb_value str)
char *p, *pend;
mrb_bool modify = FALSE;
- mrb_str_modify(mrb, s);
+ mrb_str_modify_keep_ascii(mrb, s);
p = RSTRING_PTR(str);
pend = RSTRING_END(str);
while (p < pend) {
@@ -2222,7 +2609,7 @@ mrb_str_upcase_bang(mrb_state *mrb, mrb_value str)
*
* Returns a copy of <i>str</i> with all lowercase letters replaced with their
* uppercase counterparts. The operation is locale insensitive---only
- * characters ``a'' to ``z'' are affected.
+ * characters 'a' to 'z' are affected.
*
* "hEllO".upcase #=> "HELLO"
*/
@@ -2236,8 +2623,6 @@ mrb_str_upcase(mrb_state *mrb, mrb_value self)
return str;
}
-#define IS_EVSTR(p,e) ((p) < (e) && (*(p) == '$' || *(p) == '@' || *(p) == '{'))
-
/*
* call-seq:
* str.dump -> new_str
@@ -2248,143 +2633,76 @@ mrb_str_upcase(mrb_state *mrb, mrb_value self)
mrb_value
mrb_str_dump(mrb_state *mrb, mrb_value str)
{
- mrb_int len;
- const char *p, *pend;
- char *q;
- struct RString *result;
-
- len = 2; /* "" */
- p = RSTRING_PTR(str); pend = p + RSTRING_LEN(str);
- while (p < pend) {
- unsigned char c = *p++;
- switch (c) {
- case '"': case '\\':
- case '\n': case '\r':
- case '\t': case '\f':
- case '\013': case '\010': case '\007': case '\033':
- len += 2;
- break;
+ return str_escape(mrb, str, FALSE);
+}
- case '#':
- len += IS_EVSTR(p, pend) ? 2 : 1;
- break;
+MRB_API mrb_value
+mrb_str_cat(mrb_state *mrb, mrb_value str, const char *ptr, size_t len)
+{
+ struct RString *s = mrb_str_ptr(str);
+ size_t capa;
+ size_t total;
+ ptrdiff_t off = -1;
- default:
- if (ISPRINT(c)) {
- len++;
- }
- else {
- len += 4; /* \NNN */
- }
- break;
- }
+ if (len == 0) return str;
+ mrb_str_modify(mrb, s);
+ if (ptr >= RSTR_PTR(s) && ptr <= RSTR_PTR(s) + (size_t)RSTR_LEN(s)) {
+ off = ptr - RSTR_PTR(s);
}
- result = str_new(mrb, 0, len);
- str_with_class(mrb, result, str);
- p = RSTRING_PTR(str); pend = p + RSTRING_LEN(str);
- q = RSTR_PTR(result);
- *q++ = '"';
- while (p < pend) {
- unsigned char c = *p++;
-
- switch (c) {
- case '"':
- case '\\':
- *q++ = '\\';
- *q++ = c;
- break;
-
- case '\n':
- *q++ = '\\';
- *q++ = 'n';
- break;
-
- case '\r':
- *q++ = '\\';
- *q++ = 'r';
- break;
-
- case '\t':
- *q++ = '\\';
- *q++ = 't';
- break;
-
- case '\f':
- *q++ = '\\';
- *q++ = 'f';
- break;
-
- case '\013':
- *q++ = '\\';
- *q++ = 'v';
- break;
-
- case '\010':
- *q++ = '\\';
- *q++ = 'b';
- break;
-
- case '\007':
- *q++ = '\\';
- *q++ = 'a';
- break;
-
- case '\033':
- *q++ = '\\';
- *q++ = 'e';
- break;
-
- case '#':
- if (IS_EVSTR(p, pend)) *q++ = '\\';
- *q++ = '#';
- break;
-
- default:
- if (ISPRINT(c)) {
- *q++ = c;
- }
- else {
- *q++ = '\\';
- q[2] = '0' + c % 8; c /= 8;
- q[1] = '0' + c % 8; c /= 8;
- q[0] = '0' + c % 8;
- q += 3;
- }
+ capa = RSTR_CAPA(s);
+ total = RSTR_LEN(s)+len;
+ if (total >= MRB_SSIZE_MAX) {
+ size_error:
+ mrb_raise(mrb, E_ARGUMENT_ERROR, "string size too big");
+ }
+ if (capa <= total) {
+ if (capa == 0) capa = 1;
+ while (capa <= total) {
+ if (capa <= MRB_SSIZE_MAX / 2) {
+ capa *= 2;
+ }
+ else {
+ capa = total+1;
+ }
}
+ if (capa <= total || capa > MRB_SSIZE_MAX) {
+ goto size_error;
+ }
+ resize_capa(mrb, s, capa);
}
- *q = '"';
- return mrb_obj_value(result);
-}
-
-MRB_API mrb_value
-mrb_str_cat(mrb_state *mrb, mrb_value str, const char *ptr, size_t len)
-{
- str_buf_cat(mrb, mrb_str_ptr(str), ptr, len);
+ if (off != -1) {
+ ptr = RSTR_PTR(s) + off;
+ }
+ memcpy(RSTR_PTR(s) + RSTR_LEN(s), ptr, len);
+ mrb_assert_int_fit(size_t, total, mrb_ssize, MRB_SSIZE_MAX);
+ RSTR_SET_LEN(s, total);
+ RSTR_PTR(s)[total] = '\0'; /* sentinel */
return str;
}
MRB_API mrb_value
mrb_str_cat_cstr(mrb_state *mrb, mrb_value str, const char *ptr)
{
- return mrb_str_cat(mrb, str, ptr, strlen(ptr));
+ return mrb_str_cat(mrb, str, ptr, ptr ? strlen(ptr) : 0);
}
MRB_API mrb_value
mrb_str_cat_str(mrb_state *mrb, mrb_value str, mrb_value str2)
{
+ if (mrb_str_ptr(str) == mrb_str_ptr(str2)) {
+ mrb_str_modify(mrb, mrb_str_ptr(str));
+ }
return mrb_str_cat(mrb, str, RSTRING_PTR(str2), RSTRING_LEN(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);
+ mrb_to_str(mrb, str2);
+ return mrb_str_cat_str(mrb, str1, str2);
}
-#define CHAR_ESC_LEN 13 /* sizeof(\x{ hex of 32bit unsigned int } \0) */
-
/*
* call-seq:
* str.inspect -> string
@@ -2399,54 +2717,7 @@ mrb_str_append(mrb_state *mrb, mrb_value str, mrb_value str2)
mrb_value
mrb_str_inspect(mrb_state *mrb, mrb_value str)
{
- const char *p, *pend;
- char buf[CHAR_ESC_LEN + 1];
- mrb_value result = mrb_str_new_lit(mrb, "\"");
-
- p = RSTRING_PTR(str); pend = RSTRING_END(str);
- for (;p < pend; p++) {
- unsigned char c, cc;
-
- c = *p;
- if (c == '"'|| c == '\\' || (c == '#' && IS_EVSTR(p, pend))) {
- buf[0] = '\\'; buf[1] = c;
- mrb_str_cat(mrb, result, buf, 2);
- continue;
- }
- if (ISPRINT(c)) {
- buf[0] = c;
- mrb_str_cat(mrb, result, buf, 1);
- continue;
- }
- switch (c) {
- case '\n': cc = 'n'; break;
- case '\r': cc = 'r'; break;
- case '\t': cc = 't'; break;
- case '\f': cc = 'f'; break;
- case '\013': cc = 'v'; break;
- case '\010': cc = 'b'; break;
- case '\007': cc = 'a'; break;
- case 033: cc = 'e'; break;
- default: cc = 0; break;
- }
- if (cc) {
- buf[0] = '\\';
- buf[1] = (char)cc;
- mrb_str_cat(mrb, result, buf, 2);
- continue;
- }
- else {
- buf[0] = '\\';
- buf[3] = '0' + c % 8; c /= 8;
- buf[2] = '0' + c % 8; c /= 8;
- buf[1] = '0' + c % 8;
- mrb_str_cat(mrb, result, buf, 4);
- continue;
- }
- }
- mrb_str_cat_lit(mrb, result, "\"");
-
- return result;
+ return str_escape(mrb, str, TRUE);
}
/*
@@ -2472,30 +2743,137 @@ mrb_str_bytes(mrb_state *mrb, mrb_value str)
return a;
}
+/*
+ * call-seq:
+ * str.getbyte(index) -> 0 .. 255
+ *
+ * returns the <i>index</i>th byte as an integer.
+ */
+static mrb_value
+mrb_str_getbyte(mrb_state *mrb, mrb_value str)
+{
+ mrb_int pos;
+ mrb_get_args(mrb, "i", &pos);
+
+ if (pos < 0)
+ pos += RSTRING_LEN(str);
+ if (pos < 0 || RSTRING_LEN(str) <= pos)
+ return mrb_nil_value();
+
+ return mrb_fixnum_value((unsigned char)RSTRING_PTR(str)[pos]);
+}
+
+/*
+ * call-seq:
+ * str.setbyte(index, integer) -> integer
+ *
+ * modifies the <i>index</i>th byte as <i>integer</i>.
+ */
+static mrb_value
+mrb_str_setbyte(mrb_state *mrb, mrb_value str)
+{
+ mrb_int pos, byte;
+ mrb_int len;
+
+ mrb_get_args(mrb, "ii", &pos, &byte);
+
+ len = RSTRING_LEN(str);
+ if (pos < -len || len <= pos)
+ mrb_raisef(mrb, E_INDEX_ERROR, "index %i out of string", pos);
+ if (pos < 0)
+ pos += len;
+
+ mrb_str_modify(mrb, mrb_str_ptr(str));
+ byte &= 0xff;
+ RSTRING_PTR(str)[pos] = (unsigned char)byte;
+ return mrb_fixnum_value((unsigned char)byte);
+}
+
+/*
+ * call-seq:
+ * str.byteslice(integer) -> new_str or nil
+ * str.byteslice(integer, integer) -> new_str or nil
+ * str.byteslice(range) -> new_str or nil
+ *
+ * Byte Reference---If passed a single Integer, returns a
+ * substring of one byte at that position. If passed two Integer
+ * objects, returns a substring starting at the offset given by the first, and
+ * a length given by the second. If given a Range, a substring containing
+ * bytes at offsets given by the range is returned. In all three cases, if
+ * an offset is negative, it is counted from the end of <i>str</i>. Returns
+ * <code>nil</code> if the initial offset falls outside the string, the length
+ * is negative, or the beginning of the range is greater than the end.
+ * The encoding of the resulted string keeps original encoding.
+ *
+ * "hello".byteslice(1) #=> "e"
+ * "hello".byteslice(-1) #=> "o"
+ * "hello".byteslice(1, 2) #=> "el"
+ * "\x80\u3042".byteslice(1, 3) #=> "\u3042"
+ * "\x03\u3042\xff".byteslice(1..3) #=> "\u3042"
+ */
+static mrb_value
+mrb_str_byteslice(mrb_state *mrb, mrb_value str)
+{
+ mrb_value a1;
+ mrb_int str_len = RSTRING_LEN(str), beg, len;
+ mrb_bool empty = TRUE;
+
+ len = mrb_get_argc(mrb);
+ switch (len) {
+ case 2:
+ mrb_get_args(mrb, "ii", &beg, &len);
+ break;
+ case 1:
+ a1 = mrb_get_arg1(mrb);
+ if (mrb_range_p(a1)) {
+ if (mrb_range_beg_len(mrb, a1, &beg, &len, str_len, TRUE) != MRB_RANGE_OK) {
+ return mrb_nil_value();
+ }
+ }
+ else {
+ beg = mrb_integer(mrb_to_int(mrb, a1));
+ len = 1;
+ empty = FALSE;
+ }
+ break;
+ default:
+ mrb_argnum_error(mrb, len, 1, 2);
+ break;
+ }
+ if (mrb_str_beg_len(str_len, &beg, &len) && (empty || len != 0)) {
+ return mrb_str_byte_subseq(mrb, str, beg, len);
+ }
+ else {
+ return mrb_nil_value();
+ }
+}
+
/* ---------------------------*/
void
mrb_init_string(mrb_state *mrb)
{
struct RClass *s;
- mrb_static_assert(RSTRING_EMBED_LEN_MAX < (1 << 5), "pointer size too big for embedded string");
+ mrb_static_assert(RSTRING_EMBED_LEN_MAX < (1 << MRB_STR_EMBED_LEN_BIT),
+ "pointer size too big for embedded string");
- s = mrb->string_class = mrb_define_class(mrb, "String", mrb->object_class); /* 15.2.10 */
+ mrb->string_class = s = 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 */
mrb_define_method(mrb, s, "+", mrb_str_plus_m, MRB_ARGS_REQ(1)); /* 15.2.10.5.4 */
mrb_define_method(mrb, s, "*", mrb_str_times, MRB_ARGS_REQ(1)); /* 15.2.10.5.5 */
mrb_define_method(mrb, s, "[]", mrb_str_aref_m, MRB_ARGS_ANY()); /* 15.2.10.5.6 */
+ mrb_define_method(mrb, s, "[]=", mrb_str_aset_m, MRB_ARGS_ANY());
mrb_define_method(mrb, s, "capitalize", mrb_str_capitalize, MRB_ARGS_NONE()); /* 15.2.10.5.7 */
mrb_define_method(mrb, s, "capitalize!", mrb_str_capitalize_bang, MRB_ARGS_NONE()); /* 15.2.10.5.8 */
mrb_define_method(mrb, s, "chomp", mrb_str_chomp, MRB_ARGS_ANY()); /* 15.2.10.5.9 */
mrb_define_method(mrb, s, "chomp!", mrb_str_chomp_bang, MRB_ARGS_ANY()); /* 15.2.10.5.10 */
- mrb_define_method(mrb, s, "chop", mrb_str_chop, MRB_ARGS_REQ(1)); /* 15.2.10.5.11 */
- mrb_define_method(mrb, s, "chop!", mrb_str_chop_bang, MRB_ARGS_REQ(1)); /* 15.2.10.5.12 */
+ mrb_define_method(mrb, s, "chop", mrb_str_chop, MRB_ARGS_NONE()); /* 15.2.10.5.11 */
+ mrb_define_method(mrb, s, "chop!", mrb_str_chop_bang, MRB_ARGS_NONE()); /* 15.2.10.5.12 */
mrb_define_method(mrb, s, "downcase", mrb_str_downcase, MRB_ARGS_NONE()); /* 15.2.10.5.13 */
mrb_define_method(mrb, s, "downcase!", mrb_str_downcase_bang, MRB_ARGS_NONE()); /* 15.2.10.5.14 */
mrb_define_method(mrb, s, "empty?", mrb_str_empty_p, MRB_ARGS_NONE()); /* 15.2.10.5.16 */
@@ -2503,7 +2881,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_m, MRB_ARGS_ARG(1,1)); /* 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 */
@@ -2511,12 +2889,14 @@ 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 */
+#ifndef MRB_NO_FLOAT
mrb_define_method(mrb, s, "to_f", mrb_str_to_f, MRB_ARGS_NONE()); /* 15.2.10.5.38 */
+#endif
mrb_define_method(mrb, s, "to_i", mrb_str_to_i, MRB_ARGS_ANY()); /* 15.2.10.5.39 */
mrb_define_method(mrb, s, "to_s", mrb_str_to_s, MRB_ARGS_NONE()); /* 15.2.10.5.40 */
mrb_define_method(mrb, s, "to_str", mrb_str_to_s, MRB_ARGS_NONE());
@@ -2525,4 +2905,8 @@ 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, "getbyte", mrb_str_getbyte, MRB_ARGS_REQ(1));
+ mrb_define_method(mrb, s, "setbyte", mrb_str_setbyte, MRB_ARGS_REQ(2));
+ mrb_define_method(mrb, s, "byteslice", mrb_str_byteslice, MRB_ARGS_ARG(1,1));
}
diff --git a/src/symbol.c b/src/symbol.c
index 390d69c31..007b8c885 100644
--- a/src/symbol.c
+++ b/src/symbol.c
@@ -4,72 +4,202 @@
** See Copyright Notice in mruby.h
*/
-#include <ctype.h>
#include <limits.h>
#include <string.h>
-#include "mruby.h"
-#include "mruby/khash.h"
-#include "mruby/string.h"
-#include "mruby/dump.h"
+#include <mruby.h>
+#include <mruby/khash.h>
+#include <mruby/string.h>
+#include <mruby/dump.h>
+#include <mruby/class.h>
+#include <mruby/presym.h>
+
+#ifndef MRB_NO_PRESYM
+
+#ifndef MRB_PRESYM_SCANNING
+/* const uint16_t presym_length_table[] */
+/* const char * const presym_name_table[] */
+# include <mruby/presym/table.h>
+#endif
+
+static mrb_sym
+presym_find(const char *name, size_t len)
+{
+ if (presym_length_table[MRB_PRESYM_MAX-1] < len) return 0;
+
+ mrb_sym start, idx, presym_size = MRB_PRESYM_MAX;
+ int cmp;
+ for (start = 0; presym_size != 0; presym_size/=2) {
+ idx = start+presym_size/2;
+ cmp = (int)len-(int)presym_length_table[idx];
+ if (cmp == 0) {
+ cmp = memcmp(name, presym_name_table[idx], len);
+ if (cmp == 0) return idx+1;
+ }
+ if (0 < cmp) {
+ start = ++idx;
+ --presym_size;
+ }
+ }
+ return 0;
+}
+
+static const char*
+presym_sym2name(mrb_sym sym, mrb_int *lenp)
+{
+ if (sym > MRB_PRESYM_MAX) return NULL;
+ if (lenp) *lenp = presym_length_table[sym-1];
+ return presym_name_table[sym-1];
+}
+
+#endif /* MRB_NO_PRESYM */
/* ------------------------------------------------------ */
typedef struct symbol_name {
mrb_bool lit : 1;
+ uint8_t prev;
uint16_t len;
const char *name;
} symbol_name;
-static inline khint_t
-sym_hash_func(mrb_state *mrb, mrb_sym s)
+static void
+sym_validate_len(mrb_state *mrb, size_t len)
+{
+ if (len >= UINT16_MAX) {
+ mrb_raise(mrb, E_ARGUMENT_ERROR, "symbol length too long");
+ }
+}
+
+#ifdef MRB_USE_ALL_SYMBOLS
+# define SYMBOL_INLINE_P(sym) FALSE
+# define sym_inline_pack(name, len) 0
+# define sym_inline_unpack(sym, buf, lenp) NULL
+#else
+# define SYMBOL_INLINE_P(sym) ((sym) >= (1<<24))
+
+static const char pack_table[] = "_abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
+
+static mrb_sym
+sym_inline_pack(const char *name, size_t len)
{
- khint_t h = 0;
- size_t i, len = mrb->symtbl[s].len;
- const char *p = mrb->symtbl[s].name;
+ const size_t pack_length_max = 5;
+ char c;
+ const char *p;
+ size_t i;
+ mrb_sym sym = 0;
+
+ if (len > pack_length_max) return 0; /* too long */
+ if (len == 0) return 0; /* empty string */
for (i=0; i<len; i++) {
- h = (h << 5) - h + *p++;
+ uint32_t bits;
+
+ c = name[i];
+ if (c == 0) return 0; /* NUL in name */
+ p = strchr(pack_table, (int)c);
+ if (p == 0) return 0; /* non alnum char */
+ bits = (uint32_t)(p - pack_table)+1;
+ sym |= bits<<(24-i*6);
}
- return h;
+ mrb_assert(SYMBOL_INLINE_P(sym));
+ return sym;
}
-#define sym_hash_equal(mrb,a, b) (mrb->symtbl[a].len == mrb->symtbl[b].len && memcmp(mrb->symtbl[a].name, mrb->symtbl[b].name, mrb->symtbl[a].len) == 0)
-
-KHASH_DECLARE(n2s, mrb_sym, mrb_sym, FALSE)
-KHASH_DEFINE (n2s, mrb_sym, mrb_sym, FALSE, sym_hash_func, sym_hash_equal)
-/* ------------------------------------------------------ */
-static void
-sym_validate_len(mrb_state *mrb, size_t len)
+static const char*
+sym_inline_unpack(mrb_sym sym, char *buf, mrb_int *lenp)
{
- if (len >= RITE_LV_NULL_MARK) {
- mrb_raise(mrb, E_ARGUMENT_ERROR, "symbol length too long");
+ int i;
+
+ mrb_assert(SYMBOL_INLINE_P(sym));
+
+ for (i=0; i<5; i++) {
+ uint32_t bits = sym>>(24-i*6) & 0x3f;
+ if (bits == 0) break;
+ buf[i] = pack_table[bits-1];;
}
+ buf[i] = '\0';
+ if (lenp) *lenp = i;
+ return buf;
+}
+#endif
+
+static uint8_t
+symhash(const char *key, size_t len)
+{
+ uint32_t hash, i;
+
+ for(hash = i = 0; i < len; ++i) {
+ hash += key[i];
+ hash += (hash << 10);
+ hash ^= (hash >> 6);
+ }
+ hash += (hash << 3);
+ hash ^= (hash >> 11);
+ hash += (hash << 15);
+ return hash & 0xff;
+}
+
+static mrb_sym
+find_symbol(mrb_state *mrb, const char *name, size_t len, uint8_t *hashp)
+{
+ mrb_sym i;
+ symbol_name *sname;
+ uint8_t hash;
+
+#ifndef MRB_NO_PRESYM
+ /* presym */
+ i = presym_find(name, len);
+ if (i > 0) return i;
+#endif
+
+ /* inline symbol */
+ i = sym_inline_pack(name, len);
+ if (i > 0) return i;
+
+ hash = symhash(name, len);
+ if (hashp) *hashp = hash;
+
+ i = mrb->symhash[hash];
+ if (i == 0) return 0;
+ do {
+ sname = &mrb->symtbl[i];
+ if (sname->len == len && memcmp(sname->name, name, len) == 0) {
+ return (i+MRB_PRESYM_MAX);
+ }
+ if (sname->prev == 0xff) {
+ i -= 0xff;
+ sname = &mrb->symtbl[i];
+ while (mrb->symtbl < sname) {
+ if (sname->len == len && memcmp(sname->name, name, len) == 0) {
+ return (mrb_sym)((sname - mrb->symtbl)+MRB_PRESYM_MAX);
+ }
+ sname--;
+ }
+ return 0;
+ }
+ i -= sname->prev;
+ } while (sname->prev > 0);
+ return 0;
}
static mrb_sym
sym_intern(mrb_state *mrb, const char *name, size_t len, mrb_bool lit)
{
- khash_t(n2s) *h = mrb->name2sym;
- symbol_name *sname = mrb->symtbl; /* symtbl[0] for working memory */
- khiter_t k;
mrb_sym sym;
- char *p;
+ symbol_name *sname;
+ uint8_t hash;
sym_validate_len(mrb, len);
- if (sname) {
- sname->lit = lit;
- sname->len = (uint16_t)len;
- sname->name = name;
- k = kh_get(n2s, mrb, h, 0);
- if (k != kh_end(h))
- return kh_key(h, k);
- }
+ sym = find_symbol(mrb, name, len, &hash);
+ if (sym > 0) return sym;
/* registering a new symbol */
- sym = ++mrb->symidx;
+ sym = mrb->symidx + 1;
if (mrb->symcapa < sym) {
- if (mrb->symcapa == 0) mrb->symcapa = 100;
- else mrb->symcapa = (size_t)(mrb->symcapa * 1.2);
- mrb->symtbl = (symbol_name*)mrb_realloc(mrb, mrb->symtbl, sizeof(symbol_name)*(mrb->symcapa+1));
+ size_t symcapa = mrb->symcapa;
+ if (symcapa == 0) symcapa = 100;
+ else symcapa = (size_t)(symcapa * 6 / 5);
+ mrb->symtbl = (symbol_name*)mrb_realloc(mrb, mrb->symtbl, sizeof(symbol_name)*(symcapa+1));
+ mrb->symcapa = symcapa;
}
sname = &mrb->symtbl[sym];
sname->len = (uint16_t)len;
@@ -78,15 +208,25 @@ sym_intern(mrb_state *mrb, const char *name, size_t len, mrb_bool lit)
sname->lit = TRUE;
}
else {
- p = (char *)mrb_malloc(mrb, len+1);
+ char *p = (char *)mrb_malloc(mrb, len+1);
memcpy(p, name, len);
p[len] = 0;
sname->name = (const char*)p;
sname->lit = FALSE;
}
- kh_put(n2s, mrb, h, sym);
+ if (mrb->symhash[hash]) {
+ mrb_sym i = sym - mrb->symhash[hash];
+ if (i > 0xff)
+ sname->prev = 0xff;
+ else
+ sname->prev = i;
+ }
+ else {
+ sname->prev = 0;
+ }
+ mrb->symhash[hash] = mrb->symidx = sym;
- return sym;
+ return (sym+MRB_PRESYM_MAX);
}
MRB_API mrb_sym
@@ -113,41 +253,69 @@ mrb_intern_str(mrb_state *mrb, mrb_value str)
return mrb_intern(mrb, RSTRING_PTR(str), RSTRING_LEN(str));
}
-MRB_API mrb_value
-mrb_check_intern(mrb_state *mrb, const char *name, size_t len)
+MRB_API mrb_sym
+mrb_intern_check(mrb_state *mrb, const char *name, size_t len)
{
- khash_t(n2s) *h = mrb->name2sym;
- symbol_name *sname = mrb->symtbl;
- khiter_t k;
+ mrb_sym sym;
sym_validate_len(mrb, len);
- sname->len = (uint16_t)len;
- sname->name = name;
+ sym = find_symbol(mrb, name, len, NULL);
+ if (sym > 0) return sym;
+ return 0;
+}
- k = kh_get(n2s, mrb, h, 0);
- if (k != kh_end(h)) {
- return mrb_symbol_value(kh_key(h, k));
- }
- return mrb_nil_value();
+MRB_API mrb_value
+mrb_check_intern(mrb_state *mrb, const char *name, size_t len)
+{
+ mrb_sym sym = mrb_intern_check(mrb, name, len);
+ if (sym == 0) return mrb_nil_value();
+ return mrb_symbol_value(sym);
+}
+
+MRB_API mrb_sym
+mrb_intern_check_cstr(mrb_state *mrb, const char *name)
+{
+ return mrb_intern_check(mrb, name, strlen(name));
}
MRB_API mrb_value
mrb_check_intern_cstr(mrb_state *mrb, const char *name)
{
- return mrb_check_intern(mrb, name, (mrb_int)strlen(name));
+ mrb_sym sym = mrb_intern_check_cstr(mrb, name);
+ if (sym == 0) return mrb_nil_value();
+ return mrb_symbol_value(sym);
+}
+
+MRB_API mrb_sym
+mrb_intern_check_str(mrb_state *mrb, mrb_value str)
+{
+ return mrb_intern_check(mrb, RSTRING_PTR(str), RSTRING_LEN(str));
}
MRB_API mrb_value
mrb_check_intern_str(mrb_state *mrb, mrb_value str)
{
- return mrb_check_intern(mrb, RSTRING_PTR(str), RSTRING_LEN(str));
+ mrb_sym sym = mrb_intern_check_str(mrb, str);
+ if (sym == 0) return mrb_nil_value();
+ return mrb_symbol_value(sym);
}
-/* lenp must be a pointer to a size_t variable */
-MRB_API const char*
-mrb_sym2name_len(mrb_state *mrb, mrb_sym sym, mrb_int *lenp)
+static const char*
+sym2name_len(mrb_state *mrb, mrb_sym sym, char *buf, mrb_int *lenp)
{
- if (sym == 0 || mrb->symidx < sym) {
+ if (sym == 0) goto outofsym;
+ if (SYMBOL_INLINE_P(sym)) return sym_inline_unpack(sym, buf, lenp);
+
+#ifndef MRB_NO_PRESYM
+ {
+ const char *name = presym_sym2name(sym, lenp);
+ if (name) return name;
+ }
+#endif
+ sym -= MRB_PRESYM_MAX;
+
+ if (mrb->symidx < sym) {
+ outofsym:
if (lenp) *lenp = 0;
return NULL;
}
@@ -156,6 +324,16 @@ mrb_sym2name_len(mrb_state *mrb, mrb_sym sym, mrb_int *lenp)
return mrb->symtbl[sym].name;
}
+MRB_API const char*
+mrb_sym_name_len(mrb_state *mrb, mrb_sym sym, mrb_int *lenp)
+{
+#ifdef MRB_USE_ALL_SYMBOLS
+ return sym2name_len(mrb, sym, NULL, lenp);
+#else
+ return sym2name_len(mrb, sym, mrb->symbuf, lenp);
+#endif
+}
+
void
mrb_free_symtbl(mrb_state *mrb)
{
@@ -167,13 +345,11 @@ mrb_free_symtbl(mrb_state *mrb)
}
}
mrb_free(mrb, mrb->symtbl);
- kh_destroy(n2s, mrb, mrb->name2sym);
}
void
mrb_init_symtbl(mrb_state *mrb)
{
- mrb->name2sym = kh_init(n2s, mrb);
}
/**********************************************************************
@@ -209,46 +385,44 @@ mrb_init_symtbl(mrb_state *mrb)
*
*/
-
-/* 15.2.11.3.1 */
+/* 15.2.11.3.2 */
+/* 15.2.11.3.3 */
/*
* call-seq:
- * sym == obj -> true or false
+ * sym.to_s -> string
*
- * Equality---If <i>sym</i> and <i>obj</i> are exactly the same
- * symbol, returns <code>true</code>.
+ * Returns the name or string corresponding to <i>sym</i>.
+ *
+ * :fred.to_s #=> "fred"
*/
-
static mrb_value
-sym_equal(mrb_state *mrb, mrb_value sym1)
+sym_to_s(mrb_state *mrb, mrb_value sym)
{
- mrb_value sym2;
-
- mrb_get_args(mrb, "o", &sym2);
-
- return mrb_bool_value(mrb_obj_equal(mrb, sym1, sym2));
+ return mrb_sym_str(mrb, mrb_symbol(sym));
}
-/* 15.2.11.3.2 */
-/* 15.2.11.3.3 */
/*
* call-seq:
- * sym.id2name -> string
- * sym.to_s -> string
+ * sym.name -> string
*
- * Returns the name or string corresponding to <i>sym</i>.
+ * Returns the name or string corresponding to <i>sym</i>. Unlike #to_s, the
+ * returned string is frozen.
*
- * :fred.id2name #=> "fred"
+ * :fred.name #=> "fred"
+ * :fred.name.frozen? #=> true
*/
static mrb_value
-mrb_sym_to_s(mrb_state *mrb, mrb_value sym)
+sym_name(mrb_state *mrb, mrb_value vsym)
{
- mrb_sym id = mrb_symbol(sym);
- const char *p;
+ mrb_sym sym = mrb_symbol(vsym);
mrb_int len;
+ const char *name = mrb_sym_name_len(mrb, sym, &len);
- p = mrb_sym2name_len(mrb, id, &len);
- return mrb_str_new_static(mrb, p, len);
+ mrb_assert(name != NULL);
+ if (SYMBOL_INLINE_P(sym)) {
+ return mrb_str_new_frozen(mrb, name, len);
+ }
+ return mrb_str_new_static_frozen(mrb, name, len);
}
/* 15.2.11.3.4 */
@@ -355,7 +529,9 @@ symname_p(const char *name)
if (*++m == '*') ++m;
break;
case '!':
- if (*++m == '=') ++m;
+ switch (*++m) {
+ case '=': case '~': ++m;
+ }
break;
case '+': case '-':
if (*++m == '@') ++m;
@@ -385,8 +561,8 @@ id:
switch (*m) {
case '!': case '?': case '=': ++m;
default: break;
- }
}
+ }
break;
}
return *m ? FALSE : TRUE;
@@ -401,57 +577,77 @@ sym_inspect(mrb_state *mrb, mrb_value sym)
mrb_sym id = mrb_symbol(sym);
char *sp;
- name = mrb_sym2name_len(mrb, id, &len);
- str = mrb_str_new(mrb, 0, len+1);
+ name = mrb_sym_name_len(mrb, id, &len);
+ str = mrb_str_new(mrb, NULL, len+1);
sp = RSTRING_PTR(str);
- RSTRING_PTR(str)[0] = ':';
+ sp[0] = ':';
memcpy(sp+1, name, len);
mrb_assert_int_fit(mrb_int, len, size_t, SIZE_MAX);
if (!symname_p(name) || strlen(name) != (size_t)len) {
- str = mrb_str_dump(mrb, str);
+ str = mrb_str_inspect(mrb, str);
sp = RSTRING_PTR(str);
sp[0] = ':';
sp[1] = '"';
}
+#ifdef MRB_UTF8_STRING
+ if (SYMBOL_INLINE_P(id)) RSTR_SET_ASCII_FLAG(mrb_str_ptr(str));
+#endif
return str;
}
MRB_API mrb_value
-mrb_sym2str(mrb_state *mrb, mrb_sym sym)
+mrb_sym_str(mrb_state *mrb, mrb_sym sym)
{
mrb_int len;
- const char *name = mrb_sym2name_len(mrb, sym, &len);
+ const char *name = mrb_sym_name_len(mrb, sym, &len);
if (!name) return mrb_undef_value(); /* can't happen */
+ if (SYMBOL_INLINE_P(sym)) {
+ mrb_value str = mrb_str_new(mrb, name, len);
+ RSTR_SET_ASCII_FLAG(mrb_str_ptr(str));
+ return str;
+ }
return mrb_str_new_static(mrb, name, len);
}
-MRB_API const char*
-mrb_sym2name(mrb_state *mrb, mrb_sym sym)
+static const char*
+sym_cstr(mrb_state *mrb, mrb_sym sym, mrb_bool dump)
{
mrb_int len;
- const char *name = mrb_sym2name_len(mrb, sym, &len);
+ const char *name = mrb_sym_name_len(mrb, sym, &len);
if (!name) return NULL;
- if (symname_p(name) && strlen(name) == (size_t)len) {
+ if (strlen(name) == (size_t)len && (!dump || symname_p(name))) {
return name;
}
else {
- mrb_value str = mrb_str_dump(mrb, mrb_str_new_static(mrb, name, len));
+ mrb_value str = mrb_str_new_static(mrb, name, len);
+ str = mrb_str_dump(mrb, str);
return RSTRING_PTR(str);
}
}
+MRB_API const char*
+mrb_sym_name(mrb_state *mrb, mrb_sym sym)
+{
+ return sym_cstr(mrb, sym, FALSE);
+}
+
+MRB_API const char*
+mrb_sym_dump(mrb_state *mrb, mrb_sym sym)
+{
+ return sym_cstr(mrb, sym, TRUE);
+}
+
#define lesser(a,b) (((a)>(b))?(b):(a))
static mrb_value
sym_cmp(mrb_state *mrb, mrb_value s1)
{
- mrb_value s2;
+ mrb_value s2 = mrb_get_arg1(mrb);
mrb_sym sym1, sym2;
- mrb_get_args(mrb, "o", &s2);
- if (mrb_type(s2) != MRB_TT_SYMBOL) return mrb_nil_value();
+ if (!mrb_symbol_p(s2)) return mrb_nil_value();
sym1 = mrb_symbol(s1);
sym2 = mrb_symbol(s2);
if (sym1 == sym2) return mrb_fixnum_value(0);
@@ -459,9 +655,10 @@ sym_cmp(mrb_state *mrb, mrb_value s1)
const char *p1, *p2;
int retval;
mrb_int len, len1, len2;
+ char buf1[8], buf2[8];
- p1 = mrb_sym2name_len(mrb, sym1, &len1);
- p2 = mrb_sym2name_len(mrb, sym2, &len2);
+ p1 = sym2name_len(mrb, sym1, buf1, &len1);
+ p2 = sym2name_len(mrb, sym2, buf2, &len2);
len = lesser(len1, len2);
retval = memcmp(p1, p2, len);
if (retval == 0) {
@@ -479,12 +676,13 @@ mrb_init_symbol(mrb_state *mrb)
{
struct RClass *sym;
- sym = mrb->symbol_class = mrb_define_class(mrb, "Symbol", mrb->object_class); /* 15.2.11 */
+ mrb->symbol_class = sym = mrb_define_class(mrb, "Symbol", mrb->object_class); /* 15.2.11 */
+ MRB_SET_INSTANCE_TT(sym, MRB_TT_SYMBOL);
+ mrb_undef_class_method(mrb, sym, "new");
- mrb_define_method(mrb, sym, "===", sym_equal, MRB_ARGS_REQ(1)); /* 15.2.11.3.1 */
- mrb_define_method(mrb, sym, "id2name", mrb_sym_to_s, MRB_ARGS_NONE()); /* 15.2.11.3.2 */
- mrb_define_method(mrb, sym, "to_s", mrb_sym_to_s, MRB_ARGS_NONE()); /* 15.2.11.3.3 */
- mrb_define_method(mrb, sym, "to_sym", sym_to_sym, MRB_ARGS_NONE()); /* 15.2.11.3.4 */
- mrb_define_method(mrb, sym, "inspect", sym_inspect, MRB_ARGS_NONE()); /* 15.2.11.3.5(x) */
- mrb_define_method(mrb, sym, "<=>", sym_cmp, MRB_ARGS_REQ(1));
+ mrb_define_method(mrb, sym, "to_s", sym_to_s, MRB_ARGS_NONE()); /* 15.2.11.3.3 */
+ mrb_define_method(mrb, sym, "name", sym_name, MRB_ARGS_NONE());
+ mrb_define_method(mrb, sym, "to_sym", sym_to_sym, MRB_ARGS_NONE()); /* 15.2.11.3.4 */
+ mrb_define_method(mrb, sym, "inspect", sym_inspect, MRB_ARGS_NONE()); /* 15.2.11.3.5(x) */
+ mrb_define_method(mrb, sym, "<=>", sym_cmp, MRB_ARGS_REQ(1));
}
diff --git a/src/value_array.h b/src/value_array.h
index cabd2426d..6089b8aa0 100644
--- a/src/value_array.h
+++ b/src/value_array.h
@@ -1,11 +1,12 @@
#ifndef MRB_VALUE_ARRAY_H__
#define MRB_VALUE_ARRAY_H__
-#include "mruby.h"
+#include <mruby.h>
static inline void
value_move(mrb_value *s1, const mrb_value *s2, size_t n)
{
+ if (n == 0) return;
if (s1 > s2 && s1 < s2 + n)
{
s1 += n;
diff --git a/src/variable.c b/src/variable.c
index c4f6fb830..d89295229 100644
--- a/src/variable.c
+++ b/src/variable.c
@@ -4,392 +4,234 @@
** See Copyright Notice in mruby.h
*/
-#include <ctype.h>
-#include "mruby.h"
-#include "mruby/array.h"
-#include "mruby/class.h"
-#include "mruby/proc.h"
-#include "mruby/string.h"
-
-typedef int (iv_foreach_func)(mrb_state*,mrb_sym,mrb_value,void*);
-
-#ifdef MRB_USE_IV_SEGLIST
-
-#ifndef MRB_SEGMENT_SIZE
-#define MRB_SEGMENT_SIZE 4
-#endif
-
-typedef struct segment {
- mrb_sym key[MRB_SEGMENT_SIZE];
- mrb_value val[MRB_SEGMENT_SIZE];
- struct segment *next;
-} segment;
+#include <mruby.h>
+#include <mruby/array.h>
+#include <mruby/class.h>
+#include <mruby/proc.h>
+#include <mruby/string.h>
+#include <mruby/variable.h>
+#include <mruby/presym.h>
+
+struct iv_elem {
+ mrb_sym key;
+ mrb_value val;
+};
/* Instance variable table structure */
typedef struct iv_tbl {
- segment *rootseg;
size_t size;
- size_t last_len;
+ size_t alloc;
+ struct iv_elem *table;
} iv_tbl;
-/*
- * Creates the instance variable table.
- *
- * Parameters
- * mrb
- * Returns
- * the instance variable table.
- */
+/* Creates the instance variable table. */
static iv_tbl*
iv_new(mrb_state *mrb)
{
iv_tbl *t;
- t = mrb_malloc(mrb, sizeof(iv_tbl));
+ t = (iv_tbl*)mrb_malloc(mrb, sizeof(iv_tbl));
t->size = 0;
- t->rootseg = NULL;
- t->last_len = 0;
+ t->alloc = 0;
+ t->table = NULL;
return t;
}
-/*
- * Set the value for the symbol in the instance variable table.
- *
- * Parameters
- * mrb
- * t the instance variable table to be set in.
- * sym the symbol to be used as the key.
- * val the value to be set.
- */
+static void iv_put(mrb_state *mrb, iv_tbl *t, mrb_sym sym, mrb_value val);
+
static void
-iv_put(mrb_state *mrb, iv_tbl *t, mrb_sym sym, mrb_value val)
+iv_rehash(mrb_state *mrb, iv_tbl *t)
{
- segment *seg = t->rootseg;
- segment *prev = NULL;
- segment *matched_seg = NULL;
- size_t matched_idx = 0;
- size_t i;
+ size_t old_alloc = t->alloc;
+ size_t new_alloc = old_alloc+1;
+ struct iv_elem *old_table = t->table;
- while (seg) {
- for (i=0; i<MRB_SEGMENT_SIZE; i++) {
- mrb_sym key = seg->key[i];
- /* Found room in last segment after last_len */
- if (!seg->next && i >= t->last_len) {
- seg->key[i] = sym;
- seg->val[i] = val;
- t->last_len = i+1;
- t->size++;
- return;
- }
- if (!matched_seg && key == 0) {
- matched_seg = seg;
- matched_idx = i;
- }
- else if (key == sym) {
- seg->val[i] = val;
- return;
- }
+ khash_power2(new_alloc);
+ if (old_alloc == new_alloc) return;
+
+ t->alloc = new_alloc;
+ t->size = 0;
+ t->table = (struct iv_elem*)mrb_calloc(mrb, sizeof(struct iv_elem), new_alloc);
+
+ for (size_t i = 0; i < old_alloc; i++) {
+ struct iv_elem *slot = &old_table[i];
+
+ /* key = 0 means empty; val = undef means deleted */
+ if (slot->key != 0 && !mrb_undef_p(slot->val)) {
+ iv_put(mrb, t, slot->key, slot->val);
}
- prev = seg;
- seg = seg->next;
}
+ mrb_free(mrb, old_table);
+}
- /* Not found */
- t->size++;
- if (matched_seg) {
- matched_seg->key[matched_idx] = sym;
- matched_seg->val[matched_idx] = val;
- return;
- }
+#define slot_empty_p(slot) ((slot)->key == 0 && !mrb_undef_p((slot)->val))
- seg = mrb_malloc(mrb, sizeof(segment));
- if (!seg) return;
- seg->next = NULL;
- seg->key[0] = sym;
- seg->val[0] = val;
- t->last_len = 1;
- if (prev) {
- prev->next = seg;
+/* Set the value for the symbol in the instance variable table. */
+static void
+iv_put(mrb_state *mrb, iv_tbl *t, mrb_sym sym, mrb_value val)
+{
+ size_t hash, pos, start;
+ struct iv_elem *dslot = NULL;
+
+ if (t == NULL) return;
+ if (t->alloc == 0) {
+ iv_rehash(mrb, t);
}
- else {
- t->rootseg = seg;
+ hash = kh_int_hash_func(mrb, sym);
+ start = pos = hash & (t->alloc-1);
+ for (;;) {
+ struct iv_elem *slot = &t->table[pos];
+
+ if (slot->key == sym) {
+ slot->val = val;
+ return;
+ }
+ else if (slot_empty_p(slot)) {
+ t->size++;
+ slot->key = sym;
+ slot->val = val;
+ return;
+ }
+ else if (!dslot && mrb_undef_p(slot->val)) { /* deleted */
+ dslot = slot;
+ }
+ pos = (pos+1) & (t->alloc-1);
+ if (pos == start) { /* not found */
+ if (dslot) {
+ t->size++;
+ dslot->key = sym;
+ dslot->val = val;
+ return;
+ }
+ /* no room */
+ iv_rehash(mrb, t);
+ start = pos = hash & (t->alloc-1);
+ }
}
}
-/*
- * Get a value for a symbol from the instance variable table.
- *
- * Parameters
- * mrb
- * t the variable table to be searched.
- * sym the symbol to be used as the key.
- * vp the value pointer. Receives the value if the specified symbol is
- * contained in the instance variable table.
- * Returns
- * true if the specified symbol is contained in the instance variable table.
- */
+/* Get a value for a symbol from the instance variable table. */
static mrb_bool
iv_get(mrb_state *mrb, iv_tbl *t, mrb_sym sym, mrb_value *vp)
{
- segment *seg;
- size_t i;
+ size_t hash, pos, start;
- seg = t->rootseg;
- while (seg) {
- for (i=0; i<MRB_SEGMENT_SIZE; i++) {
- mrb_sym key = seg->key[i];
+ if (t == NULL) return FALSE;
+ if (t->alloc == 0) return FALSE;
+ if (t->size == 0) return FALSE;
- if (!seg->next && i >= t->last_len) {
- return FALSE;
- }
- if (key == sym) {
- if (vp) *vp = seg->val[i];
- return TRUE;
- }
+ hash = kh_int_hash_func(mrb, sym);
+ start = pos = hash & (t->alloc-1);
+ for (;;) {
+ struct iv_elem *slot = &t->table[pos];
+
+ if (slot->key == sym) {
+ if (vp) *vp = slot->val;
+ return TRUE;
+ }
+ else if (slot_empty_p(slot)) {
+ return FALSE;
+ }
+ pos = (pos+1) & (t->alloc-1);
+ if (pos == start) { /* not found */
+ return FALSE;
}
- seg = seg->next;
}
- return FALSE;
}
-/*
- * Deletes the value for the symbol from the instance variable table.
- *
- * Parameters
- * t the variable table to be searched.
- * sym the symbol to be used as the key.
- * vp the value pointer. Receive the deleted value if the symbol is
- * contained in the instance variable table.
- * Returns
- * true if the specified symbol is contained in the instance variable table.
- */
+/* Deletes the value for the symbol from the instance variable table. */
static mrb_bool
iv_del(mrb_state *mrb, iv_tbl *t, mrb_sym sym, mrb_value *vp)
{
- segment *seg;
- size_t i;
+ size_t hash, pos, start;
- seg = t->rootseg;
- while (seg) {
- for (i=0; i<MRB_SEGMENT_SIZE; i++) {
- mrb_sym key = seg->key[i];
+ if (t == NULL) return FALSE;
+ if (t->alloc == 0) return FALSE;
+ if (t->size == 0) return FALSE;
- if (!seg->next && i >= t->last_len) {
- return FALSE;
- }
- if (key == sym) {
- t->size--;
- seg->key[i] = 0;
- if (vp) *vp = seg->val[i];
- return TRUE;
- }
+ hash = kh_int_hash_func(mrb, sym);
+ start = pos = hash & (t->alloc-1);
+ for (;;) {
+ struct iv_elem *slot = &t->table[pos];
+
+ if (slot->key == sym) {
+ if (vp) *vp = slot->val;
+ t->size--;
+ slot->key = 0;
+ slot->val = mrb_undef_value();
+ return TRUE;
+ }
+ else if (slot_empty_p(slot)) {
+ return FALSE;
+ }
+ pos = (pos+1) & (t->alloc-1);
+ if (pos == start) { /* not found */
+ return FALSE;
}
- seg = seg->next;
}
- return FALSE;
}
-static mrb_bool
-iv_foreach(mrb_state *mrb, iv_tbl *t, iv_foreach_func *func, void *p)
+/* Iterates over the instance variable table. */
+static void
+iv_foreach(mrb_state *mrb, iv_tbl *t, mrb_iv_foreach_func *func, void *p)
{
- segment *seg;
size_t i;
- int n;
- seg = t->rootseg;
- while (seg) {
- for (i=0; i<MRB_SEGMENT_SIZE; i++) {
- mrb_sym key = seg->key[i];
+ if (t == NULL) return;
+ if (t->alloc == 0) return;
+ if (t->size == 0) return;
- /* no value in last segment after last_len */
- if (!seg->next && i >= t->last_len) {
- return FALSE;
- }
- if (key != 0) {
- n =(*func)(mrb, key, seg->val[i], p);
- if (n > 0) return FALSE;
- if (n < 0) {
- t->size--;
- seg->key[i] = 0;
- }
+ for (i=0; i<t->alloc; i++) {
+ struct iv_elem *slot = &t->table[i];
+
+ if (slot->key && !mrb_undef_p(slot->val)) {
+ if ((*func)(mrb, slot->key, slot->val, p) != 0) {
+ return;
}
}
- seg = seg->next;
}
- return TRUE;
+ return;
}
+/* Get the size of the instance variable table. */
static size_t
iv_size(mrb_state *mrb, iv_tbl *t)
{
- segment *seg;
- size_t size = 0;
-
- if (!t) return 0;
- if (t->size > 0) return t->size;
- seg = t->rootseg;
- while (seg) {
- if (seg->next == NULL) {
- size += t->last_len;
- return size;
- }
- seg = seg->next;
- size += MRB_SEGMENT_SIZE;
- }
- /* empty iv_tbl */
- return 0;
+ if (t == NULL) return 0;
+ return t->size;
}
+/* Copy the instance variable table. */
static iv_tbl*
iv_copy(mrb_state *mrb, iv_tbl *t)
{
- segment *seg;
iv_tbl *t2;
-
size_t i;
- seg = t->rootseg;
- t2 = iv_new(mrb);
+ if (t == NULL) return NULL;
+ if (t->alloc == 0) return NULL;
+ if (t->size == 0) return NULL;
- while (seg != NULL) {
- for (i=0; i<MRB_SEGMENT_SIZE; i++) {
- mrb_sym key = seg->key[i];
- mrb_value val = seg->val[i];
+ t2 = iv_new(mrb);
+ for (i=0; i<t->alloc; i++) {
+ struct iv_elem *slot = &t->table[i];
- if ((seg->next == NULL) && (i >= t->last_len)) {
- return t2;
- }
- iv_put(mrb, t2, key, val);
+ if (slot->key && !mrb_undef_p(slot->val)) {
+ iv_put(mrb, t2, slot->key, slot->val);
}
- seg = seg->next;
}
return t2;
}
+/* Free memory of the instance variable table. */
static void
iv_free(mrb_state *mrb, iv_tbl *t)
{
- segment *seg;
-
- seg = t->rootseg;
- while (seg) {
- segment *p = seg;
- seg = seg->next;
- mrb_free(mrb, p);
- }
+ mrb_free(mrb, t->table);
mrb_free(mrb, t);
}
-#else
-
-#include "mruby/khash.h"
-
-#ifndef MRB_IVHASH_INIT_SIZE
-#define MRB_IVHASH_INIT_SIZE 8
-#endif
-
-KHASH_DECLARE(iv, mrb_sym, mrb_value, TRUE)
-KHASH_DEFINE(iv, mrb_sym, mrb_value, TRUE, kh_int_hash_func, kh_int_hash_equal)
-
-typedef struct iv_tbl {
- khash_t(iv) h;
-} iv_tbl;
-
-static iv_tbl*
-iv_new(mrb_state *mrb)
-{
- return (iv_tbl*)kh_init_size(iv, mrb, MRB_IVHASH_INIT_SIZE);
-}
-
-static void
-iv_put(mrb_state *mrb, iv_tbl *t, mrb_sym sym, mrb_value val)
-{
- khash_t(iv) *h = &t->h;
- khiter_t k;
-
- k = kh_put(iv, mrb, h, sym);
- kh_value(h, k) = val;
-}
-
-static mrb_bool
-iv_get(mrb_state *mrb, iv_tbl *t, mrb_sym sym, mrb_value *vp)
-{
- khash_t(iv) *h = &t->h;
- khiter_t k;
-
- k = kh_get(iv, mrb, h, sym);
- if (k != kh_end(h)) {
- if (vp) *vp = kh_value(h, k);
- return TRUE;
- }
- return FALSE;
-}
-
-static mrb_bool
-iv_del(mrb_state *mrb, iv_tbl *t, mrb_sym sym, mrb_value *vp)
-{
- khash_t(iv) *h = &t->h;
- khiter_t k;
-
- if (h) {
- k = kh_get(iv, mrb, h, sym);
- if (k != kh_end(h)) {
- mrb_value val = kh_value(h, k);
- kh_del(iv, mrb, h, k);
- if (vp) *vp = val;
- return TRUE;
- }
- }
- return FALSE;
-}
-
-static mrb_bool
-iv_foreach(mrb_state *mrb, iv_tbl *t, iv_foreach_func *func, void *p)
-{
- khash_t(iv) *h = &t->h;
- khiter_t k;
- int n;
-
- if (h) {
- for (k = kh_begin(h); k != kh_end(h); k++) {
- if (kh_exist(h, k)) {
- n = (*func)(mrb, kh_key(h, k), kh_value(h, k), p);
- if (n > 0) return FALSE;
- if (n < 0) {
- kh_del(iv, mrb, h, k);
- }
- }
- }
- }
- return TRUE;
-}
-
-static size_t
-iv_size(mrb_state *mrb, iv_tbl *t)
-{
- khash_t(iv) *h;
-
- if (t && (h = &t->h)) {
- return kh_size(h);
- }
- return 0;
-}
-
-static iv_tbl*
-iv_copy(mrb_state *mrb, iv_tbl *t)
-{
- return (iv_tbl*)kh_copy(iv, mrb, &t->h);
-}
-
-static void
-iv_free(mrb_state *mrb, iv_tbl *t)
-{
- kh_destroy(iv, mrb, &t->h);
-}
-
-#endif
-
static int
iv_mark_i(mrb_state *mrb, mrb_sym sym, mrb_value v, void *p)
{
@@ -400,9 +242,7 @@ iv_mark_i(mrb_state *mrb, mrb_sym sym, mrb_value v, void *p)
static void
mark_tbl(mrb_state *mrb, iv_tbl *t)
{
- if (t) {
- iv_foreach(mrb, t, iv_mark_i, 0);
- }
+ iv_foreach(mrb, t, iv_mark_i, 0);
}
void
@@ -485,31 +325,64 @@ mrb_iv_get(mrb_state *mrb, mrb_value obj, mrb_sym sym)
return mrb_nil_value();
}
+static inline void assign_class_name(mrb_state *mrb, struct RObject *obj, mrb_sym sym, mrb_value v);
+
+void
+mrb_obj_iv_set_force(mrb_state *mrb, struct RObject *obj, mrb_sym sym, mrb_value v)
+{
+ assign_class_name(mrb, obj, sym, v);
+ if (!obj->iv) {
+ obj->iv = iv_new(mrb);
+ }
+ iv_put(mrb, obj->iv, sym, v);
+ mrb_field_write_barrier_value(mrb, (struct RBasic*)obj, v);
+}
+
MRB_API void
mrb_obj_iv_set(mrb_state *mrb, struct RObject *obj, mrb_sym sym, mrb_value v)
{
- iv_tbl *t = obj->iv;
-
- if (!t) {
- t = obj->iv = iv_new(mrb);
- }
- mrb_write_barrier(mrb, (struct RBasic*)obj);
- iv_put(mrb, t, sym, v);
+ mrb_check_frozen(mrb, obj);
+ mrb_obj_iv_set_force(mrb, obj, sym, v);
}
+/* Iterates over the instance variable table. */
MRB_API void
-mrb_obj_iv_ifnone(mrb_state *mrb, struct RObject *obj, mrb_sym sym, mrb_value v)
+mrb_iv_foreach(mrb_state *mrb, mrb_value obj, mrb_iv_foreach_func *func, void *p)
{
- iv_tbl *t = obj->iv;
+ if (!obj_iv_p(obj)) return;
+ iv_foreach(mrb, mrb_obj_ptr(obj)->iv, func, p);
+}
- if (!t) {
- t = obj->iv = iv_new(mrb);
- }
- else if (iv_get(mrb, t, sym, &v)) {
- return;
+static inline mrb_bool
+namespace_p(enum mrb_vtype tt)
+{
+ return tt == MRB_TT_CLASS || tt == MRB_TT_MODULE ? TRUE : FALSE;
+}
+
+static inline void
+assign_class_name(mrb_state *mrb, struct RObject *obj, mrb_sym sym, mrb_value v)
+{
+ if (namespace_p(obj->tt) && namespace_p(mrb_type(v))) {
+ struct RObject *c = mrb_obj_ptr(v);
+ if (obj != c && ISUPPER(mrb_sym_name_len(mrb, sym, NULL)[0])) {
+ mrb_sym id_classname = MRB_SYM(__classname__);
+ mrb_value o = mrb_obj_iv_get(mrb, c, id_classname);
+
+ if (mrb_nil_p(o)) {
+ mrb_sym id_outer = MRB_SYM(__outer__);
+ o = mrb_obj_iv_get(mrb, c, id_outer);
+
+ if (mrb_nil_p(o)) {
+ if ((struct RClass *)obj == mrb->object_class) {
+ mrb_obj_iv_set_force(mrb, c, id_classname, mrb_symbol_value(sym));
+ }
+ else {
+ mrb_obj_iv_set_force(mrb, c, id_outer, mrb_obj_value(obj));
+ }
+ }
+ }
+ }
}
- mrb_write_barrier(mrb, (struct RBasic*)obj);
- iv_put(mrb, t, sym, v);
}
MRB_API void
@@ -542,29 +415,24 @@ mrb_iv_defined(mrb_state *mrb, mrb_value obj, mrb_sym sym)
return mrb_obj_iv_defined(mrb, mrb_obj_ptr(obj), sym);
}
-#define identchar(c) (ISALNUM(c) || (c) == '_' || !ISASCII(c))
-
MRB_API mrb_bool
-mrb_iv_p(mrb_state *mrb, mrb_sym iv_name)
+mrb_iv_name_sym_p(mrb_state *mrb, mrb_sym iv_name)
{
const char *s;
- mrb_int i, len;
+ mrb_int len;
- s = mrb_sym2name_len(mrb, iv_name, &len);
+ s = mrb_sym_name_len(mrb, iv_name, &len);
if (len < 2) return FALSE;
if (s[0] != '@') return FALSE;
- if (s[1] == '@') return FALSE;
- for (i=1; i<len; i++) {
- if (!identchar(s[i])) return FALSE;
- }
- return TRUE;
+ if (ISDIGIT(s[1])) return FALSE;
+ return mrb_ident_p(s+1, len-1);
}
MRB_API void
-mrb_iv_check(mrb_state *mrb, mrb_sym iv_name)
+mrb_iv_name_sym_check(mrb_state *mrb, mrb_sym iv_name)
{
- if (!mrb_iv_p(mrb, iv_name)) {
- mrb_name_error(mrb, iv_name, "`%S' is not allowed as an instance variable name", mrb_sym2str(mrb, iv_name));
+ if (!mrb_iv_name_sym_p(mrb, iv_name)) {
+ mrb_name_error(mrb, iv_name, "'%n' is not allowed as an instance variable name", iv_name);
}
}
@@ -601,16 +469,16 @@ inspect_i(mrb_state *mrb, mrb_sym sym, mrb_value v, void *p)
else {
mrb_str_cat_lit(mrb, str, ", ");
}
- s = mrb_sym2name_len(mrb, sym, &len);
+ s = mrb_sym_name_len(mrb, sym, &len);
mrb_str_cat(mrb, str, s, len);
mrb_str_cat_lit(mrb, str, "=");
- if (mrb_type(v) == MRB_TT_OBJECT) {
+ if (mrb_object_p(v)) {
ins = mrb_any_to_s(mrb, v);
}
else {
ins = mrb_inspect(mrb, v);
}
- mrb_str_append(mrb, str, ins);
+ mrb_str_cat_str(mrb, str, ins);
return 0;
}
@@ -622,12 +490,12 @@ mrb_obj_iv_inspect(mrb_state *mrb, struct RObject *obj)
if (len > 0) {
const char *cn = mrb_obj_classname(mrb, mrb_obj_value(obj));
- mrb_value str = mrb_str_buf_new(mrb, 30);
+ mrb_value str = mrb_str_new_capa(mrb, 30);
mrb_str_cat_lit(mrb, str, "-<");
mrb_str_cat_cstr(mrb, str, cn);
mrb_str_cat_lit(mrb, str, ":");
- mrb_str_concat(mrb, str, mrb_ptr_to_str(mrb, obj));
+ mrb_str_cat_str(mrb, str, mrb_ptr_to_str(mrb, obj));
iv_foreach(mrb, t, inspect_i, &str);
mrb_str_cat_lit(mrb, str, ">");
@@ -643,27 +511,14 @@ mrb_iv_remove(mrb_state *mrb, mrb_value obj, mrb_sym sym)
iv_tbl *t = mrb_obj_ptr(obj)->iv;
mrb_value val;
- if (t && iv_del(mrb, t, sym, &val)) {
+ mrb_check_frozen(mrb, mrb_obj_ptr(obj));
+ if (iv_del(mrb, t, sym, &val)) {
return val;
}
}
return mrb_undef_value();
}
-mrb_value
-mrb_vm_iv_get(mrb_state *mrb, mrb_sym sym)
-{
- /* get self */
- return mrb_iv_get(mrb, mrb->c->stack[0], sym);
-}
-
-void
-mrb_vm_iv_set(mrb_state *mrb, mrb_sym sym, mrb_value v)
-{
- /* get self */
- mrb_iv_set(mrb, mrb->c->stack[0], sym, v);
-}
-
static int
iv_i(mrb_state *mrb, mrb_sym sym, mrb_value v, void *p)
{
@@ -672,7 +527,7 @@ iv_i(mrb_state *mrb, mrb_sym sym, mrb_value v, void *p)
mrb_int len;
ary = *(mrb_value*)p;
- s = mrb_sym2name_len(mrb, sym, &len);
+ s = mrb_sym_name_len(mrb, sym, &len);
if (len > 1 && s[0] == '@' && s[1] != '@') {
mrb_ary_push(mrb, ary, mrb_symbol_value(sym));
}
@@ -702,7 +557,7 @@ mrb_obj_instance_variables(mrb_state *mrb, mrb_value self)
mrb_value ary;
ary = mrb_ary_new(mrb);
- if (obj_iv_p(self) && mrb_obj_ptr(self)->iv) {
+ if (obj_iv_p(self)) {
iv_foreach(mrb, mrb_obj_ptr(self)->iv, iv_i, &ary);
}
return ary;
@@ -716,7 +571,7 @@ cv_i(mrb_state *mrb, mrb_sym sym, mrb_value v, void *p)
mrb_int len;
ary = *(mrb_value*)p;
- s = mrb_sym2name_len(mrb, sym, &len);
+ s = mrb_sym_name_len(mrb, sym, &len);
if (len > 2 && s[0] == '@' && s[1] == '@') {
mrb_ary_push(mrb, ary, mrb_symbol_value(sym));
}
@@ -726,7 +581,7 @@ cv_i(mrb_state *mrb, mrb_sym sym, mrb_value v, void *p)
/* 15.2.2.4.19 */
/*
* call-seq:
- * mod.class_variables -> array
+ * mod.class_variables(inherit=true) -> array
*
* Returns an array of the names of class variables in <i>mod</i>.
*
@@ -744,35 +599,50 @@ mrb_mod_class_variables(mrb_state *mrb, mrb_value mod)
{
mrb_value ary;
struct RClass *c;
+ mrb_bool inherit = TRUE;
+ mrb_get_args(mrb, "|b", &inherit);
ary = mrb_ary_new(mrb);
c = mrb_class_ptr(mod);
while (c) {
- if (c->iv) {
- iv_foreach(mrb, c->iv, cv_i, &ary);
- }
+ iv_foreach(mrb, c->iv, cv_i, &ary);
+ if (!inherit) break;
c = c->super;
}
return ary;
}
-MRB_API mrb_value
-mrb_mod_cv_get(mrb_state *mrb, struct RClass * c, mrb_sym sym)
+mrb_value
+mrb_mod_cv_get(mrb_state *mrb, struct RClass *c, mrb_sym sym)
{
struct RClass * cls = c;
+ mrb_value v;
+ int given = FALSE;
while (c) {
- if (c->iv) {
- iv_tbl *t = c->iv;
- mrb_value v;
-
- if (iv_get(mrb, t, sym, &v))
- return v;
+ if (c->iv && iv_get(mrb, c->iv, sym, &v)) {
+ given = TRUE;
}
c = c->super;
}
- mrb_name_error(mrb, sym, "uninitialized class variable %S in %S",
- mrb_sym2str(mrb, sym), mrb_obj_value(cls));
+ if (given) return v;
+ if (cls && cls->tt == MRB_TT_SCLASS) {
+ mrb_value klass;
+
+ klass = mrb_obj_iv_get(mrb, (struct RObject *)cls, MRB_SYM(__attached__));
+ c = mrb_class_ptr(klass);
+ if (c->tt == MRB_TT_CLASS || c->tt == MRB_TT_MODULE) {
+ given = FALSE;
+ while (c) {
+ if (c->iv && iv_get(mrb, c->iv, sym, &v)) {
+ given = TRUE;
+ }
+ c = c->super;
+ }
+ if (given) return v;
+ }
+ }
+ mrb_name_error(mrb, sym, "uninitialized class variable %n in %C", sym, cls);
/* not reached */
return mrb_nil_value();
}
@@ -789,24 +659,43 @@ mrb_mod_cv_set(mrb_state *mrb, struct RClass *c, mrb_sym sym, mrb_value v)
struct RClass * cls = c;
while (c) {
- if (c->iv) {
- iv_tbl *t = c->iv;
+ iv_tbl *t = c->iv;
- if (iv_get(mrb, t, sym, NULL)) {
- mrb_write_barrier(mrb, (struct RBasic*)c);
- iv_put(mrb, t, sym, v);
- return;
- }
+ if (iv_get(mrb, t, sym, NULL)) {
+ mrb_check_frozen(mrb, c);
+ iv_put(mrb, t, sym, v);
+ mrb_field_write_barrier_value(mrb, (struct RBasic*)c, v);
+ return;
}
c = c->super;
}
- if (!cls->iv) {
- cls->iv = iv_new(mrb);
+ if (cls && cls->tt == MRB_TT_SCLASS) {
+ mrb_value klass;
+
+ klass = mrb_obj_iv_get(mrb, (struct RObject*)cls, MRB_SYM(__attached__));
+ switch (mrb_type(klass)) {
+ case MRB_TT_CLASS:
+ case MRB_TT_MODULE:
+ case MRB_TT_SCLASS:
+ c = mrb_class_ptr(klass);
+ break;
+ default:
+ c = cls;
+ break;
+ }
+ }
+ else{
+ c = cls;
+ }
+
+ mrb_check_frozen(mrb, c);
+ if (!c->iv) {
+ c->iv = iv_new(mrb);
}
- mrb_write_barrier(mrb, (struct RBasic*)cls);
- iv_put(mrb, cls->iv, sym, v);
+ iv_put(mrb, c->iv, sym, v);
+ mrb_field_write_barrier_value(mrb, (struct RBasic*)c, v);
}
MRB_API void
@@ -815,14 +704,12 @@ mrb_cv_set(mrb_state *mrb, mrb_value mod, mrb_sym sym, mrb_value v)
mrb_mod_cv_set(mrb, mrb_class_ptr(mod), sym, v);
}
-MRB_API mrb_bool
+mrb_bool
mrb_mod_cv_defined(mrb_state *mrb, struct RClass * c, mrb_sym sym)
{
while (c) {
- if (c->iv) {
- iv_tbl *t = c->iv;
- if (iv_get(mrb, t, sym, NULL)) return TRUE;
- }
+ iv_tbl *t = c->iv;
+ if (iv_get(mrb, t, sym, NULL)) return TRUE;
c = c->super;
}
@@ -838,19 +725,29 @@ mrb_cv_defined(mrb_state *mrb, mrb_value mod, mrb_sym sym)
mrb_value
mrb_vm_cv_get(mrb_state *mrb, mrb_sym sym)
{
- struct RClass *c = mrb->c->ci->proc->target_class;
+ struct RClass *c;
- if (!c) c = mrb->c->ci->target_class;
+ const struct RProc *p = mrb->c->ci->proc;
+ for (;;) {
+ c = MRB_PROC_TARGET_CLASS(p);
+ if (c && c->tt != MRB_TT_SCLASS) break;
+ p = p->upper;
+ }
return mrb_mod_cv_get(mrb, c, sym);
}
void
mrb_vm_cv_set(mrb_state *mrb, mrb_sym sym, mrb_value v)
{
- struct RClass *c = mrb->c->ci->proc->target_class;
+ struct RClass *c;
+ const struct RProc *p = mrb->c->ci->proc;
- if (!c) c = mrb->c->ci->target_class;
+ for (;;) {
+ c = MRB_PROC_TARGET_CLASS(p);
+ if (c && c->tt != MRB_TT_SCLASS) break;
+ p = p->upper;
+ }
mrb_mod_cv_set(mrb, c, sym, v);
}
@@ -869,77 +766,90 @@ mod_const_check(mrb_state *mrb, mrb_value mod)
}
static mrb_value
-const_get(mrb_state *mrb, struct RClass *base, mrb_sym sym)
+const_get(mrb_state *mrb, struct RClass *base, mrb_sym sym, mrb_bool skip)
{
struct RClass *c = base;
mrb_value v;
- iv_tbl *t;
mrb_bool retry = FALSE;
mrb_value name;
+ if (skip) c = c->super;
L_RETRY:
while (c) {
- if (c->iv) {
- t = c->iv;
- if (iv_get(mrb, t, sym, &v))
+ if (!MRB_FLAG_TEST(c, MRB_FL_CLASS_IS_PREPENDED) && c->iv) {
+ if (iv_get(mrb, c->iv, sym, &v))
return v;
}
c = c->super;
}
- if (!retry && base && base->tt == MRB_TT_MODULE) {
+ if (!retry && base->tt == MRB_TT_MODULE) {
c = mrb->object_class;
retry = TRUE;
goto L_RETRY;
}
name = mrb_symbol_value(sym);
- return mrb_funcall_argv(mrb, mrb_obj_value(base), mrb_intern_lit(mrb, "const_missing"), 1, &name);
+ return mrb_funcall_argv(mrb, mrb_obj_value(base), MRB_SYM(const_missing), 1, &name);
}
MRB_API mrb_value
mrb_const_get(mrb_state *mrb, mrb_value mod, mrb_sym sym)
{
mod_const_check(mrb, mod);
- return const_get(mrb, mrb_class_ptr(mod), sym);
+ return const_get(mrb, mrb_class_ptr(mod), sym, FALSE);
}
mrb_value
mrb_vm_const_get(mrb_state *mrb, mrb_sym sym)
{
- struct RClass *c = mrb->c->ci->proc->target_class;
+ struct RClass *c;
+ struct RClass *c2;
+ mrb_value v;
+ const struct RProc *proc = mrb->c->ci->proc;
- if (!c) c = mrb->c->ci->target_class;
- if (c) {
- struct RClass *c2;
- mrb_value v;
+ c = MRB_PROC_TARGET_CLASS(proc);
+ if (!c) c = mrb->object_class;
+ if (iv_get(mrb, c->iv, sym, &v)) {
+ return v;
+ }
+ c2 = c;
+ while (c2 && c2->tt == MRB_TT_SCLASS) {
+ mrb_value klass;
- if (c->iv && iv_get(mrb, c->iv, sym, &v)) {
- return v;
+ if (!iv_get(mrb, c2->iv, MRB_SYM(__attached__), &klass)) {
+ c2 = NULL;
+ break;
}
- c2 = c;
- for (;;) {
- c2 = mrb_class_outer_module(mrb, c2);
- if (!c2) break;
- if (c2->iv && iv_get(mrb, c2->iv, sym, &v)) {
- return v;
- }
+ c2 = mrb_class_ptr(klass);
+ }
+ if (c2 && (c2->tt == MRB_TT_CLASS || c2->tt == MRB_TT_MODULE)) c = c2;
+ proc = proc->upper;
+ while (proc) {
+ c2 = MRB_PROC_TARGET_CLASS(proc);
+ if (c2 && iv_get(mrb, c2->iv, sym, &v)) {
+ return v;
}
+ proc = proc->upper;
}
- return const_get(mrb, c, sym);
+ return const_get(mrb, c, sym, TRUE);
}
MRB_API void
mrb_const_set(mrb_state *mrb, mrb_value mod, mrb_sym sym, mrb_value v)
{
mod_const_check(mrb, mod);
+ if (mrb_type(v) == MRB_TT_CLASS || mrb_type(v) == MRB_TT_MODULE) {
+ mrb_class_name_class(mrb, mrb_class_ptr(mod), mrb_class_ptr(v), sym);
+ }
mrb_iv_set(mrb, mod, sym, v);
}
void
mrb_vm_const_set(mrb_state *mrb, mrb_sym sym, mrb_value v)
{
- struct RClass *c = mrb->c->ci->proc->target_class;
+ struct RClass *c;
- if (!c) c = mrb->c->ci->target_class;
+ c = MRB_PROC_TARGET_CLASS(mrb->c->ci->proc);
+ if (!c) c = mrb->object_class;
mrb_obj_iv_set(mrb, (struct RObject*)c, sym, v);
}
@@ -951,6 +861,12 @@ mrb_const_remove(mrb_state *mrb, mrb_value mod, mrb_sym sym)
}
MRB_API void
+mrb_define_const_id(mrb_state *mrb, struct RClass *mod, mrb_sym name, mrb_value v)
+{
+ mrb_obj_iv_set(mrb, (struct RObject*)mod, name, v);
+}
+
+MRB_API void
mrb_define_const(mrb_state *mrb, struct RClass *mod, const char *name, mrb_value v)
{
mrb_obj_iv_set(mrb, (struct RObject*)mod, mrb_intern_cstr(mrb, name), v);
@@ -970,9 +886,17 @@ const_i(mrb_state *mrb, mrb_sym sym, mrb_value v, void *p)
mrb_int len;
ary = *(mrb_value*)p;
- s = mrb_sym2name_len(mrb, sym, &len);
+ s = mrb_sym_name_len(mrb, sym, &len);
if (len >= 1 && ISUPPER(s[0])) {
- mrb_ary_push(mrb, ary, mrb_symbol_value(sym));
+ mrb_int i, alen = RARRAY_LEN(ary);
+
+ for (i=0; i<alen; i++) {
+ if (mrb_symbol(RARRAY_PTR(ary)[i]) == sym)
+ break;
+ }
+ if (i==alen) {
+ mrb_ary_push(mrb, ary, mrb_symbol_value(sym));
+ }
}
return 0;
}
@@ -982,7 +906,7 @@ const_i(mrb_state *mrb, mrb_sym sym, mrb_value v, void *p)
* call-seq:
* mod.constants -> array
*
- * Returns an array of all names of contants defined in the receiver.
+ * Returns an array of all names of constants defined in the receiver.
*/
mrb_value
mrb_mod_constants(mrb_state *mrb, mrb_value mod)
@@ -994,9 +918,7 @@ mrb_mod_constants(mrb_state *mrb, mrb_value mod)
mrb_get_args(mrb, "|b", &inherit);
ary = mrb_ary_new(mrb);
while (c) {
- if (c->iv) {
- iv_foreach(mrb, c->iv, const_i, &ary);
- }
+ iv_foreach(mrb, c->iv, const_i, &ary);
if (!inherit) break;
c = c->super;
if (c == mrb->object_class) break;
@@ -1009,9 +931,6 @@ mrb_gv_get(mrb_state *mrb, mrb_sym sym)
{
mrb_value v;
- if (!mrb->globals) {
- return mrb_nil_value();
- }
if (iv_get(mrb, mrb->globals, sym, &v))
return v;
return mrb_nil_value();
@@ -1023,20 +942,15 @@ mrb_gv_set(mrb_state *mrb, mrb_sym sym, mrb_value v)
iv_tbl *t;
if (!mrb->globals) {
- t = mrb->globals = iv_new(mrb);
- }
- else {
- t = mrb->globals;
+ mrb->globals = iv_new(mrb);
}
+ t = mrb->globals;
iv_put(mrb, t, sym, v);
}
MRB_API void
mrb_gv_remove(mrb_state *mrb, mrb_sym sym)
{
- if (!mrb->globals) {
- return;
- }
iv_del(mrb, mrb->globals, sym, NULL);
}
@@ -1065,18 +979,8 @@ mrb_f_global_variables(mrb_state *mrb, mrb_value self)
{
iv_tbl *t = mrb->globals;
mrb_value ary = mrb_ary_new(mrb);
- size_t i;
- char buf[3];
- if (t) {
- iv_foreach(mrb, t, gv_i, &ary);
- }
- buf[0] = '$';
- buf[2] = 0;
- for (i = 1; i <= 9; ++i) {
- buf[1] = (char)(i + '0');
- mrb_ary_push(mrb, ary, mrb_symbol_value(mrb_intern(mrb, buf, 2)));
- }
+ iv_foreach(mrb, t, gv_i, &ary);
return ary;
}
@@ -1085,19 +989,19 @@ mrb_const_defined_0(mrb_state *mrb, mrb_value mod, mrb_sym id, mrb_bool exclude,
{
struct RClass *klass = mrb_class_ptr(mod);
struct RClass *tmp;
- mrb_bool mod_retry = 0;
+ mrb_bool mod_retry = FALSE;
tmp = klass;
retry:
while (tmp) {
- if (tmp->iv && iv_get(mrb, tmp->iv, id, NULL)) {
+ if (iv_get(mrb, tmp->iv, id, NULL)) {
return TRUE;
}
if (!recurse && (klass != mrb->object_class)) break;
tmp = tmp->super;
}
if (!exclude && !mod_retry && (klass->tt == MRB_TT_MODULE)) {
- mod_retry = 1;
+ mod_retry = TRUE;
tmp = mrb->object_class;
goto retry;
}
@@ -1140,23 +1044,99 @@ csym_i(mrb_state *mrb, mrb_sym sym, mrb_value v, void *p)
return 0;
}
-mrb_sym
-mrb_class_sym(mrb_state *mrb, struct RClass *c, struct RClass *outer)
+static mrb_sym
+find_class_sym(mrb_state *mrb, struct RClass *outer, struct RClass *c)
{
- mrb_value name;
+ struct csym_arg arg;
- name = mrb_obj_iv_get(mrb, (struct RObject*)c, mrb_intern_lit(mrb, "__classid__"));
- if (mrb_nil_p(name)) {
+ if (!outer) return 0;
+ if (outer == c) return 0;
+ arg.c = c;
+ arg.sym = 0;
+ iv_foreach(mrb, outer->iv, csym_i, &arg);
+ return arg.sym;
+}
- if (!outer) return 0;
- else {
- struct csym_arg arg;
+static struct RClass*
+outer_class(mrb_state *mrb, struct RClass *c)
+{
+ mrb_value ov;
- arg.c = c;
- arg.sym = 0;
- iv_foreach(mrb, outer->iv, csym_i, &arg);
- return arg.sym;
- }
+ ov = mrb_obj_iv_get(mrb, (struct RObject*)c, MRB_SYM(__outer__));
+ if (mrb_nil_p(ov)) return NULL;
+ switch (mrb_type(ov)) {
+ case MRB_TT_CLASS:
+ case MRB_TT_MODULE:
+ return mrb_class_ptr(ov);
+ default:
+ break;
+ }
+ return NULL;
+}
+
+static mrb_bool
+detect_outer_loop(mrb_state *mrb, struct RClass *c)
+{
+ struct RClass *t = c; /* tortoise */
+ struct RClass *h = c; /* hare */
+
+ for (;;) {
+ if (h == NULL) return FALSE;
+ h = outer_class(mrb, h);
+ if (h == NULL) return FALSE;
+ h = outer_class(mrb, h);
+ t = outer_class(mrb, t);
+ if (t == h) return TRUE;
+ }
+}
+
+mrb_value
+mrb_class_find_path(mrb_state *mrb, struct RClass *c)
+{
+ struct RClass *outer;
+ mrb_value path;
+ mrb_sym name;
+ const char *str;
+ mrb_int len;
+
+ if (detect_outer_loop(mrb, c)) return mrb_nil_value();
+ outer = outer_class(mrb, c);
+ if (outer == NULL) return mrb_nil_value();
+ name = find_class_sym(mrb, outer, c);
+ if (name == 0) return mrb_nil_value();
+ str = mrb_class_name(mrb, outer);
+ path = mrb_str_new_capa(mrb, 40);
+ mrb_str_cat_cstr(mrb, path, str);
+ mrb_str_cat_cstr(mrb, path, "::");
+
+ str = mrb_sym_name_len(mrb, name, &len);
+ mrb_str_cat(mrb, path, str, len);
+ if (RSTRING_PTR(path)[0] != '#') {
+ iv_del(mrb, c->iv, MRB_SYM(__outer__), NULL);
+ iv_put(mrb, c->iv, MRB_SYM(__classname__), path);
+ mrb_field_write_barrier_value(mrb, (struct RBasic*)c, path);
+ path = mrb_str_dup(mrb, path);
}
- return mrb_symbol(name);
+ return path;
+}
+
+size_t
+mrb_obj_iv_tbl_memsize(mrb_value obj)
+{
+ iv_tbl *t = mrb_obj_ptr(obj)->iv;
+ if (t == NULL) return 0;
+ return sizeof(iv_tbl) + t->alloc*sizeof(struct iv_elem);
+}
+
+#define identchar(c) (ISALNUM(c) || (c) == '_' || !ISASCII(c))
+
+mrb_bool
+mrb_ident_p(const char *s, mrb_int len)
+{
+ mrb_int i;
+
+ for (i = 0; i < len; i++) {
+ if (!identchar(s[i])) return FALSE;
+ }
+ return TRUE;
}
diff --git a/src/version.c b/src/version.c
index 7aac44d62..350bc1673 100644
--- a/src/version.c
+++ b/src/version.c
@@ -1,12 +1,16 @@
-#include "mruby.h"
-#include "mruby/variable.h"
+#include <mruby.h>
+#include <mruby/variable.h>
void
mrb_init_version(mrb_state* mrb)
{
+ mrb_value mruby_version = mrb_str_new_lit(mrb, MRUBY_VERSION);
+
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, "RUBY_ENGINE_VERSION", mruby_version);
+ mrb_define_global_const(mrb, "MRUBY_VERSION", 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));
diff --git a/src/vm.c b/src/vm.c
index 88c0f2e49..cd70faf11 100644
--- a/src/vm.c
+++ b/src/vm.c
@@ -6,23 +6,27 @@
#include <stddef.h>
#include <stdarg.h>
+#ifndef MRB_NO_FLOAT
#include <math.h>
-#include "mruby.h"
-#include "mruby/array.h"
-#include "mruby/class.h"
-#include "mruby/hash.h"
-#include "mruby/irep.h"
-#include "mruby/numeric.h"
-#include "mruby/proc.h"
-#include "mruby/range.h"
-#include "mruby/string.h"
-#include "mruby/variable.h"
-#include "mruby/error.h"
-#include "mruby/opcode.h"
+#endif
+#include <mruby.h>
+#include <mruby/array.h>
+#include <mruby/class.h>
+#include <mruby/hash.h>
+#include <mruby/irep.h>
+#include <mruby/numeric.h>
+#include <mruby/proc.h>
+#include <mruby/range.h>
+#include <mruby/string.h>
+#include <mruby/variable.h>
+#include <mruby/error.h>
+#include <mruby/opcode.h>
#include "value_array.h"
-#include "mrb_throw.h"
+#include <mruby/throw.h>
+#include <mruby/dump.h>
+#include <mruby/presym.h>
-#ifndef ENABLE_STDIO
+#ifdef MRB_NO_STDIO
#if defined(__cplusplus)
extern "C" {
#endif
@@ -40,6 +44,11 @@ void abort(void);
#define MRB_STACK_GROWTH 128
#endif
+/* Maximum mrb_funcall() depth. Should be set lower on memory constrained systems. */
+#ifndef MRB_FUNCALL_DEPTH_MAX
+#define MRB_FUNCALL_DEPTH_MAX 512
+#endif
+
/* Maximum stack depth. Should be set lower on memory constrained systems.
The value below allows about 60000 recursive calls in the simplest case. */
#ifndef MRB_STACK_MAX
@@ -52,31 +61,50 @@ The value below allows about 60000 recursive calls in the simplest case. */
# define DEBUG(x)
#endif
-#define ARENA_RESTORE(mrb,ai) (mrb)->arena_idx = (ai)
-static inline void
-stack_clear(mrb_value *from, size_t count)
+#ifndef MRB_GC_FIXED_ARENA
+static void
+mrb_gc_arena_shrink(mrb_state *mrb, int idx)
{
-#ifndef MRB_NAN_BOXING
- const mrb_value mrb_value_zero = { { 0 } };
+ mrb_gc *gc = &mrb->gc;
+ int capa = gc->arena_capa;
- while (count-- > 0) {
- *from++ = mrb_value_zero;
+ if (idx < capa / 4) {
+ capa >>= 2;
+ if (capa < MRB_GC_ARENA_SIZE) {
+ capa = MRB_GC_ARENA_SIZE;
+ }
+ if (capa != gc->arena_capa) {
+ gc->arena = (struct RBasic**)mrb_realloc(mrb, gc->arena, sizeof(struct RBasic*)*capa);
+ gc->arena_capa = capa;
+ }
}
+}
#else
+#define mrb_gc_arena_shrink(mrb,idx)
+#endif
+
+#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)
+{
+#ifdef MRB_NAN_BOXING
while (count-- > 0) {
SET_NIL_VALUE(*from);
from++;
}
+#else
+ memset(from, 0, sizeof(mrb_value)*count);
#endif
}
static inline void
stack_copy(mrb_value *dst, const mrb_value *src, size_t size)
{
- while (size-- > 0) {
- *dst++ = *src++;
- }
+ memcpy(dst, src, sizeof(mrb_value)*size);
}
static void
@@ -87,54 +115,62 @@ stack_init(mrb_state *mrb)
/* mrb_assert(mrb->stack == NULL); */
c->stbase = (mrb_value *)mrb_calloc(mrb, STACK_INIT_SIZE, sizeof(mrb_value));
c->stend = c->stbase + STACK_INIT_SIZE;
- c->stack = c->stbase;
/* mrb_assert(ci == NULL); */
c->cibase = (mrb_callinfo *)mrb_calloc(mrb, CALLINFO_INIT_SIZE, sizeof(mrb_callinfo));
c->ciend = c->cibase + CALLINFO_INIT_SIZE;
c->ci = c->cibase;
- c->ci->target_class = mrb->object_class;
- c->ci->stackent = c->stack;
+ c->ci->u.target_class = mrb->object_class;
+ c->ci->stack = c->stbase;
}
static inline void
-envadjust(mrb_state *mrb, mrb_value *oldbase, mrb_value *newbase)
+envadjust(mrb_state *mrb, mrb_value *oldbase, mrb_value *newbase, size_t oldsize)
{
mrb_callinfo *ci = mrb->c->cibase;
if (newbase == oldbase) return;
while (ci <= mrb->c->ci) {
- struct REnv *e = ci->env;
- if (e && MRB_ENV_STACK_SHARED_P(e)) {
+ struct REnv *e = mrb_vm_ci_env(ci);
+ mrb_value *st;
+
+ if (e && MRB_ENV_ONSTACK_P(e) &&
+ (st = e->stack) && oldbase <= st && st < oldbase+oldsize) {
ptrdiff_t off = e->stack - oldbase;
e->stack = newbase + off;
}
- ci->stackent = newbase + (ci->stackent - oldbase);
- ci++;
- }
-}
-static inline void
-init_new_stack_space(mrb_state *mrb, int room, int keep)
-{
- if (room > keep) {
- /* do not leave uninitialized malloc region */
- stack_clear(&(mrb->c->stack[keep]), room - keep);
+ if (ci->proc && MRB_PROC_ENV_P(ci->proc) && e != MRB_PROC_ENV(ci->proc)) {
+ e = MRB_PROC_ENV(ci->proc);
+
+ if (e && MRB_ENV_ONSTACK_P(e) &&
+ (st = e->stack) && oldbase <= st && st < oldbase+oldsize) {
+ ptrdiff_t off = e->stack - oldbase;
+
+ e->stack = newbase + off;
+ }
+ }
+
+ ci->stack = newbase + (ci->stack - oldbase);
+ ci++;
}
}
/** def rec ; $deep =+ 1 ; if $deep > 1000 ; return 0 ; end ; rec ; end */
static void
-stack_extend_alloc(mrb_state *mrb, int room, int keep)
+stack_extend_alloc(mrb_state *mrb, mrb_int room)
{
mrb_value *oldbase = mrb->c->stbase;
- int size = mrb->c->stend - mrb->c->stbase;
- int off = mrb->c->stack - mrb->c->stbase;
+ mrb_value *newstack;
+ size_t oldsize = mrb->c->stend - mrb->c->stbase;
+ size_t size = oldsize;
+ size_t off = mrb->c->ci->stack ? mrb->c->stend - mrb->c->ci->stack : 0;
+ if (off > size) size = off;
#ifdef MRB_STACK_EXTEND_DOUBLING
- if (room <= size)
+ if ((size_t)room <= size)
size *= 2;
else
size += room;
@@ -148,143 +184,174 @@ stack_extend_alloc(mrb_state *mrb, int room, int keep)
size += room;
#endif
- mrb->c->stbase = (mrb_value *)mrb_realloc(mrb, mrb->c->stbase, sizeof(mrb_value) * size);
- mrb->c->stack = mrb->c->stbase + off;
+ newstack = (mrb_value *)mrb_realloc_simple(mrb, mrb->c->stbase, sizeof(mrb_value) * size);
+ if (newstack == NULL) {
+ mrb_exc_raise(mrb, mrb_obj_value(mrb->stack_err));
+ }
+ stack_clear(&(newstack[oldsize]), size - oldsize);
+ envadjust(mrb, oldbase, newstack, oldsize);
+ mrb->c->stbase = newstack;
mrb->c->stend = mrb->c->stbase + size;
- envadjust(mrb, oldbase, mrb->c->stbase);
/* Raise an exception if the new stack size will be too large,
to prevent infinite recursion. However, do this only after resizing the stack, so mrb_raise has stack space to work with. */
if (size > MRB_STACK_MAX) {
- init_new_stack_space(mrb, room, keep);
- mrb_raise(mrb, E_SYSSTACK_ERROR, "stack level too deep. (limit=" MRB_STRINGIZE(MRB_STACK_MAX) ")");
+ mrb_exc_raise(mrb, mrb_obj_value(mrb->stack_err));
}
}
-static inline void
-stack_extend(mrb_state *mrb, int room, int keep)
+MRB_API void
+mrb_stack_extend(mrb_state *mrb, mrb_int room)
{
- if (mrb->c->stack + room >= mrb->c->stend) {
- stack_extend_alloc(mrb, room, keep);
+ if (!mrb->c->ci->stack || mrb->c->ci->stack + room >= mrb->c->stend) {
+ stack_extend_alloc(mrb, room);
}
- init_new_stack_space(mrb, room, keep);
}
static inline struct REnv*
-uvenv(mrb_state *mrb, int up)
+uvenv(mrb_state *mrb, mrb_int up)
{
- struct REnv *e = mrb->c->ci->proc->env;
+ const struct RProc *proc = mrb->c->ci->proc;
+ struct REnv *e;
while (up--) {
- if (!e) return NULL;
- e = (struct REnv*)e->c;
+ proc = proc->upper;
+ if (!proc) return NULL;
}
- return e;
-}
-
-static inline mrb_bool
-is_strict(mrb_state *mrb, struct REnv *e)
-{
- int cioff = e->cioff;
+ e = MRB_PROC_ENV(proc);
+ if (e) return e; /* proc has enclosed env */
+ else {
+ mrb_callinfo *ci = mrb->c->ci;
+ mrb_callinfo *cb = mrb->c->cibase;
- if (MRB_ENV_STACK_SHARED_P(e) && mrb->c->cibase[cioff].proc &&
- MRB_PROC_STRICT_P(mrb->c->cibase[cioff].proc)) {
- return TRUE;
+ while (cb <= ci) {
+ if (ci->proc == proc) {
+ return mrb_vm_ci_env(ci);
+ }
+ ci--;
+ }
}
- return FALSE;
+ return NULL;
}
-static inline struct REnv*
-top_env(mrb_state *mrb, struct RProc *proc)
+static inline const struct RProc*
+top_proc(mrb_state *mrb, const struct RProc *proc)
{
- struct REnv *e = proc->env;
-
- if (is_strict(mrb, e)) return e;
- while (e->c) {
- e = (struct REnv*)e->c;
- if (is_strict(mrb, e)) return e;
+ while (proc->upper) {
+ if (MRB_PROC_SCOPE_P(proc) || MRB_PROC_STRICT_P(proc))
+ return proc;
+ proc = proc->upper;
}
- return e;
+ return proc;
}
#define CI_ACC_SKIP -1
#define CI_ACC_DIRECT -2
+#define CI_ACC_RESUMED -3
-static mrb_callinfo*
-cipush(mrb_state *mrb)
+static inline mrb_callinfo*
+cipush(mrb_state *mrb, mrb_int push_stacks, mrb_int acc,
+ struct RClass *target_class, const struct RProc *proc, mrb_sym mid, mrb_int argc)
{
struct mrb_context *c = mrb->c;
mrb_callinfo *ci = c->ci;
- int eidx = ci->eidx;
- int ridx = ci->ridx;
-
if (ci + 1 == c->ciend) {
- size_t size = ci - c->cibase;
+ ptrdiff_t size = ci - c->cibase;
c->cibase = (mrb_callinfo *)mrb_realloc(mrb, c->cibase, sizeof(mrb_callinfo)*size*2);
c->ci = c->cibase + size;
c->ciend = c->cibase + size * 2;
}
ci = ++c->ci;
- ci->eidx = eidx;
- ci->ridx = ridx;
- ci->env = 0;
- ci->pc = 0;
- ci->err = 0;
- ci->proc = 0;
+ ci->mid = mid;
+ mrb_vm_ci_proc_set(ci, proc);
+ ci->stack = ci[-1].stack + push_stacks;
+ ci->argc = (int16_t)argc;
+ ci->acc = (int16_t)acc;
+ ci->u.target_class = target_class;
return ci;
}
-static void
-cipop(mrb_state *mrb)
+void
+mrb_env_unshare(mrb_state *mrb, struct REnv *e)
{
- struct mrb_context *c = mrb->c;
-
- if (c->ci->env) {
- struct REnv *e = c->ci->env;
- size_t len = (size_t)MRB_ENV_STACK_LEN(e);
- mrb_value *p = (mrb_value *)mrb_malloc(mrb, sizeof(mrb_value)*len);
+ if (e == NULL) return;
+ else {
+ size_t len = (size_t)MRB_ENV_LEN(e);
+ mrb_value *p;
- MRB_ENV_UNSHARE_STACK(e);
+ if (!MRB_ENV_ONSTACK_P(e)) return;
+ if (e->cxt != mrb->c) return;
+ if (e == mrb_vm_ci_env(mrb->c->cibase)) return; /* for mirb */
+ p = (mrb_value *)mrb_malloc(mrb, sizeof(mrb_value)*len);
if (len > 0) {
stack_copy(p, e->stack, len);
}
e->stack = p;
+ MRB_ENV_CLOSE(e);
mrb_write_barrier(mrb, (struct RBasic *)e);
}
+}
+
+static inline mrb_callinfo*
+cipop(mrb_state *mrb)
+{
+ struct mrb_context *c = mrb->c;
+ struct REnv *env = mrb_vm_ci_env(c->ci);
c->ci--;
+ if (env) mrb_env_unshare(mrb, env);
+ return c->ci;
}
-static void
-ecall(mrb_state *mrb, int i)
+MRB_API mrb_value
+mrb_protect_error(mrb_state *mrb, mrb_protect_error_func *body, void *userdata, mrb_bool *error)
{
- struct RProc *p;
- mrb_callinfo *ci;
- mrb_value *self = mrb->c->stack;
- struct RObject *exc;
-
- p = mrb->c->ensure[i];
- if (!p) return;
- if (mrb->c->ci->eidx > i)
- mrb->c->ci->eidx = i;
- ci = cipush(mrb);
- ci->stackent = mrb->c->stack;
- ci->mid = ci[-1].mid;
- ci->acc = CI_ACC_SKIP;
- ci->argc = 0;
- ci->proc = p;
- ci->nregs = p->body.irep->nregs;
- ci->target_class = p->target_class;
- mrb->c->stack = mrb->c->stack + ci[-1].nregs;
- exc = mrb->exc; mrb->exc = 0;
- mrb_run(mrb, p, *self);
- mrb->c->ensure[i] = NULL;
- if (!mrb->exc) mrb->exc = exc;
+ struct mrb_jmpbuf *prev_jmp = mrb->jmp;
+ struct mrb_jmpbuf c_jmp;
+ mrb_value result = mrb_nil_value();
+ int ai = mrb_gc_arena_save(mrb);
+ const struct mrb_context *c = mrb->c;
+ int ci_index = c->ci - c->cibase;
+
+ if (error) { *error = FALSE; }
+
+ MRB_TRY(&c_jmp) {
+ mrb->jmp = &c_jmp;
+ result = body(mrb, userdata);
+ mrb->jmp = prev_jmp;
+ }
+ MRB_CATCH(&c_jmp) {
+ mrb->jmp = prev_jmp;
+ result = mrb_obj_value(mrb->exc);
+ mrb->exc = NULL;
+ if (error) { *error = TRUE; }
+ if (mrb->c == c) {
+ while (c->ci - c->cibase > ci_index) {
+ cipop(mrb);
+ }
+ }
+ else {
+ // It was probably switched by mrb_fiber_resume().
+ // Simply destroy all successive CI_ACC_DIRECTs once the fiber has been switched.
+ c = mrb->c;
+ while (c->ci > c->cibase && c->ci->acc == CI_ACC_DIRECT) {
+ cipop(mrb);
+ }
+ }
+ }
+ MRB_END_EXC(&c_jmp);
+
+ mrb_gc_arena_restore(mrb, ai);
+ mrb_gc_protect(mrb, result);
+ return result;
}
+void mrb_exc_set(mrb_state *mrb, mrb_value exc);
+static mrb_value mrb_run(mrb_state *mrb, const struct RProc* proc, mrb_value self);
+
#ifndef MRB_FUNCALL_ARGC_MAX
#define MRB_FUNCALL_ARGC_MAX 16
#endif
@@ -310,13 +377,57 @@ mrb_funcall(mrb_state *mrb, mrb_value self, const char *name, mrb_int argc, ...)
}
MRB_API mrb_value
+mrb_funcall_id(mrb_state *mrb, mrb_value self, mrb_sym mid, mrb_int argc, ...)
+{
+ mrb_value argv[MRB_FUNCALL_ARGC_MAX];
+ va_list ap;
+ mrb_int i;
+
+ if (argc > MRB_FUNCALL_ARGC_MAX) {
+ mrb_raise(mrb, E_ARGUMENT_ERROR, "Too long arguments. (limit=" MRB_STRINGIZE(MRB_FUNCALL_ARGC_MAX) ")");
+ }
+
+ va_start(ap, argc);
+ for (i = 0; i < argc; i++) {
+ argv[i] = va_arg(ap, mrb_value);
+ }
+ va_end(ap);
+ return mrb_funcall_argv(mrb, self, mid, argc, argv);
+}
+
+static mrb_int
+ci_nregs(mrb_callinfo *ci)
+{
+ const struct RProc *p;
+ mrb_int n = 0;
+
+ if (!ci) return 3;
+ p = ci->proc;
+ if (!p) {
+ if (ci->argc < 0) return 3;
+ return ci->argc+2;
+ }
+ if (!MRB_PROC_CFUNC_P(p) && p->body.irep) {
+ n = p->body.irep->nregs;
+ }
+ if (ci->argc < 0) {
+ if (n < 3) n = 3; /* self + args + blk */
+ }
+ if (ci->argc > n) {
+ n = ci->argc + 2; /* self + blk */
+ }
+ return n;
+}
+
+MRB_API mrb_value
mrb_funcall_with_block(mrb_state *mrb, mrb_value self, mrb_sym mid, mrb_int argc, const mrb_value *argv, mrb_value blk)
{
mrb_value val;
+ int ai = mrb_gc_arena_save(mrb);
if (!mrb->jmp) {
struct mrb_jmpbuf c_jmp;
- mrb_callinfo *old_ci = mrb->c->ci;
+ ptrdiff_t nth_ci = mrb->c->ci - mrb->c->cibase;
MRB_TRY(&c_jmp) {
mrb->jmp = &c_jmp;
@@ -325,78 +436,86 @@ mrb_funcall_with_block(mrb_state *mrb, mrb_value self, mrb_sym mid, mrb_int argc
mrb->jmp = 0;
}
MRB_CATCH(&c_jmp) { /* error */
- while (old_ci != mrb->c->ci) {
- mrb->c->stack = mrb->c->ci->stackent;
+ while (nth_ci < (mrb->c->ci - mrb->c->cibase)) {
cipop(mrb);
}
mrb->jmp = 0;
val = mrb_obj_value(mrb->exc);
}
MRB_END_EXC(&c_jmp);
+ mrb->jmp = 0;
}
else {
- struct RProc *p;
+ mrb_method_t m;
struct RClass *c;
- mrb_sym undef = 0;
mrb_callinfo *ci;
- int n;
+ mrb_int n = ci_nregs(mrb->c->ci);
+ ptrdiff_t voff = -1;
- if (!mrb->c->stack) {
+ if (!mrb->c->stbase) {
stack_init(mrb);
}
- n = mrb->c->ci->nregs;
if (argc < 0) {
- mrb_raisef(mrb, E_ARGUMENT_ERROR, "negative argc for funcall (%S)", mrb_fixnum_value(argc));
+ mrb_raisef(mrb, E_ARGUMENT_ERROR, "negative argc for funcall (%i)", argc);
}
c = mrb_class(mrb, self);
- p = mrb_method_search_vm(mrb, &c, mid);
- if (!p) {
- undef = mid;
- mid = mrb_intern_lit(mrb, "method_missing");
- p = mrb_method_search_vm(mrb, &c, mid);
- n++; argc++;
- }
- ci = cipush(mrb);
- ci->mid = mid;
- ci->proc = p;
- ci->stackent = mrb->c->stack;
- ci->argc = argc;
- ci->target_class = c;
- mrb->c->stack = mrb->c->stack + n;
- if (MRB_PROC_CFUNC_P(p)) {
- ci->nregs = argc + 2;
- stack_extend(mrb, ci->nregs, 0);
+ m = mrb_method_search_vm(mrb, &c, mid);
+ if (MRB_METHOD_UNDEF_P(m)) {
+ mrb_sym missing = MRB_SYM(method_missing);
+ mrb_value args = mrb_ary_new_from_values(mrb, argc, argv);
+ m = mrb_method_search_vm(mrb, &c, missing);
+ if (MRB_METHOD_UNDEF_P(m)) {
+ mrb_method_missing(mrb, mid, self, args);
+ }
+ mrb_ary_unshift(mrb, args, mrb_symbol_value(mid));
+ mrb_stack_extend(mrb, n+2);
+ mrb->c->ci->stack[n+1] = args;
+ argc = -1;
}
- else {
- ci->nregs = p->body.irep->nregs + n;
- stack_extend(mrb, ci->nregs, argc+2);
+ if (mrb->c->ci - mrb->c->cibase > MRB_FUNCALL_DEPTH_MAX) {
+ mrb_exc_raise(mrb, mrb_obj_value(mrb->stack_err));
}
- mrb->c->stack[0] = self;
- if (undef) {
- mrb->c->stack[1] = mrb_symbol_value(undef);
- if (argc > 1) {
- stack_copy(mrb->c->stack+2, argv, argc-1);
- }
+ ci = cipush(mrb, n, 0, c, NULL, mid, argc);
+ if (argc < 0) argc = 1;
+ if (mrb->c->stbase <= argv && argv < mrb->c->stend) {
+ voff = argv - mrb->c->stbase;
}
- else if (argc > 0) {
- stack_copy(mrb->c->stack+1, argv, argc);
+ if (argc >= CALL_MAXARGS) {
+ mrb_value args = mrb_ary_new_from_values(mrb, argc, argv);
+
+ mrb->c->ci->stack[1] = args;
+ ci->argc = -1;
+ argc = 1;
}
- mrb->c->stack[argc+1] = blk;
+ mrb_stack_extend(mrb, argc + 2);
+ if (MRB_METHOD_PROC_P(m)) {
+ struct RProc *p = MRB_METHOD_PROC(m);
- if (MRB_PROC_CFUNC_P(p)) {
- int ai = mrb_gc_arena_save(mrb);
+ mrb_vm_ci_proc_set(ci, p);
+ if (!MRB_PROC_CFUNC_P(p)) {
+ mrb_stack_extend(mrb, p->body.irep->nregs + argc);
+ }
+ }
+ if (voff >= 0) {
+ argv = mrb->c->stbase + voff;
+ }
+ mrb->c->ci->stack[0] = self;
+ if (ci->argc > 0) {
+ stack_copy(mrb->c->ci->stack+1, argv, argc);
+ }
+ mrb->c->ci->stack[argc+1] = blk;
+ if (MRB_METHOD_CFUNC_P(m)) {
ci->acc = CI_ACC_DIRECT;
- val = p->body.func(mrb, self);
- mrb->c->stack = mrb->c->ci->stackent;
+ val = MRB_METHOD_CFUNC(m)(mrb, self);
cipop(mrb);
- mrb_gc_arena_restore(mrb, ai);
}
else {
ci->acc = CI_ACC_SKIP;
- val = mrb_run(mrb, p, self);
+ val = mrb_run(mrb, MRB_METHOD_PROC(m), self);
}
}
+ mrb_gc_arena_restore(mrb, ai);
mrb_gc_protect(mrb, val);
return val;
}
@@ -407,6 +526,58 @@ mrb_funcall_argv(mrb_state *mrb, mrb_value self, mrb_sym mid, mrb_int argc, cons
return mrb_funcall_with_block(mrb, self, mid, argc, argv, mrb_nil_value());
}
+static mrb_value
+exec_irep(mrb_state *mrb, mrb_value self, struct RProc *p)
+{
+ mrb_callinfo *ci = mrb->c->ci;
+ int keep, nregs;
+
+ ci->stack[0] = self;
+ mrb_vm_ci_proc_set(ci, p);
+ if (MRB_PROC_CFUNC_P(p)) {
+ return MRB_PROC_CFUNC(p)(mrb, self);
+ }
+ nregs = p->body.irep->nregs;
+ if (ci->argc < 0) keep = 3;
+ else keep = ci->argc + 2;
+ if (nregs < keep) {
+ mrb_stack_extend(mrb, keep);
+ }
+ else {
+ mrb_stack_extend(mrb, nregs);
+ stack_clear(ci->stack+keep, nregs-keep);
+ }
+
+ cipush(mrb, 0, 0, NULL, NULL, 0, 0);
+
+ return self;
+}
+
+mrb_value
+mrb_exec_irep(mrb_state *mrb, mrb_value self, struct RProc *p)
+{
+ mrb_callinfo *ci = mrb->c->ci;
+ if (ci->acc >= 0) {
+ return exec_irep(mrb, self, p);
+ }
+ else {
+ mrb_value ret;
+ if (MRB_PROC_CFUNC_P(p)) {
+ cipush(mrb, 0, CI_ACC_DIRECT, mrb_vm_ci_target_class(ci), p, ci->mid, ci->argc);
+ ret = MRB_PROC_CFUNC(p)(mrb, self);
+ cipop(mrb);
+ }
+ else {
+ int keep = (ci->argc < 0 ? 1 : ci->argc) + 2 /* receiver + block */;
+ ret = mrb_top_run(mrb, p, self, keep);
+ }
+ if (mrb->exc && mrb->jmp) {
+ mrb_exc_raise(mrb, mrb_obj_value(mrb->exc));
+ }
+ return ret;
+ }
+}
+
/* 15.3.1.3.4 */
/* 15.3.1.3.44 */
/*
@@ -426,30 +597,33 @@ mrb_funcall_argv(mrb_state *mrb, mrb_value self, mrb_sym mid, mrb_int argc, cons
* k = Klass.new
* k.send :hello, "gentle", "readers" #=> "Hello gentle readers"
*/
-MRB_API mrb_value
+mrb_value
mrb_f_send(mrb_state *mrb, mrb_value self)
{
mrb_sym name;
- mrb_value block, *argv, *regs;
+ mrb_value block, *regs;
+ const mrb_value *argv;
mrb_int argc, i, len;
- struct RProc *p;
+ mrb_method_t m;
struct RClass *c;
mrb_callinfo *ci;
mrb_get_args(mrb, "n*&", &name, &argv, &argc, &block);
+ ci = mrb->c->ci;
+ if (ci->acc < 0) {
+ funcall:
+ return mrb_funcall_with_block(mrb, self, name, argc, argv, block);
+ }
c = mrb_class(mrb, self);
- p = mrb_method_search_vm(mrb, &c, name);
-
- if (!p) { /* call method_mising */
- return mrb_funcall_with_block(mrb, self, name, argc, argv, block);
+ m = mrb_method_search_vm(mrb, &c, name);
+ if (MRB_METHOD_UNDEF_P(m)) { /* call method_mising */
+ goto funcall;
}
- ci = mrb->c->ci;
ci->mid = name;
- ci->target_class = c;
- ci->proc = p;
- regs = mrb->c->stack+1;
+ ci->u.target_class = c;
+ regs = mrb->c->ci->stack+1;
/* remove first symbol from arguments */
if (ci->argc >= 0) {
for (i=0,len=ci->argc; i<len; i++) {
@@ -458,22 +632,16 @@ mrb_f_send(mrb_state *mrb, mrb_value self)
ci->argc--;
}
else { /* variable length arguments */
- mrb_ary_shift(mrb, regs[0]);
+ regs[0] = mrb_ary_subseq(mrb, regs[0], 1, RARRAY_LEN(regs[0]) - 1);
}
- if (MRB_PROC_CFUNC_P(p)) {
- return p->body.func(mrb, self);
+ if (MRB_METHOD_CFUNC_P(m)) {
+ if (MRB_METHOD_PROC_P(m)) {
+ mrb_vm_ci_proc_set(ci, MRB_METHOD_PROC(m));
+ }
+ return MRB_METHOD_CFUNC(m)(mrb, self);
}
-
- ci->nregs = p->body.irep->nregs;
- ci = cipush(mrb);
- ci->nregs = 0;
- ci->target_class = 0;
- ci->pc = p->body.irep->iseq;
- ci->stackent = mrb->c->stack;
- ci->acc = 0;
-
- return self;
+ return exec_irep(mrb, self, MRB_METHOD_PROC(m));
}
static mrb_value
@@ -481,27 +649,34 @@ eval_under(mrb_state *mrb, mrb_value self, mrb_value blk, struct RClass *c)
{
struct RProc *p;
mrb_callinfo *ci;
+ int nregs;
if (mrb_nil_p(blk)) {
mrb_raise(mrb, E_ARGUMENT_ERROR, "no block given");
}
ci = mrb->c->ci;
if (ci->acc == CI_ACC_DIRECT) {
- return mrb_yield_with_class(mrb, blk, 0, 0, self, c);
+ return mrb_yield_with_class(mrb, blk, 1, &self, self, c);
}
- ci->target_class = c;
+ ci->u.target_class = c;
p = mrb_proc_ptr(blk);
- ci->proc = p;
+ mrb_vm_ci_proc_set(ci, p);
+ ci->argc = 1;
+ ci->mid = ci[-1].mid;
if (MRB_PROC_CFUNC_P(p)) {
- return p->body.func(mrb, self);
+ mrb_stack_extend(mrb, 3);
+ mrb->c->ci->stack[0] = self;
+ mrb->c->ci->stack[1] = self;
+ mrb->c->ci->stack[2] = mrb_nil_value();
+ return MRB_PROC_CFUNC(p)(mrb, self);
}
- ci->nregs = p->body.irep->nregs;
- ci = cipush(mrb);
- ci->nregs = 0;
- ci->target_class = 0;
- ci->pc = p->body.irep->iseq;
- ci->stackent = mrb->c->stack;
- ci->acc = 0;
+ nregs = p->body.irep->nregs;
+ if (nregs < 3) nregs = 3;
+ mrb_stack_extend(mrb, nregs);
+ mrb->c->ci->stack[0] = self;
+ mrb->c->ci->stack[1] = self;
+ stack_clear(mrb->c->ci->stack+2, nregs-2);
+ ci = cipush(mrb, 0, 0, NULL, NULL, 0, 0);
return self;
}
@@ -552,24 +727,11 @@ mrb_value
mrb_obj_instance_eval(mrb_state *mrb, mrb_value self)
{
mrb_value a, b;
- mrb_value cv;
- struct RClass *c;
if (mrb_get_args(mrb, "|S&", &a, &b) == 1) {
mrb_raise(mrb, E_NOTIMP_ERROR, "instance_eval with string not implemented");
}
- switch (mrb_type(self)) {
- case MRB_TT_SYMBOL:
- case MRB_TT_FIXNUM:
- case MRB_TT_FLOAT:
- c = 0;
- break;
- default:
- cv = mrb_singleton_class(mrb, self);
- c = mrb_class_ptr(cv);
- break;
- }
- return eval_under(mrb, self, b, c);
+ return eval_under(mrb, self, b, mrb_singleton_class_ptr(mrb, self));
}
MRB_API mrb_value
@@ -578,39 +740,41 @@ mrb_yield_with_class(mrb_state *mrb, mrb_value b, mrb_int argc, const mrb_value
struct RProc *p;
mrb_sym mid = mrb->c->ci->mid;
mrb_callinfo *ci;
- int n = mrb->c->ci->nregs;
mrb_value val;
+ mrb_int n;
if (mrb_nil_p(b)) {
mrb_raise(mrb, E_ARGUMENT_ERROR, "no block given");
}
+ ci = mrb->c->ci;
+ n = ci_nregs(ci);
+ if (ci - mrb->c->cibase > MRB_FUNCALL_DEPTH_MAX) {
+ mrb_exc_raise(mrb, mrb_obj_value(mrb->stack_err));
+ }
p = mrb_proc_ptr(b);
- ci = cipush(mrb);
- ci->mid = mid;
- ci->proc = p;
- ci->stackent = mrb->c->stack;
- ci->argc = argc;
- ci->target_class = c;
- ci->acc = CI_ACC_SKIP;
- mrb->c->stack = mrb->c->stack + n;
- if (MRB_PROC_CFUNC_P(p)) {
- ci->nregs = argc + 2;
- stack_extend(mrb, ci->nregs, 0);
+ ci = cipush(mrb, n, CI_ACC_SKIP, c, p, mid, 0 /* dummy */);
+ if (argc >= CALL_MAXARGS) {
+ ci->argc = -1;
+ n = 3;
}
else {
- ci->nregs = p->body.irep->nregs;
- stack_extend(mrb, ci->nregs, argc+2);
+ ci->argc = (int)argc;
+ n = argc + 2;
}
-
- mrb->c->stack[0] = self;
- if (argc > 0) {
- stack_copy(mrb->c->stack+1, argv, argc);
+ mrb_stack_extend(mrb, n);
+ mrb->c->ci->stack[0] = self;
+ if (ci->argc < 0) {
+ mrb->c->ci->stack[1] = mrb_ary_new_from_values(mrb, argc, argv);
+ argc = 1;
}
- mrb->c->stack[argc+1] = mrb_nil_value();
+ else if (argc > 0) {
+ stack_copy(mrb->c->ci->stack+1, argv, argc);
+ }
+ mrb->c->ci->stack[argc+1] = mrb_nil_value();
if (MRB_PROC_CFUNC_P(p)) {
- val = p->body.func(mrb, self);
- mrb->c->stack = mrb->c->ci->stackent;
+ ci->acc = CI_ACC_DIRECT;
+ val = MRB_PROC_CFUNC(p)(mrb, self);
cipop(mrb);
}
else {
@@ -624,7 +788,7 @@ mrb_yield_argv(mrb_state *mrb, mrb_value b, mrb_int argc, const mrb_value *argv)
{
struct RProc *p = mrb_proc_ptr(b);
- return mrb_yield_with_class(mrb, b, argc, argv, p->env->stack[0], p->target_class);
+ return mrb_yield_with_class(mrb, b, argc, argv, MRB_PROC_ENV(p)->stack[0], MRB_PROC_TARGET_CLASS(p));
}
MRB_API mrb_value
@@ -632,7 +796,81 @@ mrb_yield(mrb_state *mrb, mrb_value b, mrb_value arg)
{
struct RProc *p = mrb_proc_ptr(b);
- return mrb_yield_with_class(mrb, b, 1, &arg, p->env->stack[0], p->target_class);
+ return mrb_yield_with_class(mrb, b, 1, &arg, MRB_PROC_ENV(p)->stack[0], MRB_PROC_TARGET_CLASS(p));
+}
+
+mrb_value
+mrb_yield_cont(mrb_state *mrb, mrb_value b, mrb_value self, mrb_int argc, const mrb_value *argv)
+{
+ struct RProc *p;
+ mrb_callinfo *ci;
+
+ if (mrb_nil_p(b)) {
+ mrb_raise(mrb, E_ARGUMENT_ERROR, "no block given");
+ }
+ if (!mrb_proc_p(b)) {
+ mrb_raise(mrb, E_TYPE_ERROR, "not a block");
+ }
+
+ p = mrb_proc_ptr(b);
+ ci = mrb->c->ci;
+
+ mrb_stack_extend(mrb, 3);
+ mrb->c->ci->stack[1] = mrb_ary_new_from_values(mrb, argc, argv);
+ mrb->c->ci->stack[2] = mrb_nil_value();
+ ci->argc = -1;
+ return exec_irep(mrb, self, p);
+}
+
+static struct RBreak*
+break_new(mrb_state *mrb, uint32_t tag, const struct RProc *p, mrb_value val)
+{
+ struct RBreak *brk;
+
+ brk = MRB_OBJ_ALLOC(mrb, MRB_TT_BREAK, NULL);
+ mrb_break_proc_set(brk, p);
+ mrb_break_value_set(brk, val);
+ mrb_break_tag_set(brk, tag);
+
+ return brk;
+}
+
+#define MRB_CATCH_FILTER_RESCUE (UINT32_C(1) << MRB_CATCH_RESCUE)
+#define MRB_CATCH_FILTER_ENSURE (UINT32_C(1) << MRB_CATCH_ENSURE)
+#define MRB_CATCH_FILTER_ALL (MRB_CATCH_FILTER_RESCUE | MRB_CATCH_FILTER_ENSURE)
+
+static const struct mrb_irep_catch_handler *
+catch_handler_find(mrb_state *mrb, mrb_callinfo *ci, const mrb_code *pc, uint32_t filter)
+{
+ const mrb_irep *irep;
+ ptrdiff_t xpc;
+ size_t cnt;
+ const struct mrb_irep_catch_handler *e;
+
+/* The comparison operators use `>` and `<=` because pc already points to the next instruction */
+#define catch_cover_p(pc, beg, end) ((pc) > (ptrdiff_t)(beg) && (pc) <= (ptrdiff_t)(end))
+
+ if (ci->proc == NULL || MRB_PROC_CFUNC_P(ci->proc)) return NULL;
+ irep = ci->proc->body.irep;
+ if (irep->clen < 1) return NULL;
+ xpc = pc - irep->iseq;
+ /* If it retry at the top level, pc will be 0, so check with -1 as the start position */
+ mrb_assert(catch_cover_p(xpc, -1, irep->ilen));
+ if (!catch_cover_p(xpc, -1, irep->ilen)) return NULL;
+
+ /* Currently uses a simple linear search to avoid processing complexity. */
+ cnt = irep->clen;
+ e = mrb_irep_catch_handler_table(irep) + cnt - 1;
+ for (; cnt > 0; cnt --, e --) {
+ if (((UINT32_C(1) << e->type) & filter) &&
+ catch_cover_p(xpc, mrb_irep_catch_handler_unpack(e->begin), mrb_irep_catch_handler_unpack(e->end))) {
+ return e;
+ }
+ }
+
+#undef catch_cover_p
+
+ return NULL;
}
typedef enum {
@@ -650,11 +888,11 @@ localjump_error(mrb_state *mrb, localjump_error_kind kind)
mrb_value msg;
mrb_value exc;
- msg = mrb_str_buf_new(mrb, sizeof(lead) + 7);
+ msg = mrb_str_new_capa(mrb, sizeof(lead) + 7);
mrb_str_cat(mrb, msg, lead, sizeof(lead) - 1);
mrb_str_cat(mrb, msg, kind_str[kind], kind_str_len[kind]);
exc = mrb_exc_new_str(mrb, E_LOCALJUMP_ERROR, msg);
- mrb->exc = mrb_obj_ptr(exc);
+ mrb_exc_set(mrb, exc);
}
static void
@@ -662,92 +900,213 @@ argnum_error(mrb_state *mrb, mrb_int num)
{
mrb_value exc;
mrb_value str;
+ mrb_int argc = mrb->c->ci->argc;
+ if (argc < 0) {
+ mrb_value args = mrb->c->ci->stack[1];
+ if (mrb_array_p(args)) {
+ argc = RARRAY_LEN(args);
+ }
+ }
if (mrb->c->ci->mid) {
- str = mrb_format(mrb, "'%S': wrong number of arguments (%S for %S)",
- mrb_sym2str(mrb, mrb->c->ci->mid),
- mrb_fixnum_value(mrb->c->ci->argc), mrb_fixnum_value(num));
+ str = mrb_format(mrb, "'%n': wrong number of arguments (%i for %i)",
+ mrb->c->ci->mid, argc, num);
}
else {
- str = mrb_format(mrb, "wrong number of arguments (%S for %S)",
- mrb_fixnum_value(mrb->c->ci->argc), mrb_fixnum_value(num));
+ str = mrb_format(mrb, "wrong number of arguments (%i for %i)", argc, num);
}
exc = mrb_exc_new_str(mrb, E_ARGUMENT_ERROR, str);
- mrb->exc = mrb_obj_ptr(exc);
+ mrb_exc_set(mrb, exc);
+}
+
+static mrb_bool
+break_tag_p(struct RBreak *brk, uint32_t tag)
+{
+ return (brk != NULL && brk->tt == MRB_TT_BREAK) ? TRUE : FALSE;
+}
+
+static void
+prepare_tagged_break(mrb_state *mrb, uint32_t tag, const struct RProc *proc, mrb_value val)
+{
+ if (break_tag_p((struct RBreak*)mrb->exc, tag)) {
+ mrb_break_tag_set((struct RBreak*)mrb->exc, tag);
+ }
+ else {
+ mrb->exc = (struct RObject*)break_new(mrb, tag, proc, val);
+ }
}
-#define ERR_PC_SET(mrb, pc) mrb->c->ci->err = pc;
-#define ERR_PC_CLR(mrb) mrb->c->ci->err = 0;
-#ifdef ENABLE_DEBUG
+#define THROW_TAGGED_BREAK(mrb, tag, proc, val) \
+ do { \
+ prepare_tagged_break(mrb, tag, proc, val); \
+ goto L_CATCH_TAGGED_BREAK; \
+ } while (0)
+
+#define UNWIND_ENSURE(mrb, ci, pc, tag, proc, val) \
+ do { \
+ ch = catch_handler_find(mrb, ci, pc, MRB_CATCH_FILTER_ENSURE); \
+ if (ch) { \
+ THROW_TAGGED_BREAK(mrb, tag, proc, val); \
+ } \
+ } while (0)
+
+/*
+ * CHECKPOINT_RESTORE(tag) {
+ * This part is executed when jumping by the same "tag" of RBreak (it is not executed the first time).
+ * Write the code required (initialization of variables, etc.) for the subsequent processing.
+ * }
+ * CHECKPOINT_MAIN(tag) {
+ * This part is always executed.
+ * }
+ * CHECKPOINT_END(tag);
+ *
+ * ...
+ *
+ * // Jump to CHECKPOINT_RESTORE with the same "tag".
+ * goto CHECKPOINT_LABEL_MAKE(tag);
+ */
+
+#define CHECKPOINT_LABEL_MAKE(tag) L_CHECKPOINT_ ## tag
+
+#define CHECKPOINT_RESTORE(tag) \
+ do { \
+ if (FALSE) { \
+ CHECKPOINT_LABEL_MAKE(tag): \
+ do {
+
+#define CHECKPOINT_MAIN(tag) \
+ } while (0); \
+ } \
+ do {
+
+#define CHECKPOINT_END(tag) \
+ } while (0); \
+ } while (0)
+
+#ifdef MRB_USE_DEBUG_HOOK
#define CODE_FETCH_HOOK(mrb, irep, pc, regs) if ((mrb)->code_fetch_hook) (mrb)->code_fetch_hook((mrb), (irep), (pc), (regs));
#else
#define CODE_FETCH_HOOK(mrb, irep, pc, regs)
#endif
+#ifdef MRB_BYTECODE_DECODE_OPTION
+#define BYTECODE_DECODER(x) ((mrb)->bytecode_decoder)?(mrb)->bytecode_decoder((mrb), (x)):(x)
+#else
+#define BYTECODE_DECODER(x) (x)
+#endif
+
+#ifndef MRB_NO_DIRECT_THREADING
#if defined __GNUC__ || defined __clang__ || defined __INTEL_COMPILER
#define DIRECT_THREADED
#endif
+#endif /* ifndef MRB_NO_DIRECT_THREADING */
#ifndef DIRECT_THREADED
-#define INIT_DISPATCH for (;;) { i = *pc; CODE_FETCH_HOOK(mrb, irep, pc, regs); switch (GET_OPCODE(i)) {
-#define CASE(op) case op:
-#define NEXT pc++; break
-#define JUMP break
-#define END_DISPATCH }}
+#define INIT_DISPATCH for (;;) { insn = BYTECODE_DECODER(*pc); CODE_FETCH_HOOK(mrb, irep, pc, regs); switch (insn) {
+#define CASE(insn,ops) case insn: pc++; FETCH_ ## ops (); mrb->c->ci->pc = pc; L_ ## insn ## _BODY:
+#define NEXT goto L_END_DISPATCH
+#define JUMP NEXT
+#define END_DISPATCH L_END_DISPATCH:;}}
#else
#define INIT_DISPATCH JUMP; return mrb_nil_value();
-#define CASE(op) L_ ## op:
-#define NEXT i=*++pc; CODE_FETCH_HOOK(mrb, irep, pc, regs); goto *optable[GET_OPCODE(i)]
-#define JUMP i=*pc; CODE_FETCH_HOOK(mrb, irep, pc, regs); goto *optable[GET_OPCODE(i)]
+#define CASE(insn,ops) L_ ## insn: pc++; FETCH_ ## ops (); mrb->c->ci->pc = pc; L_ ## insn ## _BODY:
+#define NEXT insn=BYTECODE_DECODER(*pc); CODE_FETCH_HOOK(mrb, irep, pc, regs); goto *optable[insn]
+#define JUMP NEXT
#define END_DISPATCH
#endif
-mrb_value mrb_gv_val_get(mrb_state *mrb, mrb_sym sym);
-void mrb_gv_val_set(mrb_state *mrb, mrb_sym sym, mrb_value val);
+MRB_API mrb_value
+mrb_vm_run(mrb_state *mrb, const struct RProc *proc, mrb_value self, mrb_int stack_keep)
+{
+ const mrb_irep *irep = proc->body.irep;
+ mrb_value result;
+ struct mrb_context *c = mrb->c;
+ ptrdiff_t cioff = c->ci - c->cibase;
+ mrb_int nregs = irep->nregs;
-#define CALL_MAXARGS 127
+ if (!c->stbase) {
+ stack_init(mrb);
+ }
+ if (stack_keep > nregs)
+ nregs = stack_keep;
+ mrb_stack_extend(mrb, nregs);
+ stack_clear(c->ci->stack + stack_keep, nregs - stack_keep);
+ c->ci->stack[0] = self;
+ result = mrb_vm_exec(mrb, proc, irep->iseq);
+ if (mrb->c != c) {
+ if (mrb->c->fib) {
+ mrb_write_barrier(mrb, (struct RBasic*)mrb->c->fib);
+ }
+ mrb->c = c;
+ }
+ else if (c->ci - c->cibase > cioff) {
+ c->ci = c->cibase + cioff;
+ }
+ return result;
+}
+
+static mrb_bool
+check_target_class(mrb_state *mrb)
+{
+ if (!mrb_vm_ci_target_class(mrb->c->ci)) {
+ mrb_value exc = mrb_exc_new_lit(mrb, E_TYPE_ERROR, "no target class or module");
+ mrb_exc_set(mrb, exc);
+ return FALSE;
+ }
+ return TRUE;
+}
+
+mrb_value
+get_send_args(mrb_state *mrb, mrb_int argc, mrb_value *regs)
+{
+ if (argc < 0) return regs[0];
+ return mrb_ary_new_from_values(mrb, argc, regs);
+}
+
+static void
+proc_adjust_upper(struct RProc *p)
+{
+ /* skip upper procs while unnamed blocks and method closures */
+ while (p->upper) {
+ if (MRB_FLAG_TEST(p->upper, MRB_PROC_SCOPE) &&
+ !MRB_FLAG_TEST(p->upper, MRB_PROC_STRICT)) {
+ break;
+ }
+ p->upper = p->upper->upper;
+ }
+}
+
+mrb_value mrb_obj_missing(mrb_state *mrb, mrb_value mod);
+void mrb_hash_check_kdict(mrb_state *mrb, mrb_value self);
+void mrb_method_added(mrb_state *mrb, struct RClass *c, mrb_sym mid);
MRB_API mrb_value
-mrb_context_run(mrb_state *mrb, struct RProc *proc, mrb_value self, unsigned int stack_keep)
+mrb_vm_exec(mrb_state *mrb, const struct RProc *proc, const mrb_code *pc)
{
- /* mrb_assert(mrb_proc_cfunc_p(proc)) */
- mrb_irep *irep = proc->body.irep;
- mrb_code *pc = irep->iseq;
- mrb_value *pool = irep->pool;
- mrb_sym *syms = irep->syms;
- mrb_value *regs = NULL;
- mrb_code i;
+ /* mrb_assert(MRB_PROC_CFUNC_P(proc)) */
+ const mrb_irep *irep = proc->body.irep;
+ const mrb_pool_value *pool = irep->pool;
+ const mrb_sym *syms = irep->syms;
+ mrb_code insn;
int ai = mrb_gc_arena_save(mrb);
struct mrb_jmpbuf *prev_jmp = mrb->jmp;
struct mrb_jmpbuf c_jmp;
+ uint32_t a;
+ uint16_t b;
+ uint16_t c;
+ mrb_sym mid;
+ const struct mrb_irep_catch_handler *ch;
#ifdef DIRECT_THREADED
- static void *optable[] = {
- &&L_OP_NOP, &&L_OP_MOVE,
- &&L_OP_LOADL, &&L_OP_LOADI, &&L_OP_LOADSYM, &&L_OP_LOADNIL,
- &&L_OP_LOADSELF, &&L_OP_LOADT, &&L_OP_LOADF,
- &&L_OP_GETGLOBAL, &&L_OP_SETGLOBAL, &&L_OP_GETSPECIAL, &&L_OP_SETSPECIAL,
- &&L_OP_GETIV, &&L_OP_SETIV, &&L_OP_GETCV, &&L_OP_SETCV,
- &&L_OP_GETCONST, &&L_OP_SETCONST, &&L_OP_GETMCNST, &&L_OP_SETMCNST,
- &&L_OP_GETUPVAR, &&L_OP_SETUPVAR,
- &&L_OP_JMP, &&L_OP_JMPIF, &&L_OP_JMPNOT,
- &&L_OP_ONERR, &&L_OP_RESCUE, &&L_OP_POPERR, &&L_OP_RAISE, &&L_OP_EPUSH, &&L_OP_EPOP,
- &&L_OP_SEND, &&L_OP_SENDB, &&L_OP_FSEND,
- &&L_OP_CALL, &&L_OP_SUPER, &&L_OP_ARGARY, &&L_OP_ENTER,
- &&L_OP_KARG, &&L_OP_KDICT, &&L_OP_RETURN, &&L_OP_TAILCALL, &&L_OP_BLKPUSH,
- &&L_OP_ADD, &&L_OP_ADDI, &&L_OP_SUB, &&L_OP_SUBI, &&L_OP_MUL, &&L_OP_DIV,
- &&L_OP_EQ, &&L_OP_LT, &&L_OP_LE, &&L_OP_GT, &&L_OP_GE,
- &&L_OP_ARRAY, &&L_OP_ARYCAT, &&L_OP_ARYPUSH, &&L_OP_AREF, &&L_OP_ASET, &&L_OP_APOST,
- &&L_OP_STRING, &&L_OP_STRCAT, &&L_OP_HASH,
- &&L_OP_LAMBDA, &&L_OP_RANGE, &&L_OP_OCLASS,
- &&L_OP_CLASS, &&L_OP_MODULE, &&L_OP_EXEC,
- &&L_OP_METHOD, &&L_OP_SCLASS, &&L_OP_TCLASS,
- &&L_OP_DEBUG, &&L_OP_STOP, &&L_OP_ERR,
+ static const void * const optable[] = {
+#define OPCODE(x,_) &&L_OP_ ## x,
+#include "mruby/ops.h"
+#undef OPCODE
};
#endif
@@ -758,425 +1117,483 @@ RETRY_TRY_BLOCK:
if (exc_catched) {
exc_catched = FALSE;
+ mrb_gc_arena_restore(mrb, ai);
+ if (mrb->exc && mrb->exc->tt == MRB_TT_BREAK)
+ goto L_BREAK;
goto L_RAISE;
}
mrb->jmp = &c_jmp;
- if (!mrb->c->stack) {
- stack_init(mrb);
- }
- stack_extend(mrb, irep->nregs, stack_keep);
- mrb->c->ci->proc = proc;
- mrb->c->ci->nregs = irep->nregs;
- regs = mrb->c->stack;
- regs[0] = self;
+ mrb_vm_ci_proc_set(mrb->c->ci, proc);
+#define regs (mrb->c->ci->stack)
INIT_DISPATCH {
- CASE(OP_NOP) {
+ CASE(OP_NOP, Z) {
/* do nothing */
NEXT;
}
- CASE(OP_MOVE) {
- /* A B R(A) := R(B) */
- regs[GETARG_A(i)] = regs[GETARG_B(i)];
+ CASE(OP_MOVE, BB) {
+ regs[a] = regs[b];
+ NEXT;
+ }
+
+ CASE(OP_LOADL, BB) {
+ switch (pool[b].tt) { /* number */
+ case IREP_TT_INT32:
+ regs[a] = mrb_int_value(mrb, (mrb_int)pool[b].u.i32);
+ break;
+ case IREP_TT_INT64:
+#if defined(MRB_INT64)
+ regs[a] = mrb_int_value(mrb, (mrb_int)pool[b].u.i64);
+ break;
+#else
+#if defined(MRB_64BIT)
+ if (INT32_MIN <= pool[b].u.i64 && pool[b].u.i64 <= INT32_MAX) {
+ regs[a] = mrb_int_value(mrb, (mrb_int)pool[b].u.i64);
+ break;
+ }
+#endif
+ goto L_INT_OVERFLOW;
+#endif
+ case IREP_TT_BIGINT:
+ goto L_INT_OVERFLOW;
+#ifndef MRB_NO_FLOAT
+ case IREP_TT_FLOAT:
+ regs[a] = mrb_float_value(mrb, pool[b].u.f);
+ break;
+#endif
+ default:
+ /* should not happen (tt:string) */
+ regs[a] = mrb_nil_value();
+ break;
+ }
NEXT;
}
- CASE(OP_LOADL) {
- /* A Bx R(A) := Pool(Bx) */
- regs[GETARG_A(i)] = pool[GETARG_Bx(i)];
+ CASE(OP_LOADI, BB) {
+ SET_FIXNUM_VALUE(regs[a], b);
NEXT;
}
- CASE(OP_LOADI) {
- /* A sBx R(A) := sBx */
- SET_INT_VALUE(regs[GETARG_A(i)], GETARG_sBx(i));
+ CASE(OP_LOADINEG, BB) {
+ SET_FIXNUM_VALUE(regs[a], -b);
NEXT;
}
- CASE(OP_LOADSYM) {
- /* A Bx R(A) := Syms(Bx) */
- SET_SYM_VALUE(regs[GETARG_A(i)], syms[GETARG_Bx(i)]);
+ CASE(OP_LOADI__1,B) goto L_LOADI;
+ CASE(OP_LOADI_0,B) goto L_LOADI;
+ CASE(OP_LOADI_1,B) goto L_LOADI;
+ CASE(OP_LOADI_2,B) goto L_LOADI;
+ CASE(OP_LOADI_3,B) goto L_LOADI;
+ CASE(OP_LOADI_4,B) goto L_LOADI;
+ CASE(OP_LOADI_5,B) goto L_LOADI;
+ CASE(OP_LOADI_6,B) goto L_LOADI;
+ CASE(OP_LOADI_7, B) {
+ L_LOADI:
+ SET_FIXNUM_VALUE(regs[a], (mrb_int)insn - (mrb_int)OP_LOADI_0);
NEXT;
}
- CASE(OP_LOADSELF) {
- /* A R(A) := self */
- regs[GETARG_A(i)] = regs[0];
+ CASE(OP_LOADI16, BS) {
+ SET_FIXNUM_VALUE(regs[a], (mrb_int)(int16_t)b);
NEXT;
}
- CASE(OP_LOADT) {
- /* A R(A) := true */
- SET_TRUE_VALUE(regs[GETARG_A(i)]);
+ CASE(OP_LOADI32, BSS) {
+ SET_INT_VALUE(mrb, regs[a], (int32_t)(((uint32_t)b<<16)+c));
NEXT;
}
- CASE(OP_LOADF) {
- /* A R(A) := false */
- SET_FALSE_VALUE(regs[GETARG_A(i)]);
+ CASE(OP_LOADSYM, BB) {
+ SET_SYM_VALUE(regs[a], syms[b]);
NEXT;
}
- CASE(OP_GETGLOBAL) {
- /* A Bx R(A) := getglobal(Syms(Bx)) */
- regs[GETARG_A(i)] = mrb_gv_get(mrb, syms[GETARG_Bx(i)]);
+ CASE(OP_LOADNIL, B) {
+ SET_NIL_VALUE(regs[a]);
NEXT;
}
- CASE(OP_SETGLOBAL) {
- /* setglobal(Syms(Bx), R(A)) */
- mrb_gv_set(mrb, syms[GETARG_Bx(i)], regs[GETARG_A(i)]);
+ CASE(OP_LOADSELF, B) {
+ regs[a] = regs[0];
NEXT;
}
- CASE(OP_GETSPECIAL) {
- /* A Bx R(A) := Special[Bx] */
- regs[GETARG_A(i)] = mrb_vm_special_get(mrb, GETARG_Bx(i));
+ CASE(OP_LOADT, B) {
+ SET_TRUE_VALUE(regs[a]);
NEXT;
}
- CASE(OP_SETSPECIAL) {
- /* A Bx Special[Bx] := R(A) */
- mrb_vm_special_set(mrb, GETARG_Bx(i), regs[GETARG_A(i)]);
+ CASE(OP_LOADF, B) {
+ SET_FALSE_VALUE(regs[a]);
NEXT;
}
- CASE(OP_GETIV) {
- /* A Bx R(A) := ivget(Bx) */
- regs[GETARG_A(i)] = mrb_vm_iv_get(mrb, syms[GETARG_Bx(i)]);
+ CASE(OP_GETGV, BB) {
+ mrb_value val = mrb_gv_get(mrb, syms[b]);
+ regs[a] = val;
NEXT;
}
- CASE(OP_SETIV) {
- /* ivset(Syms(Bx),R(A)) */
- mrb_vm_iv_set(mrb, syms[GETARG_Bx(i)], regs[GETARG_A(i)]);
+ CASE(OP_SETGV, BB) {
+ mrb_gv_set(mrb, syms[b], regs[a]);
NEXT;
}
- CASE(OP_GETCV) {
- /* A Bx R(A) := cvget(Syms(Bx)) */
- ERR_PC_SET(mrb, pc);
- regs[GETARG_A(i)] = mrb_vm_cv_get(mrb, syms[GETARG_Bx(i)]);
- ERR_PC_CLR(mrb);
+ CASE(OP_GETSV, BB) {
+ mrb_value val = mrb_vm_special_get(mrb, syms[b]);
+ regs[a] = val;
NEXT;
}
- CASE(OP_SETCV) {
- /* cvset(Syms(Bx),R(A)) */
- mrb_vm_cv_set(mrb, syms[GETARG_Bx(i)], regs[GETARG_A(i)]);
+ CASE(OP_SETSV, BB) {
+ mrb_vm_special_set(mrb, syms[b], regs[a]);
NEXT;
}
- CASE(OP_GETCONST) {
- /* A Bx R(A) := constget(Syms(Bx)) */
- mrb_value val;
+ CASE(OP_GETIV, BB) {
+ regs[a] = mrb_iv_get(mrb, regs[0], syms[b]);
+ NEXT;
+ }
- ERR_PC_SET(mrb, pc);
- val = mrb_vm_const_get(mrb, syms[GETARG_Bx(i)]);
- ERR_PC_CLR(mrb);
- regs = mrb->c->stack;
- regs[GETARG_A(i)] = val;
+ CASE(OP_SETIV, BB) {
+ mrb_iv_set(mrb, regs[0], syms[b], regs[a]);
NEXT;
}
- CASE(OP_SETCONST) {
- /* A Bx constset(Syms(Bx),R(A)) */
- mrb_vm_const_set(mrb, syms[GETARG_Bx(i)], regs[GETARG_A(i)]);
+ CASE(OP_GETCV, BB) {
+ mrb_value val;
+ val = mrb_vm_cv_get(mrb, syms[b]);
+ regs[a] = val;
NEXT;
}
- CASE(OP_GETMCNST) {
- /* A Bx R(A) := R(A)::Syms(Bx) */
+ CASE(OP_SETCV, BB) {
+ mrb_vm_cv_set(mrb, syms[b], regs[a]);
+ NEXT;
+ }
+
+ CASE(OP_GETCONST, BB) {
mrb_value val;
- int a = GETARG_A(i);
+ mrb_sym sym = syms[b];
- ERR_PC_SET(mrb, pc);
- val = mrb_const_get(mrb, regs[a], syms[GETARG_Bx(i)]);
- ERR_PC_CLR(mrb);
- regs = mrb->c->stack;
+ val = mrb_vm_const_get(mrb, sym);
regs[a] = val;
NEXT;
}
- CASE(OP_SETMCNST) {
- /* A Bx R(A+1)::Syms(Bx) := R(A) */
- int a = GETARG_A(i);
+ CASE(OP_SETCONST, BB) {
+ mrb_vm_const_set(mrb, syms[b], regs[a]);
+ NEXT;
+ }
+
+ CASE(OP_GETMCNST, BB) {
+ mrb_value val;
- mrb_const_set(mrb, regs[a+1], syms[GETARG_Bx(i)], regs[a]);
+ val = mrb_const_get(mrb, regs[a], syms[b]);
+ regs[a] = val;
NEXT;
}
- CASE(OP_GETUPVAR) {
- /* A B C R(A) := uvget(B,C) */
- mrb_value *regs_a = regs + GETARG_A(i);
- int up = GETARG_C(i);
+ CASE(OP_SETMCNST, BB) {
+ mrb_const_set(mrb, regs[a+1], syms[b], regs[a]);
+ NEXT;
+ }
- struct REnv *e = uvenv(mrb, up);
+ CASE(OP_GETUPVAR, BBB) {
+ mrb_value *regs_a = regs + a;
+ struct REnv *e = uvenv(mrb, c);
- if (!e) {
- *regs_a = mrb_nil_value();
+ if (e && b < MRB_ENV_LEN(e)) {
+ *regs_a = e->stack[b];
}
else {
- int idx = GETARG_B(i);
- *regs_a = e->stack[idx];
+ *regs_a = mrb_nil_value();
}
NEXT;
}
- CASE(OP_SETUPVAR) {
- /* A B C uvset(B,C,R(A)) */
- int up = GETARG_C(i);
-
- struct REnv *e = uvenv(mrb, up);
+ CASE(OP_SETUPVAR, BBB) {
+ struct REnv *e = uvenv(mrb, c);
if (e) {
- mrb_value *regs_a = regs + GETARG_A(i);
- int idx = GETARG_B(i);
- e->stack[idx] = *regs_a;
- mrb_write_barrier(mrb, (struct RBasic*)e);
+ mrb_value *regs_a = regs + a;
+
+ if (b < MRB_ENV_LEN(e)) {
+ e->stack[b] = *regs_a;
+ mrb_write_barrier(mrb, (struct RBasic*)e);
+ }
}
NEXT;
}
- CASE(OP_JMP) {
- /* sBx pc+=sBx */
- pc += GETARG_sBx(i);
+ CASE(OP_JMP, S) {
+ pc += (int16_t)a;
JUMP;
}
-
- CASE(OP_JMPIF) {
- /* A sBx if R(A) pc+=sBx */
- if (mrb_test(regs[GETARG_A(i)])) {
- pc += GETARG_sBx(i);
+ CASE(OP_JMPIF, BS) {
+ if (mrb_test(regs[a])) {
+ pc += (int16_t)b;
JUMP;
}
NEXT;
}
-
- CASE(OP_JMPNOT) {
- /* A sBx if !R(A) pc+=sBx */
- if (!mrb_test(regs[GETARG_A(i)])) {
- pc += GETARG_sBx(i);
+ CASE(OP_JMPNOT, BS) {
+ if (!mrb_test(regs[a])) {
+ pc += (int16_t)b;
JUMP;
}
NEXT;
}
-
- CASE(OP_ONERR) {
- /* sBx pc+=sBx on exception */
- if (mrb->c->rsize <= mrb->c->ci->ridx) {
- if (mrb->c->rsize == 0) mrb->c->rsize = 16;
- else mrb->c->rsize *= 2;
- mrb->c->rescue = (mrb_code **)mrb_realloc(mrb, mrb->c->rescue, sizeof(mrb_code*) * mrb->c->rsize);
+ CASE(OP_JMPNIL, BS) {
+ if (mrb_nil_p(regs[a])) {
+ pc += (int16_t)b;
+ JUMP;
}
- mrb->c->rescue[mrb->c->ci->ridx++] = pc + GETARG_sBx(i);
NEXT;
}
- CASE(OP_RESCUE) {
- /* A R(A) := exc; clear(exc) */
- SET_OBJ_VALUE(regs[GETARG_A(i)], mrb->exc);
- mrb->exc = 0;
- NEXT;
+ CASE(OP_JMPUW, S) {
+ a = (uint32_t)((pc - irep->iseq) + (int16_t)a);
+ CHECKPOINT_RESTORE(RBREAK_TAG_JUMP) {
+ struct RBreak *brk = (struct RBreak*)mrb->exc;
+ mrb_value target = mrb_break_value_get(brk);
+ mrb_assert(mrb_integer_p(target));
+ a = (uint32_t)mrb_integer(target);
+ mrb_assert(a >= 0 && a < irep->ilen);
+ }
+ CHECKPOINT_MAIN(RBREAK_TAG_JUMP) {
+ ch = catch_handler_find(mrb, mrb->c->ci, pc, MRB_CATCH_FILTER_ENSURE);
+ if (ch) {
+ /* avoiding a jump from a catch handler into the same handler */
+ if (a < mrb_irep_catch_handler_unpack(ch->begin) || a >= mrb_irep_catch_handler_unpack(ch->end)) {
+ THROW_TAGGED_BREAK(mrb, RBREAK_TAG_JUMP, proc, mrb_fixnum_value(a));
+ }
+ }
+ }
+ CHECKPOINT_END(RBREAK_TAG_JUMP);
+
+ mrb->exc = NULL; /* clear break object */
+ pc = irep->iseq + a;
+ JUMP;
}
- CASE(OP_POPERR) {
- /* A A.times{rescue_pop()} */
- int a = GETARG_A(i);
+ CASE(OP_EXCEPT, B) {
+ mrb_value exc;
- while (a--) {
- mrb->c->ci->ridx--;
+ if (mrb->exc == NULL) {
+ exc = mrb_nil_value();
}
+ else {
+ switch (mrb->exc->tt) {
+ case MRB_TT_BREAK:
+ case MRB_TT_EXCEPTION:
+ exc = mrb_obj_value(mrb->exc);
+ break;
+ default:
+ mrb_assert(!"bad mrb_type");
+ exc = mrb_nil_value();
+ break;
+ }
+ mrb->exc = NULL;
+ }
+ regs[a] = exc;
NEXT;
}
+ CASE(OP_RESCUE, BB) {
+ mrb_value exc = regs[a]; /* exc on stack */
+ mrb_value e = regs[b];
+ struct RClass *ec;
- CASE(OP_RAISE) {
- /* A raise(R(A)) */
- mrb->exc = mrb_obj_ptr(regs[GETARG_A(i)]);
- goto L_RAISE;
- }
-
- CASE(OP_EPUSH) {
- /* Bx ensure_push(SEQ[Bx]) */
- struct RProc *p;
+ switch (mrb_type(e)) {
+ case MRB_TT_CLASS:
+ case MRB_TT_MODULE:
+ break;
+ default:
+ {
+ mrb_value exc;
- p = mrb_closure_new(mrb, irep->reps[GETARG_Bx(i)]);
- /* push ensure_stack */
- if (mrb->c->esize <= mrb->c->ci->eidx) {
- if (mrb->c->esize == 0) mrb->c->esize = 16;
- else mrb->c->esize *= 2;
- mrb->c->ensure = (struct RProc **)mrb_realloc(mrb, mrb->c->ensure, sizeof(struct RProc*) * mrb->c->esize);
+ exc = mrb_exc_new_lit(mrb, E_TYPE_ERROR,
+ "class or module required for rescue clause");
+ mrb_exc_set(mrb, exc);
+ goto L_RAISE;
+ }
}
- mrb->c->ensure[mrb->c->ci->eidx++] = p;
- ARENA_RESTORE(mrb, ai);
+ ec = mrb_class_ptr(e);
+ regs[b] = mrb_bool_value(mrb_obj_is_kind_of(mrb, exc, ec));
NEXT;
}
- CASE(OP_EPOP) {
- /* A A.times{ensure_pop().call} */
- int a = GETARG_A(i);
- mrb_callinfo *ci = mrb->c->ci;
- int n, eidx = ci->eidx;
-
- for (n=0; n<a && eidx > ci[-1].eidx; n++) {
- ecall(mrb, --eidx);
- ARENA_RESTORE(mrb, ai);
+ CASE(OP_RAISEIF, B) {
+ mrb_value exc = regs[a];
+ if (mrb_break_p(exc)) {
+ mrb->exc = mrb_obj_ptr(exc);
+ goto L_BREAK;
+ }
+ mrb_exc_set(mrb, exc);
+ if (mrb->exc) {
+ goto L_RAISE;
}
NEXT;
}
- CASE(OP_LOADNIL) {
- /* A R(A) := nil */
- int a = GETARG_A(i);
+ CASE(OP_SENDV, BB) {
+ c = CALL_MAXARGS;
+ goto L_SEND;
+ };
- SET_NIL_VALUE(regs[a]);
- NEXT;
- }
+ CASE(OP_SENDVB, BB) {
+ c = CALL_MAXARGS;
+ goto L_SENDB;
+ };
- CASE(OP_SENDB) {
- /* A B C R(A) := call(R(A),Syms(B),R(A+1),...,R(A+C),&R(A+C+1))*/
- /* fall through */
+ CASE(OP_SEND, BBB)
+ L_SEND:
+ {
+ /* push nil after arguments */
+ int bidx = (c == CALL_MAXARGS) ? a+2 : a+c+1;
+ SET_NIL_VALUE(regs[bidx]);
+ goto L_SENDB;
+ };
+ L_SEND_SYM:
+ {
+ /* push nil after arguments */
+ int bidx = (c == CALL_MAXARGS) ? a+2 : a+c+1;
+ SET_NIL_VALUE(regs[bidx]);
+ goto L_SENDB_SYM;
};
- L_SEND:
- CASE(OP_SEND) {
- /* A B C R(A) := call(R(A),Syms(B),R(A+1),...,R(A+C)) */
- int a = GETARG_A(i);
- int n = GETARG_C(i);
- struct RProc *m;
- struct RClass *c;
- mrb_callinfo *ci;
- mrb_value recv, result;
- mrb_sym mid = syms[GETARG_B(i)];
+ CASE(OP_SENDB, BBB)
+ L_SENDB:
+ mid = syms[b];
+ L_SENDB_SYM:
+ {
+ mrb_int argc = (c == CALL_MAXARGS) ? -1 : c;
+ mrb_int bidx = (argc < 0) ? a+2 : a+c+1;
+ mrb_method_t m;
+ struct RClass *cls;
+ mrb_callinfo *ci = mrb->c->ci;
+ mrb_value recv, blk;
- recv = regs[a];
- if (GET_OPCODE(i) != OP_SENDB) {
- if (n == CALL_MAXARGS) {
- SET_NIL_VALUE(regs[a+2]);
- }
- else {
- SET_NIL_VALUE(regs[a+n+1]);
- }
- }
- c = mrb_class(mrb, recv);
- m = mrb_method_search_vm(mrb, &c, mid);
- if (!m) {
- mrb_value sym = mrb_symbol_value(mid);
+ mrb_assert(bidx < irep->nregs);
- mid = mrb_intern_lit(mrb, "method_missing");
- m = mrb_method_search_vm(mrb, &c, mid);
- if (n == CALL_MAXARGS) {
- mrb_ary_unshift(mrb, regs[a+1], sym);
- }
- else {
- value_move(regs+a+2, regs+a+1, ++n);
- regs[a+1] = sym;
+ recv = regs[a];
+ blk = regs[bidx];
+ if (!mrb_nil_p(blk) && !mrb_proc_p(blk)) {
+ blk = mrb_type_convert(mrb, blk, MRB_TT_PROC, MRB_SYM(to_proc));
+ /* The stack might have been reallocated during mrb_type_convert(),
+ see #3622 */
+ regs[bidx] = blk;
+ }
+ cls = mrb_class(mrb, recv);
+ m = mrb_method_search_vm(mrb, &cls, mid);
+ if (MRB_METHOD_UNDEF_P(m)) {
+ mrb_sym missing = MRB_SYM(method_missing);
+ mrb_value args;
+
+ if (mrb_func_basic_p(mrb, recv, missing, mrb_obj_missing)) {
+ method_missing:
+ args = get_send_args(mrb, argc, regs+a+1);
+ mrb_method_missing(mrb, mid, recv, args);
+ }
+ if (mid != missing) {
+ cls = mrb_class(mrb, recv);
+ }
+ m = mrb_method_search_vm(mrb, &cls, missing);
+ if (MRB_METHOD_UNDEF_P(m)) goto method_missing; /* just in case */
+ if (argc >= 0) {
+ if (a+2 >= irep->nregs) {
+ mrb_stack_extend(mrb, a+3);
+ }
+ regs[a+1] = mrb_ary_new_from_values(mrb, c, regs+a+1);
+ regs[a+2] = blk;
+ argc = -1;
}
+ mrb_ary_unshift(mrb, regs[a+1], mrb_symbol_value(mid));
+ mid = missing;
}
/* push callinfo */
- ci = cipush(mrb);
- ci->mid = mid;
- ci->proc = m;
- ci->stackent = mrb->c->stack;
- if (c->tt == MRB_TT_ICLASS) {
- ci->target_class = c->c;
- }
- else {
- ci->target_class = c;
- }
+ ci = cipush(mrb, a, a, cls, NULL, mid, argc);
- ci->pc = pc + 1;
- ci->acc = a;
+ if (MRB_METHOD_CFUNC_P(m)) {
+ if (MRB_METHOD_PROC_P(m)) {
+ struct RProc *p = MRB_METHOD_PROC(m);
- /* prepare stack */
- mrb->c->stack += a;
-
- if (MRB_PROC_CFUNC_P(m)) {
- if (n == CALL_MAXARGS) {
- ci->argc = -1;
- ci->nregs = 3;
+ mrb_vm_ci_proc_set(ci, p);
+ recv = p->body.func(mrb, recv);
+ }
+ else if (MRB_METHOD_NOARG_P(m) &&
+ (argc > 0 || (argc == -1 && RARRAY_LEN(regs[1]) != 0))) {
+ argnum_error(mrb, 0);
+ goto L_RAISE;
}
else {
- ci->argc = n;
- ci->nregs = n + 2;
+ recv = MRB_METHOD_FUNC(m)(mrb, recv);
}
- result = m->body.func(mrb, recv);
- mrb->c->stack[0] = result;
mrb_gc_arena_restore(mrb, ai);
+ mrb_gc_arena_shrink(mrb, ai);
if (mrb->exc) goto L_RAISE;
- /* pop stackpos */
ci = mrb->c->ci;
- if (!ci->target_class) { /* return from context modifying method (resume/yield) */
- if (!MRB_PROC_CFUNC_P(ci[-1].proc)) {
+ if (mrb_proc_p(blk)) {
+ struct RProc *p = mrb_proc_ptr(blk);
+ if (p && !MRB_PROC_STRICT_P(p) && MRB_PROC_ENV(p) == mrb_vm_ci_env(&ci[-1])) {
+ p->flags |= MRB_PROC_ORPHAN;
+ }
+ }
+ if (!ci->u.target_class) { /* return from context modifying method (resume/yield) */
+ if (ci->acc == CI_ACC_RESUMED) {
+ mrb->jmp = prev_jmp;
+ return recv;
+ }
+ else {
+ mrb_assert(!MRB_PROC_CFUNC_P(ci[-1].proc));
proc = ci[-1].proc;
irep = proc->body.irep;
pool = irep->pool;
syms = irep->syms;
}
}
- regs = mrb->c->stack = ci->stackent;
+ mrb->c->ci->stack[0] = recv;
+ /* pop stackpos */
+ ci = cipop(mrb);
pc = ci->pc;
- cipop(mrb);
- JUMP;
}
else {
/* setup environment for calling method */
- proc = mrb->c->ci->proc = m;
- irep = m->body.irep;
+ mrb_vm_ci_proc_set(ci, (proc = MRB_METHOD_PROC(m)));
+ irep = proc->body.irep;
pool = irep->pool;
syms = irep->syms;
- ci->nregs = irep->nregs;
- if (n == CALL_MAXARGS) {
- ci->argc = -1;
- stack_extend(mrb, (irep->nregs < 3) ? 3 : irep->nregs, 3);
- }
- else {
- ci->argc = n;
- stack_extend(mrb, irep->nregs, n+2);
- }
- regs = mrb->c->stack;
+ mrb_stack_extend(mrb, (argc < 0 && irep->nregs < 3) ? 3 : irep->nregs);
pc = irep->iseq;
- JUMP;
}
}
+ JUMP;
- CASE(OP_FSEND) {
- /* A B C R(A) := fcall(R(A),Syms(B),R(A+1),... ,R(A+C-1)) */
- NEXT;
- }
-
- CASE(OP_CALL) {
- /* A R(A) := self.call(frame.argc, frame.argv) */
+ CASE(OP_CALL, Z) {
mrb_callinfo *ci;
- mrb_value recv = mrb->c->stack[0];
+ mrb_value recv = mrb->c->ci->stack[0];
struct RProc *m = mrb_proc_ptr(recv);
/* replace callinfo */
ci = mrb->c->ci;
- ci->target_class = m->target_class;
- ci->proc = m;
- if (m->env) {
- if (m->env->mid) {
- ci->mid = m->env->mid;
- }
- if (!m->env->stack) {
- m->env->stack = mrb->c->stack;
- }
+ ci->u.target_class = MRB_PROC_TARGET_CLASS(m);
+ mrb_vm_ci_proc_set(ci, m);
+ if (MRB_PROC_ENV_P(m)) {
+ ci->mid = MRB_PROC_ENV(m)->mid;
}
/* prepare stack */
if (MRB_PROC_CFUNC_P(m)) {
- recv = m->body.func(mrb, recv);
+ recv = MRB_PROC_CFUNC(m)(mrb, recv);
mrb_gc_arena_restore(mrb, ai);
+ mrb_gc_arena_shrink(mrb, ai);
if (mrb->exc) goto L_RAISE;
/* pop stackpos */
- ci = mrb->c->ci;
- regs = mrb->c->stack = ci->stackent;
- regs[ci->acc] = recv;
+ ci = cipop(mrb);
pc = ci->pc;
- cipop(mrb);
+ regs[ci[1].acc] = recv;
irep = mrb->c->ci->proc->body.irep;
pool = irep->pool;
syms = irep->syms;
@@ -1187,232 +1604,323 @@ RETRY_TRY_BLOCK:
proc = m;
irep = m->body.irep;
if (!irep) {
- mrb->c->stack[0] = mrb_nil_value();
- goto L_RETURN;
+ mrb->c->ci->stack[0] = mrb_nil_value();
+ a = 0;
+ c = OP_R_NORMAL;
+ goto L_OP_RETURN_BODY;
}
pool = irep->pool;
syms = irep->syms;
- ci->nregs = irep->nregs;
+ mrb_stack_extend(mrb, irep->nregs);
if (ci->argc < 0) {
- stack_extend(mrb, (irep->nregs < 3) ? 3 : irep->nregs, 3);
+ if (irep->nregs > 3) {
+ stack_clear(regs+3, irep->nregs-3);
+ }
}
- else {
- stack_extend(mrb, irep->nregs, ci->argc+2);
+ else if (ci->argc+2 < irep->nregs) {
+ stack_clear(regs+ci->argc+2, irep->nregs-ci->argc-2);
+ }
+ if (MRB_PROC_ENV_P(m)) {
+ regs[0] = MRB_PROC_ENV(m)->stack[0];
}
- regs = mrb->c->stack;
- regs[0] = m->env->stack[0];
pc = irep->iseq;
JUMP;
}
}
- CASE(OP_SUPER) {
- /* A C R(A) := super(R(A+1),... ,R(A+C+1)) */
- mrb_value recv;
+ CASE(OP_SUPER, BB) {
+ mrb_int argc = (b == CALL_MAXARGS) ? -1 : b;
+ int bidx = (argc < 0) ? a+2 : a+b+1;
+ mrb_method_t m;
+ struct RClass *cls;
mrb_callinfo *ci = mrb->c->ci;
- struct RProc *m;
- struct RClass *c;
+ mrb_value recv, blk;
+ const struct RProc *p = ci->proc;
mrb_sym mid = ci->mid;
- int a = GETARG_A(i);
- int n = GETARG_C(i);
+ struct RClass* target_class = MRB_PROC_TARGET_CLASS(p);
+ if (MRB_PROC_ENV_P(p) && p->e.env->mid && p->e.env->mid != mid) { /* alias support */
+ mid = p->e.env->mid; /* restore old mid */
+ }
+ mrb_assert(bidx < irep->nregs);
+
+ if (mid == 0 || !target_class) {
+ mrb_value exc = mrb_exc_new_lit(mrb, E_NOMETHOD_ERROR, "super called outside of method");
+ mrb_exc_set(mrb, exc);
+ goto L_RAISE;
+ }
+ if (target_class->flags & MRB_FL_CLASS_IS_PREPENDED) {
+ target_class = mrb_vm_ci_target_class(ci);
+ }
+ else if (target_class->tt == MRB_TT_MODULE) {
+ target_class = mrb_vm_ci_target_class(ci);
+ if (target_class->tt != MRB_TT_ICLASS) {
+ goto super_typeerror;
+ }
+ }
recv = regs[0];
- 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);
- if (n == CALL_MAXARGS) {
- mrb_ary_unshift(mrb, regs[a+1], mrb_symbol_value(ci->mid));
+ if (!mrb_obj_is_kind_of(mrb, recv, target_class)) {
+ super_typeerror: ;
+ mrb_value exc = mrb_exc_new_lit(mrb, E_TYPE_ERROR,
+ "self has wrong type to call super in this context");
+ mrb_exc_set(mrb, exc);
+ goto L_RAISE;
+ }
+ blk = regs[bidx];
+ if (!mrb_nil_p(blk) && !mrb_proc_p(blk)) {
+ blk = mrb_type_convert(mrb, blk, MRB_TT_PROC, MRB_SYM(to_proc));
+ /* The stack or ci stack might have been reallocated during
+ mrb_type_convert(), see #3622 and #3784 */
+ regs[bidx] = blk;
+ ci = mrb->c->ci;
+ }
+ cls = target_class->super;
+ m = mrb_method_search_vm(mrb, &cls, mid);
+ if (MRB_METHOD_UNDEF_P(m)) {
+ mrb_sym missing = MRB_SYM(method_missing);
+ mrb_value args;
+
+ if (mrb_func_basic_p(mrb, recv, missing, mrb_obj_missing)) {
+ super_missing:
+ args = get_send_args(mrb, argc, regs+a+1);
+ mrb_no_method_error(mrb, mid, args, "no superclass method '%n'", mid);
}
- else {
- value_move(regs+a+2, regs+a+1, ++n);
- SET_SYM_VALUE(regs[a+1], ci->mid);
+ if (mid != missing) {
+ cls = mrb_class(mrb, recv);
+ }
+ m = mrb_method_search_vm(mrb, &cls, missing);
+ if (MRB_METHOD_UNDEF_P(m)) goto super_missing; /* just in case */
+ if (argc >= 0) {
+ if (a+2 >= irep->nregs) {
+ mrb_stack_extend(mrb, a+3);
+ }
+ regs[a+1] = mrb_ary_new_from_values(mrb, b, regs+a+1);
+ regs[a+2] = blk;
+ argc = -1;
}
+ mrb_ary_unshift(mrb, regs[a+1], mrb_symbol_value(mid));
+ mid = missing;
}
/* push callinfo */
- ci = cipush(mrb);
- ci->mid = mid;
- ci->proc = m;
- ci->stackent = mrb->c->stack;
- if (n == CALL_MAXARGS) {
- ci->argc = -1;
- }
- else {
- ci->argc = n;
- }
- ci->target_class = c;
- ci->pc = pc + 1;
+ ci = cipush(mrb, a, 0, cls, NULL, mid, argc);
/* prepare stack */
- mrb->c->stack += a;
- mrb->c->stack[0] = recv;
+ mrb->c->ci->stack[0] = recv;
- if (MRB_PROC_CFUNC_P(m)) {
- if (n == CALL_MAXARGS) {
- ci->nregs = 3;
- }
- else {
- ci->nregs = n + 2;
+ if (MRB_METHOD_CFUNC_P(m)) {
+ mrb_value v;
+
+ if (MRB_METHOD_PROC_P(m)) {
+ mrb_vm_ci_proc_set(ci, MRB_METHOD_PROC(m));
}
- mrb->c->stack[0] = m->body.func(mrb, recv);
+ v = MRB_METHOD_CFUNC(m)(mrb, recv);
mrb_gc_arena_restore(mrb, ai);
if (mrb->exc) goto L_RAISE;
- /* pop stackpos */
- regs = mrb->c->stack = mrb->c->ci->stackent;
- cipop(mrb);
- NEXT;
+ ci = mrb->c->ci;
+ mrb_assert(!mrb_break_p(v));
+ if (!mrb_vm_ci_target_class(ci)) { /* return from context modifying method (resume/yield) */
+ if (ci->acc == CI_ACC_RESUMED) {
+ mrb->jmp = prev_jmp;
+ return v;
+ }
+ else {
+ mrb_assert(!MRB_PROC_CFUNC_P(ci[-1].proc));
+ proc = ci[-1].proc;
+ irep = proc->body.irep;
+ pool = irep->pool;
+ syms = irep->syms;
+ }
+ }
+ mrb->c->ci->stack[0] = v;
+ ci = cipop(mrb);
+ pc = ci->pc;
+ JUMP;
}
else {
/* fill callinfo */
ci->acc = a;
/* setup environment for calling method */
- ci->proc = m;
- irep = m->body.irep;
+ mrb_vm_ci_proc_set(ci, (proc = MRB_METHOD_PROC(m)));
+ irep = proc->body.irep;
pool = irep->pool;
syms = irep->syms;
- ci->nregs = irep->nregs;
- if (n == CALL_MAXARGS) {
- stack_extend(mrb, (irep->nregs < 3) ? 3 : irep->nregs, 3);
- }
- else {
- stack_extend(mrb, irep->nregs, ci->argc+2);
- }
- regs = mrb->c->stack;
+ mrb_stack_extend(mrb, (argc < 0 && irep->nregs < 3) ? 3 : irep->nregs);
pc = irep->iseq;
JUMP;
}
}
- CASE(OP_ARGARY) {
- /* A Bx R(A) := argument array (16=6:1:5:4) */
- int a = GETARG_A(i);
- int bx = GETARG_Bx(i);
- int m1 = (bx>>10)&0x3f;
- int r = (bx>>9)&0x1;
- int m2 = (bx>>4)&0x1f;
- int lv = (bx>>0)&0xf;
+ CASE(OP_ARGARY, BS) {
+ mrb_int m1 = (b>>11)&0x3f;
+ mrb_int r = (b>>10)&0x1;
+ mrb_int m2 = (b>>5)&0x1f;
+ mrb_int kd = (b>>4)&0x1;
+ mrb_int lv = (b>>0)&0xf;
mrb_value *stack;
+ if (mrb->c->ci->mid == 0 || mrb_vm_ci_target_class(mrb->c->ci) == NULL) {
+ mrb_value exc;
+
+ L_NOSUPER:
+ exc = mrb_exc_new_lit(mrb, E_NOMETHOD_ERROR, "super called outside of method");
+ mrb_exc_set(mrb, exc);
+ goto L_RAISE;
+ }
if (lv == 0) stack = regs + 1;
else {
struct REnv *e = uvenv(mrb, lv-1);
- if (!e) {
- mrb_value exc;
- exc = mrb_exc_new_str_lit(mrb, E_NOMETHOD_ERROR, "super called outside of method");
- mrb->exc = mrb_obj_ptr(exc);
- goto L_RAISE;
- }
+ if (!e) goto L_NOSUPER;
+ if (MRB_ENV_LEN(e) <= m1+r+m2+kd+1)
+ goto L_NOSUPER;
stack = e->stack + 1;
}
if (r == 0) {
- regs[a] = mrb_ary_new_from_values(mrb, m1+m2, stack);
+ regs[a] = mrb_ary_new_from_values(mrb, m1+m2+kd, stack);
}
else {
mrb_value *pp = NULL;
struct RArray *rest;
- int len = 0;
+ mrb_int len = 0;
if (mrb_array_p(stack[m1])) {
struct RArray *ary = mrb_ary_ptr(stack[m1]);
- pp = ary->ptr;
- len = ary->len;
+ pp = ARY_PTR(ary);
+ len = ARY_LEN(ary);
}
- regs[a] = mrb_ary_new_capa(mrb, m1+len+m2);
+ regs[a] = mrb_ary_new_capa(mrb, m1+len+m2+kd);
rest = mrb_ary_ptr(regs[a]);
if (m1 > 0) {
- stack_copy(rest->ptr, stack, m1);
+ stack_copy(ARY_PTR(rest), stack, m1);
}
if (len > 0) {
- stack_copy(rest->ptr+m1, pp, len);
+ stack_copy(ARY_PTR(rest)+m1, pp, len);
}
if (m2 > 0) {
- stack_copy(rest->ptr+m1+len, stack+m1+1, m2);
+ stack_copy(ARY_PTR(rest)+m1+len, stack+m1+1, m2);
}
- rest->len = m1+len+m2;
+ if (kd) {
+ stack_copy(ARY_PTR(rest)+m1+len+m2, stack+m1+m2+1, kd);
+ }
+ ARY_SET_LEN(rest, m1+len+m2+kd);
}
regs[a+1] = stack[m1+r+m2];
- ARENA_RESTORE(mrb, ai);
+ mrb_gc_arena_restore(mrb, ai);
NEXT;
}
- CASE(OP_ENTER) {
- /* Ax arg setup according to flags (23=5:5:1:5:5:1:1) */
- /* number of optional arguments times OP_JMP should follow */
- mrb_aspec ax = GETARG_Ax(i);
- int m1 = MRB_ASPEC_REQ(ax);
- int o = MRB_ASPEC_OPT(ax);
- int r = MRB_ASPEC_REST(ax);
- int m2 = MRB_ASPEC_POST(ax);
+ CASE(OP_ENTER, W) {
+ mrb_int m1 = MRB_ASPEC_REQ(a);
+ mrb_int o = MRB_ASPEC_OPT(a);
+ mrb_int r = MRB_ASPEC_REST(a);
+ mrb_int m2 = MRB_ASPEC_POST(a);
+ mrb_int kd = (MRB_ASPEC_KEY(a) > 0 || MRB_ASPEC_KDICT(a))? 1 : 0;
/* unused
- int k = MRB_ASPEC_KEY(ax);
- int kd = MRB_ASPEC_KDICT(ax);
- int b = MRB_ASPEC_BLOCK(ax);
+ int b = MRB_ASPEC_BLOCK(a);
*/
- int argc = mrb->c->ci->argc;
+ mrb_int argc = mrb->c->ci->argc;
mrb_value *argv = regs+1;
- mrb_value *argv0 = argv;
- int len = m1 + o + r + m2;
+ mrb_value * const argv0 = argv;
+ mrb_int const len = m1 + o + r + m2;
+ mrb_int const blk_pos = len + kd + 1;
mrb_value *blk = &argv[argc < 0 ? 1 : argc];
+ mrb_value kdict = mrb_nil_value();
+ mrb_int kargs = kd;
- if (!mrb_nil_p(*blk) && mrb_type(*blk) != MRB_TT_PROC) {
- *blk = mrb_convert_type(mrb, *blk, MRB_TT_PROC, "Proc", "to_proc");
- }
+ /* arguments is passed with Array */
if (argc < 0) {
struct RArray *ary = mrb_ary_ptr(regs[1]);
- argv = ary->ptr;
- argc = ary->len;
+ argv = ARY_PTR(ary);
+ argc = (int)ARY_LEN(ary);
mrb_gc_protect(mrb, regs[1]);
}
+
+ /* strict argument check */
if (mrb->c->ci->proc && MRB_PROC_STRICT_P(mrb->c->ci->proc)) {
- if (argc >= 0) {
- if (argc < m1 + m2 || (r == 0 && argc > len)) {
- argnum_error(mrb, m1+m2);
- goto L_RAISE;
- }
+ if (argc < m1 + m2 || (r == 0 && argc > len + kd)) {
+ argnum_error(mrb, m1+m2);
+ goto L_RAISE;
}
}
+ /* extract first argument array to arguments */
else if (len > 1 && argc == 1 && mrb_array_p(argv[0])) {
mrb_gc_protect(mrb, argv[0]);
- argc = mrb_ary_ptr(argv[0])->len;
- argv = mrb_ary_ptr(argv[0])->ptr;
+ argc = (int)RARRAY_LEN(argv[0]);
+ argv = RARRAY_PTR(argv[0]);
}
- mrb->c->ci->argc = len;
- if (argc < len) {
- int mlen = m2;
+
+ if (kd) {
+ /* check last arguments is hash if method takes keyword arguments */
+ if (argc == m1+m2) {
+ kdict = mrb_hash_new(mrb);
+ kargs = 0;
+ }
+ else {
+ if (argv && argc > 0 && mrb_hash_p(argv[argc-1])) {
+ kdict = argv[argc-1];
+ mrb_hash_check_kdict(mrb, kdict);
+ }
+ else if (r || argc <= m1+m2+o
+ || !(mrb->c->ci->proc && MRB_PROC_STRICT_P(mrb->c->ci->proc))) {
+ kdict = mrb_hash_new(mrb);
+ kargs = 0;
+ }
+ else {
+ argnum_error(mrb, m1+m2);
+ goto L_RAISE;
+ }
+ if (MRB_ASPEC_KEY(a) > 0) {
+ kdict = mrb_hash_dup(mrb, kdict);
+ }
+ }
+ }
+
+ /* no rest arguments */
+ if (argc-kargs < len) {
+ mrb_int mlen = m2;
if (argc < m1+m2) {
- if (m1 < argc)
- mlen = argc - m1;
- else
- mlen = 0;
+ mlen = m1 < argc ? argc - m1 : 0;
}
- regs[len+1] = *blk; /* move block */
- SET_NIL_VALUE(regs[argc+1]);
- if (argv0 != argv) {
+ regs[blk_pos] = *blk; /* move block */
+ if (kd) regs[len + 1] = kdict;
+
+ /* copy mandatory and optional arguments */
+ if (argv0 != argv && argv) {
value_move(&regs[1], argv, argc-mlen); /* m1 + o */
}
+ if (argc < m1) {
+ stack_clear(&regs[argc+1], m1-argc);
+ }
+ /* copy post mandatory arguments */
if (mlen) {
value_move(&regs[len-m2+1], &argv[argc-mlen], mlen);
}
+ if (mlen < m2) {
+ stack_clear(&regs[len-m2+mlen+1], m2-mlen);
+ }
+ /* initialize rest arguments with empty Array */
if (r) {
regs[m1+o+1] = mrb_ary_new_capa(mrb, 0);
}
- if (o == 0 || argc < m1+m2) pc++;
- else
- pc += argc - m1 - m2 + 1;
+ /* skip initializer of passed arguments */
+ if (o > 0 && argc-kargs > m1+m2)
+ pc += (argc - kargs - m1 - m2)*3;
}
else {
- int rnum = 0;
+ mrb_int rnum = 0;
if (argv0 != argv) {
- regs[len+1] = *blk; /* move block */
+ regs[blk_pos] = *blk; /* move block */
+ if (kd) regs[len + 1] = kdict;
value_move(&regs[1], argv, m1+o);
}
if (r) {
- rnum = argc-m1-o-m2;
- regs[m1+o+1] = mrb_ary_new_from_values(mrb, rnum, argv+m1+o);
+ mrb_value ary;
+
+ rnum = argc-m1-o-m2-kargs;
+ ary = mrb_ary_new_from_values(mrb, rnum, argv+m1+o);
+ regs[m1+o+1] = ary;
}
if (m2) {
if (argc-m2 > m1) {
@@ -1420,68 +1928,122 @@ RETRY_TRY_BLOCK:
}
}
if (argv0 == argv) {
- regs[len+1] = *blk; /* move block */
+ regs[blk_pos] = *blk; /* move block */
+ if (kd) regs[len + 1] = kdict;
}
- pc += o + 1;
+ pc += o*3;
+ }
+
+ /* format arguments for generated code */
+ mrb->c->ci->argc = (int16_t)(len + kd);
+
+ /* clear local (but non-argument) variables */
+ if (irep->nlocals-blk_pos-1 > 0) {
+ stack_clear(&regs[blk_pos+1], irep->nlocals-blk_pos-1);
}
JUMP;
}
- CASE(OP_KARG) {
- /* A B C R(A) := kdict[Syms(B)]; if C kdict.rm(Syms(B)) */
- /* if C == 2; raise unless kdict.empty? */
- /* OP_JMP should follow to skip init code */
+ CASE(OP_KARG, BB) {
+ mrb_value k = mrb_symbol_value(syms[b]);
+ mrb_value kdict = regs[mrb->c->ci->argc];
+
+ if (!mrb_hash_p(kdict) || !mrb_hash_key_p(mrb, kdict, k)) {
+ mrb_value str = mrb_format(mrb, "missing keyword: %v", k);
+ mrb_exc_set(mrb, mrb_exc_new_str(mrb, E_ARGUMENT_ERROR, str));
+ goto L_RAISE;
+ }
+ regs[a] = mrb_hash_get(mrb, kdict, k);
+ mrb_hash_delete_key(mrb, kdict, k);
NEXT;
}
- CASE(OP_KDICT) {
- /* A C R(A) := kdict */
+ CASE(OP_KEY_P, BB) {
+ mrb_value k = mrb_symbol_value(syms[b]);
+ mrb_value kdict = regs[mrb->c->ci->argc];
+ mrb_bool key_p = FALSE;
+
+ if (mrb_hash_p(kdict)) {
+ key_p = mrb_hash_key_p(mrb, kdict, k);
+ }
+ regs[a] = mrb_bool_value(key_p);
NEXT;
}
+ CASE(OP_KEYEND, Z) {
+ mrb_value kdict = regs[mrb->c->ci->argc];
+
+ if (mrb_hash_p(kdict) && !mrb_hash_empty_p(mrb, kdict)) {
+ mrb_value keys = mrb_hash_keys(mrb, kdict);
+ mrb_value key1 = RARRAY_PTR(keys)[0];
+ mrb_value str = mrb_format(mrb, "unknown keyword: %v", key1);
+ mrb_exc_set(mrb, mrb_exc_new_str(mrb, E_ARGUMENT_ERROR, str));
+ goto L_RAISE;
+ }
+ NEXT;
+ }
+
+ CASE(OP_BREAK, B) {
+ c = OP_R_BREAK;
+ goto L_RETURN;
+ }
+ CASE(OP_RETURN_BLK, B) {
+ c = OP_R_RETURN;
+ goto L_RETURN;
+ }
+ CASE(OP_RETURN, B)
+ c = OP_R_NORMAL;
L_RETURN:
- i = MKOP_AB(OP_RETURN, GETARG_A(i), OP_R_NORMAL);
- /* fall through */
- CASE(OP_RETURN) {
- /* A B return R(A) (B=normal,in-block return/break) */
- if (mrb->exc) {
- mrb_callinfo *ci;
- int eidx;
+ {
+ mrb_callinfo *ci;
+
+ ci = mrb->c->ci;
+ if (ci->mid) {
+ mrb_value blk;
+ if (ci->argc < 0) {
+ blk = regs[2];
+ }
+ else {
+ blk = regs[ci->argc+1];
+ }
+ if (mrb_proc_p(blk)) {
+ struct RProc *p = mrb_proc_ptr(blk);
+
+ if (!MRB_PROC_STRICT_P(p) &&
+ ci > mrb->c->cibase && MRB_PROC_ENV(p) == mrb_vm_ci_env(&ci[-1])) {
+ p->flags |= MRB_PROC_ORPHAN;
+ }
+ }
+ }
+
+ if (mrb->exc) {
L_RAISE:
ci = mrb->c->ci;
- mrb_obj_iv_ifnone(mrb, mrb->exc, mrb_intern_lit(mrb, "lastpc"), mrb_cptr_value(mrb, pc));
- mrb_obj_iv_ifnone(mrb, mrb->exc, mrb_intern_lit(mrb, "ciidx"), mrb_fixnum_value(ci - mrb->c->cibase));
- eidx = ci->eidx;
if (ci == mrb->c->cibase) {
- if (ci->ridx == 0) goto L_STOP;
- goto L_RESCUE;
- }
- while (eidx > ci[-1].eidx) {
- ecall(mrb, --eidx);
+ ch = catch_handler_find(mrb, ci, pc, MRB_CATCH_FILTER_ALL);
+ if (ch == NULL) goto L_FTOP;
+ goto L_CATCH;
}
- while (ci[0].ridx == ci[-1].ridx) {
- cipop(mrb);
- ci = mrb->c->ci;
- mrb->c->stack = ci[1].stackent;
+ while ((ch = catch_handler_find(mrb, ci, pc, MRB_CATCH_FILTER_ALL)) == NULL) {
+ ci = cipop(mrb);
if (ci[1].acc == CI_ACC_SKIP && prev_jmp) {
mrb->jmp = prev_jmp;
MRB_THROW(prev_jmp);
}
- if (ci > mrb->c->cibase) {
- while (eidx > ci[-1].eidx) {
- ecall(mrb, --eidx);
- }
- }
- else if (ci == mrb->c->cibase) {
- if (ci->ridx == 0) {
+ pc = ci[0].pc;
+ if (ci == mrb->c->cibase) {
+ ch = catch_handler_find(mrb, ci, pc, MRB_CATCH_FILTER_ALL);
+ if (ch == NULL) {
+ L_FTOP: /* fiber top */
if (mrb->c == mrb->root_c) {
- regs = mrb->c->stack = mrb->c->stbase;
+ mrb->c->ci->stack = mrb->c->stbase;
goto L_STOP;
}
else {
struct mrb_context *c = mrb->c;
+ c->status = MRB_FIBER_TERMINATED;
mrb->c = c->prev;
c->prev = NULL;
goto L_RAISE;
@@ -1490,521 +2052,459 @@ RETRY_TRY_BLOCK:
break;
}
}
- L_RESCUE:
- if (ci->ridx == 0) goto L_STOP;
+ L_CATCH:
+ if (ch == NULL) goto L_STOP;
+ if (FALSE) {
+ L_CATCH_TAGGED_BREAK: /* from THROW_TAGGED_BREAK() or UNWIND_ENSURE() */
+ ci = mrb->c->ci;
+ }
proc = ci->proc;
irep = proc->body.irep;
pool = irep->pool;
syms = irep->syms;
- regs = mrb->c->stack = ci[1].stackent;
- pc = mrb->c->rescue[--ci->ridx];
+ mrb_stack_extend(mrb, irep->nregs);
+ pc = irep->iseq + mrb_irep_catch_handler_unpack(ch->target);
}
else {
- mrb_callinfo *ci = mrb->c->ci;
- int acc, eidx = mrb->c->ci->eidx;
- mrb_value v = regs[GETARG_A(i)];
+ mrb_int acc;
+ mrb_value v;
- switch (GETARG_B(i)) {
+ ci = mrb->c->ci;
+ v = regs[a];
+ mrb_gc_protect(mrb, v);
+ switch (c) {
case OP_R_RETURN:
/* Fall through to OP_R_NORMAL otherwise */
- if (proc->env && !MRB_PROC_STRICT_P(proc)) {
- struct REnv *e = top_env(mrb, proc);
+ if (ci->acc >=0 && MRB_PROC_ENV_P(proc) && !MRB_PROC_STRICT_P(proc)) {
+ const struct RProc *dst;
+ mrb_callinfo *cibase;
+ cibase = mrb->c->cibase;
+ dst = top_proc(mrb, proc);
- if (!MRB_ENV_STACK_SHARED_P(e)) {
- localjump_error(mrb, LOCALJUMP_ERROR_RETURN);
- goto L_RAISE;
+ if (MRB_PROC_ENV_P(dst)) {
+ struct REnv *e = MRB_PROC_ENV(dst);
+
+ if (!MRB_ENV_ONSTACK_P(e) || (e->cxt && e->cxt != mrb->c)) {
+ localjump_error(mrb, LOCALJUMP_ERROR_RETURN);
+ goto L_RAISE;
+ }
+ }
+ /* check jump destination */
+ while (cibase <= ci && ci->proc != dst) {
+ if (ci->acc < 0) { /* jump cross C boundary */
+ localjump_error(mrb, LOCALJUMP_ERROR_RETURN);
+ goto L_RAISE;
+ }
+ ci--;
}
- ci = mrb->c->cibase + e->cioff;
- if (ci == mrb->c->cibase) {
+ if (ci <= cibase) { /* no jump destination */
localjump_error(mrb, LOCALJUMP_ERROR_RETURN);
goto L_RAISE;
}
- mrb->c->ci = ci;
+ ci = mrb->c->ci;
+ while (cibase <= ci && ci->proc != dst) {
+ CHECKPOINT_RESTORE(RBREAK_TAG_RETURN_BLOCK) {
+ cibase = mrb->c->cibase;
+ dst = top_proc(mrb, proc);
+ }
+ CHECKPOINT_MAIN(RBREAK_TAG_RETURN_BLOCK) {
+ UNWIND_ENSURE(mrb, ci, pc, RBREAK_TAG_RETURN_BLOCK, proc, v);
+ }
+ CHECKPOINT_END(RBREAK_TAG_RETURN_BLOCK);
+ ci = cipop(mrb);
+ pc = ci->pc;
+ }
+ proc = ci->proc;
+ mrb->exc = NULL; /* clear break object */
break;
}
+ /* fallthrough */
case OP_R_NORMAL:
+ NORMAL_RETURN:
if (ci == mrb->c->cibase) {
- if (!mrb->c->prev) { /* toplevel return */
- localjump_error(mrb, LOCALJUMP_ERROR_RETURN);
- goto L_RAISE;
+ struct mrb_context *c;
+ c = mrb->c;
+
+ if (!c->prev) { /* toplevel return */
+ regs[irep->nlocals] = v;
+ goto CHECKPOINT_LABEL_MAKE(RBREAK_TAG_STOP);
}
- if (mrb->c->prev->ci == mrb->c->prev->cibase) {
- mrb_value exc = mrb_exc_new_str_lit(mrb, E_FIBER_ERROR, "double resume");
- mrb->exc = mrb_obj_ptr(exc);
+ if (!c->vmexec && c->prev->ci == c->prev->cibase) {
+ mrb_value exc = mrb_exc_new_lit(mrb, E_FIBER_ERROR, "double resume");
+ mrb_exc_set(mrb, exc);
goto L_RAISE;
}
+ CHECKPOINT_RESTORE(RBREAK_TAG_RETURN_TOPLEVEL) {
+ c = mrb->c;
+ }
+ CHECKPOINT_MAIN(RBREAK_TAG_RETURN_TOPLEVEL) {
+ UNWIND_ENSURE(mrb, ci, pc, RBREAK_TAG_RETURN_TOPLEVEL, proc, v);
+ }
+ CHECKPOINT_END(RBREAK_TAG_RETURN_TOPLEVEL);
/* automatic yield at the end */
- mrb->c->status = MRB_FIBER_TERMINATED;
- mrb->c = mrb->c->prev;
+ c->status = MRB_FIBER_TERMINATED;
+ mrb->c = c->prev;
mrb->c->status = MRB_FIBER_RUNNING;
+ c->prev = NULL;
+ if (c->vmexec) {
+ mrb_gc_arena_restore(mrb, ai);
+ c->vmexec = FALSE;
+ mrb->jmp = prev_jmp;
+ return v;
+ }
+ ci = mrb->c->ci;
}
- ci = mrb->c->ci;
+ CHECKPOINT_RESTORE(RBREAK_TAG_RETURN) {
+ /* do nothing */
+ }
+ CHECKPOINT_MAIN(RBREAK_TAG_RETURN) {
+ UNWIND_ENSURE(mrb, ci, pc, RBREAK_TAG_RETURN, proc, v);
+ }
+ CHECKPOINT_END(RBREAK_TAG_RETURN);
+ mrb->exc = NULL; /* clear break object */
break;
case OP_R_BREAK:
- if (!proc->env || !MRB_ENV_STACK_SHARED_P(proc->env)) {
- localjump_error(mrb, LOCALJUMP_ERROR_BREAK);
+ if (MRB_PROC_STRICT_P(proc)) goto NORMAL_RETURN;
+ if (MRB_PROC_ORPHAN_P(proc)) {
+ mrb_value exc;
+
+ L_BREAK_ERROR:
+ exc = mrb_exc_new_lit(mrb, E_LOCALJUMP_ERROR,
+ "break from proc-closure");
+ mrb_exc_set(mrb, exc);
goto L_RAISE;
}
+ if (!MRB_PROC_ENV_P(proc) || !MRB_ENV_ONSTACK_P(MRB_PROC_ENV(proc))) {
+ goto L_BREAK_ERROR;
+ }
+ else {
+ struct REnv *e = MRB_PROC_ENV(proc);
+
+ if (e->cxt != mrb->c) {
+ goto L_BREAK_ERROR;
+ }
+ }
+ CHECKPOINT_RESTORE(RBREAK_TAG_BREAK) {
+ /* do nothing */
+ }
+ CHECKPOINT_MAIN(RBREAK_TAG_BREAK) {
+ UNWIND_ENSURE(mrb, ci, pc, RBREAK_TAG_BREAK, proc, v);
+ }
+ CHECKPOINT_END(RBREAK_TAG_BREAK);
/* break from fiber block */
- if (mrb->c->ci == mrb->c->cibase && mrb->c->ci->pc) {
+ if (ci == mrb->c->cibase && ci->pc) {
struct mrb_context *c = mrb->c;
mrb->c = c->prev;
c->prev = NULL;
+ ci = mrb->c->ci;
}
- ci = mrb->c->ci;
- mrb->c->ci = mrb->c->cibase + proc->env->cioff + 1;
- while (ci > mrb->c->ci) {
+ if (ci->acc < 0) {
+ ci = cipop(mrb);
+ mrb_gc_arena_restore(mrb, ai);
+ mrb->c->vmexec = FALSE;
+ mrb->exc = (struct RObject*)break_new(mrb, RBREAK_TAG_BREAK, proc, v);
+ mrb->jmp = prev_jmp;
+ MRB_THROW(prev_jmp);
+ }
+ if (FALSE) {
+ struct RBreak *brk;
+
+ L_BREAK:
+ brk = (struct RBreak*)mrb->exc;
+ proc = mrb_break_proc_get(brk);
+ v = mrb_break_value_get(brk);
+ ci = mrb->c->ci;
+
+ switch (mrb_break_tag_get(brk)) {
+#define DISPATCH_CHECKPOINTS(n, i) case n: goto CHECKPOINT_LABEL_MAKE(n);
+ RBREAK_TAG_FOREACH(DISPATCH_CHECKPOINTS)
+#undef DISPATCH_CHECKPOINTS
+ default:
+ mrb_assert(!"wrong break tag");
+ }
+ }
+ while (mrb->c->cibase < ci && ci[-1].proc != proc->upper) {
if (ci[-1].acc == CI_ACC_SKIP) {
- mrb->c->ci = ci;
- break;
+ goto L_BREAK_ERROR;
+ }
+ CHECKPOINT_RESTORE(RBREAK_TAG_BREAK_UPPER) {
+ /* do nothing */
}
- ci--;
+ CHECKPOINT_MAIN(RBREAK_TAG_BREAK_UPPER) {
+ UNWIND_ENSURE(mrb, ci, pc, RBREAK_TAG_BREAK_UPPER, proc, v);
+ }
+ CHECKPOINT_END(RBREAK_TAG_BREAK_UPPER);
+ ci = cipop(mrb);
+ pc = ci->pc;
+ }
+ CHECKPOINT_RESTORE(RBREAK_TAG_BREAK_INTARGET) {
+ /* do nothing */
+ }
+ CHECKPOINT_MAIN(RBREAK_TAG_BREAK_INTARGET) {
+ UNWIND_ENSURE(mrb, ci, pc, RBREAK_TAG_BREAK_INTARGET, proc, v);
+ }
+ CHECKPOINT_END(RBREAK_TAG_BREAK_INTARGET);
+ if (ci == mrb->c->cibase) {
+ goto L_BREAK_ERROR;
}
+ mrb->exc = NULL; /* clear break object */
break;
default:
/* cannot happen */
break;
}
- while (eidx > mrb->c->ci[-1].eidx) {
- ecall(mrb, --eidx);
+ mrb_assert(ci == mrb->c->ci);
+ mrb_assert(mrb->exc == NULL);
+
+ if (mrb->c->vmexec && !mrb_vm_ci_target_class(ci)) {
+ mrb_gc_arena_restore(mrb, ai);
+ mrb->c->vmexec = FALSE;
+ mrb->jmp = prev_jmp;
+ return v;
}
- cipop(mrb);
acc = ci->acc;
- pc = ci->pc;
- regs = mrb->c->stack = ci->stackent;
- if (acc == CI_ACC_SKIP) {
+ ci = cipop(mrb);
+ if (acc == CI_ACC_SKIP || acc == CI_ACC_DIRECT) {
+ mrb_gc_arena_restore(mrb, ai);
mrb->jmp = prev_jmp;
return v;
}
- DEBUG(printf("from :%s\n", mrb_sym2name(mrb, ci->mid)));
+ pc = ci[0].pc;
+ DEBUG(fprintf(stderr, "from :%s\n", mrb_sym_name(mrb, ci->mid)));
proc = mrb->c->ci->proc;
irep = proc->body.irep;
pool = irep->pool;
syms = irep->syms;
regs[acc] = v;
- }
- JUMP;
- }
-
- CASE(OP_TAILCALL) {
- /* A B C return call(R(A),Syms(B),R(A+1),... ,R(A+C+1)) */
- int a = GETARG_A(i);
- int n = GETARG_C(i);
- struct RProc *m;
- struct RClass *c;
- mrb_callinfo *ci;
- mrb_value recv;
- mrb_sym mid = syms[GETARG_B(i)];
-
- recv = regs[a];
- c = mrb_class(mrb, recv);
- m = mrb_method_search_vm(mrb, &c, mid);
- if (!m) {
- mrb_value sym = mrb_symbol_value(mid);
-
- mid = mrb_intern_lit(mrb, "method_missing");
- m = mrb_method_search_vm(mrb, &c, mid);
- if (n == CALL_MAXARGS) {
- mrb_ary_unshift(mrb, regs[a+1], sym);
- }
- else {
- value_move(regs+a+2, regs+a+1, ++n);
- regs[a+1] = sym;
- }
- }
-
- /* replace callinfo */
- ci = mrb->c->ci;
- ci->mid = mid;
- ci->target_class = c;
- if (n == CALL_MAXARGS) {
- ci->argc = -1;
- }
- else {
- ci->argc = n;
- }
-
- /* move stack */
- value_move(mrb->c->stack, &regs[a], ci->argc+1);
-
- if (MRB_PROC_CFUNC_P(m)) {
- mrb->c->stack[0] = m->body.func(mrb, recv);
mrb_gc_arena_restore(mrb, ai);
- goto L_RETURN;
- }
- else {
- /* setup environment for calling method */
- irep = m->body.irep;
- pool = irep->pool;
- syms = irep->syms;
- if (ci->argc < 0) {
- stack_extend(mrb, (irep->nregs < 3) ? 3 : irep->nregs, 3);
- }
- else {
- stack_extend(mrb, irep->nregs, ci->argc+2);
- }
- regs = mrb->c->stack;
- pc = irep->iseq;
}
JUMP;
}
- CASE(OP_BLKPUSH) {
- /* A Bx R(A) := block (16=6:1:5:4) */
- int a = GETARG_A(i);
- int bx = GETARG_Bx(i);
- int m1 = (bx>>10)&0x3f;
- int r = (bx>>9)&0x1;
- int m2 = (bx>>4)&0x1f;
- int lv = (bx>>0)&0xf;
+ CASE(OP_BLKPUSH, BS) {
+ int m1 = (b>>11)&0x3f;
+ int r = (b>>10)&0x1;
+ int m2 = (b>>5)&0x1f;
+ int kd = (b>>4)&0x1;
+ int lv = (b>>0)&0xf;
mrb_value *stack;
if (lv == 0) stack = regs + 1;
else {
struct REnv *e = uvenv(mrb, lv-1);
- if (!e) {
+ if (!e || (!MRB_ENV_ONSTACK_P(e) && e->mid == 0) ||
+ MRB_ENV_LEN(e) <= m1+r+m2+1) {
localjump_error(mrb, LOCALJUMP_ERROR_YIELD);
goto L_RAISE;
}
stack = e->stack + 1;
}
- regs[a] = stack[m1+r+m2];
- NEXT;
- }
-
-#define TYPES2(a,b) ((((uint16_t)(a))<<8)|(((uint16_t)(b))&0xff))
-#define OP_MATH_BODY(op,v1,v2) do {\
- v1(regs[a]) = v1(regs[a]) op v2(regs[a+1]);\
-} while(0)
-
- CASE(OP_ADD) {
- /* A B C R(A) := R(A)+R(A+1) (Syms[B]=:+,C=1)*/
- int a = GETARG_A(i);
-
- /* need to check if op is overridden */
- switch (TYPES2(mrb_type(regs[a]),mrb_type(regs[a+1]))) {
- case TYPES2(MRB_TT_FIXNUM,MRB_TT_FIXNUM):
- {
- mrb_int x, y, z;
- mrb_value *regs_a = regs + a;
-
- x = mrb_fixnum(regs_a[0]);
- y = mrb_fixnum(regs_a[1]);
- if (mrb_int_add_overflow(x, y, &z)) {
- SET_FLOAT_VALUE(mrb, regs_a[0], (mrb_float)x + (mrb_float)y);
- break;
- }
- SET_INT_VALUE(regs[a], z);
- }
- break;
- case TYPES2(MRB_TT_FIXNUM,MRB_TT_FLOAT):
- {
- mrb_int x = mrb_fixnum(regs[a]);
- mrb_float y = mrb_float(regs[a+1]);
- SET_FLOAT_VALUE(mrb, regs[a], (mrb_float)x + y);
- }
- break;
- case TYPES2(MRB_TT_FLOAT,MRB_TT_FIXNUM):
-#ifdef MRB_WORD_BOXING
- {
- mrb_float x = mrb_float(regs[a]);
- mrb_int y = mrb_fixnum(regs[a+1]);
- SET_FLOAT_VALUE(mrb, regs[a], x + y);
- }
-#else
- OP_MATH_BODY(+,mrb_float,mrb_fixnum);
-#endif
- break;
- case TYPES2(MRB_TT_FLOAT,MRB_TT_FLOAT):
-#ifdef MRB_WORD_BOXING
- {
- mrb_float x = mrb_float(regs[a]);
- mrb_float y = mrb_float(regs[a+1]);
- SET_FLOAT_VALUE(mrb, regs[a], x + y);
- }
-#else
- OP_MATH_BODY(+,mrb_float,mrb_float);
-#endif
- break;
- case TYPES2(MRB_TT_STRING,MRB_TT_STRING):
- regs[a] = mrb_str_plus(mrb, regs[a], regs[a+1]);
- break;
- default:
- goto L_SEND;
+ if (mrb_nil_p(stack[m1+r+m2])) {
+ localjump_error(mrb, LOCALJUMP_ERROR_YIELD);
+ goto L_RAISE;
}
- ARENA_RESTORE(mrb, ai);
+ regs[a] = stack[m1+r+m2+kd];
NEXT;
}
- CASE(OP_SUB) {
- /* A B C R(A) := R(A)-R(A+1) (Syms[B]=:-,C=1)*/
- int a = GETARG_A(i);
-
- /* need to check if op is overridden */
- switch (TYPES2(mrb_type(regs[a]),mrb_type(regs[a+1]))) {
- case TYPES2(MRB_TT_FIXNUM,MRB_TT_FIXNUM):
- {
- mrb_int x, y, z;
+ L_INT_OVERFLOW:
+ {
+ mrb_value exc = mrb_exc_new_lit(mrb, E_RANGE_ERROR, "integer overflow");
+ mrb_exc_set(mrb, exc);
+ }
+ goto L_RAISE;
- x = mrb_fixnum(regs[a]);
- y = mrb_fixnum(regs[a+1]);
- if (mrb_int_sub_overflow(x, y, &z)) {
- SET_FLOAT_VALUE(mrb, regs[a], (mrb_float)x - (mrb_float)y);
- break;
- }
- SET_INT_VALUE(regs[a], z);
- }
- break;
- case TYPES2(MRB_TT_FIXNUM,MRB_TT_FLOAT):
- {
- mrb_int x = mrb_fixnum(regs[a]);
- mrb_float y = mrb_float(regs[a+1]);
- SET_FLOAT_VALUE(mrb, regs[a], (mrb_float)x - y);
- }
- break;
- case TYPES2(MRB_TT_FLOAT,MRB_TT_FIXNUM):
-#ifdef MRB_WORD_BOXING
- {
- mrb_float x = mrb_float(regs[a]);
- mrb_int y = mrb_fixnum(regs[a+1]);
- SET_FLOAT_VALUE(mrb, regs[a], x - y);
- }
-#else
- OP_MATH_BODY(-,mrb_float,mrb_fixnum);
-#endif
- break;
- case TYPES2(MRB_TT_FLOAT,MRB_TT_FLOAT):
-#ifdef MRB_WORD_BOXING
- {
- mrb_float x = mrb_float(regs[a]);
- mrb_float y = mrb_float(regs[a+1]);
- SET_FLOAT_VALUE(mrb, regs[a], x - y);
- }
+#define TYPES2(a,b) ((((uint16_t)(a))<<8)|(((uint16_t)(b))&0xff))
+#define OP_MATH(op_name) \
+ /* need to check if op is overridden */ \
+ switch (TYPES2(mrb_type(regs[a]),mrb_type(regs[a+1]))) { \
+ OP_MATH_CASE_INTEGER(op_name); \
+ OP_MATH_CASE_FLOAT(op_name, integer, float); \
+ OP_MATH_CASE_FLOAT(op_name, float, integer); \
+ OP_MATH_CASE_FLOAT(op_name, float, float); \
+ OP_MATH_CASE_STRING_##op_name(); \
+ default: \
+ c = 1; \
+ mid = MRB_OPSYM(op_name); \
+ goto L_SEND_SYM; \
+ } \
+ NEXT;
+#define OP_MATH_CASE_INTEGER(op_name) \
+ case TYPES2(MRB_TT_INTEGER, MRB_TT_INTEGER): \
+ { \
+ mrb_int x = mrb_integer(regs[a]), y = mrb_integer(regs[a+1]), z; \
+ if (mrb_int_##op_name##_overflow(x, y, &z)) \
+ OP_MATH_OVERFLOW_INT(); \
+ else \
+ SET_INT_VALUE(mrb,regs[a], z); \
+ } \
+ break
+#ifdef MRB_NO_FLOAT
+#define OP_MATH_CASE_FLOAT(op_name, t1, t2) (void)0
#else
- OP_MATH_BODY(-,mrb_float,mrb_float);
+#define OP_MATH_CASE_FLOAT(op_name, t1, t2) \
+ case TYPES2(OP_MATH_TT_##t1, OP_MATH_TT_##t2): \
+ { \
+ mrb_float z = mrb_##t1(regs[a]) OP_MATH_OP_##op_name mrb_##t2(regs[a+1]); \
+ SET_FLOAT_VALUE(mrb, regs[a], z); \
+ } \
+ break
#endif
- break;
- default:
- goto L_SEND;
- }
- NEXT;
- }
+#define OP_MATH_OVERFLOW_INT() goto L_INT_OVERFLOW
+#define OP_MATH_CASE_STRING_add() \
+ case TYPES2(MRB_TT_STRING, MRB_TT_STRING): \
+ regs[a] = mrb_str_plus(mrb, regs[a], regs[a+1]); \
+ mrb_gc_arena_restore(mrb, ai); \
+ break
+#define OP_MATH_CASE_STRING_sub() (void)0
+#define OP_MATH_CASE_STRING_mul() (void)0
+#define OP_MATH_OP_add +
+#define OP_MATH_OP_sub -
+#define OP_MATH_OP_mul *
+#define OP_MATH_TT_integer MRB_TT_INTEGER
+#define OP_MATH_TT_float MRB_TT_FLOAT
- CASE(OP_MUL) {
- /* A B C R(A) := R(A)*R(A+1) (Syms[B]=:*,C=1)*/
- int a = GETARG_A(i);
-
- /* need to check if op is overridden */
- switch (TYPES2(mrb_type(regs[a]),mrb_type(regs[a+1]))) {
- case TYPES2(MRB_TT_FIXNUM,MRB_TT_FIXNUM):
- {
- mrb_value z;
+ CASE(OP_ADD, B) {
+ OP_MATH(add);
+ }
- z = mrb_fixnum_mul(mrb, regs[a], regs[a+1]);
+ CASE(OP_SUB, B) {
+ OP_MATH(sub);
+ }
- switch (mrb_type(z)) {
- case MRB_TT_FIXNUM:
- {
- SET_INT_VALUE(regs[a], mrb_fixnum(z));
- }
- break;
- case MRB_TT_FLOAT:
- {
- SET_FLOAT_VALUE(mrb, regs[a], mrb_float(z));
- }
- break;
- default:
- /* cannot happen */
- break;
- }
- }
- break;
- case TYPES2(MRB_TT_FIXNUM,MRB_TT_FLOAT):
- {
- mrb_int x = mrb_fixnum(regs[a]);
- mrb_float y = mrb_float(regs[a+1]);
- SET_FLOAT_VALUE(mrb, regs[a], (mrb_float)x * y);
- }
- break;
- case TYPES2(MRB_TT_FLOAT,MRB_TT_FIXNUM):
-#ifdef MRB_WORD_BOXING
- {
- mrb_float x = mrb_float(regs[a]);
- mrb_int y = mrb_fixnum(regs[a+1]);
- SET_FLOAT_VALUE(mrb, regs[a], x * y);
- }
-#else
- OP_MATH_BODY(*,mrb_float,mrb_fixnum);
-#endif
- break;
- case TYPES2(MRB_TT_FLOAT,MRB_TT_FLOAT):
-#ifdef MRB_WORD_BOXING
- {
- mrb_float x = mrb_float(regs[a]);
- mrb_float y = mrb_float(regs[a+1]);
- SET_FLOAT_VALUE(mrb, regs[a], x * y);
- }
-#else
- OP_MATH_BODY(*,mrb_float,mrb_float);
-#endif
- break;
- default:
- goto L_SEND;
- }
- NEXT;
+ CASE(OP_MUL, B) {
+ OP_MATH(mul);
}
- CASE(OP_DIV) {
- /* A B C R(A) := R(A)/R(A+1) (Syms[B]=:/,C=1)*/
- int a = GETARG_A(i);
+ CASE(OP_DIV, B) {
+#ifndef MRB_NO_FLOAT
+ mrb_float x, y, f;
+#endif
/* need to check if op is overridden */
switch (TYPES2(mrb_type(regs[a]),mrb_type(regs[a+1]))) {
- case TYPES2(MRB_TT_FIXNUM,MRB_TT_FIXNUM):
+ case TYPES2(MRB_TT_INTEGER,MRB_TT_INTEGER):
{
- mrb_int x = mrb_fixnum(regs[a]);
- mrb_int y = mrb_fixnum(regs[a+1]);
- SET_FLOAT_VALUE(mrb, regs[a], (mrb_float)x / (mrb_float)y);
+ mrb_int x = mrb_integer(regs[a]);
+ mrb_int y = mrb_integer(regs[a+1]);
+ mrb_int div = mrb_div_int(mrb, x, y);
+ SET_INT_VALUE(mrb, regs[a], div);
}
+ NEXT;
+#ifndef MRB_NO_FLOAT
+ case TYPES2(MRB_TT_INTEGER,MRB_TT_FLOAT):
+ x = (mrb_float)mrb_integer(regs[a]);
+ y = mrb_float(regs[a+1]);
break;
- case TYPES2(MRB_TT_FIXNUM,MRB_TT_FLOAT):
- {
- mrb_int x = mrb_fixnum(regs[a]);
- mrb_float y = mrb_float(regs[a+1]);
- SET_FLOAT_VALUE(mrb, regs[a], (mrb_float)x / y);
- }
- break;
- case TYPES2(MRB_TT_FLOAT,MRB_TT_FIXNUM):
-#ifdef MRB_WORD_BOXING
- {
- mrb_float x = mrb_float(regs[a]);
- mrb_int y = mrb_fixnum(regs[a+1]);
- SET_FLOAT_VALUE(mrb, regs[a], x / y);
- }
-#else
- OP_MATH_BODY(/,mrb_float,mrb_fixnum);
-#endif
+ case TYPES2(MRB_TT_FLOAT,MRB_TT_INTEGER):
+ x = mrb_float(regs[a]);
+ y = (mrb_float)mrb_integer(regs[a+1]);
break;
case TYPES2(MRB_TT_FLOAT,MRB_TT_FLOAT):
-#ifdef MRB_WORD_BOXING
- {
- mrb_float x = mrb_float(regs[a]);
- mrb_float y = mrb_float(regs[a+1]);
- SET_FLOAT_VALUE(mrb, regs[a], x / y);
- }
-#else
- OP_MATH_BODY(/,mrb_float,mrb_float);
-#endif
+ x = mrb_float(regs[a]);
+ y = mrb_float(regs[a+1]);
break;
+#endif
default:
- goto L_SEND;
- }
-#ifdef MRB_NAN_BOXING
- if (isnan(mrb_float(regs[a]))) {
- regs[a] = mrb_float_value(mrb, mrb_float(regs[a]));
+ c = 1;
+ mid = MRB_OPSYM(div);
+ goto L_SEND_SYM;
}
+
+#ifndef MRB_NO_FLOAT
+ f = mrb_div_float(x, y);
+ SET_FLOAT_VALUE(mrb, regs[a], f);
#endif
NEXT;
}
- CASE(OP_ADDI) {
- /* A B C R(A) := R(A)+C (Syms[B]=:+)*/
- int a = GETARG_A(i);
-
- /* need to check if + is overridden */
- switch (mrb_type(regs[a])) {
- case MRB_TT_FIXNUM:
- {
- mrb_int x = mrb_fixnum(regs[a]);
- mrb_int y = GETARG_C(i);
- mrb_int z;
-
- if (mrb_int_add_overflow(x, y, &z)) {
- SET_FLOAT_VALUE(mrb, regs[a], (mrb_float)x + (mrb_float)y);
- break;
- }
- mrb_fixnum(regs[a]) = z;
- }
- break;
- case MRB_TT_FLOAT:
-#ifdef MRB_WORD_BOXING
- {
- mrb_float x = mrb_float(regs[a]);
- SET_FLOAT_VALUE(mrb, regs[a], x + GETARG_C(i));
- }
+#define OP_MATHI(op_name) \
+ /* need to check if op is overridden */ \
+ switch (mrb_type(regs[a])) { \
+ OP_MATHI_CASE_INTEGER(op_name); \
+ OP_MATHI_CASE_FLOAT(op_name); \
+ default: \
+ SET_INT_VALUE(mrb,regs[a+1], b); \
+ c = 1; \
+ mid = MRB_OPSYM(op_name); \
+ goto L_SEND_SYM; \
+ } \
+ NEXT;
+#define OP_MATHI_CASE_INTEGER(op_name) \
+ case MRB_TT_INTEGER: \
+ { \
+ mrb_int x = mrb_integer(regs[a]), y = (mrb_int)b, z; \
+ if (mrb_int_##op_name##_overflow(x, y, &z)) \
+ OP_MATH_OVERFLOW_INT(); \
+ else \
+ SET_INT_VALUE(mrb,regs[a], z); \
+ } \
+ break
+#ifdef MRB_NO_FLOAT
+#define OP_MATHI_CASE_FLOAT(op_name) (void)0
#else
- mrb_float(regs[a]) += GETARG_C(i);
+#define OP_MATHI_CASE_FLOAT(op_name) \
+ case MRB_TT_FLOAT: \
+ { \
+ mrb_float z = mrb_float(regs[a]) OP_MATH_OP_##op_name b; \
+ SET_FLOAT_VALUE(mrb, regs[a], z); \
+ } \
+ break
#endif
- break;
- default:
- SET_INT_VALUE(regs[a+1], GETARG_C(i));
- i = MKOP_ABC(OP_SEND, a, GETARG_B(i), 1);
- goto L_SEND;
- }
- NEXT;
- }
- CASE(OP_SUBI) {
- /* A B C R(A) := R(A)-C (Syms[B]=:-)*/
- int a = GETARG_A(i);
- mrb_value *regs_a = regs + a;
-
- /* need to check if + is overridden */
- switch (mrb_type(regs_a[0])) {
- case MRB_TT_FIXNUM:
- {
- mrb_int x = mrb_fixnum(regs_a[0]);
- mrb_int y = GETARG_C(i);
- mrb_int z;
+ CASE(OP_ADDI, BB) {
+ OP_MATHI(add);
+ }
- if (mrb_int_sub_overflow(x, y, &z)) {
- SET_FLOAT_VALUE(mrb, regs_a[0], (mrb_float)x - (mrb_float)y);
- }
- else {
- mrb_fixnum(regs_a[0]) = z;
- }
- }
- break;
- case MRB_TT_FLOAT:
-#ifdef MRB_WORD_BOXING
- {
- mrb_float x = mrb_float(regs[a]);
- SET_FLOAT_VALUE(mrb, regs[a], x - GETARG_C(i));
- }
-#else
- mrb_float(regs_a[0]) -= GETARG_C(i);
-#endif
- break;
- default:
- SET_INT_VALUE(regs_a[1], GETARG_C(i));
- i = MKOP_ABC(OP_SEND, a, GETARG_B(i), 1);
- goto L_SEND;
- }
- NEXT;
+ CASE(OP_SUBI, BB) {
+ OP_MATHI(sub);
}
#define OP_CMP_BODY(op,v1,v2) (v1(regs[a]) op v2(regs[a+1]))
-#define OP_CMP(op) do {\
+#ifdef MRB_NO_FLOAT
+#define OP_CMP(op,sym) do {\
int result;\
/* need to check if - is overridden */\
switch (TYPES2(mrb_type(regs[a]),mrb_type(regs[a+1]))) {\
- case TYPES2(MRB_TT_FIXNUM,MRB_TT_FIXNUM):\
+ case TYPES2(MRB_TT_INTEGER,MRB_TT_INTEGER):\
result = OP_CMP_BODY(op,mrb_fixnum,mrb_fixnum);\
break;\
- case TYPES2(MRB_TT_FIXNUM,MRB_TT_FLOAT):\
+ default:\
+ c = 1;\
+ mid = MRB_OPSYM(sym);\
+ goto L_SEND_SYM;\
+ }\
+ if (result) {\
+ SET_TRUE_VALUE(regs[a]);\
+ }\
+ else {\
+ SET_FALSE_VALUE(regs[a]);\
+ }\
+} while(0)
+#else
+#define OP_CMP(op, sym) do {\
+ int result;\
+ /* need to check if - is overridden */\
+ switch (TYPES2(mrb_type(regs[a]),mrb_type(regs[a+1]))) {\
+ case TYPES2(MRB_TT_INTEGER,MRB_TT_INTEGER):\
+ result = OP_CMP_BODY(op,mrb_fixnum,mrb_fixnum);\
+ break;\
+ case TYPES2(MRB_TT_INTEGER,MRB_TT_FLOAT):\
result = OP_CMP_BODY(op,mrb_fixnum,mrb_float);\
break;\
- case TYPES2(MRB_TT_FLOAT,MRB_TT_FIXNUM):\
+ case TYPES2(MRB_TT_FLOAT,MRB_TT_INTEGER):\
result = OP_CMP_BODY(op,mrb_float,mrb_fixnum);\
break;\
case TYPES2(MRB_TT_FLOAT,MRB_TT_FLOAT):\
result = OP_CMP_BODY(op,mrb_float,mrb_float);\
break;\
default:\
- goto L_SEND;\
+ c = 1;\
+ mid = MRB_OPSYM(sym);\
+ goto L_SEND_SYM;\
}\
if (result) {\
SET_TRUE_VALUE(regs[a]);\
@@ -2013,318 +2513,357 @@ RETRY_TRY_BLOCK:
SET_FALSE_VALUE(regs[a]);\
}\
} while(0)
+#endif
- CASE(OP_EQ) {
- /* A B C R(A) := R(A)==R(A+1) (Syms[B]=:==,C=1)*/
- int a = GETARG_A(i);
+ CASE(OP_EQ, B) {
if (mrb_obj_eq(mrb, regs[a], regs[a+1])) {
SET_TRUE_VALUE(regs[a]);
}
else {
- OP_CMP(==);
+ OP_CMP(==,eq);
}
NEXT;
}
- CASE(OP_LT) {
- /* A B C R(A) := R(A)<R(A+1) (Syms[B]=:<,C=1)*/
- int a = GETARG_A(i);
- OP_CMP(<);
+ CASE(OP_LT, B) {
+ OP_CMP(<,lt);
NEXT;
}
- CASE(OP_LE) {
- /* A B C R(A) := R(A)<=R(A+1) (Syms[B]=:<=,C=1)*/
- int a = GETARG_A(i);
- OP_CMP(<=);
+ CASE(OP_LE, B) {
+ OP_CMP(<=,le);
NEXT;
}
- CASE(OP_GT) {
- /* A B C R(A) := R(A)>R(A+1) (Syms[B]=:>,C=1)*/
- int a = GETARG_A(i);
- OP_CMP(>);
+ CASE(OP_GT, B) {
+ OP_CMP(>,gt);
NEXT;
}
- CASE(OP_GE) {
- /* A B C R(A) := R(A)>=R(A+1) (Syms[B]=:>=,C=1)*/
- int a = GETARG_A(i);
- OP_CMP(>=);
+ CASE(OP_GE, B) {
+ OP_CMP(>=,ge);
NEXT;
}
- CASE(OP_ARRAY) {
- /* A B C R(A) := ary_new(R(B),R(B+1)..R(B+C)) */
- regs[GETARG_A(i)] = mrb_ary_new_from_values(mrb, GETARG_C(i), &regs[GETARG_B(i)]);
- ARENA_RESTORE(mrb, ai);
+ CASE(OP_ARRAY, BB) {
+ mrb_value v = mrb_ary_new_from_values(mrb, b, &regs[a]);
+ regs[a] = v;
+ mrb_gc_arena_restore(mrb, ai);
+ NEXT;
+ }
+ CASE(OP_ARRAY2, BBB) {
+ mrb_value v = mrb_ary_new_from_values(mrb, c, &regs[b]);
+ regs[a] = v;
+ mrb_gc_arena_restore(mrb, ai);
NEXT;
}
- CASE(OP_ARYCAT) {
- /* A B mrb_ary_concat(R(A),R(B)) */
- mrb_ary_concat(mrb, regs[GETARG_A(i)],
- mrb_ary_splat(mrb, regs[GETARG_B(i)]));
- ARENA_RESTORE(mrb, ai);
+ CASE(OP_ARYCAT, B) {
+ mrb_value splat = mrb_ary_splat(mrb, regs[a+1]);
+ if (mrb_nil_p(regs[a])) {
+ regs[a] = splat;
+ }
+ else {
+ mrb_ary_concat(mrb, regs[a], splat);
+ }
+ mrb_gc_arena_restore(mrb, ai);
NEXT;
}
- CASE(OP_ARYPUSH) {
- /* A B R(A).push(R(B)) */
- mrb_ary_push(mrb, regs[GETARG_A(i)], regs[GETARG_B(i)]);
+ CASE(OP_ARYPUSH, B) {
+ mrb_ary_push(mrb, regs[a], regs[a+1]);
+ NEXT;
+ }
+
+ CASE(OP_ARYDUP, B) {
+ mrb_value ary = regs[a];
+ if (mrb_array_p(ary)) {
+ ary = mrb_ary_new_from_values(mrb, RARRAY_LEN(ary), RARRAY_PTR(ary));
+ }
+ else {
+ ary = mrb_ary_new_from_values(mrb, 1, &ary);
+ }
+ regs[a] = ary;
NEXT;
}
- CASE(OP_AREF) {
- /* A B C R(A) := R(B)[C] */
- int a = GETARG_A(i);
- int c = GETARG_C(i);
- mrb_value v = regs[GETARG_B(i)];
+ CASE(OP_AREF, BBB) {
+ mrb_value v = regs[b];
if (!mrb_array_p(v)) {
if (c == 0) {
- regs[GETARG_A(i)] = v;
+ regs[a] = v;
}
else {
SET_NIL_VALUE(regs[a]);
}
}
else {
- regs[GETARG_A(i)] = mrb_ary_ref(mrb, v, c);
+ v = mrb_ary_ref(mrb, v, c);
+ regs[a] = v;
}
NEXT;
}
- CASE(OP_ASET) {
- /* A B C R(B)[C] := R(A) */
- mrb_ary_set(mrb, regs[GETARG_B(i)], GETARG_C(i), regs[GETARG_A(i)]);
+ CASE(OP_ASET, BBB) {
+ mrb_ary_set(mrb, regs[b], c, regs[a]);
NEXT;
}
- CASE(OP_APOST) {
- /* A B C *R(A),R(A+1)..R(A+C) := R(A) */
- int a = GETARG_A(i);
+ CASE(OP_APOST, BBB) {
mrb_value v = regs[a];
- int pre = GETARG_B(i);
- int post = GETARG_C(i);
+ int pre = b;
+ int post = c;
+ struct RArray *ary;
+ int len, idx;
if (!mrb_array_p(v)) {
- regs[a++] = mrb_ary_new_capa(mrb, 0);
+ v = mrb_ary_new_from_values(mrb, 1, &regs[a]);
+ }
+ ary = mrb_ary_ptr(v);
+ len = (int)ARY_LEN(ary);
+ if (len > pre + post) {
+ v = mrb_ary_new_from_values(mrb, len - pre - post, ARY_PTR(ary)+pre);
+ regs[a++] = v;
while (post--) {
- SET_NIL_VALUE(regs[a]);
- a++;
+ regs[a++] = ARY_PTR(ary)[len-post-1];
}
}
else {
- struct RArray *ary = mrb_ary_ptr(v);
- int len = ary->len;
- int idx;
-
- if (len > pre + post) {
- regs[a++] = mrb_ary_new_from_values(mrb, len - pre - post, ary->ptr+pre);
- while (post--) {
- regs[a++] = ary->ptr[len-post-1];
- }
+ v = mrb_ary_new_capa(mrb, 0);
+ regs[a++] = v;
+ for (idx=0; idx+pre<len; idx++) {
+ regs[a+idx] = ARY_PTR(ary)[pre+idx];
}
- else {
- regs[a++] = mrb_ary_new_capa(mrb, 0);
- for (idx=0; idx+pre<len; idx++) {
- regs[a+idx] = ary->ptr[pre+idx];
- }
- while (idx < post) {
- SET_NIL_VALUE(regs[a+idx]);
- idx++;
- }
+ while (idx < post) {
+ SET_NIL_VALUE(regs[a+idx]);
+ idx++;
}
}
- ARENA_RESTORE(mrb, ai);
+ mrb_gc_arena_restore(mrb, ai);
NEXT;
}
- CASE(OP_STRING) {
- /* A Bx R(A) := str_new(Lit(Bx)) */
- regs[GETARG_A(i)] = mrb_str_dup(mrb, pool[GETARG_Bx(i)]);
- ARENA_RESTORE(mrb, ai);
+ CASE(OP_INTERN, B) {
+ mrb_sym sym = mrb_intern_str(mrb, regs[a]);
+
+ regs[a] = mrb_symbol_value(sym);
+ mrb_gc_arena_restore(mrb, ai);
NEXT;
}
- CASE(OP_STRCAT) {
- /* A B R(A).concat(R(B)) */
- mrb_str_concat(mrb, regs[GETARG_A(i)], regs[GETARG_B(i)]);
+ CASE(OP_STRING, BB) {
+ size_t len;
+
+ len = pool[b].tt >> 2;
+ if (pool[b].tt & IREP_TT_SFLAG) {
+ regs[a] = mrb_str_new_static(mrb, pool[b].u.str, len);
+ }
+ else {
+ regs[a] = mrb_str_new(mrb, pool[b].u.str, len);
+ }
+ mrb_gc_arena_restore(mrb, ai);
+ NEXT;
+ }
+
+ CASE(OP_STRCAT, B) {
+ mrb_str_concat(mrb, regs[a], regs[a+1]);
+ NEXT;
+ }
+
+ CASE(OP_HASH, BB) {
+ mrb_value hash = mrb_hash_new_capa(mrb, b);
+ int i;
+ int lim = a+b*2;
+
+ for (i=a; i<lim; i+=2) {
+ mrb_hash_set(mrb, hash, regs[i], regs[i+1]);
+ }
+ regs[a] = hash;
+ mrb_gc_arena_restore(mrb, ai);
NEXT;
}
- CASE(OP_HASH) {
- /* A B C R(A) := hash_new(R(B),R(B+1)..R(B+C)) */
- int b = GETARG_B(i);
- int c = GETARG_C(i);
- int lim = b+c*2;
- mrb_value hash = mrb_hash_new_capa(mrb, c);
+ CASE(OP_HASHADD, BB) {
+ mrb_value hash;
+ int i;
+ int lim = a+b*2+1;
- while (b < lim) {
- mrb_hash_set(mrb, hash, regs[b], regs[b+1]);
- b+=2;
+ hash = mrb_ensure_hash_type(mrb, regs[a]);
+ for (i=a+1; i<lim; i+=2) {
+ mrb_hash_set(mrb, hash, regs[i], regs[i+1]);
}
- regs[GETARG_A(i)] = hash;
- ARENA_RESTORE(mrb, ai);
+ mrb_gc_arena_restore(mrb, ai);
NEXT;
}
+ CASE(OP_HASHCAT, B) {
+ mrb_value hash = mrb_ensure_hash_type(mrb, regs[a]);
- CASE(OP_LAMBDA) {
- /* A b c R(A) := lambda(SEQ[b],c) (b:c = 14:2) */
+ mrb_hash_merge(mrb, hash, regs[a+1]);
+ mrb_gc_arena_restore(mrb, ai);
+ NEXT;
+ }
+
+ CASE(OP_LAMBDA, BB)
+ c = OP_L_LAMBDA;
+ L_MAKE_LAMBDA:
+ {
struct RProc *p;
- int c = GETARG_c(i);
+ const mrb_irep *nirep = irep->reps[b];
if (c & OP_L_CAPTURE) {
- p = mrb_closure_new(mrb, irep->reps[GETARG_b(i)]);
+ p = mrb_closure_new(mrb, nirep);
}
else {
- p = mrb_proc_new(mrb, irep->reps[GETARG_b(i)]);
- if (c & OP_L_METHOD) {
- if (p->target_class->tt == MRB_TT_SCLASS) {
- mrb_value klass;
- klass = mrb_obj_iv_get(mrb,
- (struct RObject *)p->target_class,
- mrb_intern_lit(mrb, "__attached__"));
- p->target_class = mrb_class_ptr(klass);
- }
- }
+ p = mrb_proc_new(mrb, nirep);
+ p->flags |= MRB_PROC_SCOPE;
}
if (c & OP_L_STRICT) p->flags |= MRB_PROC_STRICT;
- regs[GETARG_A(i)] = mrb_obj_value(p);
- ARENA_RESTORE(mrb, ai);
+ if (c == OP_L_METHOD) proc_adjust_upper(p);
+ regs[a] = mrb_obj_value(p);
+ mrb_gc_arena_restore(mrb, ai);
NEXT;
}
+ CASE(OP_BLOCK, BB) {
+ c = OP_L_BLOCK;
+ goto L_MAKE_LAMBDA;
+ }
+ CASE(OP_METHOD, BB) {
+ c = OP_L_METHOD;
+ goto L_MAKE_LAMBDA;
+ }
- CASE(OP_OCLASS) {
- /* A R(A) := ::Object */
- regs[GETARG_A(i)] = mrb_obj_value(mrb->object_class);
+ CASE(OP_RANGE_INC, B) {
+ mrb_value val = mrb_range_new(mrb, regs[a], regs[a+1], FALSE);
+ regs[a] = val;
+ mrb_gc_arena_restore(mrb, ai);
+ NEXT;
+ }
+
+ CASE(OP_RANGE_EXC, B) {
+ mrb_value val = mrb_range_new(mrb, regs[a], regs[a+1], TRUE);
+ regs[a] = val;
+ mrb_gc_arena_restore(mrb, ai);
NEXT;
}
- CASE(OP_CLASS) {
- /* A B R(A) := newclass(R(A),Syms(B),R(A+1)) */
- struct RClass *c = 0;
- int a = GETARG_A(i);
+ CASE(OP_OCLASS, B) {
+ regs[a] = mrb_obj_value(mrb->object_class);
+ NEXT;
+ }
+
+ CASE(OP_CLASS, BB) {
+ struct RClass *c = 0, *baseclass;
mrb_value base, super;
- mrb_sym id = syms[GETARG_B(i)];
+ mrb_sym id = syms[b];
base = regs[a];
super = regs[a+1];
if (mrb_nil_p(base)) {
- base = mrb_obj_value(mrb->c->ci->target_class);
+ baseclass = MRB_PROC_TARGET_CLASS(mrb->c->ci->proc);
+ if (!baseclass) baseclass = mrb->object_class;
+ base = mrb_obj_value(baseclass);
}
c = mrb_vm_define_class(mrb, base, super, id);
regs[a] = mrb_obj_value(c);
- ARENA_RESTORE(mrb, ai);
+ mrb_gc_arena_restore(mrb, ai);
NEXT;
}
- CASE(OP_MODULE) {
- /* A B R(A) := newmodule(R(A),Syms(B)) */
- struct RClass *c = 0;
- int a = GETARG_A(i);
+ CASE(OP_MODULE, BB) {
+ struct RClass *cls = 0, *baseclass;
mrb_value base;
- mrb_sym id = syms[GETARG_B(i)];
+ mrb_sym id = syms[b];
base = regs[a];
if (mrb_nil_p(base)) {
- base = mrb_obj_value(mrb->c->ci->target_class);
+ baseclass = MRB_PROC_TARGET_CLASS(mrb->c->ci->proc);
+ if (!baseclass) baseclass = mrb->object_class;
+ base = mrb_obj_value(baseclass);
}
- c = mrb_vm_define_module(mrb, base, id);
- regs[a] = mrb_obj_value(c);
- ARENA_RESTORE(mrb, ai);
+ cls = mrb_vm_define_module(mrb, base, id);
+ regs[a] = mrb_obj_value(cls);
+ mrb_gc_arena_restore(mrb, ai);
NEXT;
}
- CASE(OP_EXEC) {
- /* A Bx R(A) := blockexec(R(A),SEQ[Bx]) */
- int a = GETARG_A(i);
- mrb_callinfo *ci;
+ CASE(OP_EXEC, BB)
+ {
mrb_value recv = regs[a];
struct RProc *p;
-
- /* prepare stack */
- ci = cipush(mrb);
- ci->pc = pc + 1;
- ci->acc = a;
- ci->mid = 0;
- ci->stackent = mrb->c->stack;
- ci->argc = 0;
- ci->target_class = mrb_class_ptr(recv);
-
- /* prepare stack */
- mrb->c->stack += a;
-
- p = mrb_proc_new(mrb, irep->reps[GETARG_Bx(i)]);
- p->target_class = ci->target_class;
- ci->proc = p;
-
- if (MRB_PROC_CFUNC_P(p)) {
- ci->nregs = 0;
- mrb->c->stack[0] = p->body.func(mrb, recv);
- mrb_gc_arena_restore(mrb, ai);
- if (mrb->exc) goto L_RAISE;
- /* pop stackpos */
- regs = mrb->c->stack = mrb->c->ci->stackent;
- cipop(mrb);
- NEXT;
- }
- else {
- irep = p->body.irep;
- pool = irep->pool;
- syms = irep->syms;
- stack_extend(mrb, irep->nregs, 1);
- ci->nregs = irep->nregs;
- regs = mrb->c->stack;
- pc = irep->iseq;
- JUMP;
- }
+ const mrb_irep *nirep = irep->reps[b];
+
+ /* prepare closure */
+ p = mrb_proc_new(mrb, nirep);
+ p->c = NULL;
+ mrb_field_write_barrier(mrb, (struct RBasic*)p, (struct RBasic*)proc);
+ MRB_PROC_SET_TARGET_CLASS(p, mrb_class_ptr(recv));
+ p->flags |= MRB_PROC_SCOPE;
+ proc_adjust_upper(p);
+
+ /* prepare call stack */
+ cipush(mrb, a, a, mrb_class_ptr(recv), p, 0, 0);
+
+ irep = p->body.irep;
+ pool = irep->pool;
+ syms = irep->syms;
+ mrb_stack_extend(mrb, irep->nregs);
+ stack_clear(regs+1, irep->nregs-1);
+ pc = irep->iseq;
+ JUMP;
}
- CASE(OP_METHOD) {
- /* A B R(A).newmethod(Syms(B),R(A+1)) */
- int a = GETARG_A(i);
- struct RClass *c = mrb_class_ptr(regs[a]);
+ CASE(OP_DEF, BB) {
+ struct RClass *target = mrb_class_ptr(regs[a]);
+ struct RProc *p = mrb_proc_ptr(regs[a+1]);
+ mrb_method_t m;
+ mrb_sym mid = syms[b];
- mrb_define_method_vm(mrb, c, syms[GETARG_B(i)], regs[a+1]);
- ARENA_RESTORE(mrb, ai);
+ MRB_METHOD_FROM_PROC(m, p);
+ mrb_define_method_raw(mrb, target, mid, m);
+ mrb_method_added(mrb, target, mid);
+ mrb_gc_arena_restore(mrb, ai);
+ regs[a] = mrb_symbol_value(mid);
NEXT;
}
- CASE(OP_SCLASS) {
- /* A B R(A) := R(B).singleton_class */
- regs[GETARG_A(i)] = mrb_singleton_class(mrb, regs[GETARG_B(i)]);
- ARENA_RESTORE(mrb, ai);
+ CASE(OP_SCLASS, B) {
+ regs[a] = mrb_singleton_class(mrb, regs[a]);
+ mrb_gc_arena_restore(mrb, ai);
NEXT;
}
- CASE(OP_TCLASS) {
- /* A R(A) := target_class */
- if (!mrb->c->ci->target_class) {
- mrb_value exc = mrb_exc_new_str_lit(mrb, E_TYPE_ERROR, "no target class or module");
- mrb->exc = mrb_obj_ptr(exc);
- goto L_RAISE;
- }
- regs[GETARG_A(i)] = mrb_obj_value(mrb->c->ci->target_class);
+ CASE(OP_TCLASS, B) {
+ if (!check_target_class(mrb)) goto L_RAISE;
+ regs[a] = mrb_obj_value(mrb_vm_ci_target_class(mrb->c->ci));
NEXT;
}
- CASE(OP_RANGE) {
- /* A B C R(A) := range_new(R(B),R(B+1),C) */
- int b = GETARG_B(i);
- regs[GETARG_A(i)] = mrb_range_new(mrb, regs[b], regs[b+1], GETARG_C(i));
- ARENA_RESTORE(mrb, ai);
+ CASE(OP_ALIAS, BB) {
+ struct RClass *target;
+
+ if (!check_target_class(mrb)) goto L_RAISE;
+ target = mrb_vm_ci_target_class(mrb->c->ci);
+ mrb_alias_method(mrb, target, syms[a], syms[b]);
+ mrb_method_added(mrb, target, syms[a]);
+ NEXT;
+ }
+ CASE(OP_UNDEF, B) {
+ struct RClass *target;
+
+ if (!check_target_class(mrb)) goto L_RAISE;
+ target = mrb_vm_ci_target_class(mrb->c->ci);
+ mrb_undef_method_id(mrb, target, syms[a]);
NEXT;
}
- CASE(OP_DEBUG) {
- /* A B C debug print R(A),R(B),R(C) */
-#ifdef ENABLE_DEBUG
+ CASE(OP_DEBUG, Z) {
+ FETCH_BBB();
+#ifdef MRB_USE_DEBUG_HOOK
mrb->debug_op_hook(mrb, irep, pc, regs);
#else
-#ifdef ENABLE_STDIO
- printf("OP_DEBUG %d %d %d\n", GETARG_A(i), GETARG_B(i), GETARG_C(i));
+#ifndef MRB_NO_STDIO
+ printf("OP_DEBUG %d %d %d\n", a, b, c);
#else
abort();
#endif
@@ -2332,76 +2871,119 @@ RETRY_TRY_BLOCK:
NEXT;
}
- CASE(OP_STOP) {
+ CASE(OP_ERR, B) {
+ size_t len = pool[a].tt >> 2;
+ mrb_value exc;
+
+ mrb_assert((pool[a].tt&IREP_TT_NFLAG)==0);
+ exc = mrb_exc_new(mrb, E_LOCALJUMP_ERROR, pool[a].u.str, len);
+ mrb_exc_set(mrb, exc);
+ goto L_RAISE;
+ }
+
+ CASE(OP_SENDVK, BB) { /* not yet implemented */
+ NEXT;
+ }
+
+ CASE(OP_EXT1, Z) {
+ insn = READ_B();
+ switch (insn) {
+#define OPCODE(insn,ops) case OP_ ## insn: FETCH_ ## ops ## _1(); mrb->c->ci->pc = pc; goto L_OP_ ## insn ## _BODY;
+#include "mruby/ops.h"
+#undef OPCODE
+ }
+ pc--;
+ NEXT;
+ }
+ CASE(OP_EXT2, Z) {
+ insn = READ_B();
+ switch (insn) {
+#define OPCODE(insn,ops) case OP_ ## insn: FETCH_ ## ops ## _2(); mrb->c->ci->pc = pc; goto L_OP_ ## insn ## _BODY;
+#include "mruby/ops.h"
+#undef OPCODE
+ }
+ pc--;
+ NEXT;
+ }
+ CASE(OP_EXT3, Z) {
+ uint8_t insn = READ_B();
+ switch (insn) {
+#define OPCODE(insn,ops) case OP_ ## insn: FETCH_ ## ops ## _3(); mrb->c->ci->pc = pc; goto L_OP_ ## insn ## _BODY;
+#include "mruby/ops.h"
+#undef OPCODE
+ }
+ pc--;
+ NEXT;
+ }
+
+ CASE(OP_STOP, Z) {
/* stop VM */
- L_STOP:
- {
- int eidx_stop = mrb->c->ci == mrb->c->cibase ? 0 : mrb->c->ci[-1].eidx;
- int eidx = mrb->c->ci->eidx;
- while (eidx > eidx_stop) {
- ecall(mrb, --eidx);
- }
+ CHECKPOINT_RESTORE(RBREAK_TAG_STOP) {
+ /* do nothing */
}
- ERR_PC_CLR(mrb);
+ CHECKPOINT_MAIN(RBREAK_TAG_STOP) {
+ UNWIND_ENSURE(mrb, mrb->c->ci, pc, RBREAK_TAG_STOP, proc, mrb_nil_value());
+ }
+ CHECKPOINT_END(RBREAK_TAG_STOP);
+ L_STOP:
mrb->jmp = prev_jmp;
if (mrb->exc) {
+ mrb_assert(mrb->exc->tt == MRB_TT_EXCEPTION);
return mrb_obj_value(mrb->exc);
}
return regs[irep->nlocals];
}
-
- CASE(OP_ERR) {
- /* Bx raise RuntimeError with message Lit(Bx) */
- mrb_value msg = mrb_str_dup(mrb, pool[GETARG_Bx(i)]);
- mrb_value exc;
-
- if (GETARG_A(i) == 0) {
- exc = mrb_exc_new_str(mrb, E_RUNTIME_ERROR, msg);
- }
- else {
- exc = mrb_exc_new_str(mrb, E_LOCALJUMP_ERROR, msg);
- }
- mrb->exc = mrb_obj_ptr(exc);
- goto L_RAISE;
- }
}
END_DISPATCH;
-
+#undef regs
}
MRB_CATCH(&c_jmp) {
+ mrb_callinfo *ci = mrb->c->ci;
+ while (ci > mrb->c->cibase && ci->acc == CI_ACC_DIRECT) {
+ ci = cipop(mrb);
+ }
exc_catched = TRUE;
+ pc = ci->pc;
goto RETRY_TRY_BLOCK;
}
MRB_END_EXC(&c_jmp);
}
-MRB_API mrb_value
-mrb_run(mrb_state *mrb, struct RProc *proc, mrb_value self)
+static mrb_value
+mrb_run(mrb_state *mrb, const struct RProc *proc, mrb_value self)
{
- return mrb_context_run(mrb, proc, self, mrb->c->ci->argc + 2); /* argc + 2 (receiver and block) */
+ if (mrb->c->ci->argc < 0) {
+ return mrb_vm_run(mrb, proc, self, 3); /* receiver, args and block) */
+ }
+ else {
+ return mrb_vm_run(mrb, proc, self, mrb->c->ci->argc + 2); /* argc + 2 (receiver and block) */
+ }
}
MRB_API mrb_value
-mrb_toplevel_run_keep(mrb_state *mrb, struct RProc *proc, unsigned int stack_keep)
+mrb_top_run(mrb_state *mrb, const struct RProc *proc, mrb_value self, mrb_int stack_keep)
{
- mrb_callinfo *ci;
mrb_value v;
- if (!mrb->c->cibase || mrb->c->ci == mrb->c->cibase) {
- return mrb_context_run(mrb, proc, mrb_top_self(mrb), stack_keep);
+ if (!mrb->c->cibase) {
+ return mrb_vm_run(mrb, proc, self, stack_keep);
}
- ci = cipush(mrb);
- ci->nregs = 1; /* protect the receiver */
- ci->acc = CI_ACC_SKIP;
- ci->target_class = mrb->object_class;
- v = mrb_context_run(mrb, proc, mrb_top_self(mrb), stack_keep);
- cipop(mrb);
+ if (mrb->c->ci == mrb->c->cibase) {
+ mrb_vm_ci_env_set(mrb->c->ci, NULL);
+ return mrb_vm_run(mrb, proc, self, stack_keep);
+ }
+ cipush(mrb, 0, CI_ACC_SKIP, mrb->object_class, NULL, 0, 0);
+ v = mrb_vm_run(mrb, proc, self, stack_keep);
return v;
}
-MRB_API mrb_value
-mrb_toplevel_run(mrb_state *mrb, struct RProc *proc)
-{
- return mrb_toplevel_run_keep(mrb, proc, 0);
-}
+#if defined(MRB_USE_CXX_EXCEPTION) && defined(__cplusplus)
+# if !defined(MRB_USE_CXX_ABI)
+} /* end of extern "C" */
+# endif
+mrb_int mrb_jmpbuf::jmpbuf_id = 0;
+# if !defined(MRB_USE_CXX_ABI)
+extern "C" {
+# endif
+#endif