static int setup_parameters_complex(rb_thread_t * const th, const rb_iseq_t * const iseq, struct rb_calling_info *const calling, const struct rb_call_info *ci, VALUE * const locals, const enum arg_setup_type arg_setup_type) { const int min_argc = iseq->body->param.lead_num + iseq->body->param.post_num; const int max_argc = (iseq->body->param.flags.has_rest == FALSE) ? min_argc + iseq->body->param.opt_num : UNLIMITED_ARGUMENTS; int opt_pc = 0; int given_argc; struct args_info args_body, *args; VALUE keyword_hash = Qnil; VALUE * const orig_sp = th->cfp->sp; unsigned int i; /* * Extend SP for GC. * * [pushed values] [uninitialized values] * <- ci->argc --> * <- iseq->body->param.size------------> * ^ locals ^ sp * * => * [pushed values] [initialized values ] * <- ci->argc --> * <- iseq->body->param.size------------> * ^ locals ^ sp */ for (i=calling->argc; i<iseq->body->param.size; i++) { locals[i] = Qnil; } th->cfp->sp = &locals[i]; /* setup args */ args = &args_body; given_argc = args->argc = calling->argc; args->argv = locals; if (ci->flag & VM_CALL_KWARG) { args->kw_arg = ((struct rb_call_info_with_kwarg *)ci)->kw_arg; if (iseq->body->param.flags.has_kw) { int kw_len = args->kw_arg->keyword_len; /* copy kw_argv */ args->kw_argv = ALLOCA_N(VALUE, kw_len); args->argc -= kw_len; given_argc -= kw_len; MEMCPY(args->kw_argv, locals + args->argc, VALUE, kw_len); } else { args->kw_argv = NULL; given_argc = args_kw_argv_to_hash(args); } } else { args->kw_arg = NULL; args->kw_argv = NULL; } if (ci->flag & VM_CALL_ARGS_SPLAT) { args->rest = locals[--args->argc]; args->rest_index = 0; given_argc += RARRAY_LENINT(args->rest) - 1; } else { args->rest = Qfalse; } switch (arg_setup_type) { case arg_setup_method: break; /* do nothing special */ case arg_setup_block: if (given_argc == 1 && (min_argc > 0 || iseq->body->param.opt_num > 1 || iseq->body->param.flags.has_kw || iseq->body->param.flags.has_kwrest) && !iseq->body->param.flags.ambiguous_param0 && args_check_block_arg0(args, th)) { given_argc = RARRAY_LENINT(args->rest); } break; case arg_setup_lambda: if (given_argc == 1 && given_argc != iseq->body->param.lead_num && !iseq->body->param.flags.has_rest && args_check_block_arg0(args, th)) { given_argc = RARRAY_LENINT(args->rest); } } /* argc check */ if (given_argc < min_argc) { if (given_argc == min_argc - 1 && args->kw_argv) { args_stored_kw_argv_to_hash(args); given_argc = args_argc(args); } else { if (arg_setup_type == arg_setup_block) { CHECK_VM_STACK_OVERFLOW(th->cfp, min_argc); given_argc = min_argc; args_extend(args, min_argc); } else { argument_arity_error(th, iseq, given_argc, min_argc, max_argc); } } } if (given_argc > min_argc && (iseq->body->param.flags.has_kw || iseq->body->param.flags.has_kwrest) && args->kw_argv == NULL) { if (args_pop_keyword_hash(args, &keyword_hash, th)) { given_argc--; } } if (given_argc > max_argc && max_argc != UNLIMITED_ARGUMENTS) { if (arg_setup_type == arg_setup_block) { /* truncate */ args_reduce(args, given_argc - max_argc); given_argc = max_argc; } else { argument_arity_error(th, iseq, given_argc, min_argc, max_argc); } } if (iseq->body->param.flags.has_lead) { args_setup_lead_parameters(args, iseq->body->param.lead_num, locals + 0); } if (iseq->body->param.flags.has_post) { args_setup_post_parameters(args, iseq->body->param.post_num, locals + iseq->body->param.post_start); } if (iseq->body->param.flags.has_opt) { int opt = args_setup_opt_parameters(args, iseq->body->param.opt_num, locals + iseq->body->param.lead_num); opt_pc = (int)iseq->body->param.opt_table[opt]; } if (iseq->body->param.flags.has_rest) { args_setup_rest_parameter(args, locals + iseq->body->param.rest_start); } if (iseq->body->param.flags.has_kw) { VALUE * const klocals = locals + iseq->body->param.keyword->bits_start - iseq->body->param.keyword->num; if (args->kw_argv != NULL) { const struct rb_call_info_kw_arg *kw_arg = args->kw_arg; args_setup_kw_parameters(args->kw_argv, kw_arg->keyword_len, kw_arg->keywords, iseq, klocals); } else if (!NIL_P(keyword_hash)) { int kw_len = rb_long2int(RHASH_SIZE(keyword_hash)); struct fill_values_arg arg; /* copy kw_argv */ arg.keys = args->kw_argv = ALLOCA_N(VALUE, kw_len * 2); arg.vals = arg.keys + kw_len; arg.argc = 0; rb_hash_foreach(keyword_hash, fill_keys_values, (VALUE)&arg); VM_ASSERT(arg.argc == kw_len); args_setup_kw_parameters(arg.vals, kw_len, arg.keys, iseq, klocals); } else { VM_ASSERT(args_argc(args) == 0); args_setup_kw_parameters(NULL, 0, NULL, iseq, klocals); } } else if (iseq->body->param.flags.has_kwrest) { args_setup_kw_rest_parameter(keyword_hash, locals + iseq->body->param.keyword->rest_start); } if (iseq->body->param.flags.has_block) { args_setup_block_parameter(th, calling, locals + iseq->body->param.block_start); } #if 0 { int i; for (i=0; i<iseq->body->param.size; i++) { fprintf(stderr, "local[%d] = %p\n", i, (void *)locals[i]); } } #endif th->cfp->sp = orig_sp; return opt_pc; }
static VALUE function_call(int argc, VALUE argv[], VALUE self) { ffi_cif * cif; fiddle_generic retval; fiddle_generic *generic_args; void **values; VALUE cfunc, types, cPointer; int i; cfunc = rb_iv_get(self, "@ptr"); types = rb_iv_get(self, "@args"); cPointer = rb_const_get(mFiddle, rb_intern("Pointer")); if(argc != RARRAY_LENINT(types)) { rb_raise(rb_eArgError, "wrong number of arguments (%d for %d)", argc, RARRAY_LENINT(types)); } TypedData_Get_Struct(self, ffi_cif, &function_data_type, cif); if (rb_safe_level() >= 1) { for (i = 0; i < argc; i++) { VALUE src = argv[i]; if (OBJ_TAINTED(src)) { rb_raise(rb_eSecurityError, "tainted parameter not allowed"); } } } values = xcalloc((size_t)argc + 1, (size_t)sizeof(void *)); generic_args = xcalloc((size_t)argc, (size_t)sizeof(fiddle_generic)); for (i = 0; i < argc; i++) { VALUE type = RARRAY_PTR(types)[i]; VALUE src = argv[i]; if(NUM2INT(type) == TYPE_VOIDP) { if(NIL_P(src)) { src = INT2NUM(0); } else if(cPointer != CLASS_OF(src)) { src = rb_funcall(cPointer, rb_intern("[]"), 1, src); } src = rb_Integer(src); } VALUE2GENERIC(NUM2INT(type), src, &generic_args[i]); values[i] = (void *)&generic_args[i]; } values[argc] = NULL; ffi_call(cif, NUM2PTR(rb_Integer(cfunc)), &retval, values); rb_funcall(mFiddle, rb_intern("last_error="), 1, INT2NUM(errno)); #if defined(_WIN32) rb_funcall(mFiddle, rb_intern("win32_last_error="), 1, INT2NUM(errno)); #endif xfree(values); xfree(generic_args); return GENERIC2VALUE(rb_iv_get(self, "@return_type"), retval); }