[macruby-changes] [3345] MacRuby/trunk

source_changes at macosforge.org source_changes at macosforge.org
Tue Jan 26 15:21:44 PST 2010


Revision: 3345
          http://trac.macosforge.org/projects/ruby/changeset/3345
Author:   lsansonetti at apple.com
Date:     2010-01-26 15:21:44 -0800 (Tue, 26 Jan 2010)
Log Message:
-----------
finished Hash refactoring

Modified Paths:
--------------
    MacRuby/trunk/hash.c
    MacRuby/trunk/include/ruby/intern.h
    MacRuby/trunk/include/ruby/ruby.h
    MacRuby/trunk/rakelib/builder.rb
    MacRuby/trunk/spec/macruby/tags/macruby/core/hash_tags.txt

Added Paths:
-----------
    MacRuby/trunk/NSDictionary.m

Added: MacRuby/trunk/NSDictionary.m
===================================================================
--- MacRuby/trunk/NSDictionary.m	                        (rev 0)
+++ MacRuby/trunk/NSDictionary.m	2010-01-26 23:21:44 UTC (rev 3345)
@@ -0,0 +1,610 @@
+/*
+ * MacRuby extensions to NSDictionary.
+ * 
+ * This file is covered by the Ruby license. See COPYING for more details.
+ *
+ * Copyright (C) 2010, Apple Inc. All rights reserved.
+ */
+
+#import <Foundation/Foundation.h>
+
+#include "ruby/ruby.h"
+#include "ruby/node.h"
+#include "objc.h"
+#include "vm.h"
+
+VALUE rb_cHash;
+VALUE rb_cNSHash;
+VALUE rb_cNSMutableHash;
+static VALUE rb_cCFHash;
+
+static id
+to_hash(id hash)
+{
+    return (id)rb_convert_type((VALUE)hash, T_HASH, "Hash", "to_hash");
+}
+
+static id
+nshash_dup(id rcv, SEL sel)
+{
+    id dup = [rcv mutableCopy];
+    if (OBJ_TAINTED(rcv)) {
+	OBJ_TAINT(dup);
+    }
+    return dup;
+}
+
+static id
+nshash_clone(id rcv, SEL sel)
+{
+    id clone = nshash_dup(rcv, 0);
+    if (OBJ_FROZEN(rcv)) {
+	OBJ_FREEZE(clone);
+    }
+    return clone;
+}
+
+static id
+nshash_rehash(id rcv, SEL sel)
+{
+    NSArray *keys = [rcv allKeys];
+    NSArray *values = [rcv allValues];
+    assert([keys count] == [values count]);
+    [rcv removeAllObjects];
+    for (unsigned i = 0, count = [keys count]; i < count; i++) {
+	[rcv setObject:[values objectAtIndex:i] forKey:[keys objectAtIndex:i]];
+    }
+    return rcv;
+}
+
+static id
+nshash_to_hash(id rcv, SEL sel)
+{
+    return rcv;
+}
+
+static id
+nshash_to_a(id rcv, SEL sel)
+{
+    NSMutableArray *ary = [NSMutableArray new];
+    for (id key in rcv) {
+	id value = [rcv objectForKey:key];
+	[ary addObject:[NSArray arrayWithObjects:key, value, nil]];
+    }
+    return ary;
+}
+
+static id
+nshash_inspect(id rcv, SEL sel)
+{
+    NSMutableString *str = [NSMutableString new];
+    [str appendString:@"{"];
+    for (id key in rcv) {
+	if ([str length] > 1) {
+	    [str appendString:@", "];
+	}
+	id value = [rcv objectForKey:key];
+	[str appendString:(NSString *)rb_inspect(OC2RB(key))];
+	[str appendString:@"=>"];
+	[str appendString:(NSString *)rb_inspect(OC2RB(value))];
+    }
+    [str appendString:@"}"];
+    return str;
+}
+
+static VALUE
+nshash_equal(id rcv, SEL sel, id other)
+{
+    return [rcv isEqualToDictionary:other] ? Qtrue : Qfalse;
+}
+
+static VALUE
+nshash_aref(id rcv, SEL sel, VALUE key)
+{
+    return OC2RB([rcv objectForKey:RB2OC(key)]);
+}
+
+static VALUE
+nshash_aset(id rcv, SEL sel, VALUE key, VALUE val)
+{
+    [rcv setObject:RB2OC(val) forKey:RB2OC(key)];
+    return val;
+}
+
+static VALUE
+nshash_fetch(id rcv, SEL sel, int argc, VALUE *argv)
+{
+    VALUE key, if_none;
+    rb_scan_args(argc, argv, "11", &key, &if_none);
+
+    const bool block_given = rb_block_given_p();
+    if (block_given && argc == 2) {
+	rb_warn("block supersedes default value argument");
+    }
+
+    id value = [rcv objectForKey:RB2OC(key)];
+    if (value != nil) {
+	return OC2RB(value);
+    }
+    if (block_given) {
+	return rb_yield(key);
+    }
+    if (argc == 1) {
+	rb_raise(rb_eKeyError, "key not found");
+    }
+    return if_none;
+}
+
+static VALUE
+nshash_default(id rcv, SEL sel, int argc, VALUE *argv)
+{
+    // TODO
+    return Qnil;
+}
+
+static VALUE
+nshash_set_default(id rcv, SEL sel, VALUE default_value)
+{
+    // TODO
+    return Qnil;
+}
+
+static VALUE
+nshash_default_proc(id rcv, SEL sel)
+{
+    // Default procs are never possible with NSDictionaries.
+    return Qnil;
+}
+
+static VALUE
+nshash_key(id rcv, SEL sel, VALUE value)
+{
+    NSArray *keys = [rcv allKeysForObject:RB2OC(value)];
+    if ([keys count] > 0) {
+	return OC2RB([keys objectAtIndex:0]);
+    }
+    return Qnil;
+}
+
+static VALUE
+nshash_index(id rcv, SEL sel, VALUE value)
+{
+    rb_warn("Hash#index is deprecated; use Hash#key");
+    return nshash_key(rcv, 0, value);
+}
+
+static VALUE
+nshash_size(id rcv, SEL sel)
+{
+    return LONG2FIX([rcv count]);
+}
+
+static VALUE
+nshash_empty(id rcv, SEL sel)
+{
+    return [rcv count] == 0 ? Qtrue : Qfalse;
+}
+
+static VALUE
+nshash_each_value(id rcv, SEL sel)
+{
+    RETURN_ENUMERATOR(rcv, 0, 0);
+    for (id key in rcv) {
+	rb_yield(OC2RB([rcv objectForKey:key]));
+    }
+    return (VALUE)rcv;
+}
+
+static VALUE
+nshash_each_key(id rcv, SEL sel)
+{
+    RETURN_ENUMERATOR(rcv, 0, 0);
+    for (id key in rcv) {
+	rb_yield(OC2RB(key));
+    }
+    return (VALUE)rcv;
+}
+
+static VALUE
+nshash_each_pair(id rcv, SEL sel)
+{
+    RETURN_ENUMERATOR(rcv, 0, 0);
+    for (id key in rcv) {
+	id value = [rcv objectForKey:key];
+	rb_yield(rb_assoc_new(OC2RB(key), OC2RB(value)));
+    }
+    return (VALUE)rcv;
+}
+
+static id
+nshash_keys(id rcv, SEL sel)
+{
+    return [[rcv allKeys] mutableCopy];
+}
+
+static id
+nshash_values(id rcv, SEL sel)
+{
+    return [[rcv allValues] mutableCopy];
+}
+
+static id
+nshash_values_at(id rcv, SEL sel, int argc, VALUE *argv)
+{
+    NSMutableArray *ary = [NSMutableArray new];
+    for (int i = 0; i < argc; i++) {
+	id value = [rcv objectForKey:RB2OC(argv[i])];
+	[ary addObject:value];
+    }
+    return ary;
+}
+
+static VALUE
+nshash_shift(id rcv, SEL sel)
+{
+    if ([rcv count] > 0) {
+	id key = [[rcv keyEnumerator] nextObject];
+	assert(key != NULL);
+	id value = [rcv objectForKey:key];
+	[rcv removeObjectForKey:key];
+	return rb_assoc_new(OC2RB(key), OC2RB(value));
+    }
+    return nshash_default(rcv, 0, 0, NULL);
+}
+
+static VALUE
+nshash_delete(id rcv, SEL sel, VALUE key)
+{
+    id ockey = RB2OC(key);
+    id value = [rcv objectForKey:ockey];
+    if (value != nil) {
+	[rcv removeObjectForKey:ockey];
+	return OC2RB(value);
+    }
+    if (rb_block_given_p()) {
+	return rb_yield(key);
+    }
+    return Qnil;
+}
+
+static VALUE
+nshash_delete_if(id rcv, SEL sel)
+{
+    RETURN_ENUMERATOR(rcv, 0, 0);
+    NSMutableArray *ary = [NSMutableArray new];
+    for (id key in rcv) {
+	id value = [rcv objectForKey:key];
+	if (RTEST(rb_yield_values(2, OC2RB(key), OC2RB(value)))) {
+	    [ary addObject:key];
+	}
+    }
+    [rcv removeObjectsForKeys:ary];
+    return (VALUE)rcv;
+}
+
+static VALUE
+nshash_select(id rcv, SEL sel)
+{
+    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];
+	}
+    }
+    return (VALUE)dict;
+}
+
+static VALUE
+nshash_reject(id rcv, SEL sel)
+{
+    RETURN_ENUMERATOR(rcv, 0, 0);
+    return nshash_delete_if([rcv mutableCopy], 0); 
+}
+
+static VALUE
+nshash_reject_bang(id rcv, SEL sel)
+{
+    RETURN_ENUMERATOR(rcv, 0, 0);
+    unsigned size = [rcv count];
+    nshash_delete_if(rcv, 0);
+    return size != [rcv count] ? (VALUE)rcv : Qnil;
+}
+
+static id
+nshash_clear(id rcv, SEL sel)
+{
+    [rcv removeAllObjects];
+    return rcv;
+}
+
+static id
+nshash_update(id rcv, SEL sel, id hash)
+{
+    hash = to_hash(hash);
+    if (rb_block_given_p()) {
+	for (id key in hash) {
+	    id value = [hash objectForKey:key];
+	    id old_value = [rcv objectForKey:key];
+	    if (old_value != nil) {
+		value = RB2OC(rb_yield_values(3, OC2RB(key), OC2RB(old_value),
+			    OC2RB(value)));
+	    }
+	    [rcv setObject:value forKey:key];
+	}
+    }
+    else {
+	for (id key in hash) {
+	    id value = [hash objectForKey:key];
+	    [rcv setObject:value forKey:key];
+	}
+    }    
+    return rcv;
+}
+
+static id
+nshash_merge(id rcv, SEL sel, id hash)
+{
+    return nshash_update([rcv mutableCopy], 0, hash);
+}
+
+static id
+nshash_replace(id rcv, SEL sel, id hash)
+{
+    hash = to_hash(hash);
+    [rcv setDictionary:hash];
+    return rcv;
+}
+
+static VALUE
+nshash_assoc(id rcv, SEL sel, VALUE obj)
+{
+    for (id key in rcv) {
+	if (rb_equal(OC2RB(key), obj)) {
+	    id value = [rcv objectForKey:key];
+	    return rb_assoc_new(obj, OC2RB(value));
+	}
+    }
+    return Qnil;
+}
+
+static VALUE
+nshash_rassoc(id rcv, SEL sel, VALUE obj)
+{
+    for (id key in rcv) {
+	id value = [rcv objectForKey:key];
+	if (rb_equal(OC2RB(value), obj)) {
+	    return rb_assoc_new(OC2RB(key), obj);
+	}
+    }
+    return Qnil;
+}
+
+static id
+nshash_flatten(id rcv, SEL sel, int argc, VALUE *argv)
+{
+    id ary = nshash_to_a(rcv, 0);
+    VALUE tmp;
+    if (argc == 0) {
+	argc = 1;
+	tmp = INT2FIX(1);
+	argv = &tmp;
+    }
+    rb_vm_call((VALUE)ary, sel_registerName("flatten!:"), argc, argv, false);
+    return ary;
+}
+
+static VALUE
+nshash_has_key(id rcv, SEL sel, VALUE key)
+{
+    return [rcv objectForKey:RB2OC(key)] == nil ? Qfalse : Qtrue;
+}
+
+static VALUE
+nshash_has_value(id rcv, SEL sel, VALUE value)
+{
+    return [[rcv allKeysForObject:RB2OC(value)] count] == 0 ? Qfalse : Qtrue;
+}
+
+static id
+nshash_compare_by_id(id rcv, SEL sel)
+{
+    // Not implemented.
+    return rcv;
+}
+
+static VALUE
+nshash_compare_by_id_p(id rcv, SEL sel)
+{
+    // Not implemented.
+    return Qfalse;
+}
+
+void
+Init_NSDictionary(void)
+{
+#if MAC_OS_X_VERSION_MAX_ALLOWED < 1070
+    rb_cCFHash = (VALUE)objc_getClass("NSCFDictionary");
+#else
+    rb_cCFHash = (VALUE)objc_getClass("__NSCFDictionary");
+#endif
+    assert(rb_cCFHash != 0);
+    rb_cNSHash = (VALUE)objc_getClass("NSDictionary");
+    assert(rb_cNSHash != 0);
+    rb_cHash = rb_cNSHash;
+    rb_cNSMutableHash = (VALUE)objc_getClass("NSMutableDictionary");
+    assert(rb_cNSMutableHash != 0);
+
+    rb_include_module(rb_cHash, rb_mEnumerable);
+
+    rb_objc_define_method(rb_cHash, "dup", nshash_dup, 0);
+    rb_objc_define_method(rb_cHash, "clone", nshash_clone, 0);
+    rb_objc_define_method(rb_cHash, "rehash", nshash_rehash, 0);
+    rb_objc_define_method(rb_cHash, "to_hash", nshash_to_hash, 0);
+    rb_objc_define_method(rb_cHash, "to_a", nshash_to_a, 0);
+    rb_objc_define_method(rb_cHash, "to_s", nshash_inspect, 0);
+    rb_objc_define_method(rb_cHash, "inspect", nshash_inspect, 0);
+    rb_objc_define_method(rb_cHash, "==", nshash_equal, 1);
+    rb_objc_define_method(rb_cHash, "eql?", nshash_equal, 1);
+    rb_objc_define_method(rb_cHash, "[]", nshash_aref, 1);
+    rb_objc_define_method(rb_cHash, "[]=", nshash_aset, 2);
+    rb_objc_define_method(rb_cHash, "fetch", nshash_fetch, -1);
+    rb_objc_define_method(rb_cHash, "store", nshash_aset, 2);
+    rb_objc_define_method(rb_cHash, "default", nshash_default, -1);
+    rb_objc_define_method(rb_cHash, "default=", nshash_set_default, 1);
+    rb_objc_define_method(rb_cHash, "default_proc", nshash_default_proc, 0);
+    rb_objc_define_method(rb_cHash, "key", nshash_key, 1);
+    rb_objc_define_method(rb_cHash, "index", nshash_index, 1);
+    rb_objc_define_method(rb_cHash, "size", nshash_size, 0);
+    rb_objc_define_method(rb_cHash, "length", nshash_size, 0);
+    rb_objc_define_method(rb_cHash, "empty?", nshash_empty, 0);
+    rb_objc_define_method(rb_cHash, "each_value", nshash_each_value, 0);
+    rb_objc_define_method(rb_cHash, "each_key", nshash_each_key, 0);
+    rb_objc_define_method(rb_cHash, "each_pair", nshash_each_pair, 0);
+    rb_objc_define_method(rb_cHash, "each", nshash_each_pair, 0);
+    rb_objc_define_method(rb_cHash, "keys", nshash_keys, 0);
+    rb_objc_define_method(rb_cHash, "values", nshash_values, 0);
+    rb_objc_define_method(rb_cHash, "values_at", nshash_values_at, -1);
+    rb_objc_define_method(rb_cHash, "shift", nshash_shift, 0);
+    rb_objc_define_method(rb_cHash, "delete", nshash_delete, 1);
+    rb_objc_define_method(rb_cHash, "delete_if", nshash_delete_if, 0);
+    rb_objc_define_method(rb_cHash, "select", nshash_select, 0);
+    rb_objc_define_method(rb_cHash, "reject", nshash_reject, 0);
+    rb_objc_define_method(rb_cHash, "reject!", nshash_reject_bang, 0);
+    rb_objc_define_method(rb_cHash, "clear", nshash_clear, 0);
+    // XXX: #invert is a private method on NSMutableDictionary, so to not
+    // break things we do not implement it.
+    rb_objc_define_method(rb_cHash, "update", nshash_update, 1);
+    rb_objc_define_method(rb_cHash, "merge!", nshash_update, 1);
+    rb_objc_define_method(rb_cHash, "merge", nshash_merge, 1);
+    rb_objc_define_method(rb_cHash, "replace", nshash_replace, 1);
+    rb_objc_define_method(rb_cHash, "assoc", nshash_assoc, 1);
+    rb_objc_define_method(rb_cHash, "rassoc", nshash_rassoc, 1);
+    rb_objc_define_method(rb_cHash, "flatten", nshash_flatten, -1);
+    rb_objc_define_method(rb_cHash, "include?", nshash_has_key, 1);
+    rb_objc_define_method(rb_cHash, "member?", nshash_has_key, 1);
+    rb_objc_define_method(rb_cHash, "key?", nshash_has_key, 1);
+    rb_objc_define_method(rb_cHash, "has_key?", nshash_has_key, 1);
+    rb_objc_define_method(rb_cHash, "value?", nshash_has_value, 1);
+    rb_objc_define_method(rb_cHash, "has_value?", nshash_has_value, 1);
+    rb_objc_define_method(rb_cHash, "compare_by_identity",
+	    nshash_compare_by_id, 0);
+    rb_objc_define_method(rb_cHash, "compare_by_identity?",
+	    nshash_compare_by_id_p, 0);
+}
+
+// NSDictionary + NSMutableDictionary primitives. These are added automatically
+// on singleton classes of pure NSDictionaries. Our implementation just calls
+// the original methods, by tricking the receiver's class.
+
+#define PREPARE_RCV(x) \
+    Class __old = *(Class *)x; \
+    *(Class *)x = (Class)rb_cCFHash;
+
+#define RESTORE_RCV(x) \
+    *(Class *)x = __old;
+
+static unsigned
+nshash_count(id rcv, SEL sel) 
+{
+    PREPARE_RCV(rcv);
+    const unsigned count = [rcv count];
+    RESTORE_RCV(rcv);
+    return count; 
+}
+
+static id
+nshash_keyEnumerator(id rcv, SEL sel)
+{
+    PREPARE_RCV(rcv);
+    id keys = [rcv allKeys]; 
+    RESTORE_RCV(rcv);
+    return [keys objectEnumerator];
+}
+
+static id
+nshash_objectForKey(id rcv, SEL sel, id key)
+{
+    PREPARE_RCV(rcv);
+    id value = [rcv objectForKey:key];
+    RESTORE_RCV(rcv);
+    return value;
+}
+
+static void 
+nshash_setObjectForKey(id rcv, SEL sel, id value, id key) 
+{
+    PREPARE_RCV(rcv);
+    [rcv setObject:value forKey:key];
+    RESTORE_RCV(rcv);
+} 
+
+static void
+nshash_getObjectsAndKeys(id rcv, SEL sel, id *objs, id *keys)
+{
+    PREPARE_RCV(rcv);
+    [rcv getObjects:objs andKeys:keys];
+    RESTORE_RCV(rcv);
+}
+
+static void
+nshash_removeObjectForKey(id rcv, SEL sel, id key)
+{
+    PREPARE_RCV(rcv);
+    [rcv removeObjectForKey:key];
+    RESTORE_RCV(rcv);
+}
+
+static void
+nshash_removeAllObjects(id rcv, SEL sel)
+{
+    PREPARE_RCV(rcv);
+    [rcv removeAllObjects];
+    RESTORE_RCV(rcv);
+}
+
+static bool
+nshash_isEqual(id rcv, SEL sel, id other)
+{
+    PREPARE_RCV(rcv);
+    const bool res = [rcv isEqualToDictionary:other];
+    RESTORE_RCV(rcv);
+    return res;
+}
+
+static bool
+nshash_containsObject(id rcv, SEL sel, id value)
+{
+    PREPARE_RCV(rcv);
+    const bool res = [[rcv allKeysForObject:value] count] > 0;
+    RESTORE_RCV(rcv);
+    return res;
+}
+
+void
+rb_objc_install_hash_primitives(Class klass)
+{
+    rb_objc_install_method2(klass, "count", (IMP)nshash_count);
+    rb_objc_install_method2(klass, "keyEnumerator", (IMP)nshash_keyEnumerator);
+    rb_objc_install_method2(klass, "objectForKey:", (IMP)nshash_objectForKey);
+    rb_objc_install_method2(klass, "getObjects:andKeys:",
+	    (IMP)nshash_getObjectsAndKeys);
+    rb_objc_install_method2(klass, "isEqual:", (IMP)nshash_isEqual);
+    rb_objc_install_method2(klass, "containsObject:",
+	    (IMP)nshash_containsObject);
+
+    const bool mutable =
+	class_getSuperclass(klass) == (Class)rb_cNSMutableHash; 
+
+    if (mutable) {
+	rb_objc_install_method2(klass, "setObject:forKey:",
+		(IMP)nshash_setObjectForKey);
+	rb_objc_install_method2(klass, "removeObjectForKey:",
+		(IMP)nshash_removeObjectForKey);
+	rb_objc_install_method2(klass, "removeAllObjects",
+		(IMP)nshash_removeAllObjects);
+    }
+
+    //rb_objc_define_method(*(VALUE *)klass, "alloc", hash_alloc, 0);
+}

Modified: MacRuby/trunk/hash.c
===================================================================
--- MacRuby/trunk/hash.c	2010-01-26 23:20:58 UTC (rev 3344)
+++ MacRuby/trunk/hash.c	2010-01-26 23:21:44 UTC (rev 3345)
@@ -20,7 +20,7 @@
 
 #include <crt_externs.h>
 
-static VALUE rb_hash_s_try_convert(VALUE, SEL, VALUE);
+static VALUE rhash_try_convert(VALUE, SEL, VALUE);
 
 VALUE
 rb_hash_freeze(VALUE hash)
@@ -28,10 +28,6 @@
     return rb_obj_freeze(hash);
 }
 
-VALUE rb_cHash;
-VALUE rb_cCFHash;
-VALUE rb_cNSHash;
-VALUE rb_cNSMutableHash;
 VALUE rb_cRubyHash;
 
 typedef struct {
@@ -41,19 +37,17 @@
     bool has_proc_default; 
 } rb_hash_t;
 
-//#define IS_RHASH(x) (*(VALUE *)x == rb_cRubyHash)
-// XXX temporary
-#define IS_RHASH(x) __is_rhash(*(VALUE *)x)
+#define IS_RHASH(x) __klass_is_rhash(*(VALUE *)x)
 #define RHASH(x) ((rb_hash_t *)x)
 
-static force_inline bool
-__is_rhash(VALUE k)
+static inline bool
+__klass_is_rhash(VALUE k)
 {
     while (k != 0) {
 	if (k == rb_cRubyHash) {
 	    return true;
 	}
-	if (k == rb_cCFHash) {
+	if (k == rb_cNSHash) {
 	    return false;
 	}
 	k = RCLASS_SUPER(k);
@@ -64,7 +58,7 @@
 bool
 rb_klass_is_rhash(VALUE klass)
 {
-    return __is_rhash(klass);
+    return __klass_is_rhash(klass);
 }
 
 static VALUE envtbl;
@@ -72,9 +66,16 @@
 
 static void *defaultCache = NULL;
 static void *hashCache = NULL;
+static SEL selFlattenBang = 0;
 static SEL selDefault = 0;
 static SEL selHash = 0;
 
+static inline long
+rhash_len(VALUE hash)
+{
+    return RHASH(hash)->tbl->num_entries;
+}
+
 VALUE
 rb_hash(VALUE obj)
 {
@@ -123,11 +124,19 @@
     st_foreach(table, foreach_safe_i, (st_data_t)&arg);
 }
 
+static void
+rhash_iterate(VALUE hash, int (*func)(ANYARGS), VALUE farg)
+{
+    st_foreach_safe(RHASH(hash)->tbl, func, (st_data_t)farg);
+}
+
+#define rhash_foreach rhash_iterate
+
 void
 rb_hash_foreach(VALUE hash, int (*func)(ANYARGS), VALUE farg)
 {
     if (IS_RHASH(hash)) {
-	st_foreach_safe(RHASH(hash)->tbl, func, (st_data_t)farg);
+	rhash_foreach(hash, func, farg);
     }
     else {
 	CFIndex count = CFDictionaryGetCount((CFDictionaryRef)hash);
@@ -163,7 +172,7 @@
 rb_hash_iterate(VALUE hash, int (*func)(ANYARGS), VALUE farg)
 {
     if (IS_RHASH(hash)) {
-	st_foreach_safe(RHASH(hash)->tbl, func, (st_data_t)farg);
+	rhash_iterate(hash, func, farg);
     }
     else {
 	struct rb_cfhash_iterate_context ctx = {func, farg};
@@ -210,8 +219,6 @@
 	    return CFHash((CFTypeRef)a);
     }
 
-    // XXX optimize for string
-
     return (int)FIX2LONG(rb_hash(a));
 }
 
@@ -221,108 +228,87 @@
 };
 
 static VALUE
-hash_alloc(VALUE klass)
+rhash_alloc(VALUE klass, SEL sel)
 {
-    if (rb_cRubyHash != 0 && (klass == 0 || __is_rhash(klass))) {
-	NEWOBJ(hash, rb_hash_t);
-	hash->basic.flags = 0;
-	hash->basic.klass = klass == 0 ? rb_cRubyHash : klass;
-	GC_WB(&hash->tbl, st_init_table(&objhash));
-	hash->ifnone = Qnil;
-	hash->has_proc_default = false;
-	return (VALUE)hash;
+    NEWOBJ(hash, rb_hash_t);
+    hash->basic.flags = 0;
+    hash->basic.klass = klass;
+    GC_WB(&hash->tbl, st_init_table(&objhash));
+    hash->ifnone = Qnil;
+    hash->has_proc_default = false;
+    return (VALUE)hash;
+}
+
+static VALUE
+rhash_dup(VALUE rcv, SEL sel)
+{
+    NEWOBJ(dup, rb_hash_t);
+    dup->basic.flags = 0;
+    dup->basic.klass = rb_cRubyHash;
+    GC_WB(&dup->tbl, st_copy(RHASH(rcv)->tbl));
+    GC_WB(&dup->ifnone, RHASH(rcv)->ifnone);
+    dup->has_proc_default = RHASH(rcv)->has_proc_default;
+    if (OBJ_TAINTED(rcv)) {
+	OBJ_TAINT(dup);
     }
-    else {
-	CFMutableDictionaryRef hash = CFDictionaryCreateMutable(NULL, 0,
-		&kCFTypeDictionaryKeyCallBacks,
-		&kCFTypeDictionaryValueCallBacks);
-	if (klass != 0 && klass != rb_cNSHash && klass != rb_cNSMutableHash) {
-	    *(Class *)hash = (Class)klass;
-	}
-	CFMakeCollectable(hash);
-	return (VALUE)hash;
+    return (VALUE)dup;
+}
+
+static VALUE
+rhash_clone(VALUE rcv, SEL sel)
+{
+    VALUE clone = rhash_dup(rcv, 0);
+    if (OBJ_FROZEN(rcv)) {
+	OBJ_FREEZE(clone);
     }
+    return clone;
 }
 
 VALUE
 rb_hash_dup(VALUE rcv)
 {
     if (IS_RHASH(rcv)) {
-	NEWOBJ(dup, rb_hash_t);
-	dup->basic.flags = 0;
-	dup->basic.klass = rb_cRubyHash;
-	GC_WB(&dup->tbl, st_copy(RHASH(rcv)->tbl));
-	GC_WB(&dup->ifnone, RHASH(rcv)->ifnone);
-	dup->has_proc_default = RHASH(rcv)->has_proc_default;
-	return (VALUE)dup;
+	return rhash_dup(rcv, 0);
     }
     else {
 	VALUE dup = (VALUE)CFDictionaryCreateMutableCopy(NULL, 0,
 		(CFDictionaryRef)rcv);
 	CFMakeCollectable((CFTypeRef)dup);
-	if (OBJ_TAINTED(rcv)) {
-	    OBJ_TAINT(dup);
-	}
 	return dup;
     }
 }
 
-static VALUE
-rb_hash_dup_imp(VALUE rcv, SEL sel)
-{
-    return rb_hash_dup(rcv);
-}
-
-static VALUE
-rb_hash_clone(VALUE rcv, SEL sel)
-{
-    VALUE clone = rb_hash_dup(rcv);
-    if (OBJ_FROZEN(rcv)) {
-	OBJ_FREEZE(clone);
-    }
-    return clone;
-}
-
 VALUE
 rb_hash_new(void)
 {
-    return hash_alloc(0);
+    return rhash_alloc(rb_cRubyHash, 0);
 }
 
+static VALUE rhash_aset(VALUE hash, SEL sel, VALUE key, VALUE val);
+
 VALUE
 rb_hash_new_fast(int argc, ...)
 {
     assert(argc % 2 == 0);
 
-    VALUE hash = hash_alloc(0);
+    VALUE hash = rhash_alloc(rb_cRubyHash, 0);
 
     va_list ar;
     va_start(ar, argc);
     for (int i = 0; i < argc; i += 2) {
 	VALUE key = va_arg(ar, VALUE);
 	VALUE val = va_arg(ar, VALUE);
-	rb_hash_aset(hash, key, val);
+	rhash_aset(hash, 0, key, val);
     }
     va_end(ar);
 
     return hash;
 }
 
-bool _CFDictionaryIsMutable(void *);
-
 static inline void
-rb_hash_modify_check(VALUE hash)
+rhash_modify(VALUE hash)
 {
-    long mask;
-    if (IS_RHASH(hash)) {
-	mask = RBASIC(hash)->flags;
-    }
-    else {
-	mask = rb_objc_flag_get_mask((const void *)hash);
-	if (!_CFDictionaryIsMutable((void *)hash)) {
-	    mask |= FL_FREEZE;
-	}
-    }
+    const long mask = RBASIC(hash)->flags;
     if ((mask & FL_FREEZE) == FL_FREEZE) {
 	rb_raise(rb_eRuntimeError, "can't modify frozen/immutable hash");
     }
@@ -331,8 +317,6 @@
     }
 }
 
-#define rb_hash_modify rb_hash_modify_check
-
 /*
  *  call-seq:
  *     Hash.new                          => hash
@@ -369,42 +353,54 @@
  */
 
 static VALUE
-rb_hash_initialize(VALUE hash, SEL sel, int argc, const VALUE *argv)
+rhash_initialize(VALUE hash, SEL sel, int argc, const VALUE *argv)
 {
-    rb_hash_modify(hash);
-
-    //hash = (VALUE)objc_msgSend((id)hash, selInit);
-
-    if (IS_RHASH(hash)) {
-	if (rb_block_given_p()) {
-	    if (argc > 0) {
-		rb_raise(rb_eArgError, "wrong number of arguments");
-	    }
-	    GC_WB(&RHASH(hash)->ifnone, rb_block_proc());
-	    RHASH(hash)->has_proc_default = true;
+    rhash_modify(hash);
+    if (rb_block_given_p()) {
+	if (argc > 0) {
+	    rb_raise(rb_eArgError, "wrong number of arguments");
 	}
-	else {
-	    VALUE ifnone;
-	    rb_scan_args(argc, argv, "01", &ifnone);
-	    if (ifnone != Qnil) {
-		GC_WB(&RHASH(hash)->ifnone, ifnone);
-	    }
+	GC_WB(&RHASH(hash)->ifnone, rb_block_proc());
+	RHASH(hash)->has_proc_default = true;
+    }
+    else {
+	VALUE ifnone;
+	rb_scan_args(argc, argv, "01", &ifnone);
+	if (ifnone != Qnil) {
+	    GC_WB(&RHASH(hash)->ifnone, ifnone);
 	}
     }
-
     return hash;
 }
 
 VALUE
 rb_hash_new2(int argc, const VALUE *argv)
 {
-    VALUE h = hash_alloc(0);
-    rb_hash_initialize(h, 0, argc, argv);
+    VALUE h = rhash_alloc(rb_cRubyHash, 0);
+    rhash_initialize(h, 0, argc, argv);
     return h;
 }
 
 /*
  *  call-seq:
+ *     Hash.try_convert(obj) -> hash or nil
+ *
+ *  Try to convert <i>obj</i> into a hash, using to_hash method.
+ *  Returns converted hash or nil if <i>obj</i> cannot be converted
+ *  for any reason.
+ *
+ *     Hash.try_convert({1=>2})   # => {1=>2}
+ *     Hash.try_convert("1=>2")   # => nil
+ */
+
+static VALUE
+rhash_try_convert(VALUE dummy, SEL sel, VALUE hash)
+{
+    return rb_check_convert_type(hash, T_HASH, "Hash", "to_hash");
+}
+
+/*
+ *  call-seq:
  *     Hash[ [key =>|, value]* ]   => hash
  *
  *  Creates a new hash populated with the given objects. Equivalent to
@@ -417,12 +413,12 @@
  */
 
 static VALUE
-rb_hash_s_create(VALUE klass, SEL sel, int argc, VALUE *argv)
+rhash_create(VALUE klass, SEL sel, int argc, VALUE *argv)
 {
     if (argc == 1) {
-	VALUE tmp = rb_hash_s_try_convert(Qnil, 0, argv[0]);
+	VALUE tmp = rhash_try_convert(Qnil, 0, argv[0]);
 	if (!NIL_P(tmp)) {
-	    VALUE hash = hash_alloc(klass);
+	    VALUE hash = rhash_alloc(klass, 0);
 	    if (IS_RHASH(hash) && IS_RHASH(tmp)) {
 		GC_WB(&RHASH(hash)->tbl, st_copy(RHASH(tmp)->tbl));
 	    }
@@ -451,13 +447,14 @@
 
 	tmp = rb_check_array_type(argv[0]);
 	if (!NIL_P(tmp)) {
-	    VALUE hash = hash_alloc(klass);
+	    VALUE hash = rhash_alloc(klass, 0);
 	    for (int i = 0; i < RARRAY_LEN(tmp); ++i) {
 		VALUE v = rb_check_array_type(RARRAY_AT(tmp, i));
 		if (NIL_P(v)) {
 		    continue;
 		}
-		if (RARRAY_LEN(v) < 1 || 2 < RARRAY_LEN(v)) {
+		const long len = RARRAY_LEN(v);
+		if (len < 1 || 2 < len) {
 		    continue;
 		}
 		rb_hash_aset(hash, RARRAY_AT(v, 0), RARRAY_AT(v, 1));
@@ -469,7 +466,7 @@
 	rb_raise(rb_eArgError, "odd number of arguments for Hash");
     }
 
-    VALUE hash = hash_alloc(klass);
+    VALUE hash = rhash_alloc(klass, 0);
     for (int i = 0; i < argc; i += 2) {
         rb_hash_aset(hash, argv[i], argv[i + 1]);
     }
@@ -485,23 +482,6 @@
 
 /*
  *  call-seq:
- *     Hash.try_convert(obj) -> hash or nil
- *
- *  Try to convert <i>obj</i> into a hash, using to_hash method.
- *  Returns converted hash or nil if <i>obj</i> cannot be converted
- *  for any reason.
- *
- *     Hash.try_convert({1=>2})   # => {1=>2}
- *     Hash.try_convert("1=>2")   # => nil
- */
-static VALUE
-rb_hash_s_try_convert(VALUE dummy, SEL sel, VALUE hash)
-{
-    return rb_check_convert_type(hash, T_HASH, "Hash", "to_hash");
-}
-
-/*
- *  call-seq:
  *     hsh.rehash -> hsh
  *
  *  Rebuilds the hash based on the current hash values for each key. If
@@ -521,7 +501,7 @@
  */
 
 static int
-rb_hash_rehash_i(VALUE key, VALUE value, VALUE arg)
+rhash_rehash_i(VALUE key, VALUE value, VALUE arg)
 {
     st_table *tbl = (st_table *)arg;
     if (key != Qundef) {
@@ -531,33 +511,14 @@
 }
 
 static VALUE
-rb_hash_rehash(VALUE hash, SEL sel)
+rhash_rehash(VALUE hash, SEL sel)
 {
-    rb_hash_modify_check(hash);
-    if (IS_RHASH(hash)) {
-	st_table *tbl = st_init_table_with_size(RHASH(hash)->tbl->type,
-		RHASH(hash)->tbl->num_entries);
-	rb_hash_foreach(hash, rb_hash_rehash_i, (VALUE)tbl);
-	//st_free_table(RHASH(hash)->tbl);
-	GC_WB(&RHASH(hash)->tbl, tbl);
-    }
-    else {
-	CFIndex count = CFDictionaryGetCount((CFDictionaryRef)hash);
-	if (count == 0) {
-	    return hash;
-	}
-
-	const void **keys = (const void **)alloca(sizeof(void *) * count);
-	const void **values = (const void **)alloca(sizeof(void *) * count);
-
-	CFDictionaryGetKeysAndValues((CFDictionaryRef)hash, keys, values);
-	CFDictionaryRemoveAllValues((CFMutableDictionaryRef)hash);
-
-	for (CFIndex i = 0; i < count; i++) {
-	    CFDictionarySetValue((CFMutableDictionaryRef)hash,
-		    (const void *)keys[i], (const void *)values[i]);
-	}
-    }
+    rhash_modify(hash);
+    st_table *tbl = st_init_table_with_size(RHASH(hash)->tbl->type,
+	    RHASH(hash)->tbl->num_entries);
+    rb_hash_foreach(hash, rhash_rehash_i, (VALUE)tbl);
+    //st_free_table(RHASH(hash)->tbl);
+    GC_WB(&RHASH(hash)->tbl, tbl);
     return hash;
 }
 
@@ -575,63 +536,63 @@
  *
  */
 
+static inline VALUE
+rhash_lookup(VALUE hash, VALUE key)
+{
+    VALUE val;
+    if (st_lookup(RHASH(hash)->tbl, key, &val)) {
+	return val;
+    }
+    return Qundef;
+}
+
 VALUE
-rb_hash_aref(VALUE hash, VALUE key)
+rb_hash_lookup(VALUE hash, VALUE key)
 {
+    VALUE val;
     if (IS_RHASH(hash)) {
-	VALUE val;
-	if (!st_lookup(RHASH(hash)->tbl, key, &val)) {
-	    if (*(VALUE *)hash == rb_cRubyHash
-		    && RHASH(hash)->ifnone == Qnil) {
-		return Qnil;
-	    }
-	    return rb_vm_call_with_cache(defaultCache, hash, selDefault,
-		    1, &key);
-	}
-	return val;
+	val = rhash_lookup(hash, key);
     }
     else {
-	VALUE val;
 	if (!CFDictionaryGetValueIfPresent((CFDictionaryRef)hash,
 		    (const void *)RB2OC(key), (const void **)&val)) {
-	    return Qnil;
+	    val = Qundef;
 	}
-	return OC2RB(val);
     }
+    return val == Qundef ? Qnil : val;
 }
 
 static VALUE
-rb_hash_aref_imp(VALUE hash, SEL sel, VALUE key)
+rhash_aref(VALUE hash, SEL sel, VALUE key)
 {
-    return rb_hash_aref(hash, key);
+    VALUE val = rhash_lookup(hash, key);
+    if (val == Qundef) {
+	if (*(VALUE *)hash == rb_cRubyHash
+		&& RHASH(hash)->ifnone == Qnil) {
+	    return Qnil;
+	}
+	return rb_vm_call_with_cache(defaultCache, hash, selDefault,
+		1, &key);
+    }
+    return val;
 }
 
-static VALUE
-rb_hash_lookup0(VALUE hash, VALUE key)
+VALUE
+rb_hash_aref(VALUE hash, VALUE key)
 {
     if (IS_RHASH(hash)) {
-	VALUE val;
-	if (st_lookup(RHASH(hash)->tbl, key, &val)) {
-	    return val;
-	}
+	return rhash_aref(hash, 0, key);
     }
     else {
 	VALUE val;
-	if (CFDictionaryGetValueIfPresent((CFDictionaryRef)hash,
+	if (!CFDictionaryGetValueIfPresent((CFDictionaryRef)hash,
 		    (const void *)RB2OC(key), (const void **)&val)) {
-	    return OC2RB(val);
+	    return Qnil;
 	}
+	return OC2RB(val);
     }
-    return Qundef;
 }
 
-VALUE
-rb_hash_lookup(VALUE hash, VALUE key)
-{
-    VALUE v = rb_hash_lookup0(hash, key);
-    return v == Qundef ? Qnil : v;
-}
-
 /*
  *  call-seq:
  *     hsh.fetch(key [, default] )       => obj
@@ -662,7 +623,7 @@
  */
 
 static VALUE
-rb_hash_fetch(VALUE hash, SEL sel, int argc, VALUE *argv)
+rhash_fetch(VALUE hash, SEL sel, int argc, VALUE *argv)
 {
     VALUE key, if_none;
     rb_scan_args(argc, argv, "11", &key, &if_none);
@@ -672,7 +633,7 @@
 	rb_warn("block supersedes default value argument");
     }
 
-    VALUE v = rb_hash_lookup0(hash, key);
+    VALUE v = rhash_lookup(hash, key);
     if (v != Qundef) {
 	return v;
     }
@@ -707,21 +668,18 @@
  */
 
 static VALUE
-rb_hash_default(VALUE hash, SEL sel, int argc, VALUE *argv)
+rhash_default(VALUE hash, SEL sel, int argc, VALUE *argv)
 {
     VALUE key;
     rb_scan_args(argc, argv, "01", &key);
 
-    if (IS_RHASH(hash)) {
-	if (RHASH(hash)->has_proc_default) {
-	    if (argc == 0) {
-		return Qnil;
-	    }
-	    return rb_funcall(RHASH(hash)->ifnone, id_yield, 2, hash, key);
+    if (RHASH(hash)->has_proc_default) {
+	if (argc == 0) {
+	    return Qnil;
 	}
-	return RHASH(hash)->ifnone;
+	return rb_funcall(RHASH(hash)->ifnone, id_yield, 2, hash, key);
     }
-    return Qnil;
+    return RHASH(hash)->ifnone;
 }
 
 /*
@@ -745,20 +703,21 @@
  */
 
 static VALUE
-rb_hash_set_default(VALUE hash, SEL sel, VALUE ifnone)
+rhash_set_default(VALUE hash, SEL sel, VALUE ifnone)
 {
-    rb_hash_modify(hash);
-    if (IS_RHASH(hash)) {
-	GC_WB(&RHASH(hash)->ifnone, ifnone);
-	RHASH(hash)->has_proc_default = false;
-    }
+    rhash_modify(hash);
+    GC_WB(&RHASH(hash)->ifnone, ifnone);
+    RHASH(hash)->has_proc_default = false;
     return ifnone;
 }
 
 VALUE
 rb_hash_set_ifnone(VALUE hash, VALUE ifnone)
 {
-    return rb_hash_set_default(hash, 0, ifnone);
+    if (IS_RHASH(hash)) {
+	rhash_set_default(hash, 0, ifnone);
+    }
+    return ifnone;
 }
 
 /*
@@ -775,26 +734,12 @@
  *     a                                  #=> [nil, nil, 4]
  */
 
-
 static VALUE
-rb_hash_default_proc(VALUE hash, SEL sel)
+rhash_default_proc(VALUE hash, SEL sel)
 {
-    if (IS_RHASH(hash)) {
-	return RHASH(hash)->has_proc_default ? RHASH(hash)->ifnone : Qnil;
-    }
-    return Qnil;
+    return RHASH(hash)->has_proc_default ? RHASH(hash)->ifnone : Qnil;
 }
 
-static int
-key_i(VALUE key, VALUE value, VALUE *args)
-{
-    if (rb_equal(value, args[0])) {
-	args[1] = key;
-	return ST_STOP;
-    }
-    return ST_CONTINUE;
-}
-
 /*
  *  call-seq:
  *     hsh.key(value)    => key
@@ -807,30 +752,66 @@
  *
  */
 
+static int
+key_i(VALUE key, VALUE value, VALUE *args)
+{
+    if (rb_equal(value, args[0])) {
+	args[1] = key;
+	return ST_STOP;
+    }
+    return ST_CONTINUE;
+}
+
 static VALUE
-rb_hash_key(VALUE hash, SEL sel, VALUE value)
+rhash_key(VALUE hash, SEL sel, VALUE value)
 {
     VALUE args[2] = {value, Qnil};
-    rb_hash_iterate(hash, key_i, (st_data_t)args);
+    rhash_iterate(hash, key_i, (st_data_t)args);
     return args[1];
 }
 
 /* :nodoc: */
 static VALUE
-rb_hash_index(VALUE hash, SEL sel, VALUE value)
+rhash_index(VALUE hash, SEL sel, VALUE value)
 {
     rb_warn("Hash#index is deprecated; use Hash#key");
-    return rb_hash_key(hash, 0, value);
+    return rhash_key(hash, 0, value);
 }
 
+/*
+ *  call-seq:
+ *     hsh.delete(key)                   => value
+ *     hsh.delete(key) {| key | block }  => value
+ *
+ *  Deletes and returns the value from <i>hsh</i> whose key is
+ *  equal to <i>key</i>. If the key is not found, returns nil.
+ *  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 inline VALUE
+rhash_delete_key(VALUE hash, VALUE key)
+{
+    VALUE val;
+    if (st_delete(RHASH(hash)->tbl, &key, &val)) {
+	return val;
+    }
+    return Qundef;
+}
+
 VALUE
 rb_hash_delete_key(VALUE hash, VALUE key)
 {
     if (IS_RHASH(hash)) {
-	VALUE val;
-	if (st_delete(RHASH(hash)->tbl, &key, &val)) {
-	    return val;
-	}
+	rhash_modify(hash);
+	return rhash_delete_key(hash, key);
     }
     else {
 	VALUE val;
@@ -841,46 +822,32 @@
 		    (const void *)ockey);
 	    return OC2RB(val);
 	}
+	return Qundef;
     }
-    return Qundef;
 }
 
-/*
- *  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"
- *
- */
-
 VALUE
 rb_hash_delete(VALUE hash, VALUE key)
 {
-    rb_hash_modify(hash);
     VALUE val = rb_hash_delete_key(hash, key);
     if (val != Qundef) {
 	return val;
     }
-    if (rb_block_given_p()) {
-	return rb_yield(key);
-    }
     return Qnil;
 }
 
 static VALUE
-rb_hash_delete_imp(VALUE hash, SEL sel, VALUE key)
+rhash_delete(VALUE hash, SEL sel, VALUE key)
 {
-    return rb_hash_delete(hash, key);
+    rhash_modify(hash);
+    VALUE val = rhash_delete_key(hash, key);
+    if (val != Qundef) {
+	return val;
+    }
+    if (rb_block_given_p()) {
+	return rb_yield(key);
+    }
+    return Qnil;
 }
 
 /*
@@ -896,42 +863,37 @@
  *     h         #=> {2=>"b", 3=>"c"}
  */
 
-static VALUE rb_hash_keys_imp(VALUE, SEL);
-
-static VALUE
-rb_hash_shift(VALUE hash, SEL sel)
+static int
+shift_i(VALUE key, VALUE value, VALUE arg)
 {
-    VALUE keys = rb_hash_keys_imp(hash, 0);
-    if (RARRAY_LEN(keys) == 0) {
-	if (IS_RHASH(hash)) {
-	    if (RHASH(hash)->ifnone != Qnil) {
-		if (RHASH(hash)->has_proc_default) {
-		    return rb_funcall(RHASH(hash)->ifnone, id_yield, 2, hash,
-			    Qnil);
-		}
-		return RHASH(hash)->ifnone;
-	    }
-	}
-	return Qnil;
+    VALUE *ret = (VALUE *)arg;
+    if (key != Qundef) {
+	ret[0] = key;
+	ret[1] = value;
+	return ST_STOP;
     }
-
-    VALUE key = RARRAY_AT(keys, 0);
-    VALUE val = rb_hash_aref(hash, key);
-    rb_hash_delete(hash, key);
-
-    return rb_assoc_new(key, val);
+    return ST_CONTINUE;
 }
 
-static int
-delete_if_i(VALUE key, VALUE value, VALUE ary)
+static VALUE
+rhash_shift(VALUE hash, SEL sel)
 {
-    if (key == Qundef) {
-	return ST_CONTINUE;
+    VALUE args[2] = {0, 0};
+    rhash_iterate(hash, shift_i, (st_data_t)args);
+    if (args[0] != 0 && args[1] != 0) {
+	rhash_modify(hash);
+	rhash_delete_key(hash, args[0]);
+	return rb_assoc_new(args[0], args[1]);
     }
-    if (RTEST(rb_yield_values(2, key, value))) {
-	rb_ary_push(ary, key);
+
+    if (RHASH(hash)->ifnone != Qnil) {
+	if (RHASH(hash)->has_proc_default) {
+	    return rb_funcall(RHASH(hash)->ifnone, id_yield, 2, hash,
+		    Qnil);
+	}
+	return RHASH(hash)->ifnone;
     }
-    return ST_CONTINUE;
+    return Qnil;
 }
 
 /*
@@ -946,16 +908,27 @@
  *
  */
 
+static int
+delete_if_i(VALUE key, VALUE value, VALUE ary)
+{
+    if (key != Qundef) {
+	if (RTEST(rb_yield_values(2, key, value))) {
+	    rb_ary_push(ary, key);
+	}
+    }
+    return ST_CONTINUE;
+}
+
 static VALUE
-rb_hash_delete_if(VALUE hash, SEL sel)
+rhash_delete_if(VALUE hash, SEL sel)
 {
     RETURN_ENUMERATOR(hash, 0, 0);
-    rb_hash_modify(hash);
+    rhash_modify(hash);
     VALUE ary = rb_ary_new();
-    rb_hash_foreach(hash, delete_if_i, ary);
+    rhash_foreach(hash, delete_if_i, ary);
     for (int i = 0, count = RARRAY_LEN(ary); i < count; i++) {
 	VALUE key = RARRAY_AT(ary, i);
-	rb_hash_delete_key(hash, key);	
+	rhash_delete_key(hash, key);	
     }
     return hash;
 }
@@ -969,12 +942,12 @@
  */
 
 static VALUE
-rb_hash_reject_bang(VALUE hash, SEL sel)
+rhash_reject_bang(VALUE hash, SEL sel)
 {
     RETURN_ENUMERATOR(hash, 0, 0);
-    const long n = RHASH_SIZE(hash);
-    rb_hash_delete_if(hash, 0);
-    return n == RHASH_SIZE(hash) ? Qnil : hash;
+    const long n = rhash_len(hash);
+    rhash_delete_if(hash, 0);
+    return n == rhash_len(hash) ? Qnil : hash;
 }
 
 /*
@@ -988,9 +961,9 @@
  */
 
 static VALUE
-rb_hash_reject(VALUE hash, SEL sel)
+rhash_reject(VALUE hash, SEL sel)
 {
-    return rb_hash_delete_if(rb_hash_dup(hash), 0);
+    return rhash_delete_if(rhash_dup(hash, 0), 0);
 }
 
 /*
@@ -1005,27 +978,15 @@
  */
 
 static VALUE
-rb_hash_values_at(VALUE hash, SEL sel, int argc, VALUE *argv)
+rhash_values_at(VALUE hash, SEL sel, int argc, VALUE *argv)
 {
     VALUE result = rb_ary_new2(argc);
     for (int i = 0; i < argc; i++) {
-	rb_ary_push(result, rb_hash_aref(hash, argv[i]));
+	rb_ary_push(result, rhash_aref(hash, 0, argv[i]));
     }
     return result;
 }
 
-static int
-select_i(VALUE key, VALUE value, VALUE result)
-{
-    if (key == Qundef) {
-	return ST_CONTINUE;
-    }
-    if (RTEST(rb_yield_values(2, key, value))) {
-	rb_hash_aset(result, key, value);
-    }
-    return ST_CONTINUE;
-}
-
 /*
  *  call-seq:
  *     hsh.select {|key, value| block}   => a_hash
@@ -1037,12 +998,23 @@
  *     h.select {|k,v| v < 200}  #=> {"a" => 100}
  */
 
+static int
+select_i(VALUE key, VALUE value, VALUE result)
+{
+    if (key != Qundef) {
+	if (RTEST(rb_yield_values(2, key, value))) {
+	    rb_hash_aset(result, key, value);
+	}
+    }
+    return ST_CONTINUE;
+}
+
 static VALUE
-rb_hash_select(VALUE hash, SEL sel)
+rhash_select(VALUE hash, SEL sel)
 {
     RETURN_ENUMERATOR(hash, 0, 0);
     VALUE result = rb_hash_new();
-    rb_hash_iterate(hash, select_i, result);
+    rhash_iterate(hash, select_i, result);
     return result;
 }
 
@@ -1058,15 +1030,10 @@
  */
 
 static VALUE
-rb_hash_clear(VALUE hash, SEL sel)
+rhash_clear(VALUE hash, SEL sel)
 {
-    rb_hash_modify_check(hash);
-    if (IS_RHASH(hash)) {
-	st_clear(RHASH(hash)->tbl);
-    }
-    else {
-	CFDictionaryRemoveAllValues((CFMutableDictionaryRef)hash);
-    }
+    rhash_modify(hash);
+    st_clear(RHASH(hash)->tbl);
     return hash;
 }
 
@@ -1088,12 +1055,19 @@
  *
  */
 
+static VALUE
+rhash_aset(VALUE hash, SEL sel, VALUE key, VALUE val)
+{
+    rhash_modify(hash);
+    st_insert(RHASH(hash)->tbl, key, val);
+    return val;
+}
+
 VALUE
 rb_hash_aset(VALUE hash, VALUE key, VALUE val)
 {
-    rb_hash_modify(hash);
     if (IS_RHASH(hash)) {
-	st_insert(RHASH(hash)->tbl, key, val);
+	rhash_aset(hash, 0, key, val);
     }
     else {
 	CFDictionarySetValue((CFMutableDictionaryRef)hash,
@@ -1103,12 +1077,6 @@
     return val;
 }
 
-static VALUE
-rb_hash_aset_imp(VALUE hash, SEL sel, VALUE key, VALUE val)
-{
-    return rb_hash_aset(hash, key, val);
-}
-
 static int
 replace_i(VALUE key, VALUE val, VALUE hash)
 {
@@ -1131,17 +1099,18 @@
  */
 
 static VALUE
-rb_hash_replace(VALUE hash, SEL sel, VALUE hash2)
+rhash_replace(VALUE hash, SEL sel, VALUE hash2)
 {
-    rb_hash_modify(hash);
+    rhash_modify(hash);
     hash2 = to_hash(hash2);
     if (hash == hash2) {
 	return hash;
     }
-    rb_hash_clear(hash, 0);
+
+    rhash_clear(hash, 0);
     rb_hash_foreach(hash2, replace_i, hash);
 
-    if (IS_RHASH(hash) && IS_RHASH(hash2)) {
+    if (IS_RHASH(hash2)) {
 	GC_WB(&RHASH(hash)->ifnone, RHASH(hash2)->ifnone);
 	RHASH(hash)->has_proc_default = RHASH(hash2)->has_proc_default;
     }
@@ -1161,21 +1130,21 @@
  *     h.length        #=> 3
  */
 
+static VALUE
+rhash_size(VALUE hash, SEL sel)
+{
+    return LONG2NUM(rhash_len(hash));
+}
+
 long
 rb_hash_size(VALUE hash)
 {
     if (IS_RHASH(hash)) {
-	return RHASH(hash)->tbl->num_entries;
+	return rhash_len(hash);
     }
     return CFDictionaryGetCount((CFDictionaryRef)hash);
 }
 
-static VALUE
-rb_hash_size_imp(VALUE hash, SEL sel)
-{
-    return LONG2NUM(rb_hash_size(hash));
-}
-
 /*
  *  call-seq:
  *     hsh.empty?    => true or false
@@ -1187,21 +1156,11 @@
  */
 
 static VALUE
-rb_hash_empty_p(VALUE hash, SEL sel)
+rhash_empty(VALUE hash, SEL sel)
 {
-    return RHASH_EMPTY_P(hash) ? Qtrue : Qfalse;
+    return rhash_len(hash) == 0 ? Qtrue : Qfalse;
 }
 
-static int
-each_value_i(VALUE key, VALUE value)
-{
-    if (key == Qundef) {
-	return ST_CONTINUE;
-    }
-    rb_yield(value);
-    return ST_CONTINUE;
-}
-
 /*
  *  call-seq:
  *     hsh.each_value {| value | block } -> hsh
@@ -1218,24 +1177,23 @@
  *     200
  */
 
-static VALUE
-rb_hash_each_value(VALUE hash, SEL sel)
-{
-    RETURN_ENUMERATOR(hash, 0, 0);
-    rb_hash_iterate(hash, each_value_i, 0);
-    return hash;
-}
-
 static int
-each_key_i(VALUE key, VALUE value)
+each_value_i(VALUE key, VALUE value)
 {
-    if (key == Qundef) {
-	return ST_CONTINUE;
+    if (key != Qundef) {
+	rb_yield(value);
     }
-    rb_yield(key);
     return ST_CONTINUE;
 }
 
+static VALUE
+rhash_each_value(VALUE hash, SEL sel)
+{
+    RETURN_ENUMERATOR(hash, 0, 0);
+    rhash_iterate(hash, each_value_i, 0);
+    return hash;
+}
+
 /*
  *  call-seq:
  *     hsh.each_key {| key | block } -> hsh
@@ -1251,24 +1209,24 @@
  *     a
  *     b
  */
-static VALUE
-rb_hash_each_key(VALUE hash, SEL sel)
-{
-    RETURN_ENUMERATOR(hash, 0, 0);
-    rb_hash_iterate(hash, each_key_i, 0);
-    return hash;
-}
 
 static int
-each_pair_i(VALUE key, VALUE value)
+each_key_i(VALUE key, VALUE value)
 {
-    if (key == Qundef) {
-	return ST_CONTINUE;
+    if (key != Qundef) {
+	rb_yield(key);
     }
-    rb_yield(rb_assoc_new(key, value));
     return ST_CONTINUE;
 }
 
+static VALUE
+rhash_each_key(VALUE hash, SEL sel)
+{
+    RETURN_ENUMERATOR(hash, 0, 0);
+    rhash_iterate(hash, each_key_i, 0);
+    return hash;
+}
+
 /*
  *  call-seq:
  *     hsh.each {| key, value | block } -> hsh
@@ -1287,24 +1245,23 @@
  *
  */
 
-static VALUE
-rb_hash_each_pair(VALUE hash, SEL sel)
-{
-    RETURN_ENUMERATOR(hash, 0, 0);
-    rb_hash_iterate(hash, each_pair_i, 0);
-    return hash;
-}
-
 static int
-to_a_i(VALUE key, VALUE value, VALUE ary)
+each_pair_i(VALUE key, VALUE value)
 {
-    if (key == Qundef) {
-	return ST_CONTINUE;
+    if (key != Qundef) {
+	rb_yield(rb_assoc_new(key, value));
     }
-    rb_ary_push(ary, rb_assoc_new(key, value));
     return ST_CONTINUE;
 }
 
+static VALUE
+rhash_each_pair(VALUE hash, SEL sel)
+{
+    RETURN_ENUMERATOR(hash, 0, 0);
+    rhash_iterate(hash, each_pair_i, 0);
+    return hash;
+}
+
 /*
  *  call-seq:
  *     hsh.to_a -> array
@@ -1316,31 +1273,50 @@
  *     h.to_a   #=> [["c", 300], ["a", 100], ["d", 400]]
  */
 
+static int
+to_a_i(VALUE key, VALUE value, VALUE ary)
+{
+    if (key != Qundef) {
+	rb_ary_push(ary, rb_assoc_new(key, value));
+    }
+    return ST_CONTINUE;
+}
+
 static VALUE
-rb_hash_to_a(VALUE hash, SEL sel)
+rhash_to_a(VALUE hash, SEL sel)
 {
     VALUE ary = rb_ary_new();
-    rb_hash_iterate(hash, to_a_i, ary);
+    rhash_iterate(hash, to_a_i, ary);
     if (OBJ_TAINTED(hash)) {
 	OBJ_TAINT(ary);
     }
     return ary;
 }
 
+/*
+ * call-seq:
+ *   hsh.to_s   => string
+ *   hsh.inspect  => string
+ *
+ * Return the contents of this hash as a string.
+ *
+ *     h = { "c" => 300, "a" => 100, "d" => 400, "c" => 300  }
+ *     h.to_s   #=> "{\"c\"=>300, \"a\"=>100, \"d\"=>400}"
+ */
+
 static int
 inspect_i(VALUE key, VALUE value, VALUE str)
 {
-    if (key == Qundef) {
-	return ST_CONTINUE;
+    if (key != Qundef) {
+	if (RSTRING_LEN(str) > 1) {
+	    rb_str_cat2(str, ", ");
+	}
+	VALUE str2 = rb_inspect(key);
+	rb_str_buf_append(str, str2);
+	rb_str_buf_cat2(str, "=>");
+	str2 = rb_inspect(value);
+	rb_str_buf_append(str, str2);
     }
-    if (RSTRING_LEN(str) > 1) {
-	rb_str_cat2(str, ", ");
-    }
-    VALUE str2 = rb_inspect(key);
-    rb_str_buf_append(str, str2);
-    rb_str_buf_cat2(str, "=>");
-    str2 = rb_inspect(value);
-    rb_str_buf_append(str, str2);
     return ST_CONTINUE;
 }
 
@@ -1351,25 +1327,14 @@
 	return rb_usascii_str_new2("{...}");
     }
     VALUE str = rb_str_buf_new2("{");
-    rb_hash_iterate(hash, inspect_i, str);
+    rhash_iterate(hash, inspect_i, str);
     rb_str_buf_cat2(str, "}");
     OBJ_INFECT(str, hash);
     return str;
 }
 
-/*
- * call-seq:
- *   hsh.to_s   => string
- *   hsh.inspect  => string
- *
- * Return the contents of this hash as a string.
- *
- *     h = { "c" => 300, "a" => 100, "d" => 400, "c" => 300  }
- *     h.to_s   #=> "{\"c\"=>300, \"a\"=>100, \"d\"=>400}"
- */
-
 static VALUE
-rb_hash_inspect(VALUE hash, SEL sel)
+rhash_inspect(VALUE hash, SEL sel)
 {
     if (RHASH_EMPTY_P(hash)) {
 	return rb_usascii_str_new2("{}");
@@ -1385,21 +1350,11 @@
  */
 
 static VALUE
-rb_hash_to_hash(VALUE hash, SEL sel)
+rhash_to_hash(VALUE hash, SEL sel)
 {
     return hash;
 }
 
-static int
-keys_i(VALUE key, VALUE value, VALUE ary)
-{
-    if (key == Qundef) {
-	return ST_CONTINUE;
-    }
-    rb_ary_push(ary, key);
-    return ST_CONTINUE;
-}
-
 /*
  *  call-seq:
  *     hsh.keys    => array
@@ -1412,30 +1367,31 @@
  *
  */
 
+static int
+keys_i(VALUE key, VALUE value, VALUE ary)
+{
+    if (key != Qundef) {
+	rb_ary_push(ary, key);
+    }
+    return ST_CONTINUE;
+}
+
 static VALUE
-rb_hash_keys_imp(VALUE hash, SEL sel)
+rhash_keys(VALUE hash, SEL sel)
 {
     VALUE ary = rb_ary_new();
-    rb_hash_iterate(hash, keys_i, ary);
+    rhash_iterate(hash, keys_i, ary);
     return ary;
 }
 
 VALUE
 rb_hash_keys(VALUE hash)
 {
-    return rb_hash_keys_imp(hash, 0);
+    VALUE ary = rb_ary_new();
+    rb_hash_iterate(hash, keys_i, ary);
+    return ary;
 }
 
-static int
-values_i(VALUE key, VALUE value, VALUE ary)
-{
-    if (key == Qundef) {
-	return ST_CONTINUE;
-    }
-    rb_ary_push(ary, value);
-    return ST_CONTINUE;
-}
-
 /*
  *  call-seq:
  *     hsh.values    => array
@@ -1448,11 +1404,20 @@
  *
  */
 
+static int
+values_i(VALUE key, VALUE value, VALUE ary)
+{
+    if (key != Qundef) {
+	rb_ary_push(ary, value);
+    }
+    return ST_CONTINUE;
+}
+
 static VALUE
-rb_hash_values(VALUE hash, SEL sel)
+rhash_values(VALUE hash, SEL sel)
 {
     VALUE ary = rb_ary_new();
-    rb_hash_iterate(hash, values_i, ary);
+    rhash_iterate(hash, values_i, ary);
     return ary;
 }
 
@@ -1472,10 +1437,16 @@
  */
 
 static VALUE
-rb_hash_has_key_imp(VALUE hash, SEL sel, VALUE key)
+rhash_has_key(VALUE hash, SEL sel, VALUE key)
 {
+    return st_lookup(RHASH(hash)->tbl, key, 0) ? Qtrue : Qfalse;
+}
+
+VALUE
+rb_hash_has_key(VALUE hash, VALUE key)
+{
     if (IS_RHASH(hash)) {
-	return st_lookup(RHASH(hash)->tbl, key, 0) ? Qtrue : Qfalse;
+	return rhash_has_key(hash, 0, key);
     }
     else {
 	return CFDictionaryContainsKey((CFDictionaryRef)hash,
@@ -1483,12 +1454,6 @@
     }
 }
 
-VALUE
-rb_hash_has_key(VALUE hash, VALUE key)
-{
-    return rb_hash_has_key_imp(hash, 0, key);
-}
-
 /*
  *  call-seq:
  *     hsh.has_value?(value)    => true or false
@@ -1503,33 +1468,45 @@
  */
 
 static int
-rb_hash_search_value(VALUE key, VALUE value, VALUE arg)
+search_value(VALUE key, VALUE value, VALUE arg)
 {
     VALUE *data = (VALUE *)arg;
-    if (key == Qundef) {
-	return ST_CONTINUE;
+    if (key != Qundef) {
+	if (rb_equal(value, data[1])) {
+	    data[0] = Qtrue;
+	    return ST_STOP;
+	}
     }
-    if (rb_equal(value, data[1])) {
-        data[0] = Qtrue;
-        return ST_STOP;
-    }
     return ST_CONTINUE;
 }
 
 static VALUE
-rb_hash_has_value(VALUE hash, SEL sel, VALUE val)
+rhash_has_value(VALUE hash, SEL sel, VALUE val)
 {
-    if (IS_RHASH(hash)) {
-	VALUE data[2] = {Qfalse, val};
-	rb_hash_foreach(hash, rb_hash_search_value, (VALUE)data);
-	return data[0];
-    }
-    else {
-	return CFDictionaryContainsValue((CFDictionaryRef)hash,
-		(const void *)RB2OC(val)) ? Qtrue : Qfalse;
-    }
+    VALUE data[2] = {Qfalse, val};
+    rhash_foreach(hash, search_value, (VALUE)data);
+    return data[0];
 }
 
+/*
+ *  call-seq:
+ *     hsh == other_hash    => true or false
+ *
+ *  Equality---Two hashes are equal if they each contain the same number
+ *  of keys and if each key-value pair is equal to (according to
+ *  <code>Object#==</code>) the corresponding elements in the other
+ *  hash.
+ *
+ *     h1 = { "a" => 1, "c" => 2 }
+ *     h2 = { 7 => 35, "c" => 2, "a" => 1 }
+ *     h3 = { "a" => 1, "c" => 2, 7 => 35 }
+ *     h4 = { "a" => 1, "d" => 2, "f" => 35 }
+ *     h1 == h2   #=> false
+ *     h2 == h3   #=> true
+ *     h3 == h4   #=> false
+ *
+ */
+
 struct equal_data {
     VALUE result;
     st_table *tbl;
@@ -1595,7 +1572,7 @@
     if (RHASH_SIZE(hash1) != RHASH_SIZE(hash2)) {
 	return Qfalse;
     }
-    if (IS_RHASH(hash1) && IS_RHASH(hash2)) {
+    if (IS_RHASH(hash2)) {
 	struct equal_data data;
 	data.tbl = RHASH(hash2)->tbl;
 	data.eql = eql;
@@ -1606,27 +1583,8 @@
     }
 }
 
-/*
- *  call-seq:
- *     hsh == other_hash    => true or false
- *
- *  Equality---Two hashes are equal if they each contain the same number
- *  of keys and if each key-value pair is equal to (according to
- *  <code>Object#==</code>) the corresponding elements in the other
- *  hash.
- *
- *     h1 = { "a" => 1, "c" => 2 }
- *     h2 = { 7 => 35, "c" => 2, "a" => 1 }
- *     h3 = { "a" => 1, "c" => 2, 7 => 35 }
- *     h4 = { "a" => 1, "d" => 2, "f" => 35 }
- *     h1 == h2   #=> false
- *     h2 == h3   #=> true
- *     h3 == h4   #=> false
- *
- */
-
 static VALUE
-rb_hash_equal_imp(VALUE hash1, SEL sel, VALUE hash2)
+rhash_equal(VALUE hash1, SEL sel, VALUE hash2)
 {
     return hash_equal(hash1, hash2, false);
 }
@@ -1646,21 +1604,11 @@
  */
 
 static VALUE
-rb_hash_eql(VALUE hash1, SEL sel, VALUE hash2)
+rhash_eql(VALUE hash1, SEL sel, VALUE hash2)
 {
     return hash_equal(hash1, hash2, true);
 }
 
-static int
-rb_hash_invert_i(VALUE key, VALUE value, VALUE hash)
-{
-    if (key == Qundef) {
-	return ST_CONTINUE;
-    }
-    rb_hash_aset(hash, value, key);
-    return ST_CONTINUE;
-}
-
 /*
  *  call-seq:
  *     hsh.invert -> aHash
@@ -1673,35 +1621,21 @@
  *
  */
 
-static VALUE
-rb_hash_invert(VALUE hash, SEL sel)
-{
-    VALUE h = rb_hash_new();
-    rb_hash_foreach(hash, rb_hash_invert_i, h);
-    return h;
-}
-
 static int
-rb_hash_update_i(VALUE key, VALUE value, VALUE hash)
+invert_i(VALUE key, VALUE value, VALUE hash)
 {
-    if (key == Qundef) {
-	return ST_CONTINUE;
+    if (key != Qundef) {
+	rhash_aset(hash, 0, value, key);
     }
-    rb_hash_aset(hash, key, value);
     return ST_CONTINUE;
 }
 
-static int
-rb_hash_update_block_i(VALUE key, VALUE value, VALUE hash)
+static VALUE
+rhash_invert(VALUE hash, SEL sel)
 {
-    if (key == Qundef) {
-	return ST_CONTINUE;
-    }
-    if (rb_hash_has_key_imp(hash, 0, key)) {
-	value = rb_yield_values(3, key, rb_hash_aref(hash, key), value);
-    }
-    rb_hash_aset(hash, key, value);
-    return ST_CONTINUE;
+    VALUE h = rb_hash_new();
+    rhash_foreach(hash, invert_i, h);
+    return h;
 }
 
 /*
@@ -1727,15 +1661,36 @@
  *                     #=> {"a"=>100, "b"=>200, "c"=>300}
  */
 
+static int
+update_i(VALUE key, VALUE value, VALUE hash)
+{
+    if (key != Qundef) {
+	rhash_aset(hash, 0, key, value);
+    }
+    return ST_CONTINUE;
+}
+
+static int
+update_block_i(VALUE key, VALUE value, VALUE hash)
+{
+    if (key != Qundef) {
+	if (rhash_has_key(hash, 0, key)) {
+	    value = rb_yield_values(3, key, rhash_aref(hash, 0, key), value);
+	}
+	rhash_aset(hash, 0, key, value);
+    }
+    return ST_CONTINUE;
+}
+
 static VALUE
-rb_hash_update(VALUE hash1, SEL sel, VALUE hash2)
+rhash_update(VALUE hash1, SEL sel, VALUE hash2)
 {
     hash2 = to_hash(hash2);
     if (rb_block_given_p()) {
-	rb_hash_foreach(hash2, rb_hash_update_block_i, hash1);
+	rb_hash_foreach(hash2, update_block_i, hash1);
     }
     else {
-	rb_hash_foreach(hash2, rb_hash_update_i, hash1);
+	rb_hash_foreach(hash2, update_i, hash1);
     }
     return hash1;
 }
@@ -1757,24 +1712,11 @@
  */
 
 static VALUE
-rb_hash_merge(VALUE hash1, SEL sel, VALUE hash2)
+rhash_merge(VALUE hash1, SEL sel, VALUE hash2)
 {
-    return rb_hash_update(rb_hash_dup(hash1), 0, hash2);
+    return rhash_update(rhash_dup(hash1, 0), 0, hash2);
 }
 
-static int
-assoc_i(VALUE key, VALUE val, VALUE *args)
-{
-    if (key == Qundef) {
-	return ST_CONTINUE;
-    }
-    if (RTEST(rb_equal(args[0], key))) {
-	args[1] = rb_assoc_new(key, val);
-	return ST_STOP;
-    }
-    return ST_CONTINUE;
-}
-
 /*
  *  call-seq:
  *     hash.assoc(obj)   ->  an_array  or  nil
@@ -1789,27 +1731,26 @@
  *     h.assoc("foo")      #=> nil
  */
 
-VALUE
-rb_hash_assoc(VALUE hash, SEL sel, VALUE obj)
-{
-    VALUE args[2] = {obj, Qnil};
-    rb_hash_foreach(hash, assoc_i, (st_data_t)args);
-    return args[1];
-}
-
 static int
-rassoc_i(VALUE key, VALUE val, VALUE *args)
+assoc_i(VALUE key, VALUE val, VALUE *args)
 {
-    if (key == Qundef) {
-	return ST_CONTINUE;
+    if (key != Qundef) {
+	if (RTEST(rb_equal(args[0], key))) {
+	    args[1] = rb_assoc_new(key, val);
+	    return ST_STOP;
+	}
     }
-    if (RTEST(rb_equal(args[0], val))) {
-	args[1] = rb_assoc_new(key, val);
-	return ST_STOP;
-    }
     return ST_CONTINUE;
 }
 
+static VALUE
+rhash_assoc(VALUE hash, SEL sel, VALUE obj)
+{
+    VALUE args[2] = {obj, Qnil};
+    rhash_foreach(hash, assoc_i, (st_data_t)args);
+    return args[1];
+}
+
 /*
  *  call-seq:
  *     hash.rassoc(key) -> an_array or nil
@@ -1823,11 +1764,23 @@
  *     a.rassoc("four")   #=> nil
  */
 
+static int
+rassoc_i(VALUE key, VALUE val, VALUE *args)
+{
+    if (key != Qundef) {
+	if (RTEST(rb_equal(args[0], val))) {
+	    args[1] = rb_assoc_new(key, val);
+	    return ST_STOP;
+	}
+    }
+    return ST_CONTINUE;
+}
+
 VALUE
-rb_hash_rassoc(VALUE hash, SEL sel, VALUE obj)
+rhash_rassoc(VALUE hash, SEL sel, VALUE obj)
 {
     VALUE args[2] = {obj, Qnil};
-    rb_hash_foreach(hash, rassoc_i, (st_data_t)args);
+    rhash_foreach(hash, rassoc_i, (st_data_t)args);
     return args[1];
 }
 
@@ -1848,15 +1801,15 @@
  */
 
 static VALUE
-rb_hash_flatten(VALUE hash, SEL sel, int argc, VALUE *argv)
+rhash_flatten(VALUE hash, SEL sel, int argc, VALUE *argv)
 {
-    VALUE tmp, ary = rb_hash_to_a(hash, 0);
+    VALUE tmp, ary = rhash_to_a(hash, 0);
     if (argc == 0) {
 	argc = 1;
 	tmp = INT2FIX(1);
 	argv = &tmp;
     }
-    rb_funcall2(ary, rb_intern("flatten!"), argc, argv);
+    rb_vm_call(ary, selFlattenBang, argc, argv, false);
     return ary;
 }
 
@@ -1877,9 +1830,9 @@
  */
 
 static VALUE
-rb_hash_compare_by_id(VALUE hash, SEL sel)
+rhash_compare_by_id(VALUE hash, SEL sel)
 {
-    rb_hash_modify(hash);
+    rhash_modify(hash);
     // TODO
     return hash;
 }
@@ -1894,7 +1847,7 @@
  */
 
 static VALUE
-rb_hash_compare_by_id_p(VALUE hash, SEL sel)
+rhash_compare_by_id_p(VALUE hash, SEL sel)
 {
     // TODO
     return Qfalse;
@@ -2522,7 +2475,7 @@
 static VALUE
 env_reject(VALUE rcv, SEL sel)
 {
-    return rb_hash_delete_if(env_to_hash(Qnil, 0), 0);
+    return rhash_delete_if(env_to_hash(Qnil, 0), 0);
 }
 
 static VALUE
@@ -2548,7 +2501,7 @@
 static VALUE
 env_invert(VALUE rcv, SEL sel)
 {
-    return rb_hash_invert(env_to_hash(Qnil, 0), 0);
+    return rhash_invert(env_to_hash(Qnil, 0), 0);
 }
 
 static int
@@ -2603,13 +2556,6 @@
     return env;
 }
 
-#define PREPARE_RCV(x) \
-    Class old = *(Class *)x; \
-    *(Class *)x = (Class)rb_cCFHash;
-
-#define RESTORE_RCV(x) \
-    *(Class *)x = old;
-
 bool
 rb_objc_hash_is_pure(VALUE hash)
 {
@@ -2629,125 +2575,6 @@
     return true;
 }
 
-static CFIndex
-imp_rb_hash_count(void *rcv, SEL sel) 
-{
-    CFIndex count;
-    PREPARE_RCV(rcv);
-    count = CFDictionaryGetCount((CFDictionaryRef)rcv);
-    RESTORE_RCV(rcv);
-    return count; 
-}
-
-static void *
-imp_rb_hash_keyEnumerator(void *rcv, SEL sel)
-{
-    static SEL objectEnumerator = 0;
-    PREPARE_RCV(rcv);
-    void *keys = (void *)rb_hash_keys_imp((VALUE)rcv, 0);
-    RESTORE_RCV(rcv);
-    if (objectEnumerator == 0) {
-	objectEnumerator = sel_registerName("objectEnumerator");
-    }
-    return objc_msgSend(keys, objectEnumerator);
-}
-
-static void *
-imp_rb_hash_objectForKey(void *rcv, SEL sel, void *key)
-{
-    void *obj;
-    PREPARE_RCV(rcv);
-    if (!CFDictionaryGetValueIfPresent((CFDictionaryRef)rcv, (const void *)key,
-	(const void **)&obj)) {
-	obj = NULL;
-    }
-    RESTORE_RCV(rcv);
-    return obj;
-}
-
-static void 
-imp_rb_hash_setObjectForKey(void *rcv, SEL sel, void *obj, void *key) 
-{
-    PREPARE_RCV(rcv);
-    CFDictionarySetValue((CFMutableDictionaryRef)rcv, (const void *)key,
-	(const void *)obj);
-    RESTORE_RCV(rcv);
-} 
-
-static void
-imp_rb_hash_getObjectsAndKeys(void *rcv, SEL sel, void **objs, void **keys)
-{
-    PREPARE_RCV(rcv);
-    CFDictionaryGetKeysAndValues((CFDictionaryRef)rcv, (const void **)keys,
-	(const void **)objs);
-    RESTORE_RCV(rcv);
-}
-
-static void
-imp_rb_hash_removeObjectForKey(void *rcv, SEL sel, void *key)
-{
-    PREPARE_RCV(rcv);
-    CFDictionaryRemoveValue((CFMutableDictionaryRef)rcv, (const void *)key);
-    RESTORE_RCV(rcv);
-}
-
-static void
-imp_rb_hash_removeAllObjects(void *rcv, SEL sel)
-{
-    PREPARE_RCV(rcv);
-    CFDictionaryRemoveAllValues((CFMutableDictionaryRef)rcv);
-    RESTORE_RCV(rcv);
-}
-
-static bool
-imp_rb_hash_isEqual(void *rcv, SEL sel, void *other)
-{
-    bool res;
-    PREPARE_RCV(rcv);
-    res = CFEqual((CFTypeRef)rcv, (CFTypeRef)other);
-    RESTORE_RCV(rcv);
-    return res;
-}
-
-static bool
-imp_rb_hash_containsObject(void *rcv, SEL sel, void *obj)
-{
-    bool res;
-    PREPARE_RCV(rcv);
-    res = CFDictionaryContainsValue((CFTypeRef)rcv, (const void *)obj);
-    RESTORE_RCV(rcv);
-    return res;
-}
-
-void
-rb_objc_install_hash_primitives(Class klass)
-{
-    rb_objc_install_method2(klass, "count", (IMP)imp_rb_hash_count);
-    rb_objc_install_method2(klass, "keyEnumerator",
-	    (IMP)imp_rb_hash_keyEnumerator);
-    rb_objc_install_method2(klass, "objectForKey:",
-	    (IMP)imp_rb_hash_objectForKey);
-    rb_objc_install_method2(klass, "getObjects:andKeys:",
-	    (IMP)imp_rb_hash_getObjectsAndKeys);
-    rb_objc_install_method2(klass, "isEqual:", (IMP)imp_rb_hash_isEqual);
-    rb_objc_install_method2(klass, "containsObject:",
-	    (IMP)imp_rb_hash_containsObject);
-
-    const bool mutable = class_getSuperclass(klass)
-	== (Class)rb_cNSMutableHash; 
-
-    if (mutable) {
-	rb_objc_install_method2(klass, "setObject:forKey:",
-		(IMP)imp_rb_hash_setObjectForKey);
-	rb_objc_install_method2(klass, "removeObjectForKey:",
-		(IMP)imp_rb_hash_removeObjectForKey);
-	rb_objc_install_method2(klass, "removeAllObjects",
-		(IMP)imp_rb_hash_removeAllObjects);
-    }
-
-    rb_objc_define_method(*(VALUE *)klass, "alloc", hash_alloc, 0);
-}
-
 static VALUE rb_cRubyHashKeyEnumerator;
 
 typedef struct {
@@ -2831,15 +2658,14 @@
  *
  */
 
-#if MAC_OS_X_VERSION_MAX_ALLOWED < 1070
-# define NSCFDICTIONARY_CNAME "NSCFDictionary"
-#else
-# define NSCFDICTIONARY_CNAME "__NSCFDictionary"
-#endif
+void Init_NSDictionary(void);
 
 void
 Init_Hash(void)
 {
+    Init_NSDictionary();
+
+    selFlattenBang = sel_registerName("flatten!:");
     selDefault = sel_registerName("default:");
     selHash = sel_registerName("hash");
     defaultCache = rb_vm_get_call_cache(selDefault);
@@ -2847,86 +2673,70 @@
 
     id_yield = rb_intern("yield");
 
-    rb_cCFHash = (VALUE)objc_getClass(NSCFDICTIONARY_CNAME);
-    assert(rb_cCFHash != 0);
-    rb_cHash = rb_cNSHash = (VALUE)objc_getClass("NSDictionary");
-    rb_cNSMutableHash = (VALUE)objc_getClass("NSMutableDictionary");
-
-    rb_include_module(rb_cHash, rb_mEnumerable);
-
-    /* to return mutable copies */
-    rb_objc_define_method(rb_cHash, "dup", rb_hash_dup_imp, 0);
-    rb_objc_define_method(rb_cHash, "clone", rb_hash_clone, 0);
-
-    rb_objc_define_method(*(VALUE *)rb_cHash, "[]", rb_hash_s_create, -1);
-    rb_objc_define_method(*(VALUE *)rb_cHash, "try_convert", rb_hash_s_try_convert, 1);
-    rb_objc_define_method(rb_cHash, "initialize", rb_hash_initialize, -1);
-    rb_objc_define_method(rb_cHash, "initialize_copy", rb_hash_replace, 1);
-    rb_objc_define_method(rb_cHash, "rehash", rb_hash_rehash, 0);
-
-    rb_objc_define_method(rb_cHash, "to_hash", rb_hash_to_hash, 0);
-    rb_objc_define_method(rb_cHash, "to_a", rb_hash_to_a, 0);
-    rb_objc_define_method(rb_cHash, "to_s", rb_hash_inspect, 0);
-    rb_objc_define_method(rb_cHash, "inspect", rb_hash_inspect, 0);
-
-    rb_objc_define_method(rb_cHash, "==", rb_hash_equal_imp, 1);
-    rb_objc_define_method(rb_cHash, "[]", rb_hash_aref_imp, 1);
-    rb_objc_define_method(rb_cHash, "eql?", rb_hash_eql, 1);
-    rb_objc_define_method(rb_cHash, "fetch", rb_hash_fetch, -1);
-    rb_objc_define_method(rb_cHash, "[]=", rb_hash_aset_imp, 2);
-    rb_objc_define_method(rb_cHash, "store", rb_hash_aset_imp, 2);
-    rb_objc_define_method(rb_cHash, "default", rb_hash_default, -1);
-    rb_objc_define_method(rb_cHash, "default=", rb_hash_set_default, 1);
-    rb_objc_define_method(rb_cHash, "default_proc", rb_hash_default_proc, 0);
-    rb_objc_define_method(rb_cHash, "key", rb_hash_key, 1);
-    rb_objc_define_method(rb_cHash, "index", rb_hash_index, 1);
-    rb_objc_define_method(rb_cHash, "size", rb_hash_size_imp, 0);
-    rb_objc_define_method(rb_cHash, "length", rb_hash_size_imp, 0);
-    rb_objc_define_method(rb_cHash, "empty?", rb_hash_empty_p, 0);
-
-    rb_objc_define_method(rb_cHash, "each_value", rb_hash_each_value, 0);
-    rb_objc_define_method(rb_cHash, "each_key", rb_hash_each_key, 0);
-    rb_objc_define_method(rb_cHash, "each_pair", rb_hash_each_pair, 0);
-    rb_objc_define_method(rb_cHash, "each", rb_hash_each_pair, 0);
-
-    rb_objc_define_method(rb_cHash, "keys", rb_hash_keys_imp, 0);
-    rb_objc_define_method(rb_cHash, "values", rb_hash_values, 0);
-    rb_objc_define_method(rb_cHash, "values_at", rb_hash_values_at, -1);
-
-    rb_objc_define_method(rb_cHash, "shift", rb_hash_shift, 0);
-    rb_objc_define_method(rb_cHash, "delete", rb_hash_delete_imp, 1);
-    rb_objc_define_method(rb_cHash, "delete_if", rb_hash_delete_if, 0);
-    rb_objc_define_method(rb_cHash, "select", rb_hash_select, 0);
-    rb_objc_define_method(rb_cHash, "reject", rb_hash_reject, 0);
-    rb_objc_define_method(rb_cHash, "reject!", rb_hash_reject_bang, 0);
-    rb_objc_define_method(rb_cHash, "clear", rb_hash_clear, 0);
-    rb_objc_define_method(rb_cHash, "invert", rb_hash_invert, 0);
-
-    /* to override the private -[NSMutableDictionary invert] method */
-    rb_objc_define_method(rb_cNSMutableHash, "invert", rb_hash_invert, 0);
-
-    rb_objc_define_method(rb_cHash, "update", rb_hash_update, 1);
-    rb_objc_define_method(rb_cHash, "replace", rb_hash_replace, 1);
-    rb_objc_define_method(rb_cHash, "merge!", rb_hash_update, 1);
-    rb_objc_define_method(rb_cHash, "merge", rb_hash_merge, 1);
-    rb_objc_define_method(rb_cHash, "assoc", rb_hash_assoc, 1);
-    rb_objc_define_method(rb_cHash, "rassoc", rb_hash_rassoc, 1);
-    rb_objc_define_method(rb_cHash, "flatten", rb_hash_flatten, -1);
-
-    rb_objc_define_method(rb_cHash, "include?", rb_hash_has_key_imp, 1);
-    rb_objc_define_method(rb_cHash, "member?", rb_hash_has_key_imp, 1);
-    rb_objc_define_method(rb_cHash, "has_key?", rb_hash_has_key_imp, 1);
-    rb_objc_define_method(rb_cHash, "has_value?", rb_hash_has_value, 1);
-    rb_objc_define_method(rb_cHash, "key?", rb_hash_has_key_imp, 1);
-    rb_objc_define_method(rb_cHash, "value?", rb_hash_has_value, 1);
-
-    rb_objc_define_method(rb_cHash, "compare_by_identity", rb_hash_compare_by_id, 0);
-    rb_objc_define_method(rb_cHash, "compare_by_identity?", rb_hash_compare_by_id_p, 0);
-
     rb_cRubyHash = rb_define_class("Hash", rb_cNSMutableHash);
     rb_objc_define_method(*(VALUE *)rb_cRubyHash, "new",
 	    rb_class_new_instance_imp, -1);
-    rb_objc_define_method(*(VALUE *)rb_cRubyHash, "alloc", hash_alloc, 0);
+    rb_objc_define_method(*(VALUE *)rb_cRubyHash, "alloc", rhash_alloc, 0);
+    rb_objc_define_method(*(VALUE *)rb_cRubyHash, "[]", rhash_create, -1);
+    rb_objc_define_method(*(VALUE *)rb_cRubyHash, "try_convert",
+	    rhash_try_convert, 1);
+    rb_objc_define_method(rb_cRubyHash, "initialize", rhash_initialize, -1);
+    rb_objc_define_method(rb_cRubyHash, "initialize_copy", rhash_replace, 1);
+    rb_objc_define_method(rb_cRubyHash, "dup", rhash_dup, 0);
+    rb_objc_define_method(rb_cRubyHash, "clone", rhash_clone, 0);
+    rb_objc_define_method(rb_cRubyHash, "rehash", rhash_rehash, 0);
+    rb_objc_define_method(rb_cRubyHash, "to_hash", rhash_to_hash, 0);
+    rb_objc_define_method(rb_cRubyHash, "to_a", rhash_to_a, 0);
+    rb_objc_define_method(rb_cRubyHash, "to_s", rhash_inspect, 0);
+    rb_objc_define_method(rb_cRubyHash, "inspect", rhash_inspect, 0);
+    rb_objc_define_method(rb_cRubyHash, "==", rhash_equal, 1);
+    rb_objc_define_method(rb_cRubyHash, "[]", rhash_aref, 1);
+    rb_objc_define_method(rb_cRubyHash, "eql?", rhash_eql, 1);
+    rb_objc_define_method(rb_cRubyHash, "fetch", rhash_fetch, -1);
+    rb_objc_define_method(rb_cRubyHash, "[]=", rhash_aset, 2);
+    rb_objc_define_method(rb_cRubyHash, "store", rhash_aset, 2);
+    rb_objc_define_method(rb_cRubyHash, "default", rhash_default, -1);
+    rb_objc_define_method(rb_cRubyHash, "default=", rhash_set_default, 1);
+    rb_objc_define_method(rb_cRubyHash, "default_proc",
+	    rhash_default_proc, 0);
+    rb_objc_define_method(rb_cRubyHash, "key", rhash_key, 1);
+    rb_objc_define_method(rb_cRubyHash, "index", rhash_index, 1);
+    rb_objc_define_method(rb_cRubyHash, "size", rhash_size, 0);
+    rb_objc_define_method(rb_cRubyHash, "length", rhash_size, 0);
+    rb_objc_define_method(rb_cRubyHash, "empty?", rhash_empty, 0);
+    rb_objc_define_method(rb_cRubyHash, "each_value", rhash_each_value, 0);
+    rb_objc_define_method(rb_cRubyHash, "each_key", rhash_each_key, 0);
+    rb_objc_define_method(rb_cRubyHash, "each_pair", rhash_each_pair, 0);
+    rb_objc_define_method(rb_cRubyHash, "each", rhash_each_pair, 0);
+    rb_objc_define_method(rb_cRubyHash, "keys", rhash_keys, 0);
+    rb_objc_define_method(rb_cRubyHash, "values", rhash_values, 0);
+    rb_objc_define_method(rb_cRubyHash, "values_at", rhash_values_at, -1);
+    rb_objc_define_method(rb_cRubyHash, "shift", rhash_shift, 0);
+    rb_objc_define_method(rb_cRubyHash, "delete", rhash_delete, 1);
+    rb_objc_define_method(rb_cRubyHash, "delete_if", rhash_delete_if, 0);
+    rb_objc_define_method(rb_cRubyHash, "select", rhash_select, 0);
+    rb_objc_define_method(rb_cRubyHash, "reject", rhash_reject, 0);
+    rb_objc_define_method(rb_cRubyHash, "reject!", rhash_reject_bang, 0);
+    rb_objc_define_method(rb_cRubyHash, "clear", rhash_clear, 0);
+    rb_objc_define_method(rb_cRubyHash, "invert", rhash_invert, 0);
+    rb_objc_define_method(rb_cRubyHash, "update", rhash_update, 1);
+    rb_objc_define_method(rb_cRubyHash, "replace", rhash_replace, 1);
+    rb_objc_define_method(rb_cRubyHash, "merge!", rhash_update, 1);
+    rb_objc_define_method(rb_cRubyHash, "merge", rhash_merge, 1);
+    rb_objc_define_method(rb_cRubyHash, "assoc", rhash_assoc, 1);
+    rb_objc_define_method(rb_cRubyHash, "rassoc", rhash_rassoc, 1);
+    rb_objc_define_method(rb_cRubyHash, "flatten", rhash_flatten, -1);
+    rb_objc_define_method(rb_cRubyHash, "include?", rhash_has_key, 1);
+    rb_objc_define_method(rb_cRubyHash, "member?", rhash_has_key, 1);
+    rb_objc_define_method(rb_cRubyHash, "has_key?", rhash_has_key, 1);
+    rb_objc_define_method(rb_cRubyHash, "has_value?", rhash_has_value, 1);
+    rb_objc_define_method(rb_cRubyHash, "key?", rhash_has_key, 1);
+    rb_objc_define_method(rb_cRubyHash, "value?", rhash_has_value, 1);
+    rb_objc_define_method(rb_cRubyHash, "compare_by_identity",
+	    rhash_compare_by_id, 0);
+    rb_objc_define_method(rb_cRubyHash, "compare_by_identity?",
+	    rhash_compare_by_id_p, 0);
+
     rb_objc_install_method2((Class)rb_cRubyHash, "count",
 	    (IMP)imp_rhash_count);
     rb_objc_install_method2((Class)rb_cRubyHash, "objectForKey:",

Modified: MacRuby/trunk/include/ruby/intern.h
===================================================================
--- MacRuby/trunk/include/ruby/intern.h	2010-01-26 23:20:58 UTC (rev 3344)
+++ MacRuby/trunk/include/ruby/intern.h	2010-01-26 23:21:44 UTC (rev 3345)
@@ -197,7 +197,7 @@
 VALUE rb_enumeratorize(VALUE, SEL, int, VALUE *);
 #define RETURN_ENUMERATOR(obj, argc, argv) do {				\
 	if (!rb_block_given_p())					\
-	    return rb_enumeratorize(obj, sel, argc, argv);		\
+	    return rb_enumeratorize((VALUE)obj, sel, argc, argv);	\
     } while (0)
 /* error.c */
 VALUE rb_f_notimplement(VALUE rcv, SEL sel);

Modified: MacRuby/trunk/include/ruby/ruby.h
===================================================================
--- MacRuby/trunk/include/ruby/ruby.h	2010-01-26 23:20:58 UTC (rev 3344)
+++ MacRuby/trunk/include/ruby/ruby.h	2010-01-26 23:21:44 UTC (rev 3345)
@@ -1137,7 +1137,6 @@
 RUBY_EXTERN VALUE rb_cNSArray;
 RUBY_EXTERN VALUE rb_cNSMutableArray;
 RUBY_EXTERN VALUE rb_cRubyArray;
-RUBY_EXTERN VALUE rb_cCFHash;
 RUBY_EXTERN VALUE rb_cNSHash;
 RUBY_EXTERN VALUE rb_cNSMutableHash;
 RUBY_EXTERN VALUE rb_cRubyHash;

Modified: MacRuby/trunk/rakelib/builder.rb
===================================================================
--- MacRuby/trunk/rakelib/builder.rb	2010-01-26 23:20:58 UTC (rev 3344)
+++ MacRuby/trunk/rakelib/builder.rb	2010-01-26 23:21:44 UTC (rev 3345)
@@ -102,7 +102,8 @@
 LDFLAGS << " -lpthread -ldl -lxml2 -lobjc -lauto -framework Foundation"
 DLDFLAGS = "-dynamiclib -undefined suppress -flat_namespace -install_name #{INSTALL_NAME} -current_version #{MACRUBY_VERSION} -compatibility_version #{MACRUBY_VERSION}"
 DLDFLAGS << " -unexported_symbols_list #{UNEXPORTED_SYMBOLS_LIST}" if UNEXPORTED_SYMBOLS_LIST
-CFLAGS << " -std=c99" # we add this one later to not conflict with ObjC/C++ flags
+CFLAGS << " -std=c99" # we add this one later to not conflict with C++ flags
+OBJC_CFLAGS << " -std=c99"
 
 OBJS = %w{ 
   array bignum class compar complex enum enumerator error eval file load proc 
@@ -115,7 +116,7 @@
   ruby signal sprintf st string struct time transcode util variable version
   thread id objc bs encoding main dln dmyext marshal gcd
   vm_eval prelude miniprelude gc-stub bridgesupport compiler dispatcher vm
-  debugger MacRuby MacRubyDebuggerConnector
+  debugger MacRuby MacRubyDebuggerConnector NSDictionary
 }
 
 OBJS_CFLAGS = {

Modified: MacRuby/trunk/spec/macruby/tags/macruby/core/hash_tags.txt
===================================================================
--- MacRuby/trunk/spec/macruby/tags/macruby/core/hash_tags.txt	2010-01-26 23:20:58 UTC (rev 3344)
+++ MacRuby/trunk/spec/macruby/tags/macruby/core/hash_tags.txt	2010-01-26 23:21:44 UTC (rev 3345)
@@ -1,2 +1,3 @@
 critical:The NSDictionary class can be subclassed and later instantiated
 critical:An NSDictionary object can have a singleton class
+fails:An NSDictionary object is immutable
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.macosforge.org/pipermail/macruby-changes/attachments/20100126/bdbb18bc/attachment-0001.html>


More information about the macruby-changes mailing list