summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorTakashi Kokubun <[email protected]>2016-12-11 01:45:38 +0900
committerTakashi Kokubun <[email protected]>2016-12-11 03:44:15 +0900
commit10bb7ad693e7c7443de924a39c1fedb4461108ba (patch)
tree8ba3d7bd9992f0097f99a034012565adb43c4d4a
parent3cc913490b708fe4d0e78e48f86e6e39cf3d8576 (diff)
downloadmruby-10bb7ad693e7c7443de924a39c1fedb4461108ba.tar.gz
mruby-10bb7ad693e7c7443de924a39c1fedb4461108ba.zip
Implement Object#freeze
-rw-r--r--include/mruby/class.h1
-rw-r--r--include/mruby/object.h4
-rw-r--r--include/mruby/string.h5
-rw-r--r--src/array.c4
-rw-r--r--src/hash.c7
-rw-r--r--src/kernel.c10
-rw-r--r--src/string.c15
-rw-r--r--test/t/array.rb7
-rw-r--r--test/t/hash.rb7
-rw-r--r--test/t/kernel.rb5
10 files changed, 45 insertions, 20 deletions
diff --git a/include/mruby/class.h b/include/mruby/class.h
index 246e82e59..ce953af3b 100644
--- a/include/mruby/class.h
+++ b/include/mruby/class.h
@@ -52,6 +52,7 @@ mrb_class(mrb_state *mrb, mrb_value v)
}
// TODO: figure out where to put user flags
+#define MRB_FLAG_IS_FROZEN (1 << 18)
#define MRB_FLAG_IS_PREPENDED (1 << 19)
#define MRB_FLAG_IS_ORIGIN (1 << 20)
#define MRB_CLASS_ORIGIN(c) do {\
diff --git a/include/mruby/object.h b/include/mruby/object.h
index 9fbfe34f3..da44027e1 100644
--- a/include/mruby/object.h
+++ b/include/mruby/object.h
@@ -22,6 +22,10 @@ struct RBasic {
};
#define mrb_basic_ptr(v) ((struct RBasic*)(mrb_ptr(v)))
+#define RBASIC_FROZEN_P(o) ((o)->flags & MRB_FLAG_IS_FROZEN)
+#define RBASIC_SET_FROZEN_FLAG(o) ((o)->flags |= MRB_FLAG_IS_FROZEN)
+#define RBASIC_UNSET_FROZEN_FLAG(o) ((o)->flags &= ~MRB_FLAG_IS_FROZEN)
+
struct RObject {
MRB_OBJECT_HEADER;
struct iv_tbl *iv;
diff --git a/include/mruby/string.h b/include/mruby/string.h
index b30c1ed98..9ccf8f187 100644
--- a/include/mruby/string.h
+++ b/include/mruby/string.h
@@ -62,10 +62,6 @@ struct RString {
#define RSTR_SET_NOFREE_FLAG(s) ((s)->flags |= MRB_STR_NOFREE)
#define RSTR_UNSET_NOFREE_FLAG(s) ((s)->flags &= ~MRB_STR_NOFREE)
-#define RSTR_FROZEN_P(s) ((s)->flags & MRB_STR_FROZEN)
-#define RSTR_SET_FROZEN_FLAG(s) ((s)->flags |= MRB_STR_FROZEN)
-#define RSTR_UNSET_FROZEN_FLAG(s) ((s)->flags &= ~MRB_STR_FROZEN)
-
/*
* Returns a pointer from a Ruby string
*/
@@ -80,7 +76,6 @@ MRB_API mrb_int mrb_str_strlen(mrb_state*, struct RString*);
#define MRB_STR_SHARED 1
#define MRB_STR_NOFREE 2
-#define MRB_STR_FROZEN 4
#define MRB_STR_NO_UTF 8
#define MRB_STR_EMBED 16
#define MRB_STR_EMBED_LEN_MASK 0x3e0
diff --git a/src/array.c b/src/array.c
index f6599bd5b..c6bac7b47 100644
--- a/src/array.c
+++ b/src/array.c
@@ -108,6 +108,10 @@ ary_fill_with_nil(mrb_value *ptr, mrb_int size)
static void
ary_modify(mrb_state *mrb, struct RArray *a)
{
+ if (RBASIC_FROZEN_P(a)) {
+ mrb_raise(mrb, E_RUNTIME_ERROR, "can't modify frozen array");
+ }
+
if (ARY_SHARED_P(a)) {
mrb_shared_array *shared = a->aux.shared;
diff --git a/src/hash.c b/src/hash.c
index 93fe656e0..d0e865b5c 100644
--- a/src/hash.c
+++ b/src/hash.c
@@ -98,9 +98,9 @@ static void mrb_hash_modify(mrb_state *mrb, mrb_value hash);
static inline mrb_value
mrb_hash_ht_key(mrb_state *mrb, mrb_value key)
{
- if (mrb_string_p(key) && !RSTR_FROZEN_P(mrb_str_ptr(key))) {
+ if (mrb_string_p(key) && !RBASIC_FROZEN_P(mrb_str_ptr(key))) {
key = mrb_str_dup(mrb, key);
- RSTR_SET_FROZEN_FLAG(mrb_str_ptr(key));
+ RBASIC_SET_FROZEN_FLAG(mrb_str_ptr(key));
}
return key;
}
@@ -278,6 +278,9 @@ mrb_hash_tbl(mrb_state *mrb, mrb_value hash)
static void
mrb_hash_modify(mrb_state *mrb, mrb_value hash)
{
+ if (RBASIC_FROZEN_P(mrb_hash_ptr(hash))) {
+ mrb_raise(mrb, E_RUNTIME_ERROR, "can't modify frozen hash");
+ }
mrb_hash_tbl(mrb, hash);
}
diff --git a/src/kernel.c b/src/kernel.c
index c63e05596..78e57a17a 100644
--- a/src/kernel.c
+++ b/src/kernel.c
@@ -450,6 +450,15 @@ mrb_obj_extend_m(mrb_state *mrb, mrb_value self)
return mrb_obj_extend(mrb, argc, argv, self);
}
+static mrb_value
+mrb_obj_freeze(mrb_state *mrb, mrb_value self)
+{
+ struct RBasic *b = mrb_basic_ptr(self);
+
+ RBASIC_SET_FROZEN_FLAG(b);
+ return self;
+}
+
/* 15.3.1.3.15 */
/*
* call-seq:
@@ -1124,6 +1133,7 @@ mrb_init_kernel(mrb_state *mrb)
mrb_define_method(mrb, krn, "eql?", mrb_obj_equal_m, MRB_ARGS_REQ(1)); /* 15.3.1.3.10 */
mrb_define_method(mrb, krn, "equal?", mrb_obj_equal_m, MRB_ARGS_REQ(1)); /* 15.3.1.3.11 */
mrb_define_method(mrb, krn, "extend", mrb_obj_extend_m, MRB_ARGS_ANY()); /* 15.3.1.3.13 */
+ mrb_define_method(mrb, krn, "freeze", mrb_obj_freeze, MRB_ARGS_NONE());
mrb_define_method(mrb, krn, "global_variables", mrb_f_global_variables, MRB_ARGS_NONE()); /* 15.3.1.3.14 */
mrb_define_method(mrb, krn, "hash", mrb_obj_hash, MRB_ARGS_NONE()); /* 15.3.1.3.15 */
mrb_define_method(mrb, krn, "initialize_copy", mrb_obj_init_copy, MRB_ARGS_REQ(1)); /* 15.3.1.3.16 */
diff --git a/src/string.c b/src/string.c
index a4f8085ec..7234ecf2b 100644
--- a/src/string.c
+++ b/src/string.c
@@ -501,7 +501,7 @@ str_index(mrb_state *mrb, mrb_value str, mrb_value sub, mrb_int offset)
static void
check_frozen(mrb_state *mrb, struct RString *s)
{
- if (RSTR_FROZEN_P(s)) {
+ if (RBASIC_FROZEN_P(s)) {
mrb_raise(mrb, E_RUNTIME_ERROR, "can't modify frozen string");
}
}
@@ -700,15 +700,6 @@ mrb_str_modify(mrb_state *mrb, struct RString *s)
}
}
-static mrb_value
-mrb_str_freeze(mrb_state *mrb, mrb_value str)
-{
- struct RString *s = mrb_str_ptr(str);
-
- RSTR_SET_FROZEN_FLAG(s);
- return str;
-}
-
MRB_API mrb_value
mrb_str_resize(mrb_state *mrb, mrb_value str, mrb_int len)
{
@@ -2217,7 +2208,7 @@ mrb_string_value_cstr(mrb_state *mrb, mrb_value *ptr)
char *p = RSTR_PTR(ps);
if (!p || p[len] != '\0') {
- if (RSTR_FROZEN_P(ps)) {
+ if (RBASIC_FROZEN_P(ps)) {
*ptr = str = mrb_str_dup(mrb, str);
ps = mrb_str_ptr(str);
}
@@ -2746,8 +2737,6 @@ mrb_init_string(mrb_state *mrb)
mrb_define_method(mrb, s, "upcase!", mrb_str_upcase_bang, MRB_ARGS_NONE()); /* 15.2.10.5.43 */
mrb_define_method(mrb, s, "inspect", mrb_str_inspect, MRB_ARGS_NONE()); /* 15.2.10.5.46(x) */
mrb_define_method(mrb, s, "bytes", mrb_str_bytes, MRB_ARGS_NONE());
-
- mrb_define_method(mrb, s, "freeze", mrb_str_freeze, MRB_ARGS_NONE());
}
/*
diff --git a/test/t/array.rb b/test/t/array.rb
index 9cc2f64ad..cf7ed4704 100644
--- a/test/t/array.rb
+++ b/test/t/array.rb
@@ -373,3 +373,10 @@ assert("Array#rindex") do
$a = [2, 3, 4, 5, 6, 7, 8, 9, 10, Sneaky.new]
assert_equal 0, $a.rindex(1)
end
+
+assert('Array#freeze') do
+ a = [].freeze
+ assert_raise(RuntimeError) do
+ a[0] = 1
+ end
+end
diff --git a/test/t/hash.rb b/test/t/hash.rb
index f076db8e5..c63b8c009 100644
--- a/test/t/hash.rb
+++ b/test/t/hash.rb
@@ -366,3 +366,10 @@ assert('Hash#rehash') do
h.rehash
assert_equal("b", h[[:b]])
end
+
+assert('Hash#freeze') do
+ h = {}.freeze
+ assert_raise(RuntimeError) do
+ h[:a] = 'b'
+ end
+end
diff --git a/test/t/kernel.rb b/test/t/kernel.rb
index e59bd6a10..42abed9df 100644
--- a/test/t/kernel.rb
+++ b/test/t/kernel.rb
@@ -257,6 +257,11 @@ assert('Kernel#extend works on toplevel', '15.3.1.3.13') do
assert_true respond_to?(:test_method)
end
+assert('Kernel#freeze') do
+ obj = Object.new
+ assert_equal obj, obj.freeze
+end
+
assert('Kernel#global_variables', '15.3.1.3.14') do
assert_equal Array, global_variables.class
end