[macruby-changes] [4004] MacRuby/trunk
source_changes at macosforge.org
source_changes at macosforge.org
Sun May 2 19:13:46 PDT 2010
Revision: 4004
http://trac.macosforge.org/projects/ruby/changeset/4004
Author: lsansonetti at apple.com
Date: 2010-05-02 19:13:40 -0700 (Sun, 02 May 2010)
Log Message:
-----------
raising RuntimeError as in 0.5 in case we try to mutate a non-mutable collection
Modified Paths:
--------------
MacRuby/trunk/NSArray.m
MacRuby/trunk/NSDictionary.m
MacRuby/trunk/NSString.m
Removed Paths:
-------------
MacRuby/trunk/spec/macruby/tags/macruby/core/array_tags.txt
MacRuby/trunk/spec/macruby/tags/macruby/core/hash_tags.txt
MacRuby/trunk/spec/macruby/tags/macruby/core/string_tags.txt
Modified: MacRuby/trunk/NSArray.m
===================================================================
--- MacRuby/trunk/NSArray.m 2010-05-03 01:23:40 UTC (rev 4003)
+++ MacRuby/trunk/NSArray.m 2010-05-03 02:13:40 UTC (rev 4004)
@@ -18,6 +18,27 @@
VALUE rb_cNSArray;
VALUE rb_cNSMutableArray;
+// Some NSArray instances actually do not even respond to mutable methods.
+// So one way to know is to check if the addObject: method exists.
+#define CHECK_MUTABLE(obj) \
+ do { \
+ if (![obj respondsToSelector:@selector(addObject:)]) { \
+ rb_raise(rb_eRuntimeError, \
+ "can't modify frozen/immutable array"); \
+ } \
+ } \
+ while (0)
+
+// If a given mutable operation raises an NSException error,
+// it is likely that the object is not mutable.
+#define TRY_MOP(code) \
+ @try { \
+ code; \
+ } \
+ @catch(NSException *exc) { \
+ rb_raise(rb_eRuntimeError, "can't modify frozen/immutable array"); \
+ }
+
static id
nsary_dup(id rcv, SEL sel)
{
@@ -41,7 +62,8 @@
static id
nsary_clear(id rcv, SEL sel)
{
- [rcv removeAllObjects];
+ CHECK_MUTABLE(rcv);
+ TRY_MOP([rcv removeAllObjects]);
return rcv;
}
@@ -159,21 +181,23 @@
rlen = RARRAY_LEN(rpl);
}
+ CHECK_MUTABLE(ary);
+
if (beg >= n) {
for (long i = n; i < beg; i++) {
- [ary addObject:[NSNull null]];
+ TRY_MOP([ary addObject:[NSNull null]]);
}
if (rlen > 0 && rpl != Qundef) {
- [ary addObjectsFromArray:(id)rpl];
+ TRY_MOP([ary addObjectsFromArray:(id)rpl]);
}
}
else {
if (rlen > 0 && rpl != Qundef) {
- [ary replaceObjectsInRange:NSMakeRange(beg, len)
- withObjectsFromArray:(id)rpl];
+ TRY_MOP([ary replaceObjectsInRange:NSMakeRange(beg, len)
+ withObjectsFromArray:(id)rpl]);
}
else {
- [ary removeObjectsInRange:NSMakeRange(beg, len)];
+ TRY_MOP([ary removeObjectsInRange:NSMakeRange(beg, len)]);
}
}
}
@@ -189,12 +213,15 @@
idx - len);
}
}
+
+ CHECK_MUTABLE(ary);
+
if (len <= idx) {
for (long i = len; i <= idx; i++) {
- [ary addObject:[NSNull null]];
+ TRY_MOP([ary addObject:[NSNull null]]);
}
}
- [ary replaceObjectAtIndex:idx withObject:RB2OC(val)];
+ TRY_MOP([ary replaceObjectAtIndex:idx withObject:RB2OC(val)]);
}
static VALUE
@@ -286,8 +313,9 @@
}
if (remove) {
+ CHECK_MUTABLE(ary);
for (long i = 0; i < n; i++) {
- [ary removeObjectAtIndex:offset];
+ TRY_MOP([ary removeObjectAtIndex:offset]);
}
}
@@ -318,14 +346,16 @@
nsary_concat(id rcv, SEL sel, VALUE ary)
{
ary = to_ary(ary);
- [rcv addObjectsFromArray:(id)ary];
+ CHECK_MUTABLE(rcv);
+ TRY_MOP([rcv addObjectsFromArray:(id)ary]);
return rcv;
}
static id
nsary_push(id rcv, SEL sel, VALUE elem)
{
- [rcv addObject:RB2OC(elem)];
+ CHECK_MUTABLE(rcv);
+ TRY_MOP([rcv addObject:RB2OC(elem)]);
return rcv;
}
@@ -341,11 +371,12 @@
static VALUE
nsary_pop(id rcv, SEL sel, int argc, VALUE *argv)
{
+ CHECK_MUTABLE(rcv);
if (argc == 0) {
const long len = [rcv count];
if (len > 0) {
id elem = [rcv objectAtIndex:len - 1];
- [rcv removeObjectAtIndex:len - 1];
+ TRY_MOP([rcv removeObjectAtIndex:len - 1]);
return OC2RB(elem);
}
return Qnil;
@@ -356,11 +387,12 @@
static VALUE
nsary_shift(id rcv, SEL sel, int argc, VALUE *argv)
{
+ CHECK_MUTABLE(rcv);
if (argc == 0) {
const long len = [rcv count];
if (len > 0) {
id elem = [rcv objectAtIndex:0];
- [rcv removeObjectAtIndex:0];
+ TRY_MOP([rcv removeObjectAtIndex:0]);
return OC2RB(elem);
}
return Qnil;
@@ -371,8 +403,9 @@
static id
nsary_unshift(id rcv, SEL sel, int argc, VALUE *argv)
{
+ CHECK_MUTABLE(rcv);
for (int i = argc - 1; i >= 0; i--) {
- [rcv insertObject:RB2OC(argv[i]) atIndex:0];
+ TRY_MOP([rcv insertObject:RB2OC(argv[i]) atIndex:0]);
}
return rcv;
}
@@ -380,6 +413,7 @@
static void
nsary_insert(id rcv, long idx, VALUE elem)
{
+ CHECK_MUTABLE(rcv);
const long len = [rcv count];
if (idx < 0) {
idx += len;
@@ -389,15 +423,16 @@
}
if (idx > len) {
for (long i = len; i < idx; i++) {
- [rcv addObject:[NSNull null]];
+ TRY_MOP([rcv addObject:[NSNull null]]);
}
}
- [rcv insertObject:RB2OC(elem) atIndex:idx];
+ TRY_MOP([rcv insertObject:RB2OC(elem) atIndex:idx]);
}
static id
nsary_insert_m(id rcv, SEL sel, int argc, VALUE *argv)
{
+ CHECK_MUTABLE(rcv);
if (argc < 1) {
rb_raise(rb_eArgError, "wrong number of arguments (at least 1)");
}
@@ -539,8 +574,9 @@
static id
nsary_reverse_bang(id rcv, SEL sel)
{
+ CHECK_MUTABLE(rcv);
for (long i = 0, count = [rcv count]; i < (count / 2); i++) {
- [rcv exchangeObjectAtIndex:i withObjectAtIndex:count - i - 1];
+ TRY_MOP([rcv exchangeObjectAtIndex:i withObjectAtIndex:count - i - 1]);
}
return rcv;
}
@@ -563,12 +599,13 @@
static id
nsary_sort_bang(id rcv, SEL sel)
{
+ CHECK_MUTABLE(rcv);
if ([rcv count] > 1) {
if (rb_block_given_p()) {
- [rcv sortUsingFunction:sort_block context:NULL];
+ TRY_MOP([rcv sortUsingFunction:sort_block context:NULL]);
}
else {
- [rcv sortUsingSelector:@selector(compare:)];
+ TRY_MOP([rcv sortUsingSelector:@selector(compare:)]);
}
}
return rcv;
@@ -583,11 +620,12 @@
static VALUE
collect(id rcv)
{
+ CHECK_MUTABLE(rcv);
for (long i = 0, count = [rcv count]; i < count; i++) {
id elem = [rcv objectAtIndex:i];
id newval = RB2OC(rb_yield(OC2RB(elem)));
RETURN_IF_BROKEN();
- [rcv replaceObjectAtIndex:i withObject:newval];
+ TRY_MOP([rcv replaceObjectAtIndex:i withObject:newval]);
}
return (VALUE)rcv;
}
@@ -653,8 +691,9 @@
NSRange range = NSMakeRange(0, len);
NSUInteger index;
bool changed = false;
+ CHECK_MUTABLE(rcv);
while ((index = [rcv indexOfObject:ocelem inRange:range]) != NSNotFound) {
- [rcv removeObjectAtIndex:index];
+ TRY_MOP([rcv removeObjectAtIndex:index]);
range.location = index;
range.length = --len - index;
changed = true;
@@ -677,6 +716,7 @@
static VALUE
nsary_delete_at(id rcv, SEL sel, VALUE pos)
{
+ CHECK_MUTABLE(rcv);
long index = NUM2LONG(pos);
const long len = [rcv count];
if (index >= len) {
@@ -689,20 +729,21 @@
}
}
VALUE elem = OC2RB([rcv objectAtIndex:index]);
- [rcv removeObjectAtIndex:index];
+ TRY_MOP([rcv removeObjectAtIndex:index]);
return elem;
}
static VALUE
reject(id rcv)
{
+ CHECK_MUTABLE(rcv);
bool changed = false;
for (long i = 0, n = [rcv count]; i < n; i++) {
VALUE elem = OC2RB([rcv objectAtIndex:i]);
VALUE test = rb_yield(elem);
RETURN_IF_BROKEN();
if (RTEST(test)) {
- [rcv removeObjectAtIndex:i];
+ TRY_MOP([rcv removeObjectAtIndex:i]);
n--;
i--;
changed = true;
@@ -736,7 +777,8 @@
nsary_replace(id rcv, SEL sel, VALUE other)
{
other = to_ary(other);
- [rcv setArray:(id)other];
+ CHECK_MUTABLE(rcv);
+ TRY_MOP([rcv setArray:(id)other]);
return rcv;
}
@@ -828,6 +870,7 @@
static VALUE
nsary_uniq_bang(id rcv, SEL sel)
{
+ CHECK_MUTABLE(rcv);
long len = [rcv count];
bool changed = false;
for (long i = 0; i < len; i++) {
@@ -835,7 +878,7 @@
NSRange range = NSMakeRange(i + 1, len - i - 1);
NSUInteger index;
while ((index = [rcv indexOfObject:elem inRange:range]) != NSNotFound) {
- [rcv removeObjectAtIndex:index];
+ TRY_MOP([rcv removeObjectAtIndex:index]);
range.location = index;
range.length = --len - index;
changed = true;
@@ -919,10 +962,11 @@
static id
nsary_shuffle_bang(id rcv, SEL sel)
{
+ CHECK_MUTABLE(rcv);
long i = [rcv count];
while (i > 0) {
const long j = rb_genrand_real() * i--;
- [rcv exchangeObjectAtIndex:i withObjectAtIndex:j];
+ TRY_MOP([rcv exchangeObjectAtIndex:i withObjectAtIndex:j]);
}
return rcv;
}
Modified: MacRuby/trunk/NSDictionary.m
===================================================================
--- MacRuby/trunk/NSDictionary.m 2010-05-03 01:23:40 UTC (rev 4003)
+++ MacRuby/trunk/NSDictionary.m 2010-05-03 02:13:40 UTC (rev 4004)
@@ -18,6 +18,27 @@
VALUE rb_cNSHash;
VALUE rb_cNSMutableHash;
+// Some NSDictionary instances actually do not even respond to mutable methods.
+// So one way to know is to check if the setObject:forKey: method exists.
+#define CHECK_MUTABLE(obj) \
+ do { \
+ if (![obj respondsToSelector:@selector(setObject:forKey:)]) { \
+ rb_raise(rb_eRuntimeError, \
+ "can't modify frozen/immutable hash"); \
+ } \
+ } \
+ while (0)
+
+// If a given mutable operation raises an NSException error,
+// it is likely that the object is not mutable.
+#define TRY_MOP(code) \
+ @try { \
+ code; \
+ } \
+ @catch(NSException *exc) { \
+ rb_raise(rb_eRuntimeError, "can't modify frozen/immutable hash"); \
+ }
+
static id
to_hash(id hash)
{
@@ -47,12 +68,14 @@
static id
nshash_rehash(id rcv, SEL sel)
{
+ CHECK_MUTABLE(rcv);
NSArray *keys = [rcv allKeys];
NSArray *values = [rcv allValues];
assert([keys count] == [values count]);
- [rcv removeAllObjects];
+ TRY_MOP([rcv removeAllObjects]);
for (unsigned i = 0, count = [keys count]; i < count; i++) {
- [rcv setObject:[values objectAtIndex:i] forKey:[keys objectAtIndex:i]];
+ TRY_MOP([rcv setObject:[values objectAtIndex:i]
+ forKey:[keys objectAtIndex:i]]);
}
return rcv;
}
@@ -113,7 +136,8 @@
static VALUE
nshash_aset(id rcv, SEL sel, VALUE key, VALUE val)
{
- [rcv setObject:RB2OC(val) forKey:RB2OC(key)];
+ CHECK_MUTABLE(rcv);
+ TRY_MOP([rcv setObject:RB2OC(val) forKey:RB2OC(key)]);
return val;
}
@@ -251,11 +275,12 @@
static VALUE
nshash_shift(id rcv, SEL sel)
{
+ CHECK_MUTABLE(rcv);
if ([rcv count] > 0) {
id key = [[rcv keyEnumerator] nextObject];
assert(key != NULL);
id value = [rcv objectForKey:key];
- [rcv removeObjectForKey:key];
+ TRY_MOP([rcv removeObjectForKey:key]);
return rb_assoc_new(OC2RB(key), OC2RB(value));
}
return nshash_default(rcv, 0, 0, NULL);
@@ -264,10 +289,11 @@
static VALUE
nshash_delete(id rcv, SEL sel, VALUE key)
{
+ CHECK_MUTABLE(rcv);
id ockey = RB2OC(key);
id value = [rcv objectForKey:ockey];
if (value != nil) {
- [rcv removeObjectForKey:ockey];
+ TRY_MOP([rcv removeObjectForKey:ockey]);
return OC2RB(value);
}
if (rb_block_given_p()) {
@@ -279,6 +305,7 @@
static VALUE
nshash_delete_if(id rcv, SEL sel)
{
+ CHECK_MUTABLE(rcv);
RETURN_ENUMERATOR(rcv, 0, 0);
NSMutableArray *ary = [NSMutableArray new];
for (id key in rcv) {
@@ -288,19 +315,20 @@
}
RETURN_IF_BROKEN();
}
- [rcv removeObjectsForKeys:ary];
+ TRY_MOP([rcv removeObjectsForKeys:ary]);
return (VALUE)rcv;
}
static VALUE
nshash_select(id rcv, SEL sel)
{
+ CHECK_MUTABLE(rcv);
RETURN_ENUMERATOR(rcv, 0, 0);
NSMutableDictionary *dict = [NSMutableDictionary new];
for (id key in rcv) {
id value = [rcv objectForKey:key];
if (RTEST(rb_yield_values(2, OC2RB(key), OC2RB(value)))) {
- [dict setObject:value forKey:key];
+ TRY_MOP([dict setObject:value forKey:key]);
}
RETURN_IF_BROKEN();
}
@@ -334,6 +362,7 @@
nshash_update(id rcv, SEL sel, id hash)
{
hash = to_hash(hash);
+ CHECK_MUTABLE(rcv);
if (rb_block_given_p()) {
for (id key in hash) {
id value = [hash objectForKey:key];
@@ -342,13 +371,13 @@
value = RB2OC(rb_yield_values(3, OC2RB(key), OC2RB(old_value),
OC2RB(value)));
}
- [rcv setObject:value forKey:key];
+ TRY_MOP([rcv setObject:value forKey:key]);
}
}
else {
for (id key in hash) {
id value = [hash objectForKey:key];
- [rcv setObject:value forKey:key];
+ TRY_MOP([rcv setObject:value forKey:key]);
}
}
return rcv;
@@ -364,7 +393,8 @@
nshash_replace(id rcv, SEL sel, id hash)
{
hash = to_hash(hash);
- [rcv setDictionary:hash];
+ CHECK_MUTABLE(rcv);
+ TRY_MOP([rcv setDictionary:hash]);
return rcv;
}
Modified: MacRuby/trunk/NSString.m
===================================================================
--- MacRuby/trunk/NSString.m 2010-05-03 01:23:40 UTC (rev 4003)
+++ MacRuby/trunk/NSString.m 2010-05-03 02:13:40 UTC (rev 4004)
@@ -18,6 +18,27 @@
VALUE rb_cNSString;
VALUE rb_cNSMutableString;
+// Some NSString instances actually do not even respond to mutable methods.
+// So one way to know is to check if the setString: method exists.
+#define CHECK_MUTABLE(obj) \
+ do { \
+ if (![obj respondsToSelector:@selector(setString:)]) { \
+ rb_raise(rb_eRuntimeError, \
+ "can't modify frozen/immutable string"); \
+ } \
+ } \
+ while (0)
+
+// If a given mutable operation raises an NSException error,
+// it is likely that the object is not mutable.
+#define TRY_MOP(code) \
+ @try { \
+ code; \
+ } \
+ @catch(NSException *exc) { \
+ rb_raise(rb_eRuntimeError, "can't modify frozen/immutable string"); \
+ }
+
static inline VALUE
to_str(VALUE str)
{
@@ -59,16 +80,18 @@
static id
nsstr_replace(id rcv, SEL sel, VALUE other)
{
- [rcv setString:(id)to_str(other)];
+ CHECK_MUTABLE(rcv);
+ TRY_MOP([rcv setString:(id)to_str(other)]);
return rcv;
}
static id
nsstr_clear(id rcv, SEL sel)
{
+ CHECK_MUTABLE(rcv);
const long len = [rcv length];
if (len > 0) {
- [rcv deleteCharactersInRange:NSMakeRange(0, len)];
+ TRY_MOP([rcv deleteCharactersInRange:NSMakeRange(0, len)]);
}
return rcv;
}
@@ -95,7 +118,8 @@
static id
nsstr_concat(id rcv, SEL sel, VALUE other)
{
- [rcv appendString:(id)to_str(other)];
+ CHECK_MUTABLE(rcv);
+ TRY_MOP([rcv appendString:(id)to_str(other)]);
return rcv;
}
@@ -180,10 +204,11 @@
static VALUE
nsstr_forward_bang(id rcv, SEL sel, int argc, VALUE *argv)
{
+ CHECK_MUTABLE(rcv);
VALUE rcv_rstr = nsstr_to_rstr(rcv);
VALUE ret = rb_vm_call_with_cache2(rb_vm_get_call_cache(sel),
rb_vm_current_block(), rcv_rstr, 0, sel, argc, argv);
- [rcv setString:(id)rcv_rstr];
+ TRY_MOP([rcv setString:(id)rcv_rstr]);
return ret;
}
Deleted: MacRuby/trunk/spec/macruby/tags/macruby/core/array_tags.txt
===================================================================
--- MacRuby/trunk/spec/macruby/tags/macruby/core/array_tags.txt 2010-05-03 01:23:40 UTC (rev 4003)
+++ MacRuby/trunk/spec/macruby/tags/macruby/core/array_tags.txt 2010-05-03 02:13:40 UTC (rev 4004)
@@ -1 +0,0 @@
-critical:An NSArray object is immutable
Deleted: MacRuby/trunk/spec/macruby/tags/macruby/core/hash_tags.txt
===================================================================
--- MacRuby/trunk/spec/macruby/tags/macruby/core/hash_tags.txt 2010-05-03 01:23:40 UTC (rev 4003)
+++ MacRuby/trunk/spec/macruby/tags/macruby/core/hash_tags.txt 2010-05-03 02:13:40 UTC (rev 4004)
@@ -1 +0,0 @@
-critical:An NSDictionary object is immutable
Deleted: MacRuby/trunk/spec/macruby/tags/macruby/core/string_tags.txt
===================================================================
--- MacRuby/trunk/spec/macruby/tags/macruby/core/string_tags.txt 2010-05-03 01:23:40 UTC (rev 4003)
+++ MacRuby/trunk/spec/macruby/tags/macruby/core/string_tags.txt 2010-05-03 02:13:40 UTC (rev 4004)
@@ -1 +0,0 @@
-critical:An NSString object is immutable
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.macosforge.org/pipermail/macruby-changes/attachments/20100502/9624ab39/attachment-0001.html>
More information about the macruby-changes
mailing list