Project

General

Profile

Feature #12023 ยป ruby-method-argument-ivars.diff

mogest (Roger Nesbitt), 01/26/2016 08:58 AM

View differences:

compile.c
}
}
}
if (args->has_ivars) {
iseq->body->param.flags.has_ivars = TRUE;
}
}
return COMPILE_OK;
node.h
NODE *kw_rest_arg;
NODE *opt_args;
int has_ivars;
};
struct parser_params;
parse.y
unsigned int past_scope_enabled: 1;
# endif
unsigned int error_p: 1;
unsigned int ivar_arg_seen: 1;
unsigned int in_def_args: 1;
#ifndef RIPPER
/* Ruby core only */
......
%type <node> command_args aref_args opt_block_arg block_arg var_ref var_lhs
%type <node> command_asgn mrhs mrhs_arg superclass block_call block_command
%type <node> f_block_optarg f_block_opt
%type <node> f_arglist f_args f_arg f_arg_item f_optarg f_marg f_marg_list f_margs
%type <node> f_arglist f_def_args f_args f_arg f_arg_item f_optarg f_marg f_marg_list f_margs
%type <node> assoc_list assocs assoc undef_list backref string_dvar for_var
%type <node> block_param opt_block_param block_param_def f_opt
%type <node> f_kwarg f_kw f_block_kwarg f_block_kw
......
}
;
f_arglist : '(' f_args rparen
f_arglist : '(' f_def_args rparen
{
/*%%%*/
$$ = $2;
......
parser->in_kwarg = 1;
lex_state |= EXPR_LABEL; /* force for args */
}
f_args term
f_def_args term
{
parser->in_kwarg = !!$<num>1;
$$ = $2;
......
}
;
f_bad_arg : tCONSTANT
f_def_args : {
parser->in_def_args = 1;
parser->ivar_arg_seen = 0;
}
f_args
{
/*%%%*/
yyerror("formal argument cannot be a constant");
$$ = 0;
/*%
$$ = dispatch1(param_error, $1);
ripper_error();
%*/
parser->in_def_args = 0;
$$ = $2;
}
| tIVAR
f_bad_arg : tCONSTANT
{
/*%%%*/
yyerror("formal argument cannot be an instance variable");
yyerror("formal argument cannot be a constant");
$$ = 0;
/*%
$$ = dispatch1(param_error, $1);
......
formal_argument(get_id($1));
$$ = $1;
}
| tIVAR
{
/*%%%*/
if (!parser->in_def_args) {
yyerror("formal argument cannot be an instance variable in a block");
$$ = 0;
}
else {
parser->ivar_arg_seen = 1;
formal_argument(get_id($1));
$$ = $1;
}
/*%
if (!parser->in_def_args) {
$$ = dispatch1(param_error, $1);
ripper_error();
}
else {
parser->ivar_arg_seen = 1;
formal_argument(get_id($1));
$$ = $1;
}
%*/
}
;
f_arg_asgn : f_norm_arg
......
{
switch (id_type(lhs)) {
case ID_LOCAL:
shadowing_lvar(lhs);
break;
case ID_INSTANCE:
break;
#ifndef RIPPER
case ID_CONST:
yyerror("formal argument cannot be a constant");
return 0;
case ID_INSTANCE:
yyerror("formal argument cannot be an instance variable");
return 0;
case ID_GLOBAL:
yyerror("formal argument cannot be a global variable");
return 0;
......
return 0;
#endif
}
shadowing_lvar(lhs);
return lhs;
}
......
if (tokadd_ident(parser, c)) return 0;
SET_LEX_STATE(EXPR_END);
tokenize_ident(parser, last_state);
if (parser->in_def_args && result == tIVAR && peek(':')) {
nextc();
SET_LEX_STATE(EXPR_ARG|EXPR_LABELED);
return tLABEL;
}
return result;
}
......
args->opt_args = o;
args->has_ivars = parser->ivar_arg_seen;
ruby_sourceline = saved_line;
return tail;
test/ruby/test_method.rb
assert_equal('1', obj.foo(1))
assert_equal('1', obj.bar(1))
end
class IvarArgumentExample
def initialize(local, @ivar, @def = 9, kwarg:, @ikwarg:, @idkwarg: 10)
@stored_local = local
@stored_kwarg = kwarg
end
def ivar_values
[@ivar, @def, @ikwarg, @idkwarg]
end
def local_values
[@local, @kwarg]
end
def stored_local_values
[@stored_local, @stored_kwarg]
end
end
def ivar_argument_example
IvarArgumentExample.new(1, 2, kwarg: 4, ikwarg: 5)
end
def test_ivar_arguments_basic
assert_equal [2, 9, 5, 10], ivar_argument_example.ivar_values
end
def test_ivar_arguments_supplying_optionals
values = IvarArgumentExample.new(1, 2, 3, kwarg: 4, ikwarg: 5, idkwarg: 6).ivar_values
assert_equal [2, 3, 5, 6], values
end
def test_local_arguments_are_not_set_as_ivars
assert_equal [nil, nil], ivar_argument_example.local_values
end
def test_local_arguments_were_stored
assert_equal [1, 4], ivar_argument_example.stored_local_values
end
def test_ivar_arguments_are_not_accepted_in_a_block
assert_raise(SyntaxError) do
eval "[].each { |@x| true }"
end
end
def test_ivar_arguments_are_not_accepted_in_a_lambda
assert_raise(SyntaxError) do
eval "-> (@x) { true }"
end
end
def accessing_ivar_as_local(@ivar)
ivar # this should raise
end
def test_ivar_arguments_are_not_exposed_as_local_variables
assert_raise(NameError) { accessing_ivar_as_local(10) }
end
end
vm_args.c
args_setup_kw_parameters(VALUE* const passed_values, const int passed_keyword_len, const VALUE *const passed_keywords,
const rb_iseq_t * const iseq, VALUE * const locals)
{
const ID *acceptable_keywords = iseq->body->param.keyword->table;
const ID *acceptable_keywords;
ID modified_acceptable_keywords[iseq->body->param.keyword->num];
const int req_key_num = iseq->body->param.keyword->required_num;
const int key_num = iseq->body->param.keyword->num;
const VALUE * const default_values = iseq->body->param.keyword->default_values;
......
int unspecified_bits = 0;
VALUE unspecified_bits_value = Qnil;
if (iseq->body->param.flags.has_ivars) {
const ID *pid = iseq->body->param.keyword->table;
for (i=0; i<iseq->body->param.keyword->num; i++, pid++) {
modified_acceptable_keywords[i] = rb_is_instance_id(*pid) ? rb_intern(rb_id2name(*pid) + 1) : *pid;
}
acceptable_keywords = (const ID *)&modified_acceptable_keywords;
}
else {
acceptable_keywords = iseq->body->param.keyword->table;
}
for (i=0; i<req_key_num; i++) {
ID key = acceptable_keywords[i];
if (args_setup_kw_parameters_lookup(key, &locals[i], passed_keywords, passed_values, passed_keyword_len)) {
......
*locals = blockval;
}
static inline void
args_setup_ivar_parameters(const rb_iseq_t * const iseq, struct rb_calling_info *calling, VALUE *locals)
{
unsigned int i;
ID id;
for (i=0; i<iseq->body->local_table_size; i++) {
id = iseq->body->local_table[i];
if (rb_is_instance_id(id)) {
rb_ivar_set(calling->recv, id, locals[i]);
locals[i] = Qnil;
}
}
}
struct fill_values_arg {
VALUE *keys;
VALUE *vals;
......
args_setup_block_parameter(th, calling, locals + iseq->body->param.block_start);
}
if (iseq->body->param.flags.has_ivars) {
args_setup_ivar_parameters(iseq, calling, locals);
}
#if 0
{
int i;
vm_core.h
unsigned int has_kw : 1;
unsigned int has_kwrest : 1;
unsigned int has_block : 1;
unsigned int has_ivars : 1;
unsigned int ambiguous_param0 : 1; /* {|a|} */
} flags;
vm_insnhelper.c
iseq->body->param.flags.has_post == FALSE &&
iseq->body->param.flags.has_kw == FALSE &&
iseq->body->param.flags.has_kwrest == FALSE &&
iseq->body->param.flags.has_block == FALSE;
iseq->body->param.flags.has_block == FALSE &&
iseq->body->param.flags.has_ivars == FALSE;
}
static inline int
    (1-1/1)