diff --git a/ruby/ext/google/protobuf_c/map.c b/ruby/ext/google/protobuf_c/map.c index 59d64fcb79..55b7be4c66 100644 --- a/ruby/ext/google/protobuf_c/map.c +++ b/ruby/ext/google/protobuf_c/map.c @@ -386,6 +386,8 @@ VALUE Map_index(VALUE _self, VALUE key) { * was just inserted. */ VALUE Map_index_set(VALUE _self, VALUE key, VALUE value) { + rb_check_frozen(_self); + Map* self = ruby_to_Map(_self); char keybuf[TABLE_KEY_BUF_LENGTH]; @@ -438,6 +440,8 @@ VALUE Map_has_key(VALUE _self, VALUE key) { * nil if none was present. Throws an exception if the key is of the wrong type. */ VALUE Map_delete(VALUE _self, VALUE key) { + rb_check_frozen(_self); + Map* self = ruby_to_Map(_self); char keybuf[TABLE_KEY_BUF_LENGTH]; @@ -461,6 +465,8 @@ VALUE Map_delete(VALUE _self, VALUE key) { * Removes all entries from the map. */ VALUE Map_clear(VALUE _self) { + rb_check_frozen(_self); + Map* self = ruby_to_Map(_self); // Uninit and reinit the table -- this is faster than iterating and doing a diff --git a/ruby/ext/google/protobuf_c/message.c b/ruby/ext/google/protobuf_c/message.c index 14dc053d1a..5537b3860c 100644 --- a/ruby/ext/google/protobuf_c/message.c +++ b/ruby/ext/google/protobuf_c/message.c @@ -242,6 +242,7 @@ VALUE Message_method_missing(int argc, VALUE* argv, VALUE _self) { if (argc != 2) { rb_raise(rb_eArgError, "Expected 2 arguments, received %d", argc); } + rb_check_frozen(_self); } else if (argc != 1) { rb_raise(rb_eArgError, "Expected 1 argument, received %d", argc); } diff --git a/ruby/tests/basic.rb b/ruby/tests/basic.rb index 269c9ee27d..db97614448 100644 --- a/ruby/tests/basic.rb +++ b/ruby/tests/basic.rb @@ -357,5 +357,22 @@ module BasicTest assert_equal nil, file_descriptor.name assert_equal :proto3, file_descriptor.syntax end + + def test_map_freeze + m = proto_module::MapMessage.new + m.map_string_int32['a'] = 5 + m.map_string_msg['b'] = proto_module::TestMessage2.new + + m.map_string_int32.freeze + m.map_string_msg.freeze + + assert m.map_string_int32.frozen? + assert m.map_string_msg.frozen? + + assert_raise(FrozenError) { m.map_string_int32['foo'] = 1 } + assert_raise(FrozenError) { m.map_string_msg['bar'] = proto_module::TestMessage2.new } + assert_raise(FrozenError) { m.map_string_int32.delete('a') } + assert_raise(FrozenError) { m.map_string_int32.clear } + end end end diff --git a/ruby/tests/common_tests.rb b/ruby/tests/common_tests.rb index 12388c695b..638ad76df0 100644 --- a/ruby/tests/common_tests.rb +++ b/ruby/tests/common_tests.rb @@ -1269,6 +1269,39 @@ module CommonTests assert proto_module::TestMessage.new != nil end + def test_freeze + m = proto_module::TestMessage.new + m.optional_int32 = 10 + m.freeze + + frozen_error = assert_raise(FrozenError) { m.optional_int32 = 20 } + assert_equal "can't modify frozen #{proto_module}::TestMessage", frozen_error.message + assert_equal 10, m.optional_int32 + assert_equal true, m.frozen? + + assert_raise(FrozenError) { m.optional_int64 = 2 } + assert_raise(FrozenError) { m.optional_uint32 = 3 } + assert_raise(FrozenError) { m.optional_uint64 = 4 } + assert_raise(FrozenError) { m.optional_bool = true } + assert_raise(FrozenError) { m.optional_float = 6.0 } + assert_raise(FrozenError) { m.optional_double = 7.0 } + assert_raise(FrozenError) { m.optional_string = '8' } + assert_raise(FrozenError) { m.optional_bytes = nil } + assert_raise(FrozenError) { m.optional_msg = proto_module::TestMessage2.new } + assert_raise(FrozenError) { m.optional_enum = :A } + assert_raise(FrozenError) { m.repeated_int32 = 1 } + assert_raise(FrozenError) { m.repeated_int64 = 2 } + assert_raise(FrozenError) { m.repeated_uint32 = 3 } + assert_raise(FrozenError) { m.repeated_uint64 = 4 } + assert_raise(FrozenError) { m.repeated_bool = true } + assert_raise(FrozenError) { m.repeated_float = 6.0 } + assert_raise(FrozenError) { m.repeated_double = 7.0 } + assert_raise(FrozenError) { m.repeated_string = '8' } + assert_raise(FrozenError) { m.repeated_bytes = nil } + assert_raise(FrozenError) { m.repeated_msg = proto_module::TestMessage2.new } + assert_raise(FrozenError) { m.repeated_enum = :A } + end + def test_eq m1 = proto_module::TestMessage.new(:optional_string => 'foo', :repeated_string => ['bar1', 'bar2']) m2 = proto_module::TestMessage.new(:optional_string => 'foo', :repeated_string => ['bar1', 'bar2'])