vm_resolve_args(VALUE **pargv, size_t argv_size, int *pargc, VALUE *args) { unsigned int i, argc = *pargc, real_argc = 0, j = 0; VALUE *argv = *pargv; bool splat_arg_follows = false; for (i = 0; i < argc; i++) { VALUE arg = args[j++]; 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_new4(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_ptrs(sizeof(VALUE) * new_argv_size); memcpy(new_argv, argv, sizeof(VALUE) * argv_size); argv = new_argv; argv_size = new_argv_size; } int j; for (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_ptrs(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; }
static VALUE struct_alloc(VALUE klass) { VALUE size; long n; NEWOBJ(st, struct RStruct); OBJSETUP(st, klass, T_STRUCT); size = rb_struct_iv_get(klass, "__size__"); n = FIX2LONG(size); if (0 < n && n <= RSTRUCT_EMBED_LEN_MAX) { RBASIC(st)->flags &= ~RSTRUCT_EMBED_LEN_MASK; RBASIC(st)->flags |= n << RSTRUCT_EMBED_LEN_SHIFT; rb_mem_clear(st->as.ary, n); } else { if (n > 0) { GC_WB(&st->as.heap.ptr, xmalloc_ptrs(sizeof(VALUE) * n)); rb_mem_clear(st->as.heap.ptr, n); } else { st->as.heap.ptr = NULL; } st->as.heap.len = n; } return (VALUE)st; }
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) { if (sel == selAlloc) { rb_raise(rb_eTypeError, "allocator undefined for %s", RSTRING_PTR(rb_inspect(obj))); } GET_VM()->set_method_missing_reason(call_status); VALUE *new_argv = (VALUE *)xmalloc_ptrs(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); // In case the missing selector _is_ method_missing: OR the object does // not respond to method_missing: (this can happen for NSProxy-based // objects), directly trigger the exception. Class k = (Class)CLASS_OF(obj); if (sel == selMethodMissing || class_getInstanceMethod(k, selMethodMissing) == NULL) { rb_vm_method_missing(obj, argc + 1, new_argv); return Qnil; // never reached } else { return rb_vm_call2(block, obj, (VALUE)k, selMethodMissing, argc + 1, new_argv); } }
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) { VALUE buf[100]; if (arity.real != argc || arity.max == -1) { VALUE *new_argv; if (arity.real < 100) { new_argv = buf; } else { new_argv = (VALUE *)xmalloc_ptrs(sizeof(VALUE) * arity.real); } 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]); } #if MACRUBY_STATIC rb_raise(rb_eRuntimeError, "MacRuby static doesn't support passing more than 9 arguments"); #else rb_vm_long_arity_bstub_t *stub = (rb_vm_long_arity_bstub_t *) GET_CORE()->gen_large_arity_stub(argc, true); return (*stub)(pimp, (id)self, sel, dvars, b, argc, argv); #endif }
static force_inline VALUE vm_block_eval(RoxorVM *vm, 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) { short limit = (argc < arity.min) ? arity.min : arity.max; rb_raise(rb_eArgError, "wrong number of arguments (%d for %d)", argc, limit); } VALUE *new_argv; if (argc == 1 && TYPE(argv[0]) == T_ARRAY && (arity.min > 1 || (arity.min == 1 && arity.min != arity.max))) { // Expand the array. const long ary_len = RARRAY_LEN(argv[0]); if (ary_len > 0) { new_argv = (VALUE *)RARRAY_PTR(argv[0]); } else { new_argv = NULL; } 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; } if (new_argc > 0) { new_argv = (VALUE *)xmalloc_ptrs(sizeof(VALUE) * new_argc); for (int i = 0; i < new_argc; i++) { new_argv[i] = i < argc ? argv[i] : Qnil; } } else { new_argv = NULL; } 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 = dup_block(b); } b->flags |= VM_BLOCK_ACTIVE; 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_dispatch(vm, (struct mcache *)m->cache, 0, m->recv, (Class)m->oclass, m->sel, NULL, DISPATCH_FCALL, argc, argv); } return __rb_vm_bcall(self, sel, (VALUE)b->dvars, b, b->imp, b->arity, argc, argv); }