[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