* class.c (rb_scan_args): moved to bottom of the file to make the
effect of `#undef rb_scan_args` the minimum. * include/ruby/ruby.h (rb_scan_args): overwrite only if GCC and optimized. Visual C++ 14 or later can compile it but make it conservative. git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@55110 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
This commit is contained in:
parent
b9fe5948f3
commit
75307ce858
21
ChangeLog
21
ChangeLog
@ -1,3 +1,24 @@
|
|||||||
|
Sun May 22 11:41:12 2016 NARUSE, Yui <naruse@ruby-lang.org>
|
||||||
|
|
||||||
|
* class.c (rb_scan_args): moved to bottom of the file to make the
|
||||||
|
effect of `#undef rb_scan_args` the minimum.
|
||||||
|
|
||||||
|
* include/ruby/ruby.h (rb_scan_args): overwrite only if GCC and
|
||||||
|
optimized. Visual C++ 14 or later can compile it but make it
|
||||||
|
conservative.
|
||||||
|
|
||||||
|
Sat May 21 22:45:50 2016 NAKAMURA Usaku <usa@ruby-lang.org>
|
||||||
|
|
||||||
|
* include/ruby/ruby.h (rb_scan_args): don't use ALWAYS_INLINE with
|
||||||
|
`inline`. if gcc needs this duplication, do in ALWAYS_INLINE macro.
|
||||||
|
|
||||||
|
Sat May 21 21:11:56 2016 NARUSE, Yui <naruse@ruby-lang.org>
|
||||||
|
|
||||||
|
* include/ruby/ruby.h (rb_scan_args): use __VA_ARGS__ instead of
|
||||||
|
va_arg to allow compilers optimize more aggressive.
|
||||||
|
https://gustedt.wordpress.com/2011/07/10/avoid-writing-va_arg-functions/
|
||||||
|
rb_scan_args is now expected to be statically resolved.
|
||||||
|
|
||||||
Sun May 22 02:41:52 2016 NARUSE, Yui <naruse@ruby-lang.org>
|
Sun May 22 02:41:52 2016 NARUSE, Yui <naruse@ruby-lang.org>
|
||||||
|
|
||||||
* ext/zlib/zlib.c: remove hacky macro introduced at r30437.
|
* ext/zlib/zlib.c: remove hacky macro introduced at r30437.
|
||||||
|
257
class.c
257
class.c
@ -1754,8 +1754,135 @@ rb_obj_basic_to_s_p(VALUE obj)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
#include <stdarg.h>
|
VALUE
|
||||||
|
rb_keyword_error_new(const char *error, VALUE keys)
|
||||||
|
{
|
||||||
|
const char *msg = "";
|
||||||
|
VALUE error_message;
|
||||||
|
|
||||||
|
if (RARRAY_LEN(keys) == 1) {
|
||||||
|
keys = RARRAY_AREF(keys, 0);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
keys = rb_ary_join(keys, rb_usascii_str_new2(", "));
|
||||||
|
msg = "s";
|
||||||
|
}
|
||||||
|
|
||||||
|
error_message = rb_sprintf("%s keyword%s: %"PRIsVALUE, error, msg, keys);
|
||||||
|
|
||||||
|
return rb_exc_new_str(rb_eArgError, error_message);
|
||||||
|
}
|
||||||
|
|
||||||
|
NORETURN(static void rb_keyword_error(const char *error, VALUE keys));
|
||||||
|
static void
|
||||||
|
rb_keyword_error(const char *error, VALUE keys)
|
||||||
|
{
|
||||||
|
rb_exc_raise(rb_keyword_error_new(error, keys));
|
||||||
|
}
|
||||||
|
|
||||||
|
NORETURN(static void unknown_keyword_error(VALUE hash, const ID *table, int keywords));
|
||||||
|
static void
|
||||||
|
unknown_keyword_error(VALUE hash, const ID *table, int keywords)
|
||||||
|
{
|
||||||
|
st_table *tbl = rb_hash_tbl_raw(hash);
|
||||||
|
VALUE keys;
|
||||||
|
int i;
|
||||||
|
for (i = 0; i < keywords; i++) {
|
||||||
|
st_data_t key = ID2SYM(table[i]);
|
||||||
|
st_delete(tbl, &key, NULL);
|
||||||
|
}
|
||||||
|
keys = rb_funcallv(hash, rb_intern("keys"), 0, 0);
|
||||||
|
if (!RB_TYPE_P(keys, T_ARRAY)) rb_raise(rb_eArgError, "unknown keyword");
|
||||||
|
rb_keyword_error("unknown", keys);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
separate_symbol(st_data_t key, st_data_t value, st_data_t arg)
|
||||||
|
{
|
||||||
|
VALUE *kwdhash = (VALUE *)arg;
|
||||||
|
|
||||||
|
if (!SYMBOL_P(key)) kwdhash++;
|
||||||
|
if (!*kwdhash) *kwdhash = rb_hash_new();
|
||||||
|
rb_hash_aset(*kwdhash, (VALUE)key, (VALUE)value);
|
||||||
|
return ST_CONTINUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
VALUE
|
||||||
|
rb_extract_keywords(VALUE *orighash)
|
||||||
|
{
|
||||||
|
VALUE parthash[2] = {0, 0};
|
||||||
|
VALUE hash = *orighash;
|
||||||
|
|
||||||
|
if (RHASH_EMPTY_P(hash)) {
|
||||||
|
*orighash = 0;
|
||||||
|
return hash;
|
||||||
|
}
|
||||||
|
st_foreach(rb_hash_tbl_raw(hash), separate_symbol, (st_data_t)&parthash);
|
||||||
|
*orighash = parthash[1];
|
||||||
|
return parthash[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
rb_get_kwargs(VALUE keyword_hash, const ID *table, int required, int optional, VALUE *values)
|
||||||
|
{
|
||||||
|
int i = 0, j;
|
||||||
|
int rest = 0;
|
||||||
|
VALUE missing = Qnil;
|
||||||
|
st_data_t key;
|
||||||
|
|
||||||
|
#define extract_kwarg(keyword, val) \
|
||||||
|
(key = (st_data_t)(keyword), values ? \
|
||||||
|
st_delete(rb_hash_tbl_raw(keyword_hash), &key, (val)) : \
|
||||||
|
st_lookup(rb_hash_tbl_raw(keyword_hash), key, (val)))
|
||||||
|
|
||||||
|
if (NIL_P(keyword_hash)) keyword_hash = 0;
|
||||||
|
|
||||||
|
if (optional < 0) {
|
||||||
|
rest = 1;
|
||||||
|
optional = -1-optional;
|
||||||
|
}
|
||||||
|
if (values) {
|
||||||
|
for (j = 0; j < required + optional; j++) {
|
||||||
|
values[j] = Qundef;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (required) {
|
||||||
|
for (; i < required; i++) {
|
||||||
|
VALUE keyword = ID2SYM(table[i]);
|
||||||
|
if (keyword_hash) {
|
||||||
|
st_data_t val;
|
||||||
|
if (extract_kwarg(keyword, &val)) {
|
||||||
|
if (values) values[i] = (VALUE)val;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (NIL_P(missing)) missing = rb_ary_tmp_new(1);
|
||||||
|
rb_ary_push(missing, keyword);
|
||||||
|
}
|
||||||
|
if (!NIL_P(missing)) {
|
||||||
|
rb_keyword_error("missing", missing);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
j = i;
|
||||||
|
if (optional && keyword_hash) {
|
||||||
|
for (i = 0; i < optional; i++) {
|
||||||
|
st_data_t val;
|
||||||
|
if (extract_kwarg(ID2SYM(table[required+i]), &val)) {
|
||||||
|
if (values) values[required+i] = (VALUE)val;
|
||||||
|
j++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!rest && keyword_hash) {
|
||||||
|
if (RHASH_SIZE(keyword_hash) > (unsigned int)j) {
|
||||||
|
unknown_keyword_error(keyword_hash, table, required+optional);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return j;
|
||||||
|
#undef extract_kwarg
|
||||||
|
}
|
||||||
|
|
||||||
|
#undef rb_scan_args
|
||||||
int
|
int
|
||||||
rb_scan_args(int argc, const VALUE *argv, const char *fmt, ...)
|
rb_scan_args(int argc, const VALUE *argv, const char *fmt, ...)
|
||||||
{
|
{
|
||||||
@ -1889,134 +2016,6 @@ rb_scan_args(int argc, const VALUE *argv, const char *fmt, ...)
|
|||||||
return argc;
|
return argc;
|
||||||
}
|
}
|
||||||
|
|
||||||
VALUE
|
|
||||||
rb_keyword_error_new(const char *error, VALUE keys)
|
|
||||||
{
|
|
||||||
const char *msg = "";
|
|
||||||
VALUE error_message;
|
|
||||||
|
|
||||||
if (RARRAY_LEN(keys) == 1) {
|
|
||||||
keys = RARRAY_AREF(keys, 0);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
keys = rb_ary_join(keys, rb_usascii_str_new2(", "));
|
|
||||||
msg = "s";
|
|
||||||
}
|
|
||||||
|
|
||||||
error_message = rb_sprintf("%s keyword%s: %"PRIsVALUE, error, msg, keys);
|
|
||||||
|
|
||||||
return rb_exc_new_str(rb_eArgError, error_message);
|
|
||||||
}
|
|
||||||
|
|
||||||
NORETURN(static void rb_keyword_error(const char *error, VALUE keys));
|
|
||||||
static void
|
|
||||||
rb_keyword_error(const char *error, VALUE keys)
|
|
||||||
{
|
|
||||||
rb_exc_raise(rb_keyword_error_new(error, keys));
|
|
||||||
}
|
|
||||||
|
|
||||||
NORETURN(static void unknown_keyword_error(VALUE hash, const ID *table, int keywords));
|
|
||||||
static void
|
|
||||||
unknown_keyword_error(VALUE hash, const ID *table, int keywords)
|
|
||||||
{
|
|
||||||
st_table *tbl = rb_hash_tbl_raw(hash);
|
|
||||||
VALUE keys;
|
|
||||||
int i;
|
|
||||||
for (i = 0; i < keywords; i++) {
|
|
||||||
st_data_t key = ID2SYM(table[i]);
|
|
||||||
st_delete(tbl, &key, NULL);
|
|
||||||
}
|
|
||||||
keys = rb_funcallv(hash, rb_intern("keys"), 0, 0);
|
|
||||||
if (!RB_TYPE_P(keys, T_ARRAY)) rb_raise(rb_eArgError, "unknown keyword");
|
|
||||||
rb_keyword_error("unknown", keys);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int
|
|
||||||
separate_symbol(st_data_t key, st_data_t value, st_data_t arg)
|
|
||||||
{
|
|
||||||
VALUE *kwdhash = (VALUE *)arg;
|
|
||||||
|
|
||||||
if (!SYMBOL_P(key)) kwdhash++;
|
|
||||||
if (!*kwdhash) *kwdhash = rb_hash_new();
|
|
||||||
rb_hash_aset(*kwdhash, (VALUE)key, (VALUE)value);
|
|
||||||
return ST_CONTINUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
VALUE
|
|
||||||
rb_extract_keywords(VALUE *orighash)
|
|
||||||
{
|
|
||||||
VALUE parthash[2] = {0, 0};
|
|
||||||
VALUE hash = *orighash;
|
|
||||||
|
|
||||||
if (RHASH_EMPTY_P(hash)) {
|
|
||||||
*orighash = 0;
|
|
||||||
return hash;
|
|
||||||
}
|
|
||||||
st_foreach(rb_hash_tbl_raw(hash), separate_symbol, (st_data_t)&parthash);
|
|
||||||
*orighash = parthash[1];
|
|
||||||
return parthash[0];
|
|
||||||
}
|
|
||||||
|
|
||||||
int
|
|
||||||
rb_get_kwargs(VALUE keyword_hash, const ID *table, int required, int optional, VALUE *values)
|
|
||||||
{
|
|
||||||
int i = 0, j;
|
|
||||||
int rest = 0;
|
|
||||||
VALUE missing = Qnil;
|
|
||||||
st_data_t key;
|
|
||||||
|
|
||||||
#define extract_kwarg(keyword, val) \
|
|
||||||
(key = (st_data_t)(keyword), values ? \
|
|
||||||
st_delete(rb_hash_tbl_raw(keyword_hash), &key, (val)) : \
|
|
||||||
st_lookup(rb_hash_tbl_raw(keyword_hash), key, (val)))
|
|
||||||
|
|
||||||
if (NIL_P(keyword_hash)) keyword_hash = 0;
|
|
||||||
|
|
||||||
if (optional < 0) {
|
|
||||||
rest = 1;
|
|
||||||
optional = -1-optional;
|
|
||||||
}
|
|
||||||
if (values) {
|
|
||||||
for (j = 0; j < required + optional; j++) {
|
|
||||||
values[j] = Qundef;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (required) {
|
|
||||||
for (; i < required; i++) {
|
|
||||||
VALUE keyword = ID2SYM(table[i]);
|
|
||||||
if (keyword_hash) {
|
|
||||||
st_data_t val;
|
|
||||||
if (extract_kwarg(keyword, &val)) {
|
|
||||||
if (values) values[i] = (VALUE)val;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (NIL_P(missing)) missing = rb_ary_tmp_new(1);
|
|
||||||
rb_ary_push(missing, keyword);
|
|
||||||
}
|
|
||||||
if (!NIL_P(missing)) {
|
|
||||||
rb_keyword_error("missing", missing);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
j = i;
|
|
||||||
if (optional && keyword_hash) {
|
|
||||||
for (i = 0; i < optional; i++) {
|
|
||||||
st_data_t val;
|
|
||||||
if (extract_kwarg(ID2SYM(table[required+i]), &val)) {
|
|
||||||
if (values) values[required+i] = (VALUE)val;
|
|
||||||
j++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!rest && keyword_hash) {
|
|
||||||
if (RHASH_SIZE(keyword_hash) > (unsigned int)j) {
|
|
||||||
unknown_keyword_error(keyword_hash, table, required+optional);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return j;
|
|
||||||
#undef extract_kwarg
|
|
||||||
}
|
|
||||||
|
|
||||||
int
|
int
|
||||||
rb_class_has_methods(VALUE c)
|
rb_class_has_methods(VALUE c)
|
||||||
{
|
{
|
||||||
|
@ -2150,6 +2150,144 @@ unsigned long ruby_strtoul(const char *str, char **endptr, int base);
|
|||||||
PRINTF_ARGS(int ruby_snprintf(char *str, size_t n, char const *fmt, ...), 3, 4);
|
PRINTF_ARGS(int ruby_snprintf(char *str, size_t n, char const *fmt, ...), 3, 4);
|
||||||
int ruby_vsnprintf(char *str, size_t n, char const *fmt, va_list ap);
|
int ruby_vsnprintf(char *str, size_t n, char const *fmt, va_list ap);
|
||||||
|
|
||||||
|
#if defined(__GNUC__) && defined(__OPTIMIZE__)
|
||||||
|
# define rb_scan_args(argc,argvp,fmt,...) \
|
||||||
|
rb_scan_args0(argc,argv,fmt,(sizeof((VALUE*[]){__VA_ARGS__})/sizeof(VALUE*)),(VALUE*[]){__VA_ARGS__})
|
||||||
|
ALWAYS_INLINE(static int
|
||||||
|
rb_scan_args0(int argc, const VALUE *argv, const char *fmt, int varc, VALUE *vars[]));
|
||||||
|
inline int
|
||||||
|
rb_scan_args0(int argc, const VALUE *argv, const char *fmt, int varc, VALUE *vars[])
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
const char *p = fmt;
|
||||||
|
VALUE *var;
|
||||||
|
int f_var = 0, f_hash = 0, f_block = 0;
|
||||||
|
int n_lead = 0, n_opt = 0, n_trail = 0, n_mand;
|
||||||
|
int argi = 0, vari = 0;
|
||||||
|
VALUE hash = Qnil;
|
||||||
|
|
||||||
|
if (ISDIGIT(*p)) {
|
||||||
|
n_lead = *p - '0';
|
||||||
|
p++;
|
||||||
|
if (ISDIGIT(*p)) {
|
||||||
|
n_opt = *p - '0';
|
||||||
|
p++;
|
||||||
|
if (ISDIGIT(*p)) {
|
||||||
|
n_trail = *p - '0';
|
||||||
|
p++;
|
||||||
|
goto block_arg;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (*p == '*') {
|
||||||
|
f_var = 1;
|
||||||
|
p++;
|
||||||
|
if (ISDIGIT(*p)) {
|
||||||
|
n_trail = *p - '0';
|
||||||
|
p++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
block_arg:
|
||||||
|
if (*p == ':') {
|
||||||
|
f_hash = 1;
|
||||||
|
p++;
|
||||||
|
}
|
||||||
|
if (*p == '&') {
|
||||||
|
f_block = 1;
|
||||||
|
p++;
|
||||||
|
}
|
||||||
|
if (*p != '\0') {
|
||||||
|
rb_fatal("bad scan arg format: %s", fmt);
|
||||||
|
}
|
||||||
|
n_mand = n_lead + n_trail;
|
||||||
|
|
||||||
|
if (argc < n_mand)
|
||||||
|
goto argc_error;
|
||||||
|
|
||||||
|
/* capture an option hash - phase 1: pop */
|
||||||
|
if (f_hash && n_mand < argc) {
|
||||||
|
VALUE last = argv[argc - 1];
|
||||||
|
|
||||||
|
if (NIL_P(last)) {
|
||||||
|
/* nil is taken as an empty option hash only if it is not
|
||||||
|
ambiguous; i.e. '*' is not specified and arguments are
|
||||||
|
given more than sufficient */
|
||||||
|
if (!f_var && n_mand + n_opt < argc)
|
||||||
|
argc--;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
hash = rb_check_hash_type(last);
|
||||||
|
if (!NIL_P(hash)) {
|
||||||
|
VALUE opts = rb_extract_keywords(&hash);
|
||||||
|
if (!hash) argc--;
|
||||||
|
hash = opts ? opts : Qnil;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/* capture leading mandatory arguments */
|
||||||
|
for (i = n_lead; i-- > 0; ) {
|
||||||
|
var = vars[vari++];
|
||||||
|
if (var) *var = argv[argi];
|
||||||
|
argi++;
|
||||||
|
}
|
||||||
|
/* capture optional arguments */
|
||||||
|
for (i = n_opt; i-- > 0; ) {
|
||||||
|
var = vars[vari++];
|
||||||
|
if (argi < argc - n_trail) {
|
||||||
|
if (var) *var = argv[argi];
|
||||||
|
argi++;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if (var) *var = Qnil;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/* capture variable length arguments */
|
||||||
|
if (f_var) {
|
||||||
|
int n_var = argc - argi - n_trail;
|
||||||
|
|
||||||
|
var = vars[vari++];
|
||||||
|
if (0 < n_var) {
|
||||||
|
if (var) *var = rb_ary_new4(n_var, &argv[argi]);
|
||||||
|
argi += n_var;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if (var) *var = rb_ary_new();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/* capture trailing mandatory arguments */
|
||||||
|
for (i = n_trail; i-- > 0; ) {
|
||||||
|
var = vars[vari++];
|
||||||
|
if (var) *var = argv[argi];
|
||||||
|
argi++;
|
||||||
|
}
|
||||||
|
/* capture an option hash - phase 2: assignment */
|
||||||
|
if (f_hash) {
|
||||||
|
var = vars[vari++];
|
||||||
|
if (var) *var = hash;
|
||||||
|
}
|
||||||
|
/* capture iterator block */
|
||||||
|
if (f_block) {
|
||||||
|
var = vars[vari++];
|
||||||
|
if (rb_block_given_p()) {
|
||||||
|
*var = rb_block_proc();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
*var = Qnil;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (argi < argc) {
|
||||||
|
argc_error:
|
||||||
|
rb_error_arity(argc, n_mand, f_var ? UNLIMITED_ARGUMENTS : n_mand + n_opt);
|
||||||
|
}
|
||||||
|
if (vari != varc) {
|
||||||
|
rb_raise(rb_eRuntimeError, "variable argument length doesn't match* %d %d", vari, varc);
|
||||||
|
}
|
||||||
|
|
||||||
|
return argc;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifndef RUBY_DONT_SUBST
|
#ifndef RUBY_DONT_SUBST
|
||||||
#include "ruby/subst.h"
|
#include "ruby/subst.h"
|
||||||
#endif
|
#endif
|
||||||
|
Loading…
x
Reference in New Issue
Block a user