Skip to content

Commit

Permalink
Implement Object#freeze
Browse files Browse the repository at this point in the history
  • Loading branch information
k0kubun committed Dec 10, 2016
1 parent 3cc9134 commit 10bb7ad
Show file tree
Hide file tree
Showing 10 changed files with 45 additions and 20 deletions.
1 change: 1 addition & 0 deletions include/mruby/class.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 {\
Expand Down
4 changes: 4 additions & 0 deletions include/mruby/object.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
5 changes: 0 additions & 5 deletions include/mruby/string.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
*/
Expand All @@ -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
Expand Down
4 changes: 4 additions & 0 deletions src/array.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down
7 changes: 5 additions & 2 deletions src/hash.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand Down Expand Up @@ -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);
}

Expand Down
10 changes: 10 additions & 0 deletions src/kernel.c
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down Expand Up @@ -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 */
Expand Down
15 changes: 2 additions & 13 deletions src/string.c
Original file line number Diff line number Diff line change
Expand Up @@ -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");
}
}
Expand Down Expand Up @@ -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)
{
Expand Down Expand Up @@ -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);
}
Expand Down Expand Up @@ -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());
}

/*
Expand Down
7 changes: 7 additions & 0 deletions test/t/array.rb
Original file line number Diff line number Diff line change
Expand Up @@ -373,3 +373,10 @@ def ==(*)
$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
7 changes: 7 additions & 0 deletions test/t/hash.rb
Original file line number Diff line number Diff line change
Expand Up @@ -366,3 +366,10 @@ def hash.default(k); self[k] = 1; end
h.rehash
assert_equal("b", h[[:b]])
end

assert('Hash#freeze') do
h = {}.freeze
assert_raise(RuntimeError) do
h[:a] = 'b'
end
end
5 changes: 5 additions & 0 deletions test/t/kernel.rb
Original file line number Diff line number Diff line change
Expand Up @@ -257,6 +257,11 @@ def test_method; end
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
Expand Down

0 comments on commit 10bb7ad

Please sign in to comment.