[macruby-changes] [2706] MacRuby/trunk

source_changes at macosforge.org source_changes at macosforge.org
Thu Oct 1 20:43:08 PDT 2009


Revision: 2706
          http://trac.macosforge.org/projects/ruby/changeset/2706
Author:   lsansonetti at apple.com
Date:     2009-10-01 20:43:08 -0700 (Thu, 01 Oct 2009)
Log Message:
-----------
move the dispatcher code to a separate new file and pass the proper options to gcc to make sure everything gets inlined (gcc likes to lie about that)

Modified Paths:
--------------
    MacRuby/trunk/rakelib/builder.rb
    MacRuby/trunk/vm.cpp
    MacRuby/trunk/vm.h

Added Paths:
-----------
    MacRuby/trunk/dispatcher.cpp

Added: MacRuby/trunk/dispatcher.cpp
===================================================================
--- MacRuby/trunk/dispatcher.cpp	                        (rev 0)
+++ MacRuby/trunk/dispatcher.cpp	2009-10-02 03:43:08 UTC (rev 2706)
@@ -0,0 +1,1653 @@
+/*
+ * MacRuby VM.
+ *
+ * This file is covered by the Ruby license. See COPYING for more details.
+ * 
+ * Copyright (C) 2008-2009, Apple Inc. All rights reserved.
+ */
+
+#include "llvm.h"
+#include "ruby/ruby.h"
+#include "ruby/node.h"
+#include "id.h"
+#include "vm.h"
+#include "compiler.h"
+#include "objc.h"
+
+#include <execinfo.h>
+#include <dlfcn.h>
+
+#define MAX_DISPATCH_ARGS 100
+
+static force_inline void
+__rb_vm_fix_args(const VALUE *argv, VALUE *new_argv,
+	const rb_vm_arity_t &arity, int argc)
+{
+    assert(argc >= arity.min);
+    assert((arity.max == -1) || (argc <= arity.max));
+    const int used_opt_args = argc - arity.min;
+    int opt_args, rest_pos;
+    if (arity.max == -1) {
+	opt_args = arity.real - arity.min - 1;
+	rest_pos = arity.left_req + opt_args;
+    }
+    else {
+	opt_args = arity.real - arity.min;
+	rest_pos = -1;
+    }
+    for (int i = 0; i < arity.real; ++i) {
+	if (i < arity.left_req) {
+	    // required args before optional args
+	    new_argv[i] = argv[i];
+	}
+	else if (i < arity.left_req + opt_args) {
+	    // optional args
+	    int opt_arg_index = i - arity.left_req;
+	    if (opt_arg_index >= used_opt_args) {
+		new_argv[i] = Qundef;
+	    }
+	    else {
+		new_argv[i] = argv[i];
+	    }
+	}
+	else if (i == rest_pos) {
+	    // rest
+	    int rest_size = argc - arity.real + 1;
+	    if (rest_size <= 0) {
+		new_argv[i] = rb_ary_new();
+	    }
+	    else {
+		new_argv[i] = rb_ary_new4(rest_size, &argv[i]);
+	    }
+	}
+	else {
+	    // required args after optional args
+	    new_argv[i] = argv[argc-(arity.real - i)];
+	}
+    }
+}
+
+static force_inline VALUE
+__rb_vm_bcall(VALUE self, SEL sel, VALUE dvars, rb_vm_block_t *b,
+	      IMP pimp, const rb_vm_arity_t &arity, int argc,
+	      const VALUE *argv)
+{
+    if ((arity.real != argc) || (arity.max == -1)) {
+	VALUE *new_argv = (VALUE *)alloca(sizeof(VALUE) * arity.real);
+	__rb_vm_fix_args(argv, new_argv, arity, argc);
+	argv = new_argv;
+	argc = arity.real;
+    }
+
+    assert(pimp != NULL);
+
+    VALUE (*imp)(VALUE, SEL, VALUE, rb_vm_block_t *,  ...) = (VALUE (*)(VALUE, SEL, VALUE, rb_vm_block_t *, ...))pimp;
+
+    switch (argc) {
+	case 0:
+	    return (*imp)(self, sel, dvars, b);
+	case 1:
+	    return (*imp)(self, sel, dvars, b, argv[0]);
+	case 2:
+	    return (*imp)(self, sel, dvars, b, argv[0], argv[1]);
+	case 3:
+	    return (*imp)(self, sel, dvars, b, argv[0], argv[1], argv[2]);
+	case 4:
+	    return (*imp)(self, sel, dvars, b, argv[0], argv[1], argv[2], argv[3]);
+	case 5:
+	    return (*imp)(self, sel, dvars, b, argv[0], argv[1], argv[2], argv[3], argv[4]);
+	case 6:
+	    return (*imp)(self, sel, dvars, b, argv[0], argv[1], argv[2], argv[3], argv[4], argv[5]);
+	case 7:
+	    return (*imp)(self, sel, dvars, b, argv[0], argv[1], argv[2], argv[3], argv[4], argv[5], argv[6]);
+	case 8:
+	    return (*imp)(self, sel, dvars, b, argv[0], argv[1], argv[2], argv[3], argv[4], argv[5], argv[6], argv[7]);
+	case 9:
+	    return (*imp)(self, sel, dvars, b, argv[0], argv[1], argv[2], argv[3], argv[4], argv[5], argv[6], argv[7], argv[8]);
+    }	
+    printf("invalid argc %d\n", argc);
+    abort();
+}
+
+static force_inline VALUE
+__rb_vm_rcall(VALUE self, SEL sel, IMP pimp, const rb_vm_arity_t &arity,
+              int argc, const VALUE *argv)
+{
+    if ((arity.real != argc) || (arity.max == -1)) {
+	VALUE *new_argv = (VALUE *)alloca(sizeof(VALUE) * arity.real);
+	__rb_vm_fix_args(argv, new_argv, arity, argc);
+	argv = new_argv;
+	argc = arity.real;
+    }
+
+    assert(pimp != NULL);
+
+    VALUE (*imp)(VALUE, SEL, ...) = (VALUE (*)(VALUE, SEL, ...))pimp;
+
+    switch (argc) {
+	case 0:
+	    return (*imp)(self, sel);
+	case 1:
+	    return (*imp)(self, sel, argv[0]);
+	case 2:
+	    return (*imp)(self, sel, argv[0], argv[1]);		
+	case 3:
+	    return (*imp)(self, sel, argv[0], argv[1], argv[2]);
+	case 4:
+	    return (*imp)(self, sel, argv[0], argv[1], argv[2], argv[3]);
+	case 5:
+	    return (*imp)(self, sel, argv[0], argv[1], argv[2], argv[3], argv[4]);
+	case 6:
+	    return (*imp)(self, sel, argv[0], argv[1], argv[2], argv[3], argv[4], argv[5]);
+	case 7:
+	    return (*imp)(self, sel, argv[0], argv[1], argv[2], argv[3], argv[4], argv[5], argv[6]);
+	case 8:
+	    return (*imp)(self, sel, argv[0], argv[1], argv[2], argv[3], argv[4], argv[5], argv[6], argv[7]);
+	case 9:
+	    return (*imp)(self, sel, argv[0], argv[1], argv[2], argv[3], argv[4], argv[5], argv[6], argv[7], argv[8]);
+	case 10:
+	    return (*imp)(self, sel, argv[0], argv[1], argv[2], argv[3], argv[4], argv[5], argv[6], argv[7], argv[8], argv[9]);
+	case 11:
+	    return (*imp)(self, sel, argv[0], argv[1], argv[2], argv[3], argv[4], argv[5], argv[6], argv[7], argv[8], argv[9], argv[10]);
+    }	
+    printf("invalid argc %d\n", argc);
+    abort();
+}
+
+static void
+vm_gen_bs_func_types(int argc, const VALUE *argv,
+	bs_element_function_t *bs_func, std::string &types)
+{
+    types.append(bs_func->retval == NULL ? "v" : bs_func->retval->type);
+    int printf_arg = -1;
+    for (int i = 0; i < (int)bs_func->args_count; i++) {
+	types.append(bs_func->args[i].type);
+	if (bs_func->args[i].printf_format) {
+	    printf_arg = i;
+	}
+    }
+    if (bs_func->variadic) {
+	// TODO honor printf_format
+//	if (printf_arg != -1) {	    
+//	}
+	for (int i = bs_func->args_count; i < argc; i++) {
+	    types.append("@");
+	}
+    }
+}
+
+static SEL
+helper_sel(const char *p, size_t len)
+{
+    SEL new_sel = 0;
+    char buf[100];
+
+    assert(len < sizeof(buf));
+
+    if (len >= 3 && isalpha(p[len - 3]) && p[len - 2] == '='
+	&& p[len - 1] == ':') {
+
+	/* foo=: -> setFoo: shortcut */
+	snprintf(buf, sizeof buf, "set%s", p);
+	buf[3] = toupper(buf[3]);
+	buf[len + 1] = ':';
+	buf[len + 2] = '\0';
+	new_sel = sel_registerName(buf);
+    }
+    else if (len > 1 && p[len - 1] == '?') {
+	/* foo?: -> isFoo: shortcut */
+	snprintf(buf, sizeof buf, "is%s", p);
+	buf[2] = toupper(buf[2]);
+	buf[len + 1] = '\0';
+	new_sel = sel_registerName(buf);
+    }
+
+    return new_sel;
+}
+
+static IMP
+objc_imp(IMP imp)
+{
+    rb_vm_method_node_t *node = GET_CORE()->method_node_get(imp);
+    if (node != NULL && node->ruby_imp == imp) {
+	imp = node->objc_imp;
+    }
+    return imp;
+}
+
+static Method
+rb_vm_super_lookup(VALUE klass, SEL sel)
+{
+    // Locate the current method implementation.
+    Method m = class_getInstanceMethod((Class)klass, sel);
+    assert(m != NULL);
+    IMP self = objc_imp(method_getImplementation(m));
+
+    // Compute the stack call implementations right after our current method.
+    void *callstack[128];
+    int callstack_n = backtrace(callstack, 128);
+    std::vector<void *> callstack_funcs;
+    bool skip = true;
+    for (int i = callstack_n - 1; i >= 0; i--) {
+	void *start = NULL;
+	if (GET_CORE()->symbolize_call_address(callstack[i],
+		    &start, NULL, 0, NULL, NULL, 0)) {
+	    start = (void *)objc_imp((IMP)start);
+	    if (start == (void *)self) {
+		skip = false;
+	    }
+	    if (!skip) {
+		callstack_funcs.push_back(start);
+	    }
+	}
+    }
+
+    // Iterate over ancestors and return the first method that isn't on
+    // the stack.
+    VALUE ary = rb_mod_ancestors_nocopy(klass);
+    const int count = RARRAY_LEN(ary);
+    VALUE k = klass;
+    bool klass_located = false;
+
+#if ROXOR_VM_DEBUG
+    printf("locating super method %s of class %s in ancestor chain %s\n", 
+	    sel_getName(sel), rb_class2name(klass),
+	    RSTRING_PTR(rb_inspect(ary)));
+    printf("callstack functions: ");
+    for (std::vector<void *>::iterator iter = callstack_funcs.begin();
+	 iter != callstack_funcs.end();
+	 ++iter) {
+	printf("%p ", *iter);
+    }
+    printf("\n");
+#endif
+
+    //assert(!callstack_funcs.empty());
+
+    for (int i = 0; i < count; i++) {
+        if (!klass_located && RARRAY_AT(ary, i) == klass) {
+            klass_located = true;
+        }
+        if (klass_located) {
+            if (i < count - 1) {
+                k = RARRAY_AT(ary, i + 1);
+
+		Method method = class_getInstanceMethod((Class)k, sel);
+		VALUE super = RCLASS_SUPER(k);
+
+		if (method == NULL || (super != 0
+		    && class_getInstanceMethod((Class)super, sel) == method)) {
+		    continue;
+		}
+
+		IMP imp = method_getImplementation(method);
+
+		if (std::find(callstack_funcs.begin(), callstack_funcs.end(), 
+			    (void *)imp) == callstack_funcs.end()) {
+		    // Method is not on stack.
+#if ROXOR_VM_DEBUG
+		    printf("returning method implementation %p " \
+		    	   "from class/module %s\n", imp, rb_class2name(k));
+#endif
+		    return method;
+		}
+            }
+        }
+    }
+
+    return NULL;
+}
+
+static VALUE
+method_missing(VALUE obj, SEL sel, rb_vm_block_t *block, int argc,
+	const VALUE *argv, rb_vm_method_missing_reason_t call_status)
+{
+    GET_VM()->set_method_missing_reason(call_status);
+
+    if (sel == selMethodMissing) {
+	rb_vm_method_missing(obj, argc, argv);
+    }
+    else if (sel == selAlloc) {
+        rb_raise(rb_eTypeError, "allocator undefined for %s",
+                 rb_class2name(obj));
+    }
+
+    VALUE *new_argv = (VALUE *)alloca(sizeof(VALUE) * (argc + 1));
+
+    char buf[100];
+    int n = snprintf(buf, sizeof buf, "%s", sel_getName(sel));
+    if (buf[n - 1] == ':') {
+      // Let's see if there are more colons making this a real selector.
+      bool multiple_colons = false;
+      for (int i = 0; i < (n - 1); i++) {
+        if (buf[i] == ':') {
+          multiple_colons = true;
+          break;
+        }
+      }
+      if (!multiple_colons) {
+        // Not a typical multiple argument selector. So as this is probably a
+        // typical ruby method name, chop off the colon.
+        buf[n - 1] = '\0';
+      }
+    }
+    new_argv[0] = ID2SYM(rb_intern(buf));
+    MEMCPY(&new_argv[1], argv, VALUE, argc);
+
+    struct mcache *cache;
+    cache = GET_CORE()->method_cache_get(selMethodMissing, false);
+    return rb_vm_call_with_cache2(cache, block, obj, NULL, selMethodMissing,
+    	argc + 1, new_argv);
+}
+
+extern "C"
+void *
+rb_vm_undefined_imp(void *rcv, SEL sel)
+{
+    method_missing((VALUE)rcv, sel, NULL, NULL, NULL, METHOD_MISSING_DEFAULT);
+    return NULL; // never reached
+}
+
+static force_inline VALUE
+__rb_vm_ruby_dispatch(VALUE self, SEL sel, rb_vm_method_node_t *node,
+		      unsigned char opt, int argc, const VALUE *argv)
+{
+    const rb_vm_arity_t &arity = node->arity;
+    if ((argc < arity.min) || ((arity.max != -1) && (argc > arity.max))) {
+	rb_raise(rb_eArgError, "wrong number of arguments (%d for %d)",
+		argc, arity.min);
+    }
+
+    if ((node->flags & VM_METHOD_PRIVATE) && opt == 0) {
+	// Calling a private method with no explicit receiver OR an attribute
+	// assignment to non-self, triggering #method_missing.
+	rb_vm_block_t *b = GET_VM()->current_block();
+	return method_missing(self, sel, b, argc, argv, METHOD_MISSING_PRIVATE);
+    }
+
+    if ((node->flags & VM_METHOD_EMPTY) && arity.max == arity.min) {
+	// Calling an empty method, let's just return nil!
+	return Qnil;
+    }
+
+    if ((node->flags & VM_METHOD_FBODY) && arity.max != arity.min) {
+	// Calling a function defined with rb_objc_define_method with
+	// a negative arity, which means a different calling convention.
+	if (arity.real == 2) {
+	    return ((VALUE (*)(VALUE, SEL, int, const VALUE *))node->ruby_imp)
+		(self, sel, argc, argv);
+	}
+	else if (arity.real == 1) {
+	    return ((VALUE (*)(VALUE, SEL, ...))node->ruby_imp)
+		(self, sel, rb_ary_new4(argc, argv));
+	}
+	else {
+	    printf("invalid negative arity for C function %d\n",
+		    arity.real);
+	    abort();
+	}
+    }
+
+    return __rb_vm_rcall(self, sel, node->ruby_imp, arity, argc, argv);
+}
+
+static void
+fill_rcache(struct mcache *cache, Class klass, SEL sel,
+	rb_vm_method_node_t *node)
+{
+    cache->flag = MCACHE_RCALL;
+    rcache.klass = klass;
+    rcache.node = node;
+}
+
+static bool
+can_forwardInvocation(VALUE recv, SEL sel)
+{
+    if (!SPECIAL_CONST_P(recv)) {
+	static SEL methodSignatureForSelector = 0;
+	if (methodSignatureForSelector == 0) {
+	    methodSignatureForSelector =
+		sel_registerName("methodSignatureForSelector:");	
+	}
+	return objc_msgSend((id)recv, methodSignatureForSelector, (id)sel)
+	    != nil;
+    }
+    return false;
+}
+
+static void
+fill_ocache(struct mcache *cache, VALUE self, Class klass, IMP imp, SEL sel,
+	    Method method, int argc)
+{
+    cache->flag = MCACHE_OCALL;
+    ocache.klass = klass;
+    ocache.imp = imp;
+    ocache.bs_method = GET_CORE()->find_bs_method(klass, sel);
+
+    char types[200];
+    if (!rb_objc_get_types(self, klass, sel, method, ocache.bs_method,
+		types, sizeof types)) {
+	printf("cannot get encoding types for %c[%s %s]\n",
+		class_isMetaClass(klass) ? '+' : '-',
+		class_getName(klass),
+		sel_getName(sel));
+	abort();
+    }
+    bool variadic = false;
+    if (ocache.bs_method != NULL && ocache.bs_method->variadic
+	&& method != NULL) {
+	// TODO honor printf_format
+	const int real_argc = method_getNumberOfArguments(method) - 2;
+	if (real_argc < argc) {
+	    const size_t s = strlen(types);
+	    assert(s + argc - real_argc < sizeof types);
+	    for (int i = real_argc; i < argc; i++) {
+		strlcat(types, "@", sizeof types);
+	    }
+	    argc = real_argc;
+	}
+	variadic = true;
+    }
+    ocache.stub = (rb_vm_objc_stub_t *)GET_CORE()->gen_stub(types, variadic,
+	    argc, true);
+}
+
+static force_inline VALUE
+__rb_vm_dispatch(RoxorVM *vm, struct mcache *cache, VALUE self, Class klass,
+	SEL sel, rb_vm_block_t *block, unsigned char opt, int argc,
+	const VALUE *argv)
+{
+    assert(cache != NULL);
+
+    if (klass == NULL) {
+	klass = (Class)CLASS_OF(self);
+    }
+
+#if ROXOR_VM_DEBUG
+    bool cached = true;
+#endif
+    bool do_rcache = true;
+
+    if (cache->flag == 0) {
+recache:
+#if ROXOR_VM_DEBUG
+	cached = false;
+#endif
+
+	Method method;
+	if (opt == DISPATCH_SUPER) {
+	    method = rb_vm_super_lookup((VALUE)klass, sel);
+	}
+	else {
+	    method = class_getInstanceMethod(klass, sel);
+	}
+
+	if (method != NULL) {
+recache2:
+	    IMP imp = method_getImplementation(method);
+
+	    if (UNDEFINED_IMP(imp)) {
+		// Method was undefined.
+		goto call_method_missing;
+	    }
+
+	    rb_vm_method_node_t *node = GET_CORE()->method_node_get(method);
+
+	    if (node != NULL) {
+		// ruby call
+		fill_rcache(cache, klass, sel, node);
+	    }
+	    else {
+		// objc call
+		fill_ocache(cache, self, klass, imp, sel, method, argc);
+	    }
+	}
+	else {
+	    // Method is not found...
+
+	    // Force a method resolving, because the objc cache might be
+	    // wrong.
+	    if (rb_vm_resolve_method(klass, sel)) {
+		goto recache;
+	    }
+
+	    // Does the receiver implements -forwardInvocation:?
+	    if (opt != DISPATCH_SUPER && can_forwardInvocation(self, sel)) {
+		fill_ocache(cache, self, klass, (IMP)objc_msgSend, sel, NULL,
+			argc);
+		goto dispatch;
+	    }
+
+	    // Let's see if are not trying to call a Ruby method that accepts
+	    // a regular argument then a optional Hash argument, to be
+	    // compatible with the Ruby specification.
+	    const char *selname = (const char *)sel;
+	    size_t selname_len = strlen(selname);
+	    if (argc > 1) {
+		const char *p = strchr(selname, ':');
+		if (p != NULL && p + 1 != '\0') {
+		    char *tmp = (char *)alloca(selname_len);
+		    strncpy(tmp, selname, p - selname + 1);
+		    tmp[p - selname + 1] = '\0';
+		    SEL new_sel = sel_registerName(tmp);
+		    Method m = class_getInstanceMethod(klass, new_sel);
+		    if (m != NULL) {
+			VALUE h = rb_hash_new();
+			bool ok = true;
+			p += 1;
+			for (int i = 1; i < argc; i++) {
+			    const char *p2 = strchr(p, ':');
+			    if (p2 == NULL) {
+				ok = false;
+				break;
+			    }
+			    strlcpy(tmp, p, selname_len);
+			    tmp[p2 - p] = '\0';
+			    p = p2 + 1; 
+			    rb_hash_aset(h, ID2SYM(rb_intern(tmp)), argv[i]);
+			}
+			if (ok) {
+			    argc = 2;
+			    ((VALUE *)argv)[1] = h; // bad, I know...
+			    sel = new_sel;
+			    method = m;
+			    do_rcache = false;
+			    goto recache2;
+			}
+		    }
+		}
+	    }
+
+	    // Let's try to see if we are not given a helper selector.
+	    SEL new_sel = helper_sel(selname, selname_len);
+	    if (new_sel != NULL) {
+		Method m = class_getInstanceMethod(klass, new_sel);
+		if (m != NULL) {
+		    if (GET_CORE()->method_node_get(m) == NULL) {
+			sel = new_sel;
+			method = m;
+			goto recache2;
+		    }
+		}
+	    }
+
+	    // Let's see if we are not trying to call a BridgeSupport function.
+	    if (selname[selname_len - 1] == ':') {
+		selname_len--;
+	    }
+	    std::string name(selname, selname_len);
+	    bs_element_function_t *bs_func = GET_CORE()->find_bs_function(name);
+	    if (bs_func != NULL) {
+		std::string types;
+		vm_gen_bs_func_types(argc, argv, bs_func, types);
+
+		cache->flag = MCACHE_FCALL;
+		fcache.bs_function = bs_func;
+		fcache.imp = (IMP)dlsym(RTLD_DEFAULT, bs_func->name);
+		assert(fcache.imp != NULL);
+		fcache.stub = (rb_vm_c_stub_t *)GET_CORE()->gen_stub(types,
+			bs_func->variadic, bs_func->args_count, false);
+	    }
+	    else {
+		// Still nothing, then let's call #method_missing.
+		goto call_method_missing;
+	    }
+	}
+    }
+
+dispatch:
+    if (cache->flag == MCACHE_RCALL) {
+	if (rcache.klass != klass) {
+	    goto recache;
+	}
+	if (!do_rcache) {
+	    cache->flag = 0;
+	}
+
+#if ROXOR_VM_DEBUG
+	printf("ruby dispatch %c[<%s %p> %s] (imp=%p, block=%p, argc=%d, cached=%s)\n",
+		class_isMetaClass(klass) ? '+' : '-',
+		class_getName(klass),
+		(void *)self,
+		sel_getName(sel),
+		rcache.node->ruby_imp,
+		block,
+		argc,
+		cached ? "true" : "false");
+#endif
+
+	bool block_already_current = vm->is_block_current(block);
+	Class current_klass = vm->get_current_class();
+	if (!block_already_current) {
+	    vm->add_current_block(block);
+	}
+	vm->set_current_class(NULL);
+
+	struct Finally {
+	    bool block_already_current;
+	    Class current_class;
+	    RoxorVM *vm;
+	    Finally(bool _block_already_current, Class _current_class,
+		    RoxorVM *_vm) {
+		block_already_current = _block_already_current;
+		current_class = _current_class;
+		vm = _vm;
+	    }
+	    ~Finally() {
+		if (!block_already_current) {
+		    vm->pop_current_block();
+		}
+		vm->set_current_class(current_class);
+		vm->pop_broken_with();
+	    }
+	} finalizer(block_already_current, current_klass, vm);
+
+	return __rb_vm_ruby_dispatch(self, sel, rcache.node, opt, argc, argv);
+    }
+    else if (cache->flag == MCACHE_OCALL) {
+	if (ocache.klass != klass) {
+	    goto recache;
+	}
+
+	if (block != NULL) {
+	    if (self == rb_cNSMutableHash && sel == selNew) {
+		// Because Hash.new can accept a block.
+		vm->add_current_block(block);
+
+		struct Finally {
+		    RoxorVM *vm;
+		    Finally(RoxorVM *_vm) { vm = _vm; }
+		    ~Finally() { vm->pop_current_block(); }
+		} finalizer(vm);
+
+		return rb_hash_new2(argc, argv);
+	    }
+	    rb_warn("passing a block to an Objective-C method - " \
+		    "will be ignored");
+	}
+	else if (sel == selNew) {
+	    if (self == rb_cNSMutableArray) {
+		self = rb_cRubyArray;
+	    }
+	}
+	else if (sel == selClass) {
+	    // Because +[NSObject class] returns self.
+	    if (RCLASS_META(klass)) {
+		return RCLASS_MODULE(self) ? rb_cModule : rb_cClass;
+	    }
+	    // Because the CF classes should be hidden, for Ruby compat.
+	    if (self == Qnil) {
+		return rb_cNilClass;
+	    }
+	    if (self == Qtrue) {
+		return rb_cTrueClass;
+	    }
+	    if (self == Qfalse) {
+		return rb_cFalseClass;
+	    }
+	    if (klass == (Class)rb_cCFString) {
+		return RSTRING_IMMUTABLE(self)
+		    ? rb_cNSString : rb_cNSMutableString;
+	    }
+#if MAC_OS_X_VERSION_MAX_ALLOWED >= 1060
+	    if (klass == (Class)rb_cCFArray || klass == (Class)rb_cNSArray0) {
+#else
+	    if (klass == (Class)rb_cCFArray) {
+#endif
+		return RARRAY_IMMUTABLE(self)
+		    ? rb_cNSArray : rb_cNSMutableArray;
+	    }
+	    else if (klass == (Class)rb_cRubyArray) {
+		return rb_cNSMutableArray;
+	    }
+#if MAC_OS_X_VERSION_MAX_ALLOWED >= 1060
+	    if (klass == (Class)rb_cCFHash || klass == (Class)rb_cNSHash0) {
+#else
+	    if (klass == (Class)rb_cCFHash) {
+#endif
+		return RHASH_IMMUTABLE(self)
+		    ? rb_cNSHash : rb_cNSMutableHash;
+	    }
+	    if (klass == (Class)rb_cCFSet) {
+		return RSET_IMMUTABLE(self)
+		    ? rb_cNSSet : rb_cNSMutableSet;
+	    }
+	}
+
+#if ROXOR_VM_DEBUG
+	printf("objc dispatch %c[<%s %p> %s] imp=%p argc=%d (cached=%s)\n",
+		class_isMetaClass(klass) ? '+' : '-',
+		class_getName(klass),
+		(void *)self,
+		sel_getName(sel),
+		ocache.imp,
+		argc,
+		cached ? "true" : "false");
+#endif
+
+	return (*ocache.stub)(ocache.imp, RB2OC(self), sel, argc, argv);
+    }
+    else if (cache->flag == MCACHE_FCALL) {
+#if ROXOR_VM_DEBUG
+	printf("C dispatch %s() imp=%p argc=%d (cached=%s)\n",
+		fcache.bs_function->name,
+		fcache.imp,
+		argc,
+		cached ? "true" : "false");
+#endif
+	return (*fcache.stub)(fcache.imp, argc, argv);
+    }
+
+    printf("method dispatch is b0rked\n");
+    abort();
+
+call_method_missing:
+    // Before calling method_missing, let's check if we are not in the following
+    // cases:
+    //
+    //    def foo; end; foo(42)
+    //    def foo(x); end; foo
+    //
+    // If yes, we need to raise an ArgumentError exception instead.
+    const char *selname = sel_getName(sel);
+    const size_t selname_len = strlen(selname);
+    SEL new_sel = 0;
+    int argc_expected;
+
+    if (argc > 0 && selname[selname_len - 1] == ':') {
+	char buf[100];
+	assert(sizeof buf > selname_len - 1);
+	strlcpy(buf, selname, sizeof buf);
+	buf[selname_len - 1] = '\0';
+	new_sel = sel_registerName(buf);
+	argc_expected = 0;
+    }
+    else if (argc == 0) {
+	char buf[100];
+	snprintf(buf, sizeof buf, "%s:", selname);
+	new_sel = sel_registerName(buf);
+	argc_expected = 1;
+    }
+    if (new_sel != 0) {
+	Method m = class_getInstanceMethod(klass, new_sel);
+	if (m != NULL
+		&& GET_CORE()->method_node_get(m) != NULL) {
+	    rb_raise(rb_eArgError, "wrong number of arguments (%d for %d)",
+		    argc, argc_expected);
+	}
+    }
+
+    rb_vm_method_missing_reason_t status =
+	opt == DISPATCH_VCALL
+	    ? METHOD_MISSING_VCALL : opt == DISPATCH_SUPER
+		? METHOD_MISSING_SUPER : METHOD_MISSING_DEFAULT;
+    return method_missing((VALUE)self, sel, block, argc, argv, status);
+}
+
+static force_inline void
+__rb_vm_resolve_args(VALUE **pargv, size_t argv_size, int *pargc, va_list ar)
+{
+    // TODO we should only determine the real argc here (by taking into
+    // account the length splat arguments) and do the real unpacking of
+    // splat arguments in __rb_vm_rcall(). This way we can optimize more
+    // things (for ex. no need to unpack splats that are passed as a splat
+    // argument in the method being called!).
+    unsigned int i, argc = *pargc, real_argc = 0;
+    VALUE *argv = *pargv;
+    bool splat_arg_follows = false;
+    for (i = 0; i < argc; i++) {
+	VALUE arg = va_arg(ar, VALUE);
+	if (arg == SPLAT_ARG_FOLLOWS) {
+	    splat_arg_follows = true;
+	    i--;
+	}
+	else {
+	    if (splat_arg_follows) {
+		VALUE ary = rb_check_convert_type(arg, T_ARRAY, "Array",
+			"to_a");
+		if (NIL_P(ary)) {
+		    ary = rb_ary_new3(1, arg);
+		}
+		int count = RARRAY_LEN(ary);
+		if (real_argc + count >= argv_size) {
+		    const size_t new_argv_size = real_argc + count + 100;
+		    VALUE *new_argv = (VALUE *)xmalloc(sizeof(VALUE)
+			    * new_argv_size);
+		    memcpy(new_argv, argv, sizeof(VALUE) * argv_size);
+		    argv = new_argv;
+		    argv_size = new_argv_size;
+		}
+		for (int j = 0; j < count; j++) {
+		    argv[real_argc++] = RARRAY_AT(ary, j);
+		}
+		splat_arg_follows = false;
+	    }
+	    else {
+		if (real_argc >= argv_size) {
+		    const size_t new_argv_size = real_argc + 100;
+		    VALUE *new_argv = (VALUE *)xmalloc(sizeof(VALUE)
+			    * new_argv_size);
+		    memcpy(new_argv, argv, sizeof(VALUE) * argv_size);
+		    argv = new_argv;
+		    argv_size = new_argv_size;
+		}
+		argv[real_argc++] = arg;
+	    }
+	}
+    }
+
+    *pargv = argv;
+    *pargc = real_argc;
+}
+
+extern "C"
+VALUE
+rb_vm_dispatch(struct mcache *cache, VALUE self, SEL sel, rb_vm_block_t *block, 
+	       unsigned char opt, int argc, ...)
+{
+    VALUE base_argv[MAX_DISPATCH_ARGS];
+    VALUE *argv = base_argv;
+    if (argc > 0) {
+	va_list ar;
+	va_start(ar, argc);
+	__rb_vm_resolve_args(&argv, MAX_DISPATCH_ARGS, &argc, ar);
+	va_end(ar);
+
+	if (argc == 0) {
+	    const char *selname = sel_getName(sel);
+	    const size_t selnamelen = strlen(selname);
+	    if (selname[selnamelen - 1] == ':') {
+		// Because
+		//   def foo; end; foo(*[])
+		// creates foo but dispatches foo:.
+		char buf[100];
+		strncpy(buf, selname, sizeof buf);
+		buf[selnamelen - 1] = '\0';
+		sel = sel_registerName(buf);
+	    }
+	}
+    }
+
+    RoxorVM *vm = GET_VM();
+
+    VALUE retval = __rb_vm_dispatch(vm, cache, self, NULL, sel, block, opt,
+	    argc, argv);
+
+    vm->pop_current_binding();
+
+    return retval;
+}
+
+extern "C"
+VALUE
+rb_vm_call(VALUE self, SEL sel, int argc, const VALUE *argv, bool super)
+{
+    struct mcache *cache;
+    unsigned char opt = DISPATCH_FCALL;
+    if (super) {
+	cache = (struct mcache *)alloca(sizeof(struct mcache));
+	cache->flag = 0;
+	opt = DISPATCH_SUPER;
+    }
+    else {
+	cache = GET_CORE()->method_cache_get(sel, false);
+    }
+
+    return __rb_vm_dispatch(GET_VM(), cache, self, NULL, sel, NULL, opt, argc,
+	    argv);
+}
+
+extern "C"
+VALUE
+rb_vm_call_with_cache(void *cache, VALUE self, SEL sel, int argc, 
+	const VALUE *argv)
+{
+    return __rb_vm_dispatch(GET_VM(), (struct mcache *)cache, self, NULL, sel,
+	    NULL, DISPATCH_FCALL, argc, argv);
+}
+
+extern "C"
+VALUE
+rb_vm_call_with_cache2(void *cache, rb_vm_block_t *block, VALUE self,
+	VALUE klass, SEL sel, int argc, const VALUE *argv)
+{
+    return __rb_vm_dispatch(GET_VM(), (struct mcache *)cache, self,
+	    (Class)klass, sel, block, DISPATCH_FCALL, argc, argv);
+}
+
+// The rb_vm_fast_* functions don't check if the selector has been redefined or
+// not, because this is already handled by the compiler.
+// Also, fixnums and floats are already handled.
+
+extern "C" {
+    VALUE rb_fix_plus(VALUE x, VALUE y);
+    VALUE rb_fix_minus(VALUE x, VALUE y);
+    VALUE rb_fix_div(VALUE x, VALUE y);
+    VALUE rb_fix_mul(VALUE x, VALUE y);
+    VALUE rb_flo_plus(VALUE x, VALUE y);
+    VALUE rb_flo_minus(VALUE x, VALUE y);
+    VALUE rb_flo_div(VALUE x, VALUE y);
+    VALUE rb_flo_mul(VALUE x, VALUE y);
+    VALUE rb_nu_plus(VALUE x, VALUE y);
+    VALUE rb_nu_minus(VALUE x, VALUE y);
+    VALUE rb_nu_div(VALUE x, VALUE y);
+    VALUE rb_nu_mul(VALUE x, VALUE y);
+}
+
+extern "C"
+VALUE
+rb_vm_fast_plus(struct mcache *cache, VALUE self, VALUE other)
+{
+    switch (TYPE(self)) {
+	// TODO: Array, String
+	case T_BIGNUM:
+	    return rb_big_plus(self, other);
+	case T_FIXNUM:
+	    return rb_fix_plus(self, other);
+	case T_FLOAT:
+	    return rb_flo_plus(self, other);
+	case T_COMPLEX:
+	    return rb_nu_plus(self, other);
+    }
+    return rb_vm_dispatch(cache, self, selPLUS, NULL, 0, 1, other);
+}
+
+extern "C"
+VALUE
+rb_vm_fast_minus(struct mcache *cache, VALUE self, VALUE other)
+{
+    switch (TYPE(self)) {
+	// TODO: Array, String
+	case T_BIGNUM:
+	    return rb_big_minus(self, other);
+	case T_FIXNUM:
+	    return rb_fix_minus(self, other);
+	case T_FLOAT:
+	    return rb_flo_minus(self, other);
+	case T_COMPLEX:
+	    return rb_nu_minus(self, other);
+    }
+    return rb_vm_dispatch(cache, self, selMINUS, NULL, 0, 1, other);
+}
+
+extern "C"
+VALUE
+rb_vm_fast_div(struct mcache *cache, VALUE self, VALUE other)
+{
+    switch (TYPE(self)) {
+	case T_BIGNUM:
+	    return rb_big_div(self, other);
+	case T_FIXNUM:
+	    return rb_fix_div(self, other);
+	case T_FLOAT:
+	    return rb_flo_div(self, other);
+	case T_COMPLEX:
+	    return rb_nu_div(self, other);
+    }
+    return rb_vm_dispatch(cache, self, selDIV, NULL, 0, 1, other);
+}
+
+extern "C"
+VALUE
+rb_vm_fast_mult(struct mcache *cache, VALUE self, VALUE other)
+{
+    switch (TYPE(self)) {
+	// TODO: Array, String
+	case T_BIGNUM:
+	    return rb_big_mul(self, other);
+	case T_FIXNUM:
+	    return rb_fix_mul(self, other);
+	case T_FLOAT:
+	    return rb_flo_mul(self, other);
+	case T_COMPLEX:
+	    return rb_nu_mul(self, other);
+    }
+    return rb_vm_dispatch(cache, self, selMULT, NULL, 0, 1, other);
+}
+
+extern "C"
+VALUE
+rb_vm_fast_lt(struct mcache *cache, VALUE self, VALUE other)
+{
+    switch (TYPE(self)) {
+	case T_BIGNUM:
+	    return FIX2INT(rb_big_cmp(self, other)) < 0 ? Qtrue : Qfalse;
+    }
+    return rb_vm_dispatch(cache, self, selLT, NULL, 0, 1, other);
+}
+
+extern "C"
+VALUE
+rb_vm_fast_le(struct mcache *cache, VALUE self, VALUE other)
+{
+    switch (TYPE(self)) {
+	case T_BIGNUM:
+	    return FIX2INT(rb_big_cmp(self, other)) <= 0 ? Qtrue : Qfalse;
+    }
+    return rb_vm_dispatch(cache, self, selLE, NULL, 0, 1, other);
+}
+
+extern "C"
+VALUE
+rb_vm_fast_gt(struct mcache *cache, VALUE self, VALUE other)
+{
+    switch (TYPE(self)) {
+	case T_BIGNUM:
+	    return FIX2INT(rb_big_cmp(self, other)) > 0 ? Qtrue : Qfalse;
+    }
+    return rb_vm_dispatch(cache, self, selGT, NULL, 0, 1, other);
+}
+
+extern "C"
+VALUE
+rb_vm_fast_ge(struct mcache *cache, VALUE self, VALUE other)
+{
+    switch (TYPE(self)) {
+	case T_BIGNUM:
+	    return FIX2INT(rb_big_cmp(self, other)) >= 0 ? Qtrue : Qfalse;
+    }
+    return rb_vm_dispatch(cache, self, selGE, NULL, 0, 1, other);
+}
+
+extern "C"
+VALUE
+rb_vm_fast_eq(struct mcache *cache, VALUE self, VALUE other)
+{
+    const int self_type = TYPE(self);
+    switch (self_type) {
+	case T_SYMBOL:
+	    return self == other ? Qtrue : Qfalse;
+
+	case T_STRING:
+	case T_ARRAY:
+	case T_HASH:
+	    if (self == other) {
+		return Qtrue;
+	    }
+	    if (TYPE(other) != self_type) {
+		return Qfalse;
+	    }
+	    if (self_type == T_ARRAY) {
+		return rb_ary_equal(self, other);
+	    }
+	    return CFEqual((CFTypeRef)self, (CFTypeRef)other)
+		? Qtrue : Qfalse;
+
+	case T_BIGNUM:
+	    return rb_big_eq(self, other);
+    }
+    return rb_vm_dispatch(cache, self, selEq, NULL, 0, 1, other);
+}
+
+extern "C"
+VALUE
+rb_vm_fast_neq(struct mcache *cache, VALUE self, VALUE other)
+{
+    // TODO
+    return rb_vm_dispatch(cache, self, selNeq, NULL, 0, 1, other);
+}
+
+extern "C"
+VALUE
+rb_vm_fast_eqq(struct mcache *cache, VALUE self, VALUE other)
+{
+    switch (TYPE(self)) {
+	// TODO: Range
+	case T_STRING:
+	    if (self == other) {
+		return Qtrue;
+	    }
+	    return rb_str_equal(self, other);
+
+	case T_REGEXP:
+	    return rb_reg_eqq(self, selEqq, other);
+
+	case T_SYMBOL:
+	    return (self == other ? Qtrue : Qfalse);
+	
+	case T_MODULE:
+	case T_CLASS:
+	    return rb_obj_is_kind_of(other, self);
+
+	default:
+	    return rb_vm_dispatch(cache, self, selEqq, NULL, 0, 1, other);
+    }
+}
+
+extern "C"
+VALUE
+rb_vm_when_splat(struct mcache *cache, unsigned char overriden,
+		 VALUE comparedTo, VALUE splat)
+{
+    VALUE ary = rb_check_convert_type(splat, T_ARRAY, "Array", "to_a");
+    if (NIL_P(ary)) {
+	ary = rb_ary_new3(1, splat);
+    }
+    int count = RARRAY_LEN(ary);
+    if (overriden == 0) {
+	for (int i = 0; i < count; ++i) {
+	    VALUE o = RARRAY_AT(ary, i);
+	    if (RTEST(rb_vm_fast_eqq(cache, o, comparedTo))) {
+		return Qtrue;
+	    }
+	}
+    }
+    else {
+	for (int i = 0; i < count; ++i) {
+	    VALUE o = RARRAY_AT(ary, i);
+	    if (RTEST(rb_vm_dispatch(cache, o, selEqq, NULL, 0, 1, comparedTo))) {
+		return Qtrue;
+	    }
+	}
+    }
+    return Qfalse;
+}
+
+extern "C"
+VALUE
+rb_vm_fast_shift(VALUE obj, VALUE other, struct mcache *cache,
+		 unsigned char overriden)
+{
+    if (overriden == 0) {
+	switch (TYPE(obj)) {
+	    case T_ARRAY:
+		rb_ary_push(obj, other);
+		return obj;
+
+	    case T_STRING:
+		rb_str_concat(obj, other);
+		return obj;
+	}
+    }
+    return __rb_vm_dispatch(GET_VM(), cache, obj, NULL, selLTLT, NULL, 0, 1,
+	    &other);
+}
+
+extern "C"
+VALUE
+rb_vm_fast_aref(VALUE obj, VALUE other, struct mcache *cache,
+		unsigned char overriden)
+{
+    // TODO what about T_HASH?
+    if (overriden == 0 && TYPE(obj) == T_ARRAY) {
+	if (TYPE(other) == T_FIXNUM) {
+	    return rb_ary_entry(obj, FIX2LONG(other));
+	}
+	return rb_ary_aref(obj, 0, 1, &other);
+    }
+    return __rb_vm_dispatch(GET_VM(), cache, obj, NULL, selAREF, NULL, 0, 1,
+	    &other);
+}
+
+extern "C"
+VALUE
+rb_vm_fast_aset(VALUE obj, VALUE other1, VALUE other2, struct mcache *cache,
+		unsigned char overriden)
+{
+    // TODO what about T_HASH?
+    if (overriden == 0 && TYPE(obj) == T_ARRAY) {
+	if (TYPE(other1) == T_FIXNUM) {
+	    rb_ary_store(obj, FIX2LONG(other1), other2);
+	    return other2;
+	}
+    }
+    VALUE args[2] = { other1, other2 };
+    return __rb_vm_dispatch(GET_VM(), cache, obj, NULL, selASET, NULL, 0, 2,
+	    args);
+}
+
+static rb_vm_block_t *
+rb_vm_dup_active_block(rb_vm_block_t *src_b)
+{
+    assert(src_b->flags & VM_BLOCK_ACTIVE);
+
+    const size_t block_size = sizeof(rb_vm_block_t)
+	    + (sizeof(VALUE *) * src_b->dvars_size);
+
+    rb_vm_block_t *new_b = (rb_vm_block_t *)xmalloc(block_size);
+
+    memcpy(new_b, src_b, block_size);
+    new_b->proc = src_b->proc; // weak
+    GC_WB(&new_b->parent_block, src_b->parent_block);
+    GC_WB(&new_b->self, src_b->self);
+    new_b->flags = src_b->flags & ~VM_BLOCK_ACTIVE;
+
+    rb_vm_local_t *src_l = src_b->locals;
+    rb_vm_local_t **new_l = &new_b->locals;
+    while (src_l != NULL) {
+	GC_WB(new_l, xmalloc(sizeof(rb_vm_local_t)));
+	(*new_l)->name = src_l->name;
+	(*new_l)->value = src_l->value;
+
+	new_l = &(*new_l)->next;
+	src_l = src_l->next;
+    }
+    *new_l = NULL;
+
+    return new_b;
+}
+
+static force_inline VALUE
+rb_vm_block_eval0(rb_vm_block_t *b, SEL sel, VALUE self, int argc,
+	const VALUE *argv)
+{
+    if ((b->flags & VM_BLOCK_IFUNC) == VM_BLOCK_IFUNC) {
+	// Special case for blocks passed with rb_objc_block_call(), to
+	// preserve API compatibility.
+	VALUE (*pimp)(VALUE, VALUE, int, const VALUE *) =
+	    (VALUE (*)(VALUE, VALUE, int, const VALUE *))b->imp;
+
+	return (*pimp)(argc == 0 ? Qnil : argv[0], b->userdata, argc, argv);
+    }
+    else if ((b->flags & VM_BLOCK_EMPTY) == VM_BLOCK_EMPTY) {
+	// Trying to call an empty block!
+	return Qnil;
+    }
+
+    rb_vm_arity_t arity = b->arity;    
+
+    if (argc < arity.min || argc > arity.max) {
+	if (arity.max != -1
+		&& (b->flags & VM_BLOCK_LAMBDA) == VM_BLOCK_LAMBDA) {
+	    rb_raise(rb_eArgError, "wrong number of arguments (%d for %d)",
+		    argc, arity.min);
+	}
+	VALUE *new_argv;
+	if (argc == 1 && TYPE(argv[0]) == T_ARRAY
+	    && (arity.min > 1 || (arity.min == 1 && arity.min != arity.max))) {
+	    // Expand the array
+	    long ary_len = RARRAY_LEN(argv[0]);
+	    new_argv = (VALUE *)alloca(sizeof(VALUE) * ary_len);
+	    for (int i = 0; i < ary_len; i++) {
+		new_argv[i] = RARRAY_AT(argv[0], i);
+	    }
+	    argv = new_argv;
+	    argc = ary_len;
+	    if (argc >= arity.min && (argc <= arity.max || b->arity.max == -1)) {
+		goto block_call;
+	    }
+	}
+	int new_argc;
+	if (argc <= arity.min) {
+	    new_argc = arity.min;
+	}
+	else if (argc > arity.max && b->arity.max != -1) {
+	    new_argc = arity.max;
+	}
+	else {
+	    new_argc = argc;
+	}
+	new_argv = (VALUE *)alloca(sizeof(VALUE) * new_argc);
+	for (int i = 0; i < new_argc; i++) {
+	    new_argv[i] = i < argc ? argv[i] : Qnil;
+	}
+	argc = new_argc;
+	argv = new_argv;
+    }
+#if ROXOR_VM_DEBUG
+    printf("yield block %p argc %d arity %d\n", b, argc, arity.real);
+#endif
+
+block_call:
+
+    if (b->flags & VM_BLOCK_ACTIVE) {
+	b = rb_vm_dup_active_block(b);
+    }
+    b->flags |= VM_BLOCK_ACTIVE;
+
+    RoxorVM *vm = GET_VM();
+    Class old_current_class = vm->get_current_class();
+    vm->set_current_class((Class)b->klass);
+
+    struct Finally {
+	RoxorVM *vm;
+	rb_vm_block_t *b;
+	Class c;
+	Finally(RoxorVM *_vm, rb_vm_block_t *_b, Class _c) {
+	    vm = _vm;
+	    b = _b;
+	    c = _c;
+	}
+	~Finally() {
+	    b->flags &= ~VM_BLOCK_ACTIVE;
+	    vm->set_current_class(c);
+	}
+    } finalizer(vm, b, old_current_class);
+
+    if (b->flags & VM_BLOCK_METHOD) {
+	rb_vm_method_t *m = (rb_vm_method_t *)b->imp;
+	return rb_vm_call_with_cache2(m->cache, NULL, m->recv, m->oclass,
+		m->sel, argc, argv);
+    }
+    return __rb_vm_bcall(self, sel, (VALUE)b->dvars, b, b->imp, b->arity,
+	    argc, argv);
+}
+
+extern "C"
+VALUE
+rb_vm_block_eval(rb_vm_block_t *b, int argc, const VALUE *argv)
+{
+    return rb_vm_block_eval0(b, NULL, b->self, argc, argv);
+}
+
+extern "C"
+VALUE
+rb_vm_block_eval2(rb_vm_block_t *b, VALUE self, SEL sel, int argc,
+	const VALUE *argv)
+{
+    // TODO check given arity and raise exception
+    return rb_vm_block_eval0(b, sel, self, argc, argv);
+}
+
+static force_inline VALUE
+rb_vm_yield0(int argc, const VALUE *argv)
+{
+    RoxorVM *vm = GET_VM();
+    rb_vm_block_t *b = vm->current_block();
+    if (b == NULL) {
+	rb_raise(rb_eLocalJumpError, "no block given");
+    }
+
+    vm->pop_current_block();
+
+    struct Finally {
+	RoxorVM *vm;
+	rb_vm_block_t *b;
+	Finally(RoxorVM *_vm, rb_vm_block_t *_b) { 
+	    vm = _vm;
+	    b = _b;
+	}
+	~Finally() {
+	    vm->add_current_block(b);
+	}
+    } finalizer(vm, b);
+
+    return rb_vm_block_eval0(b, NULL, b->self, argc, argv);
+}
+
+extern "C"
+VALUE
+rb_vm_yield(int argc, const VALUE *argv)
+{
+    return rb_vm_yield0(argc, argv);
+}
+
+extern "C"
+VALUE
+rb_vm_yield_under(VALUE klass, VALUE self, int argc, const VALUE *argv)
+{
+    RoxorVM *vm = GET_VM();
+    rb_vm_block_t *b = vm->current_block();
+    vm->pop_current_block();
+
+    VALUE old_self = b->self;
+    b->self = self;
+    VALUE old_class = b->klass;
+    b->klass = klass;
+
+    struct Finally {
+	RoxorVM *vm;
+	rb_vm_block_t *b;
+	VALUE old_class;
+	VALUE old_self;
+	Finally(RoxorVM *_vm, rb_vm_block_t *_b, VALUE _old_class,
+		VALUE _old_self) {
+	    vm = _vm;
+	    b = _b;
+	    old_class = _old_class;
+	    old_self = _old_self;
+	}
+	~Finally() {
+	    b->self = old_self;
+	    b->klass = old_class;
+	    vm->add_current_block(b);
+	}
+    } finalizer(vm, b, old_class, old_self);
+
+    return rb_vm_block_eval0(b, NULL, b->self, argc, argv);
+}
+
+extern "C"
+VALUE 
+rb_vm_yield_args(int argc, ...)
+{
+    VALUE base_argv[MAX_DISPATCH_ARGS];
+    VALUE *argv = &base_argv[0];
+    if (argc > 0) {
+	va_list ar;
+	va_start(ar, argc);
+	__rb_vm_resolve_args(&argv, MAX_DISPATCH_ARGS, &argc, ar);
+	va_end(ar);
+    }
+    return rb_vm_yield0(argc, argv);
+}
+
+force_inline rb_vm_block_t *
+RoxorVM::uncache_or_create_block(void *key, bool *cached, int dvars_size)
+{
+    std::map<void *, rb_vm_block_t *>::iterator iter = blocks.find(key);
+
+    rb_vm_block_t *b;
+
+    if ((iter == blocks.end())
+	|| (iter->second->flags & (VM_BLOCK_ACTIVE | VM_BLOCK_PROC))) {
+
+	if (iter != blocks.end()) {
+	    rb_objc_release(iter->second);
+	}
+
+	b = (rb_vm_block_t *)xmalloc(sizeof(rb_vm_block_t)
+		+ (sizeof(VALUE *) * dvars_size));
+	rb_objc_retain(b);
+
+	blocks[key] = b;
+	*cached = false;
+    }
+    else {
+	b = iter->second;
+	*cached = true;
+    }
+
+    return b;
+}
+
+extern "C"
+rb_vm_block_t *
+rb_vm_prepare_block(void *function, int flags, VALUE self, rb_vm_arity_t arity,
+	rb_vm_var_uses **parent_var_uses, rb_vm_block_t *parent_block,
+	int dvars_size, ...)
+{
+    assert(function != NULL);
+    RoxorVM *vm = GET_VM();
+
+    bool cached = false;
+    rb_vm_block_t *b = vm->uncache_or_create_block(function, &cached,
+	dvars_size);
+
+    bool aot_block = false;
+    if ((flags & VM_BLOCK_AOT) == VM_BLOCK_AOT) {
+	flags ^= VM_BLOCK_AOT;
+	aot_block = true;
+    }
+
+    if (!cached) {
+	if ((flags & VM_BLOCK_IFUNC) == VM_BLOCK_IFUNC) {
+	    b->imp = (IMP)function;
+	}
+	else {
+	    if (aot_block) {
+		b->imp = (IMP)function;
+	    }
+	    else {
+		GET_CORE()->lock();
+		b->imp = GET_CORE()->compile((Function *)function);
+		GET_CORE()->unlock();
+	    }
+	    b->userdata = (VALUE)function;
+	}
+	b->arity = arity;
+	b->flags = flags;
+	b->dvars_size = dvars_size;
+	b->parent_var_uses = NULL;
+	b->parent_block = NULL;
+    }
+    else {
+	assert(b->dvars_size == dvars_size);
+	assert((b->flags & flags) == flags);
+    }
+
+    b->proc = Qnil;
+    b->self = self;
+    b->klass = (VALUE)vm->get_current_class();
+    b->parent_var_uses = parent_var_uses;
+    GC_WB(&b->parent_block, parent_block);
+
+    va_list ar;
+    va_start(ar, dvars_size);
+    for (int i = 0; i < dvars_size; ++i) {
+	b->dvars[i] = va_arg(ar, VALUE *);
+    }
+    int lvars_size = va_arg(ar, int);
+    if (lvars_size > 0) {
+	if (!cached) {
+	    rb_vm_local_t **l = &b->locals;
+	    for (int i = 0; i < lvars_size; i++) {
+		GC_WB(l, xmalloc(sizeof(rb_vm_local_t)));
+		l = &(*l)->next;
+	    }
+	}
+	rb_vm_local_t *l = b->locals;
+	for (int i = 0; i < lvars_size; ++i) {
+	    assert(l != NULL);
+	    l->name = va_arg(ar, ID);
+	    l->value = va_arg(ar, VALUE *);
+	    l = l->next;
+	}
+    }
+    va_end(ar);
+
+    return b;
+}
+
+extern "C"
+rb_vm_block_t *
+rb_vm_create_block(IMP imp, VALUE self, VALUE userdata)
+{
+    rb_vm_block_t *b = rb_vm_prepare_block((void *)imp, VM_BLOCK_IFUNC, self,
+	    rb_vm_arity(0), // not used
+	    NULL, NULL, 0, 0);
+    GC_WB(&b->userdata, userdata);
+    return b;
+}
+
+extern "C" void rb_print_undef(VALUE klass, ID id, int scope);
+
+extern "C"
+rb_vm_method_t *
+rb_vm_get_method(VALUE klass, VALUE obj, ID mid, int scope)
+{
+    SEL sel = 0;
+    IMP imp = NULL;
+    rb_vm_method_node_t *node = NULL;
+
+    // TODO honor scope
+
+    if (!rb_vm_lookup_method2((Class)klass, mid, &sel, &imp, &node)) {
+	rb_print_undef(klass, mid, 0);
+    }
+
+    Class k, oklass = (Class)klass;
+    while ((k = class_getSuperclass(oklass)) != NULL) {
+	if (!rb_vm_lookup_method(k, sel, NULL, NULL)) {
+	    break;
+	}
+	oklass = k;
+    }
+
+    Method method = class_getInstanceMethod((Class)klass, sel);
+    assert(method != NULL);
+
+    int arity;
+    rb_vm_method_node_t *new_node;
+    if (node == NULL) {
+	arity = method_getNumberOfArguments(method) - 2;
+	new_node = NULL;
+    }
+    else {
+	arity = node->arity.min;
+	if (node->arity.min != node->arity.max) {
+	    arity = -arity - 1;
+	}
+	new_node = (rb_vm_method_node_t *)xmalloc(sizeof(rb_vm_method_node_t));
+	memcpy(new_node, node, sizeof(rb_vm_method_node_t));
+    }
+
+    rb_vm_method_t *m = (rb_vm_method_t *)xmalloc(sizeof(rb_vm_method_t));
+
+    m->oclass = (VALUE)oklass;
+    m->rclass = klass;
+    GC_WB(&m->recv, obj);
+    m->sel = sel;
+    m->arity = arity;
+    GC_WB(&m->node, new_node);
+
+    // Let's allocate a static cache here, since a rb_vm_method_t must always
+    // point to the method it was created from.
+    struct mcache *c = (struct mcache *)xmalloc(sizeof(struct mcache));
+    if (new_node == NULL) {
+	fill_ocache(c, obj, oklass, imp, sel, method, arity);
+    }
+    else {
+	fill_rcache(c, oklass, sel, new_node);
+    }
+    GC_WB(&m->cache, c);
+
+    return m;
+}
+
+extern IMP basic_respond_to_imp; // vm_method.c
+
+extern "C"
+bool
+rb_vm_respond_to(VALUE obj, SEL sel, bool priv)
+{
+    VALUE klass = CLASS_OF(obj);
+
+    IMP respond_to_imp = class_getMethodImplementation((Class)klass,
+	    selRespondTo);
+
+    if (respond_to_imp == basic_respond_to_imp) {
+	// FIXME: too slow!
+	bool reject_pure_ruby_methods = false;
+	Method m = class_getInstanceMethod((Class)klass, sel);
+	if (m == NULL) {
+	    const char *selname = sel_getName(sel);
+	    sel = helper_sel(selname, strlen(selname));
+	    if (sel != NULL) {
+		m = class_getInstanceMethod((Class)klass, sel);
+		reject_pure_ruby_methods = true;
+	    }
+	}
+
+	if (m == NULL || UNDEFINED_IMP(method_getImplementation(m))) {
+	    return false;
+	}
+
+	rb_vm_method_node_t *node = GET_CORE()->method_node_get(m);
+	if (node != NULL
+	    && (reject_pure_ruby_methods
+		|| (!priv && (node->flags & VM_METHOD_PRIVATE)))) {
+	    return false;
+	}
+        return true;
+    }
+    else {
+	VALUE args[2];
+	int n = 0;
+	args[n++] = ID2SYM(rb_intern(sel_getName(sel)));
+	if (priv) {
+	    args[n++] = Qtrue;
+	}
+	return rb_vm_call(obj, selRespondTo, n, args, false) == Qtrue;
+    }
+}

Modified: MacRuby/trunk/rakelib/builder.rb
===================================================================
--- MacRuby/trunk/rakelib/builder.rb	2009-10-02 00:38:19 UTC (rev 2705)
+++ MacRuby/trunk/rakelib/builder.rb	2009-10-02 03:43:08 UTC (rev 2706)
@@ -108,9 +108,15 @@
   onig/enc/utf16_be onig/enc/utf16_le onig/enc/utf32_be onig/enc/utf32_le
   ruby set 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 vm MacRuby
+  vm_eval prelude miniprelude gc-stub bridgesupport compiler dispatcher vm
+  MacRuby
 }
 
+OBJS_CFLAGS = {
+  # Make sure everything gets inlined properly.
+  'dispatcher' => '-Winline --param inline-unit-growth=10000 --param large-function-growth=10000'
+}
+
 class Builder
   attr_reader :objs, :cflags, :cxxflags
   attr_accessor :objc_cflags, :ldflags, :dldflags
@@ -122,6 +128,7 @@
     @objc_cflags = OBJC_CFLAGS
     @ldflags = LDFLAGS
     @dldflags = DLDFLAGS
+    @objs_cflags = OBJS_CFLAGS
     @obj_sources = {}
     @header_paths = {}
   end
@@ -138,6 +145,9 @@
             when '.m' then [CC, @objc_cflags]
             when '.mm' then [CXX, @cxxflags + ' ' + @objc_cflags]
           end
+        if f = @objs_cflags[obj]
+          flags += " #{f}"
+        end
         sh("#{cc} #{flags} -c #{s} -o #{obj}.o")
       end
     end

Modified: MacRuby/trunk/vm.cpp
===================================================================
--- MacRuby/trunk/vm.cpp	2009-10-02 00:38:19 UTC (rev 2705)
+++ MacRuby/trunk/vm.cpp	2009-10-02 03:43:08 UTC (rev 2706)
@@ -537,7 +537,7 @@
     return GET_CORE()->method_cache_get(sel, false); 
 }
 
-inline rb_vm_method_node_t *
+rb_vm_method_node_t *
 RoxorCore::method_node_get(IMP imp, bool create)
 {
     rb_vm_method_node_t *n;
@@ -557,7 +557,7 @@
     return n;
 }
 
-inline rb_vm_method_node_t *
+rb_vm_method_node_t *
 RoxorCore::method_node_get(Method m, bool create)
 {
     rb_vm_method_node_t *n;
@@ -1501,7 +1501,8 @@
     return did_something;
 }
 
-static bool
+extern "C"
+bool
 rb_vm_resolve_method(Class klass, SEL sel)
 {
     if (!GET_CORE()->get_running()) {
@@ -2069,18 +2070,8 @@
     rb_vm_define_method(klass, sel, imp, body, false);
 }
 
-static VALUE method_missing(VALUE obj, SEL sel, rb_vm_block_t *block, int argc, const VALUE *argv,
-	rb_vm_method_missing_reason_t call_status);
+#define UNDEFINED_IMP(imp) (imp == NULL || imp == (IMP)rb_vm_undefined_imp)
 
-static void *
-undefined_imp(void *rcv, SEL sel)
-{
-    method_missing((VALUE)rcv, sel, NULL, NULL, NULL, METHOD_MISSING_DEFAULT);
-    return NULL; // never reached
-}
-
-#define UNDEFINED_IMP(imp) (imp == NULL || imp == (IMP)undefined_imp)
-
 void
 RoxorCore::undef_method(Class klass, SEL sel)
 {
@@ -2091,7 +2082,7 @@
 	    sel_getName(sel));
 #endif
 
-    class_replaceMethod((Class)klass, sel, (IMP)undefined_imp, "@@:");
+    class_replaceMethod((Class)klass, sel, (IMP)rb_vm_undefined_imp, "@@:");
 
 #if 0
     std::map<Method, rb_vm_method_node_t *>::iterator iter
@@ -2182,234 +2173,6 @@
     }
 }
 
-static force_inline void
-__rb_vm_fix_args(const VALUE *argv, VALUE *new_argv,
-	const rb_vm_arity_t &arity, int argc)
-{
-    assert(argc >= arity.min);
-    assert((arity.max == -1) || (argc <= arity.max));
-    const int used_opt_args = argc - arity.min;
-    int opt_args, rest_pos;
-    if (arity.max == -1) {
-	opt_args = arity.real - arity.min - 1;
-	rest_pos = arity.left_req + opt_args;
-    }
-    else {
-	opt_args = arity.real - arity.min;
-	rest_pos = -1;
-    }
-    for (int i = 0; i < arity.real; ++i) {
-	if (i < arity.left_req) {
-	    // required args before optional args
-	    new_argv[i] = argv[i];
-	}
-	else if (i < arity.left_req + opt_args) {
-	    // optional args
-	    int opt_arg_index = i - arity.left_req;
-	    if (opt_arg_index >= used_opt_args) {
-		new_argv[i] = Qundef;
-	    }
-	    else {
-		new_argv[i] = argv[i];
-	    }
-	}
-	else if (i == rest_pos) {
-	    // rest
-	    int rest_size = argc - arity.real + 1;
-	    if (rest_size <= 0) {
-		new_argv[i] = rb_ary_new();
-	    }
-	    else {
-		new_argv[i] = rb_ary_new4(rest_size, &argv[i]);
-	    }
-	}
-	else {
-	    // required args after optional args
-	    new_argv[i] = argv[argc-(arity.real - i)];
-	}
-    }
-}
-
-static force_inline VALUE
-__rb_vm_bcall(VALUE self, SEL sel, VALUE dvars, rb_vm_block_t *b,
-	      IMP pimp, const rb_vm_arity_t &arity, int argc,
-	      const VALUE *argv)
-{
-    if ((arity.real != argc) || (arity.max == -1)) {
-	VALUE *new_argv = (VALUE *)alloca(sizeof(VALUE) * arity.real);
-	__rb_vm_fix_args(argv, new_argv, arity, argc);
-	argv = new_argv;
-	argc = arity.real;
-    }
-
-    assert(pimp != NULL);
-
-    VALUE (*imp)(VALUE, SEL, VALUE, rb_vm_block_t *,  ...) = (VALUE (*)(VALUE, SEL, VALUE, rb_vm_block_t *, ...))pimp;
-
-    switch (argc) {
-	case 0:
-	    return (*imp)(self, sel, dvars, b);
-	case 1:
-	    return (*imp)(self, sel, dvars, b, argv[0]);
-	case 2:
-	    return (*imp)(self, sel, dvars, b, argv[0], argv[1]);
-	case 3:
-	    return (*imp)(self, sel, dvars, b, argv[0], argv[1], argv[2]);
-	case 4:
-	    return (*imp)(self, sel, dvars, b, argv[0], argv[1], argv[2], argv[3]);
-	case 5:
-	    return (*imp)(self, sel, dvars, b, argv[0], argv[1], argv[2], argv[3], argv[4]);
-	case 6:
-	    return (*imp)(self, sel, dvars, b, argv[0], argv[1], argv[2], argv[3], argv[4], argv[5]);
-	case 7:
-	    return (*imp)(self, sel, dvars, b, argv[0], argv[1], argv[2], argv[3], argv[4], argv[5], argv[6]);
-	case 8:
-	    return (*imp)(self, sel, dvars, b, argv[0], argv[1], argv[2], argv[3], argv[4], argv[5], argv[6], argv[7]);
-	case 9:
-	    return (*imp)(self, sel, dvars, b, argv[0], argv[1], argv[2], argv[3], argv[4], argv[5], argv[6], argv[7], argv[8]);
-    }	
-    printf("invalid argc %d\n", argc);
-    abort();
-}
-
-static force_inline VALUE
-__rb_vm_rcall(VALUE self, SEL sel, IMP pimp, const rb_vm_arity_t &arity,
-              int argc, const VALUE *argv)
-{
-    if ((arity.real != argc) || (arity.max == -1)) {
-	VALUE *new_argv = (VALUE *)alloca(sizeof(VALUE) * arity.real);
-	__rb_vm_fix_args(argv, new_argv, arity, argc);
-	argv = new_argv;
-	argc = arity.real;
-    }
-
-    assert(pimp != NULL);
-
-    VALUE (*imp)(VALUE, SEL, ...) = (VALUE (*)(VALUE, SEL, ...))pimp;
-
-    switch (argc) {
-	case 0:
-	    return (*imp)(self, sel);
-	case 1:
-	    return (*imp)(self, sel, argv[0]);
-	case 2:
-	    return (*imp)(self, sel, argv[0], argv[1]);		
-	case 3:
-	    return (*imp)(self, sel, argv[0], argv[1], argv[2]);
-	case 4:
-	    return (*imp)(self, sel, argv[0], argv[1], argv[2], argv[3]);
-	case 5:
-	    return (*imp)(self, sel, argv[0], argv[1], argv[2], argv[3], argv[4]);
-	case 6:
-	    return (*imp)(self, sel, argv[0], argv[1], argv[2], argv[3], argv[4], argv[5]);
-	case 7:
-	    return (*imp)(self, sel, argv[0], argv[1], argv[2], argv[3], argv[4], argv[5], argv[6]);
-	case 8:
-	    return (*imp)(self, sel, argv[0], argv[1], argv[2], argv[3], argv[4], argv[5], argv[6], argv[7]);
-	case 9:
-	    return (*imp)(self, sel, argv[0], argv[1], argv[2], argv[3], argv[4], argv[5], argv[6], argv[7], argv[8]);
-	case 10:
-	    return (*imp)(self, sel, argv[0], argv[1], argv[2], argv[3], argv[4], argv[5], argv[6], argv[7], argv[8], argv[9]);
-	case 11:
-	    return (*imp)(self, sel, argv[0], argv[1], argv[2], argv[3], argv[4], argv[5], argv[6], argv[7], argv[8], argv[9], argv[10]);
-    }	
-    printf("invalid argc %d\n", argc);
-    abort();
-}
-
-static inline IMP
-objc_imp(IMP imp)
-{
-    rb_vm_method_node_t *node = GET_CORE()->method_node_get(imp);
-    if (node != NULL && node->ruby_imp == imp) {
-	imp = node->objc_imp;
-    }
-    return imp;
-}
-
-static inline Method
-rb_vm_super_lookup(VALUE klass, SEL sel)
-{
-    // Locate the current method implementation.
-    Method m = class_getInstanceMethod((Class)klass, sel);
-    assert(m != NULL);
-    IMP self = objc_imp(method_getImplementation(m));
-
-    // Compute the stack call implementations right after our current method.
-    void *callstack[128];
-    int callstack_n = backtrace(callstack, 128);
-    std::vector<void *> callstack_funcs;
-    bool skip = true;
-    for (int i = callstack_n - 1; i >= 0; i--) {
-	void *start = NULL;
-	if (GET_CORE()->symbolize_call_address(callstack[i],
-		    &start, NULL, 0, NULL, NULL, 0)) {
-	    start = (void *)objc_imp((IMP)start);
-	    if (start == (void *)self) {
-		skip = false;
-	    }
-	    if (!skip) {
-		callstack_funcs.push_back(start);
-	    }
-	}
-    }
-
-    // Iterate over ancestors and return the first method that isn't on
-    // the stack.
-    VALUE ary = rb_mod_ancestors_nocopy(klass);
-    const int count = RARRAY_LEN(ary);
-    VALUE k = klass;
-    bool klass_located = false;
-
-#if ROXOR_VM_DEBUG
-    printf("locating super method %s of class %s in ancestor chain %s\n", 
-	    sel_getName(sel), rb_class2name(klass),
-	    RSTRING_PTR(rb_inspect(ary)));
-    printf("callstack functions: ");
-    for (std::vector<void *>::iterator iter = callstack_funcs.begin();
-	 iter != callstack_funcs.end();
-	 ++iter) {
-	printf("%p ", *iter);
-    }
-    printf("\n");
-#endif
-
-    //assert(!callstack_funcs.empty());
-
-    for (int i = 0; i < count; i++) {
-        if (!klass_located && RARRAY_AT(ary, i) == klass) {
-            klass_located = true;
-        }
-        if (klass_located) {
-            if (i < count - 1) {
-                k = RARRAY_AT(ary, i + 1);
-
-		Method method = class_getInstanceMethod((Class)k, sel);
-		VALUE super = RCLASS_SUPER(k);
-
-		if (method == NULL || (super != 0
-		    && class_getInstanceMethod((Class)super, sel) == method)) {
-		    continue;
-		}
-
-		IMP imp = method_getImplementation(method);
-
-		if (std::find(callstack_funcs.begin(), callstack_funcs.end(), 
-			    (void *)imp) == callstack_funcs.end()) {
-		    // Method is not on stack.
-#if ROXOR_VM_DEBUG
-		    printf("returning method implementation %p " \
-		    	   "from class/module %s\n", imp, rb_class2name(k));
-#endif
-		    return method;
-		}
-            }
-        }
-    }
-
-    return NULL;
-}
-
 extern "C"
 VALUE
 rb_vm_method_missing(VALUE obj, int argc, const VALUE *argv)
@@ -2470,48 +2233,6 @@
     abort(); // never reached
 }
 
-static VALUE
-method_missing(VALUE obj, SEL sel, rb_vm_block_t *block, int argc, const VALUE *argv,
-	       rb_vm_method_missing_reason_t call_status)
-{
-    GET_VM()->set_method_missing_reason(call_status);
-
-    if (sel == selMethodMissing) {
-	rb_vm_method_missing(obj, argc, argv);
-    }
-    else if (sel == selAlloc) {
-        rb_raise(rb_eTypeError, "allocator undefined for %s",
-                 rb_class2name(obj));
-    }
-
-    VALUE *new_argv = (VALUE *)alloca(sizeof(VALUE) * (argc + 1));
-
-    char buf[100];
-    int n = snprintf(buf, sizeof buf, "%s", sel_getName(sel));
-    if (buf[n - 1] == ':') {
-      // Let's see if there are more colons making this a real selector.
-      bool multiple_colons = false;
-      for (int i = 0; i < (n - 1); i++) {
-        if (buf[i] == ':') {
-          multiple_colons = true;
-          break;
-        }
-      }
-      if (!multiple_colons) {
-        // Not a typical multiple argument selector. So as this is probably a
-        // typical ruby method name, chop off the colon.
-        buf[n - 1] = '\0';
-      }
-    }
-    new_argv[0] = ID2SYM(rb_intern(buf));
-    MEMCPY(&new_argv[1], argv, VALUE, argc);
-
-    struct mcache *cache;
-    cache = GET_CORE()->method_cache_get(selMethodMissing, false);
-    return rb_vm_call_with_cache2(cache, block, obj, NULL, selMethodMissing,
-    	argc + 1, new_argv);
-}
-
 void *
 RoxorCore::gen_stub(std::string types, bool variadic, int min_argc,
 	bool is_objc)
@@ -2575,871 +2296,7 @@
     return convertor; 
 }
 
-static inline void
-vm_gen_bs_func_types(int argc, const VALUE *argv,
-	bs_element_function_t *bs_func, std::string &types)
-{
-    types.append(bs_func->retval == NULL ? "v" : bs_func->retval->type);
-    int printf_arg = -1;
-    for (int i = 0; i < (int)bs_func->args_count; i++) {
-	types.append(bs_func->args[i].type);
-	if (bs_func->args[i].printf_format) {
-	    printf_arg = i;
-	}
-    }
-    if (bs_func->variadic) {
-	// TODO honor printf_format
-//	if (printf_arg != -1) {	    
-//	}
-	for (int i = bs_func->args_count; i < argc; i++) {
-	    types.append("@");
-	}
-    }
-}
-
-static inline SEL
-helper_sel(const char *p, size_t len)
-{
-    SEL new_sel = 0;
-    char buf[100];
-
-    assert(len < sizeof(buf));
-
-    if (len >= 3 && isalpha(p[len - 3]) && p[len - 2] == '='
-	&& p[len - 1] == ':') {
-
-	/* foo=: -> setFoo: shortcut */
-	snprintf(buf, sizeof buf, "set%s", p);
-	buf[3] = toupper(buf[3]);
-	buf[len + 1] = ':';
-	buf[len + 2] = '\0';
-	new_sel = sel_registerName(buf);
-    }
-    else if (len > 1 && p[len - 1] == '?') {
-	/* foo?: -> isFoo: shortcut */
-	snprintf(buf, sizeof buf, "is%s", p);
-	buf[2] = toupper(buf[2]);
-	buf[len + 1] = '\0';
-	new_sel = sel_registerName(buf);
-    }
-
-    return new_sel;
-}
-
-static force_inline VALUE
-__rb_vm_ruby_dispatch(VALUE self, SEL sel, rb_vm_method_node_t *node,
-		      unsigned char opt, int argc, const VALUE *argv)
-{
-    const rb_vm_arity_t &arity = node->arity;
-    if ((argc < arity.min) || ((arity.max != -1) && (argc > arity.max))) {
-	rb_raise(rb_eArgError, "wrong number of arguments (%d for %d)",
-		argc, arity.min);
-    }
-
-    if ((node->flags & VM_METHOD_PRIVATE) && opt == 0) {
-	// Calling a private method with no explicit receiver OR an attribute
-	// assignment to non-self, triggering #method_missing.
-	rb_vm_block_t *b = GET_VM()->current_block();
-	return method_missing(self, sel, b, argc, argv, METHOD_MISSING_PRIVATE);
-    }
-
-    if ((node->flags & VM_METHOD_EMPTY) && arity.max == arity.min) {
-	// Calling an empty method, let's just return nil!
-	return Qnil;
-    }
-
-    if ((node->flags & VM_METHOD_FBODY) && arity.max != arity.min) {
-	// Calling a function defined with rb_objc_define_method with
-	// a negative arity, which means a different calling convention.
-	if (arity.real == 2) {
-	    return ((VALUE (*)(VALUE, SEL, int, const VALUE *))node->ruby_imp)
-		(self, sel, argc, argv);
-	}
-	else if (arity.real == 1) {
-	    return ((VALUE (*)(VALUE, SEL, ...))node->ruby_imp)
-		(self, sel, rb_ary_new4(argc, argv));
-	}
-	else {
-	    printf("invalid negative arity for C function %d\n",
-		    arity.real);
-	    abort();
-	}
-    }
-
-    return __rb_vm_rcall(self, sel, node->ruby_imp, arity, argc, argv);
-}
-
-static force_inline void
-fill_rcache(struct mcache *cache, Class klass, SEL sel,
-	rb_vm_method_node_t *node)
-{
-    cache->flag = MCACHE_RCALL;
-    rcache.klass = klass;
-    rcache.node = node;
-}
-
-static force_inline bool
-can_forwardInvocation(VALUE recv, SEL sel)
-{
-    if (!SPECIAL_CONST_P(recv)) {
-	static SEL methodSignatureForSelector = 0;
-	if (methodSignatureForSelector == 0) {
-	    methodSignatureForSelector =
-		sel_registerName("methodSignatureForSelector:");	
-	}
-	return objc_msgSend((id)recv, methodSignatureForSelector, (id)sel)
-	    != nil;
-    }
-    return false;
-}
-
-static force_inline void
-fill_ocache(struct mcache *cache, VALUE self, Class klass, IMP imp, SEL sel,
-	    Method method, int argc)
-{
-    cache->flag = MCACHE_OCALL;
-    ocache.klass = klass;
-    ocache.imp = imp;
-    ocache.bs_method = GET_CORE()->find_bs_method(klass, sel);
-
-    char types[200];
-    if (!rb_objc_get_types(self, klass, sel, method, ocache.bs_method,
-		types, sizeof types)) {
-	printf("cannot get encoding types for %c[%s %s]\n",
-		class_isMetaClass(klass) ? '+' : '-',
-		class_getName(klass),
-		sel_getName(sel));
-	abort();
-    }
-    bool variadic = false;
-    if (ocache.bs_method != NULL && ocache.bs_method->variadic
-	&& method != NULL) {
-	// TODO honor printf_format
-	const int real_argc = method_getNumberOfArguments(method) - 2;
-	if (real_argc < argc) {
-	    const size_t s = strlen(types);
-	    assert(s + argc - real_argc < sizeof types);
-	    for (int i = real_argc; i < argc; i++) {
-		strlcat(types, "@", sizeof types);
-	    }
-	    argc = real_argc;
-	}
-	variadic = true;
-    }
-    ocache.stub = (rb_vm_objc_stub_t *)GET_CORE()->gen_stub(types, variadic,
-	    argc, true);
-}
-
-static force_inline VALUE
-__rb_vm_dispatch(RoxorVM *vm, struct mcache *cache, VALUE self, Class klass,
-	SEL sel, rb_vm_block_t *block, unsigned char opt, int argc,
-	const VALUE *argv)
-{
-    assert(cache != NULL);
-
-    if (klass == NULL) {
-	klass = (Class)CLASS_OF(self);
-    }
-
-#if ROXOR_VM_DEBUG
-    bool cached = true;
-#endif
-    bool do_rcache = true;
-
-    if (cache->flag == 0) {
-recache:
-#if ROXOR_VM_DEBUG
-	cached = false;
-#endif
-
-	Method method;
-	if (opt == DISPATCH_SUPER) {
-	    method = rb_vm_super_lookup((VALUE)klass, sel);
-	}
-	else {
-	    method = class_getInstanceMethod(klass, sel);
-	}
-
-	if (method != NULL) {
-recache2:
-	    IMP imp = method_getImplementation(method);
-
-	    if (UNDEFINED_IMP(imp)) {
-		// Method was undefined.
-		goto call_method_missing;
-	    }
-
-	    rb_vm_method_node_t *node = GET_CORE()->method_node_get(method);
-
-	    if (node != NULL) {
-		// ruby call
-		fill_rcache(cache, klass, sel, node);
-	    }
-	    else {
-		// objc call
-		fill_ocache(cache, self, klass, imp, sel, method, argc);
-	    }
-	}
-	else {
-	    // Method is not found...
-
-	    // Force a method resolving, because the objc cache might be
-	    // wrong.
-	    if (rb_vm_resolve_method(klass, sel)) {
-		goto recache;
-	    }
-
-	    // Does the receiver implements -forwardInvocation:?
-	    if (opt != DISPATCH_SUPER && can_forwardInvocation(self, sel)) {
-		fill_ocache(cache, self, klass, (IMP)objc_msgSend, sel, NULL,
-			argc);
-		goto dispatch;
-	    }
-
-	    // Let's see if are not trying to call a Ruby method that accepts
-	    // a regular argument then a optional Hash argument, to be
-	    // compatible with the Ruby specification.
-	    const char *selname = (const char *)sel;
-	    size_t selname_len = strlen(selname);
-	    if (argc > 1) {
-		const char *p = strchr(selname, ':');
-		if (p != NULL && p + 1 != '\0') {
-		    char *tmp = (char *)alloca(selname_len);
-		    strncpy(tmp, selname, p - selname + 1);
-		    tmp[p - selname + 1] = '\0';
-		    SEL new_sel = sel_registerName(tmp);
-		    Method m = class_getInstanceMethod(klass, new_sel);
-		    if (m != NULL) {
-			VALUE h = rb_hash_new();
-			bool ok = true;
-			p += 1;
-			for (int i = 1; i < argc; i++) {
-			    const char *p2 = strchr(p, ':');
-			    if (p2 == NULL) {
-				ok = false;
-				break;
-			    }
-			    strlcpy(tmp, p, selname_len);
-			    tmp[p2 - p] = '\0';
-			    p = p2 + 1; 
-			    rb_hash_aset(h, ID2SYM(rb_intern(tmp)), argv[i]);
-			}
-			if (ok) {
-			    argc = 2;
-			    ((VALUE *)argv)[1] = h; // bad, I know...
-			    sel = new_sel;
-			    method = m;
-			    do_rcache = false;
-			    goto recache2;
-			}
-		    }
-		}
-	    }
-
-	    // Let's try to see if we are not given a helper selector.
-	    SEL new_sel = helper_sel(selname, selname_len);
-	    if (new_sel != NULL) {
-		Method m = class_getInstanceMethod(klass, new_sel);
-		if (m != NULL) {
-		    if (GET_CORE()->method_node_get(m) == NULL) {
-			sel = new_sel;
-			method = m;
-			goto recache2;
-		    }
-		}
-	    }
-
-	    // Let's see if we are not trying to call a BridgeSupport function.
-	    if (selname[selname_len - 1] == ':') {
-		selname_len--;
-	    }
-	    std::string name(selname, selname_len);
-	    bs_element_function_t *bs_func = GET_CORE()->find_bs_function(name);
-	    if (bs_func != NULL) {
-		std::string types;
-		vm_gen_bs_func_types(argc, argv, bs_func, types);
-
-		cache->flag = MCACHE_FCALL;
-		fcache.bs_function = bs_func;
-		fcache.imp = (IMP)dlsym(RTLD_DEFAULT, bs_func->name);
-		assert(fcache.imp != NULL);
-		fcache.stub = (rb_vm_c_stub_t *)GET_CORE()->gen_stub(types,
-			bs_func->variadic, bs_func->args_count, false);
-	    }
-	    else {
-		// Still nothing, then let's call #method_missing.
-		goto call_method_missing;
-	    }
-	}
-    }
-
-dispatch:
-    if (cache->flag == MCACHE_RCALL) {
-	if (rcache.klass != klass) {
-	    goto recache;
-	}
-	if (!do_rcache) {
-	    cache->flag = 0;
-	}
-
-#if ROXOR_VM_DEBUG
-	printf("ruby dispatch %c[<%s %p> %s] (imp=%p, block=%p, argc=%d, cached=%s)\n",
-		class_isMetaClass(klass) ? '+' : '-',
-		class_getName(klass),
-		(void *)self,
-		sel_getName(sel),
-		rcache.node->ruby_imp,
-		block,
-		argc,
-		cached ? "true" : "false");
-#endif
-
-	bool block_already_current = vm->is_block_current(block);
-	Class current_klass = vm->get_current_class();
-	if (!block_already_current) {
-	    vm->add_current_block(block);
-	}
-	vm->set_current_class(NULL);
-
-	struct Finally {
-	    bool block_already_current;
-	    Class current_class;
-	    RoxorVM *vm;
-	    Finally(bool _block_already_current, Class _current_class,
-		    RoxorVM *_vm) {
-		block_already_current = _block_already_current;
-		current_class = _current_class;
-		vm = _vm;
-	    }
-	    ~Finally() {
-		if (!block_already_current) {
-		    vm->pop_current_block();
-		}
-		vm->set_current_class(current_class);
-		vm->pop_broken_with();
-	    }
-	} finalizer(block_already_current, current_klass, vm);
-
-	return __rb_vm_ruby_dispatch(self, sel, rcache.node, opt, argc, argv);
-    }
-    else if (cache->flag == MCACHE_OCALL) {
-	if (ocache.klass != klass) {
-	    goto recache;
-	}
-
-	if (block != NULL) {
-	    if (self == rb_cNSMutableHash && sel == selNew) {
-		// Because Hash.new can accept a block.
-		vm->add_current_block(block);
-
-		struct Finally {
-		    RoxorVM *vm;
-		    Finally(RoxorVM *_vm) { vm = _vm; }
-		    ~Finally() { vm->pop_current_block(); }
-		} finalizer(vm);
-
-		return rb_hash_new2(argc, argv);
-	    }
-	    rb_warn("passing a block to an Objective-C method - " \
-		    "will be ignored");
-	}
-	else if (sel == selNew) {
-	    if (self == rb_cNSMutableArray) {
-		self = rb_cRubyArray;
-	    }
-	}
-	else if (sel == selClass) {
-	    // Because +[NSObject class] returns self.
-	    if (RCLASS_META(klass)) {
-		return RCLASS_MODULE(self) ? rb_cModule : rb_cClass;
-	    }
-	    // Because the CF classes should be hidden, for Ruby compat.
-	    if (self == Qnil) {
-		return rb_cNilClass;
-	    }
-	    if (self == Qtrue) {
-		return rb_cTrueClass;
-	    }
-	    if (self == Qfalse) {
-		return rb_cFalseClass;
-	    }
-	    if (klass == (Class)rb_cCFString) {
-		return RSTRING_IMMUTABLE(self)
-		    ? rb_cNSString : rb_cNSMutableString;
-	    }
-#if MAC_OS_X_VERSION_MAX_ALLOWED >= 1060
-	    if (klass == (Class)rb_cCFArray || klass == (Class)rb_cNSArray0) {
-#else
-	    if (klass == (Class)rb_cCFArray) {
-#endif
-		return RARRAY_IMMUTABLE(self)
-		    ? rb_cNSArray : rb_cNSMutableArray;
-	    }
-	    else if (klass == (Class)rb_cRubyArray) {
-		return rb_cNSMutableArray;
-	    }
-#if MAC_OS_X_VERSION_MAX_ALLOWED >= 1060
-	    if (klass == (Class)rb_cCFHash || klass == (Class)rb_cNSHash0) {
-#else
-	    if (klass == (Class)rb_cCFHash) {
-#endif
-		return RHASH_IMMUTABLE(self)
-		    ? rb_cNSHash : rb_cNSMutableHash;
-	    }
-	    if (klass == (Class)rb_cCFSet) {
-		return RSET_IMMUTABLE(self)
-		    ? rb_cNSSet : rb_cNSMutableSet;
-	    }
-	}
-
-#if ROXOR_VM_DEBUG
-	printf("objc dispatch %c[<%s %p> %s] imp=%p argc=%d (cached=%s)\n",
-		class_isMetaClass(klass) ? '+' : '-',
-		class_getName(klass),
-		(void *)self,
-		sel_getName(sel),
-		ocache.imp,
-		argc,
-		cached ? "true" : "false");
-#endif
-
-	return (*ocache.stub)(ocache.imp, RB2OC(self), sel, argc, argv);
-    }
-    else if (cache->flag == MCACHE_FCALL) {
-#if ROXOR_VM_DEBUG
-	printf("C dispatch %s() imp=%p argc=%d (cached=%s)\n",
-		fcache.bs_function->name,
-		fcache.imp,
-		argc,
-		cached ? "true" : "false");
-#endif
-	return (*fcache.stub)(fcache.imp, argc, argv);
-    }
-
-    printf("method dispatch is b0rked\n");
-    abort();
-
-call_method_missing:
-    // Before calling method_missing, let's check if we are not in the following
-    // cases:
-    //
-    //    def foo; end; foo(42)
-    //    def foo(x); end; foo
-    //
-    // If yes, we need to raise an ArgumentError exception instead.
-    const char *selname = sel_getName(sel);
-    const size_t selname_len = strlen(selname);
-    SEL new_sel = 0;
-    int argc_expected;
-
-    if (argc > 0 && selname[selname_len - 1] == ':') {
-	char buf[100];
-	assert(sizeof buf > selname_len - 1);
-	strlcpy(buf, selname, sizeof buf);
-	buf[selname_len - 1] = '\0';
-	new_sel = sel_registerName(buf);
-	argc_expected = 0;
-    }
-    else if (argc == 0) {
-	char buf[100];
-	snprintf(buf, sizeof buf, "%s:", selname);
-	new_sel = sel_registerName(buf);
-	argc_expected = 1;
-    }
-    if (new_sel != 0) {
-	Method m = class_getInstanceMethod(klass, new_sel);
-	if (m != NULL
-		&& GET_CORE()->method_node_get(m) != NULL) {
-	    rb_raise(rb_eArgError, "wrong number of arguments (%d for %d)",
-		    argc, argc_expected);
-	}
-    }
-
-    rb_vm_method_missing_reason_t status =
-	opt == DISPATCH_VCALL
-	    ? METHOD_MISSING_VCALL : opt == DISPATCH_SUPER
-		? METHOD_MISSING_SUPER : METHOD_MISSING_DEFAULT;
-    return method_missing((VALUE)self, sel, block, argc, argv, status);
-}
-
-#define MAX_DISPATCH_ARGS 100
-
-static force_inline void
-__rb_vm_resolve_args(VALUE **pargv, size_t argv_size, int *pargc, va_list ar)
-{
-    // TODO we should only determine the real argc here (by taking into
-    // account the length splat arguments) and do the real unpacking of
-    // splat arguments in __rb_vm_rcall(). This way we can optimize more
-    // things (for ex. no need to unpack splats that are passed as a splat
-    // argument in the method being called!).
-    unsigned int i, argc = *pargc, real_argc = 0;
-    VALUE *argv = *pargv;
-    bool splat_arg_follows = false;
-    for (i = 0; i < argc; i++) {
-	VALUE arg = va_arg(ar, VALUE);
-	if (arg == SPLAT_ARG_FOLLOWS) {
-	    splat_arg_follows = true;
-	    i--;
-	}
-	else {
-	    if (splat_arg_follows) {
-		VALUE ary = rb_check_convert_type(arg, T_ARRAY, "Array",
-			"to_a");
-		if (NIL_P(ary)) {
-		    ary = rb_ary_new3(1, arg);
-		}
-		int count = RARRAY_LEN(ary);
-		if (real_argc + count >= argv_size) {
-		    const size_t new_argv_size = real_argc + count + 100;
-		    VALUE *new_argv = (VALUE *)xmalloc(sizeof(VALUE)
-			    * new_argv_size);
-		    memcpy(new_argv, argv, sizeof(VALUE) * argv_size);
-		    argv = new_argv;
-		    argv_size = new_argv_size;
-		}
-		for (int j = 0; j < count; j++) {
-		    argv[real_argc++] = RARRAY_AT(ary, j);
-		}
-		splat_arg_follows = false;
-	    }
-	    else {
-		if (real_argc >= argv_size) {
-		    const size_t new_argv_size = real_argc + 100;
-		    VALUE *new_argv = (VALUE *)xmalloc(sizeof(VALUE)
-			    * new_argv_size);
-		    memcpy(new_argv, argv, sizeof(VALUE) * argv_size);
-		    argv = new_argv;
-		    argv_size = new_argv_size;
-		}
-		argv[real_argc++] = arg;
-	    }
-	}
-    }
-
-    *pargv = argv;
-    *pargc = real_argc;
-}
-
 extern "C"
-VALUE
-rb_vm_dispatch(struct mcache *cache, VALUE self, SEL sel, rb_vm_block_t *block, 
-	       unsigned char opt, int argc, ...)
-{
-    VALUE base_argv[MAX_DISPATCH_ARGS];
-    VALUE *argv = base_argv;
-    if (argc > 0) {
-	va_list ar;
-	va_start(ar, argc);
-	__rb_vm_resolve_args(&argv, MAX_DISPATCH_ARGS, &argc, ar);
-	va_end(ar);
-
-	if (argc == 0) {
-	    const char *selname = sel_getName(sel);
-	    const size_t selnamelen = strlen(selname);
-	    if (selname[selnamelen - 1] == ':') {
-		// Because
-		//   def foo; end; foo(*[])
-		// creates foo but dispatches foo:.
-		char buf[100];
-		strncpy(buf, selname, sizeof buf);
-		buf[selnamelen - 1] = '\0';
-		sel = sel_registerName(buf);
-	    }
-	}
-    }
-
-    RoxorVM *vm = GET_VM();
-
-    VALUE retval = __rb_vm_dispatch(vm, cache, self, NULL, sel, block, opt,
-	    argc, argv);
-
-    vm->pop_current_binding();
-
-    return retval;
-}
-
-// The rb_vm_fast_* functions don't check if the selector has been redefined or
-// not, because this is already handled by the compiler.
-// Also, fixnums and floats are already handled.
-
-extern "C" {
-    VALUE rb_fix_plus(VALUE x, VALUE y);
-    VALUE rb_fix_minus(VALUE x, VALUE y);
-    VALUE rb_fix_div(VALUE x, VALUE y);
-    VALUE rb_fix_mul(VALUE x, VALUE y);
-    VALUE rb_flo_plus(VALUE x, VALUE y);
-    VALUE rb_flo_minus(VALUE x, VALUE y);
-    VALUE rb_flo_div(VALUE x, VALUE y);
-    VALUE rb_flo_mul(VALUE x, VALUE y);
-    VALUE rb_nu_plus(VALUE x, VALUE y);
-    VALUE rb_nu_minus(VALUE x, VALUE y);
-    VALUE rb_nu_div(VALUE x, VALUE y);
-    VALUE rb_nu_mul(VALUE x, VALUE y);
-}
-
-extern "C"
-VALUE
-rb_vm_fast_plus(struct mcache *cache, VALUE self, VALUE other)
-{
-    switch (TYPE(self)) {
-	// TODO: Array, String
-	case T_BIGNUM:
-	    return rb_big_plus(self, other);
-	case T_FIXNUM:
-	    return rb_fix_plus(self, other);
-	case T_FLOAT:
-	    return rb_flo_plus(self, other);
-	case T_COMPLEX:
-	    return rb_nu_plus(self, other);
-    }
-    return rb_vm_dispatch(cache, self, selPLUS, NULL, 0, 1, other);
-}
-
-extern "C"
-VALUE
-rb_vm_fast_minus(struct mcache *cache, VALUE self, VALUE other)
-{
-    switch (TYPE(self)) {
-	// TODO: Array, String
-	case T_BIGNUM:
-	    return rb_big_minus(self, other);
-	case T_FIXNUM:
-	    return rb_fix_minus(self, other);
-	case T_FLOAT:
-	    return rb_flo_minus(self, other);
-	case T_COMPLEX:
-	    return rb_nu_minus(self, other);
-    }
-    return rb_vm_dispatch(cache, self, selMINUS, NULL, 0, 1, other);
-}
-
-extern "C"
-VALUE
-rb_vm_fast_div(struct mcache *cache, VALUE self, VALUE other)
-{
-    switch (TYPE(self)) {
-	case T_BIGNUM:
-	    return rb_big_div(self, other);
-	case T_FIXNUM:
-	    return rb_fix_div(self, other);
-	case T_FLOAT:
-	    return rb_flo_div(self, other);
-	case T_COMPLEX:
-	    return rb_nu_div(self, other);
-    }
-    return rb_vm_dispatch(cache, self, selDIV, NULL, 0, 1, other);
-}
-
-extern "C"
-VALUE
-rb_vm_fast_mult(struct mcache *cache, VALUE self, VALUE other)
-{
-    switch (TYPE(self)) {
-	// TODO: Array, String
-	case T_BIGNUM:
-	    return rb_big_mul(self, other);
-	case T_FIXNUM:
-	    return rb_fix_mul(self, other);
-	case T_FLOAT:
-	    return rb_flo_mul(self, other);
-	case T_COMPLEX:
-	    return rb_nu_mul(self, other);
-    }
-    return rb_vm_dispatch(cache, self, selMULT, NULL, 0, 1, other);
-}
-
-extern "C"
-VALUE
-rb_vm_fast_lt(struct mcache *cache, VALUE self, VALUE other)
-{
-    switch (TYPE(self)) {
-	case T_BIGNUM:
-	    return FIX2INT(rb_big_cmp(self, other)) < 0 ? Qtrue : Qfalse;
-    }
-    return rb_vm_dispatch(cache, self, selLT, NULL, 0, 1, other);
-}
-
-extern "C"
-VALUE
-rb_vm_fast_le(struct mcache *cache, VALUE self, VALUE other)
-{
-    switch (TYPE(self)) {
-	case T_BIGNUM:
-	    return FIX2INT(rb_big_cmp(self, other)) <= 0 ? Qtrue : Qfalse;
-    }
-    return rb_vm_dispatch(cache, self, selLE, NULL, 0, 1, other);
-}
-
-extern "C"
-VALUE
-rb_vm_fast_gt(struct mcache *cache, VALUE self, VALUE other)
-{
-    switch (TYPE(self)) {
-	case T_BIGNUM:
-	    return FIX2INT(rb_big_cmp(self, other)) > 0 ? Qtrue : Qfalse;
-    }
-    return rb_vm_dispatch(cache, self, selGT, NULL, 0, 1, other);
-}
-
-extern "C"
-VALUE
-rb_vm_fast_ge(struct mcache *cache, VALUE self, VALUE other)
-{
-    switch (TYPE(self)) {
-	case T_BIGNUM:
-	    return FIX2INT(rb_big_cmp(self, other)) >= 0 ? Qtrue : Qfalse;
-    }
-    return rb_vm_dispatch(cache, self, selGE, NULL, 0, 1, other);
-}
-
-extern "C"
-VALUE
-rb_vm_fast_eq(struct mcache *cache, VALUE self, VALUE other)
-{
-    const int self_type = TYPE(self);
-    switch (self_type) {
-	case T_SYMBOL:
-	    return self == other ? Qtrue : Qfalse;
-
-	case T_STRING:
-	case T_ARRAY:
-	case T_HASH:
-	    if (self == other) {
-		return Qtrue;
-	    }
-	    if (TYPE(other) != self_type) {
-		return Qfalse;
-	    }
-	    if (self_type == T_ARRAY) {
-		return rb_ary_equal(self, other);
-	    }
-	    return CFEqual((CFTypeRef)self, (CFTypeRef)other)
-		? Qtrue : Qfalse;
-
-	case T_BIGNUM:
-	    return rb_big_eq(self, other);
-    }
-    return rb_vm_dispatch(cache, self, selEq, NULL, 0, 1, other);
-}
-
-extern "C"
-VALUE
-rb_vm_fast_neq(struct mcache *cache, VALUE self, VALUE other)
-{
-    // TODO
-    return rb_vm_dispatch(cache, self, selNeq, NULL, 0, 1, other);
-}
-
-extern "C"
-VALUE
-rb_vm_fast_eqq(struct mcache *cache, VALUE self, VALUE other)
-{
-    switch (TYPE(self)) {
-	// TODO: Range
-	case T_STRING:
-	    if (self == other) {
-		return Qtrue;
-	    }
-	    return rb_str_equal(self, other);
-
-	case T_REGEXP:
-	    return rb_reg_eqq(self, selEqq, other);
-
-	case T_SYMBOL:
-	    return (self == other ? Qtrue : Qfalse);
-	
-	case T_MODULE:
-	case T_CLASS:
-	    return rb_obj_is_kind_of(other, self);
-
-	default:
-	    return rb_vm_dispatch(cache, self, selEqq, NULL, 0, 1, other);
-    }
-}
-
-extern "C"
-VALUE
-rb_vm_when_splat(struct mcache *cache, unsigned char overriden,
-		 VALUE comparedTo, VALUE splat)
-{
-    VALUE ary = rb_check_convert_type(splat, T_ARRAY, "Array", "to_a");
-    if (NIL_P(ary)) {
-	ary = rb_ary_new3(1, splat);
-    }
-    int count = RARRAY_LEN(ary);
-    if (overriden == 0) {
-	for (int i = 0; i < count; ++i) {
-	    VALUE o = RARRAY_AT(ary, i);
-	    if (RTEST(rb_vm_fast_eqq(cache, o, comparedTo))) {
-		return Qtrue;
-	    }
-	}
-    }
-    else {
-	for (int i = 0; i < count; ++i) {
-	    VALUE o = RARRAY_AT(ary, i);
-	    if (RTEST(rb_vm_dispatch(cache, o, selEqq, NULL, 0, 1, comparedTo))) {
-		return Qtrue;
-	    }
-	}
-    }
-    return Qfalse;
-}
-
-extern "C"
-VALUE
-rb_vm_fast_shift(VALUE obj, VALUE other, struct mcache *cache,
-		 unsigned char overriden)
-{
-    if (overriden == 0) {
-	switch (TYPE(obj)) {
-	    case T_ARRAY:
-		rb_ary_push(obj, other);
-		return obj;
-
-	    case T_STRING:
-		rb_str_concat(obj, other);
-		return obj;
-	}
-    }
-    return __rb_vm_dispatch(GET_VM(), cache, obj, NULL, selLTLT, NULL, 0, 1,
-	    &other);
-}
-
-extern "C"
-VALUE
-rb_vm_fast_aref(VALUE obj, VALUE other, struct mcache *cache,
-		unsigned char overriden)
-{
-    // TODO what about T_HASH?
-    if (overriden == 0 && TYPE(obj) == T_ARRAY) {
-	if (TYPE(other) == T_FIXNUM) {
-	    return rb_ary_entry(obj, FIX2LONG(other));
-	}
-	return rb_ary_aref(obj, 0, 1, &other);
-    }
-    return __rb_vm_dispatch(GET_VM(), cache, obj, NULL, selAREF, NULL, 0, 1,
-	    &other);
-}
-
-extern "C"
-VALUE
-rb_vm_fast_aset(VALUE obj, VALUE other1, VALUE other2, struct mcache *cache,
-		unsigned char overriden)
-{
-    // TODO what about T_HASH?
-    if (overriden == 0 && TYPE(obj) == T_ARRAY) {
-	if (TYPE(other1) == T_FIXNUM) {
-	    rb_ary_store(obj, FIX2LONG(other1), other2);
-	    return other2;
-	}
-    }
-    VALUE args[2] = { other1, other2 };
-    return __rb_vm_dispatch(GET_VM(), cache, obj, NULL, selASET, NULL, 0, 2,
-	    args);
-}
-
-extern "C"
 void *
 rb_vm_get_block(VALUE obj)
 {
@@ -3457,156 +2314,6 @@
 }
 
 extern "C"
-rb_vm_block_t *
-rb_vm_dup_active_block(rb_vm_block_t *src_b)
-{
-    assert(src_b->flags & VM_BLOCK_ACTIVE);
-
-    const size_t block_size = sizeof(rb_vm_block_t)
-	    + (sizeof(VALUE *) * src_b->dvars_size);
-
-    rb_vm_block_t *new_b = (rb_vm_block_t *)xmalloc(block_size);
-
-    memcpy(new_b, src_b, block_size);
-    new_b->proc = src_b->proc; // weak
-    GC_WB(&new_b->parent_block, src_b->parent_block);
-    GC_WB(&new_b->self, src_b->self);
-    new_b->flags = src_b->flags & ~VM_BLOCK_ACTIVE;
-
-    rb_vm_local_t *src_l = src_b->locals;
-    rb_vm_local_t **new_l = &new_b->locals;
-    while (src_l != NULL) {
-	GC_WB(new_l, xmalloc(sizeof(rb_vm_local_t)));
-	(*new_l)->name = src_l->name;
-	(*new_l)->value = src_l->value;
-
-	new_l = &(*new_l)->next;
-	src_l = src_l->next;
-    }
-    *new_l = NULL;
-
-    return new_b;
-}
-
-inline rb_vm_block_t *
-RoxorVM::uncache_or_create_block(void *key, bool *cached, int dvars_size)
-{
-    std::map<void *, rb_vm_block_t *>::iterator iter = blocks.find(key);
-
-    rb_vm_block_t *b;
-
-    if ((iter == blocks.end())
-	|| (iter->second->flags & (VM_BLOCK_ACTIVE | VM_BLOCK_PROC))) {
-
-	if (iter != blocks.end()) {
-	    rb_objc_release(iter->second);
-	}
-
-	b = (rb_vm_block_t *)xmalloc(sizeof(rb_vm_block_t)
-		+ (sizeof(VALUE *) * dvars_size));
-	rb_objc_retain(b);
-
-	blocks[key] = b;
-	*cached = false;
-    }
-    else {
-	b = iter->second;
-	*cached = true;
-    }
-
-    return b;
-}
-
-extern "C"
-rb_vm_block_t *
-rb_vm_prepare_block(void *function, int flags, VALUE self, rb_vm_arity_t arity,
-	rb_vm_var_uses **parent_var_uses, rb_vm_block_t *parent_block,
-	int dvars_size, ...)
-{
-    assert(function != NULL);
-    RoxorVM *vm = GET_VM();
-
-    bool cached = false;
-    rb_vm_block_t *b = vm->uncache_or_create_block(function, &cached,
-	dvars_size);
-
-    bool aot_block = false;
-    if ((flags & VM_BLOCK_AOT) == VM_BLOCK_AOT) {
-	flags ^= VM_BLOCK_AOT;
-	aot_block = true;
-    }
-
-    if (!cached) {
-	if ((flags & VM_BLOCK_IFUNC) == VM_BLOCK_IFUNC) {
-	    b->imp = (IMP)function;
-	}
-	else {
-	    if (aot_block) {
-		b->imp = (IMP)function;
-	    }
-	    else {
-		GET_CORE()->lock();
-		b->imp = GET_CORE()->compile((Function *)function);
-		GET_CORE()->unlock();
-	    }
-	    b->userdata = (VALUE)function;
-	}
-	b->arity = arity;
-	b->flags = flags;
-	b->dvars_size = dvars_size;
-	b->parent_var_uses = NULL;
-	b->parent_block = NULL;
-    }
-    else {
-	assert(b->dvars_size == dvars_size);
-	assert((b->flags & flags) == flags);
-    }
-
-    b->proc = Qnil;
-    b->self = self;
-    b->klass = (VALUE)vm->get_current_class();
-    b->parent_var_uses = parent_var_uses;
-    GC_WB(&b->parent_block, parent_block);
-
-    va_list ar;
-    va_start(ar, dvars_size);
-    for (int i = 0; i < dvars_size; ++i) {
-	b->dvars[i] = va_arg(ar, VALUE *);
-    }
-    int lvars_size = va_arg(ar, int);
-    if (lvars_size > 0) {
-	if (!cached) {
-	    rb_vm_local_t **l = &b->locals;
-	    for (int i = 0; i < lvars_size; i++) {
-		GC_WB(l, xmalloc(sizeof(rb_vm_local_t)));
-		l = &(*l)->next;
-	    }
-	}
-	rb_vm_local_t *l = b->locals;
-	for (int i = 0; i < lvars_size; ++i) {
-	    assert(l != NULL);
-	    l->name = va_arg(ar, ID);
-	    l->value = va_arg(ar, VALUE *);
-	    l = l->next;
-	}
-    }
-    va_end(ar);
-
-    return b;
-}
-
-extern "C"
-rb_vm_block_t *
-rb_vm_create_block(IMP imp, VALUE self, VALUE userdata)
-{
-    rb_vm_block_t *b = rb_vm_prepare_block((void *)imp, VM_BLOCK_IFUNC, self,
-	    rb_vm_arity(0), // not used
-	    NULL, NULL, 0, 0);
-    GC_WB(&b->userdata, userdata);
-    return b;
-}
-
-extern "C"
 void*
 rb_gc_read_weak_ref(void **referrer);
 
@@ -3825,43 +2532,6 @@
 }
 
 extern "C"
-VALUE
-rb_vm_call(VALUE self, SEL sel, int argc, const VALUE *argv, bool super)
-{
-    struct mcache *cache;
-    unsigned char opt = DISPATCH_FCALL;
-    if (super) {
-	cache = (struct mcache *)alloca(sizeof(struct mcache));
-	cache->flag = 0;
-	opt = DISPATCH_SUPER;
-    }
-    else {
-	cache = GET_CORE()->method_cache_get(sel, false);
-    }
-
-    return __rb_vm_dispatch(GET_VM(), cache, self, NULL, sel, NULL, opt, argc,
-	    argv);
-}
-
-extern "C"
-VALUE
-rb_vm_call_with_cache(void *cache, VALUE self, SEL sel, int argc, 
-	const VALUE *argv)
-{
-    return __rb_vm_dispatch(GET_VM(), (struct mcache *)cache, self, NULL, sel,
-	    NULL, DISPATCH_FCALL, argc, argv);
-}
-
-extern "C"
-VALUE
-rb_vm_call_with_cache2(void *cache, rb_vm_block_t *block, VALUE self,
-	VALUE klass, SEL sel, int argc, const VALUE *argv)
-{
-    return __rb_vm_dispatch(GET_VM(), (struct mcache *)cache, self,
-	    (Class)klass, sel, block, DISPATCH_FCALL, argc, argv);
-}
-
-extern "C"
 void *
 rb_vm_get_call_cache(SEL sel)
 {
@@ -3917,72 +2587,7 @@
     return Qnil;
 }
 
-void rb_print_undef(VALUE klass, ID id, int scope);
-
 extern "C"
-rb_vm_method_t *
-rb_vm_get_method(VALUE klass, VALUE obj, ID mid, int scope)
-{
-    SEL sel = 0;
-    IMP imp = NULL;
-    rb_vm_method_node_t *node = NULL;
-
-    // TODO honor scope
-
-    if (!rb_vm_lookup_method2((Class)klass, mid, &sel, &imp, &node)) {
-	rb_print_undef(klass, mid, 0);
-    }
-
-    Class k, oklass = (Class)klass;
-    while ((k = class_getSuperclass(oklass)) != NULL) {
-	if (!rb_vm_lookup_method(k, sel, NULL, NULL)) {
-	    break;
-	}
-	oklass = k;
-    }
-
-    Method method = class_getInstanceMethod((Class)klass, sel);
-    assert(method != NULL);
-
-    int arity;
-    rb_vm_method_node_t *new_node;
-    if (node == NULL) {
-	arity = method_getNumberOfArguments(method) - 2;
-	new_node = NULL;
-    }
-    else {
-	arity = node->arity.min;
-	if (node->arity.min != node->arity.max) {
-	    arity = -arity - 1;
-	}
-	new_node = (rb_vm_method_node_t *)xmalloc(sizeof(rb_vm_method_node_t));
-	memcpy(new_node, node, sizeof(rb_vm_method_node_t));
-    }
-
-    rb_vm_method_t *m = (rb_vm_method_t *)xmalloc(sizeof(rb_vm_method_t));
-
-    m->oclass = (VALUE)oklass;
-    m->rclass = klass;
-    GC_WB(&m->recv, obj);
-    m->sel = sel;
-    m->arity = arity;
-    GC_WB(&m->node, new_node);
-
-    // Let's allocate a static cache here, since a rb_vm_method_t must always
-    // point to the method it was created from.
-    struct mcache *c = (struct mcache *)xmalloc(sizeof(struct mcache));
-    if (new_node == NULL) {
-	fill_ocache(c, obj, oklass, imp, sel, method, arity);
-    }
-    else {
-	fill_rcache(c, oklass, sel, new_node);
-    }
-    GC_WB(&m->cache, c);
-
-    return m;
-}
-
-extern "C"
 rb_vm_block_t *
 rb_vm_create_block_from_method(rb_vm_method_t *method)
 {
@@ -4069,203 +2674,6 @@
     return rb_proc_alloc_with_block(rb_cProc, b);
 }
 
-static force_inline VALUE
-rb_vm_block_eval0(rb_vm_block_t *b, SEL sel, VALUE self, int argc,
-	const VALUE *argv)
-{
-    if ((b->flags & VM_BLOCK_IFUNC) == VM_BLOCK_IFUNC) {
-	// Special case for blocks passed with rb_objc_block_call(), to
-	// preserve API compatibility.
-	VALUE (*pimp)(VALUE, VALUE, int, const VALUE *) =
-	    (VALUE (*)(VALUE, VALUE, int, const VALUE *))b->imp;
-
-	return (*pimp)(argc == 0 ? Qnil : argv[0], b->userdata, argc, argv);
-    }
-    else if ((b->flags & VM_BLOCK_EMPTY) == VM_BLOCK_EMPTY) {
-	// Trying to call an empty block!
-	return Qnil;
-    }
-
-    rb_vm_arity_t arity = b->arity;    
-
-    if (argc < arity.min || argc > arity.max) {
-	if (arity.max != -1
-		&& (b->flags & VM_BLOCK_LAMBDA) == VM_BLOCK_LAMBDA) {
-	    rb_raise(rb_eArgError, "wrong number of arguments (%d for %d)",
-		    argc, arity.min);
-	}
-	VALUE *new_argv;
-	if (argc == 1 && TYPE(argv[0]) == T_ARRAY
-	    && (arity.min > 1 || (arity.min == 1 && arity.min != arity.max))) {
-	    // Expand the array
-	    long ary_len = RARRAY_LEN(argv[0]);
-	    new_argv = (VALUE *)alloca(sizeof(VALUE) * ary_len);
-	    for (int i = 0; i < ary_len; i++) {
-		new_argv[i] = RARRAY_AT(argv[0], i);
-	    }
-	    argv = new_argv;
-	    argc = ary_len;
-	    if (argc >= arity.min && (argc <= arity.max || b->arity.max == -1)) {
-		goto block_call;
-	    }
-	}
-	int new_argc;
-	if (argc <= arity.min) {
-	    new_argc = arity.min;
-	}
-	else if (argc > arity.max && b->arity.max != -1) {
-	    new_argc = arity.max;
-	}
-	else {
-	    new_argc = argc;
-	}
-	new_argv = (VALUE *)alloca(sizeof(VALUE) * new_argc);
-	for (int i = 0; i < new_argc; i++) {
-	    new_argv[i] = i < argc ? argv[i] : Qnil;
-	}
-	argc = new_argc;
-	argv = new_argv;
-    }
-#if ROXOR_VM_DEBUG
-    printf("yield block %p argc %d arity %d\n", b, argc, arity.real);
-#endif
-
-block_call:
-
-    if (b->flags & VM_BLOCK_ACTIVE) {
-	b = rb_vm_dup_active_block(b);
-    }
-    b->flags |= VM_BLOCK_ACTIVE;
-
-    RoxorVM *vm = GET_VM();
-    Class old_current_class = vm->get_current_class();
-    vm->set_current_class((Class)b->klass);
-
-    struct Finally {
-	RoxorVM *vm;
-	rb_vm_block_t *b;
-	Class c;
-	Finally(RoxorVM *_vm, rb_vm_block_t *_b, Class _c) {
-	    vm = _vm;
-	    b = _b;
-	    c = _c;
-	}
-	~Finally() {
-	    b->flags &= ~VM_BLOCK_ACTIVE;
-	    vm->set_current_class(c);
-	}
-    } finalizer(vm, b, old_current_class);
-
-    if (b->flags & VM_BLOCK_METHOD) {
-	rb_vm_method_t *m = (rb_vm_method_t *)b->imp;
-	return rb_vm_call_with_cache2(m->cache, NULL, m->recv, m->oclass,
-		m->sel, argc, argv);
-    }
-    return __rb_vm_bcall(self, sel, (VALUE)b->dvars, b, b->imp, b->arity,
-	    argc, argv);
-}
-
-extern "C"
-VALUE
-rb_vm_block_eval(rb_vm_block_t *b, int argc, const VALUE *argv)
-{
-    return rb_vm_block_eval0(b, NULL, b->self, argc, argv);
-}
-
-extern "C"
-VALUE
-rb_vm_block_eval2(rb_vm_block_t *b, VALUE self, SEL sel, int argc,
-	const VALUE *argv)
-{
-    // TODO check given arity and raise exception
-    return rb_vm_block_eval0(b, sel, self, argc, argv);
-}
-
-static inline VALUE
-rb_vm_yield0(int argc, const VALUE *argv)
-{
-    RoxorVM *vm = GET_VM();
-    rb_vm_block_t *b = vm->current_block();
-    if (b == NULL) {
-	rb_raise(rb_eLocalJumpError, "no block given");
-    }
-
-    vm->pop_current_block();
-
-    struct Finally {
-	RoxorVM *vm;
-	rb_vm_block_t *b;
-	Finally(RoxorVM *_vm, rb_vm_block_t *_b) { 
-	    vm = _vm;
-	    b = _b;
-	}
-	~Finally() {
-	    vm->add_current_block(b);
-	}
-    } finalizer(vm, b);
-
-    return rb_vm_block_eval0(b, NULL, b->self, argc, argv);
-}
-
-extern "C"
-VALUE
-rb_vm_yield(int argc, const VALUE *argv)
-{
-    return rb_vm_yield0(argc, argv);
-}
-
-extern "C"
-VALUE
-rb_vm_yield_under(VALUE klass, VALUE self, int argc, const VALUE *argv)
-{
-    RoxorVM *vm = GET_VM();
-    rb_vm_block_t *b = vm->current_block();
-    vm->pop_current_block();
-
-    VALUE old_self = b->self;
-    b->self = self;
-    VALUE old_class = b->klass;
-    b->klass = klass;
-
-    struct Finally {
-	RoxorVM *vm;
-	rb_vm_block_t *b;
-	VALUE old_class;
-	VALUE old_self;
-	Finally(RoxorVM *_vm, rb_vm_block_t *_b, VALUE _old_class,
-		VALUE _old_self) {
-	    vm = _vm;
-	    b = _b;
-	    old_class = _old_class;
-	    old_self = _old_self;
-	}
-	~Finally() {
-	    b->self = old_self;
-	    b->klass = old_class;
-	    vm->add_current_block(b);
-	}
-    } finalizer(vm, b, old_class, old_self);
-
-    return rb_vm_block_eval0(b, NULL, b->self, argc, argv);
-}
-
-extern "C"
-VALUE 
-rb_vm_yield_args(int argc, ...)
-{
-    VALUE base_argv[MAX_DISPATCH_ARGS];
-    VALUE *argv = &base_argv[0];
-    if (argc > 0) {
-	va_list ar;
-	va_start(ar, argc);
-	__rb_vm_resolve_args(&argv, MAX_DISPATCH_ARGS, &argc, ar);
-	va_end(ar);
-    }
-    return rb_vm_yield0(argc, argv);
-}
-
-extern IMP basic_respond_to_imp; // vm_method.c
-
 #if MAC_OS_X_VERSION_MAX_ALLOWED < 1060
 // the function is available on Leopard but it's not declared
 extern "C" id _objc_msgForward(id receiver, SEL sel, ...);
@@ -4288,51 +2696,6 @@
 }
 #endif
 
-extern "C"
-bool
-rb_vm_respond_to(VALUE obj, SEL sel, bool priv)
-{
-    VALUE klass = CLASS_OF(obj);
-
-    IMP respond_to_imp = class_getMethodImplementation((Class)klass,
-	    selRespondTo);
-
-    if (respond_to_imp == basic_respond_to_imp) {
-	// FIXME: too slow!
-	bool reject_pure_ruby_methods = false;
-	Method m = class_getInstanceMethod((Class)klass, sel);
-	if (m == NULL) {
-	    const char *selname = sel_getName(sel);
-	    sel = helper_sel(selname, strlen(selname));
-	    if (sel != NULL) {
-		m = class_getInstanceMethod((Class)klass, sel);
-		reject_pure_ruby_methods = true;
-	    }
-	}
-
-	if (m == NULL || UNDEFINED_IMP(method_getImplementation(m))) {
-	    return false;
-	}
-
-	rb_vm_method_node_t *node = GET_CORE()->method_node_get(m);
-	if (node != NULL
-	    && (reject_pure_ruby_methods
-		|| (!priv && (node->flags & VM_METHOD_PRIVATE)))) {
-	    return false;
-	}
-        return true;
-    }
-    else {
-	VALUE args[2];
-	int n = 0;
-	args[n++] = ID2SYM(rb_intern(sel_getName(sel)));
-	if (priv) {
-	    args[n++] = Qtrue;
-	}
-	return rb_vm_call(obj, selRespondTo, n, args, false) == Qtrue;
-    }
-}
-
 extern "C" VALUE rb_reg_match_pre(VALUE match, SEL sel);
 extern "C" VALUE rb_reg_match_post(VALUE match, SEL sel);
 

Modified: MacRuby/trunk/vm.h
===================================================================
--- MacRuby/trunk/vm.h	2009-10-02 00:38:19 UTC (rev 2705)
+++ MacRuby/trunk/vm.h	2009-10-02 03:43:08 UTC (rev 2706)
@@ -277,6 +277,9 @@
 rb_vm_method_node_t *rb_vm_define_method2(Class klass, SEL sel,
 	rb_vm_method_node_t *node, bool direct);
 void rb_vm_define_method3(Class klass, SEL sel, rb_vm_block_t *node);
+bool rb_vm_resolve_method(Class klass, SEL sel);
+void *rb_vm_undefined_imp(void *rcv, SEL sel);
+#define UNDEFINED_IMP(imp) (imp == NULL || imp == (IMP)rb_vm_undefined_imp)
 void rb_vm_define_attr(Class klass, const char *name, bool read, bool write);
 void rb_vm_undef_method(Class klass, ID name, bool must_exist);
 void rb_vm_alias(VALUE klass, ID name, ID def);
@@ -769,7 +772,7 @@
 	// The pthread specific key to retrieve the current VM thread.
 	static pthread_key_t vm_thread_key;
 
-	static RoxorVM *current(void) {
+	static force_inline RoxorVM *current(void) {
 	    if (GET_CORE()->get_multithreaded()) {
 		void *vm = pthread_getspecific(vm_thread_key);
 		if (vm == NULL) {
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.macosforge.org/pipermail/macruby-changes/attachments/20091001/085e71db/attachment-0001.html>


More information about the macruby-changes mailing list