Revision: 3345 http://trac.macosforge.org/projects/ruby/changeset/3345 Author: lsansonetti@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