Project

General

Profile

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

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

View differences:

compile.c
1435 1435
		}
1436 1436
	    }
1437 1437
	}
1438

  
1439
	if (args->has_ivars) {
1440
	    iseq->body->param.flags.has_ivars = TRUE;
1441
	}
1438 1442
    }
1439 1443

  
1440 1444
    return COMPILE_OK;
node.h
499 499
    NODE *kw_rest_arg;
500 500

  
501 501
    NODE *opt_args;
502

  
503
    int has_ivars;
502 504
};
503 505

  
504 506
struct parser_params;
parse.y
305 305
    unsigned int past_scope_enabled: 1;
306 306
# endif
307 307
    unsigned int error_p: 1;
308
    unsigned int ivar_arg_seen: 1;
309
    unsigned int in_def_args: 1;
308 310

  
309 311
#ifndef RIPPER
310 312
    /* Ruby core only */
......
857 859
%type <node> command_args aref_args opt_block_arg block_arg var_ref var_lhs
858 860
%type <node> command_asgn mrhs mrhs_arg superclass block_call block_command
859 861
%type <node> f_block_optarg f_block_opt
860
%type <node> f_arglist f_args f_arg f_arg_item f_optarg f_marg f_marg_list f_margs
862
%type <node> f_arglist f_def_args f_args f_arg f_arg_item f_optarg f_marg f_marg_list f_margs
861 863
%type <node> assoc_list assocs assoc undef_list backref string_dvar for_var
862 864
%type <node> block_param opt_block_param block_param_def f_opt
863 865
%type <node> f_kwarg f_kw f_block_kwarg f_block_kw
......
4526 4528
		    }
4527 4529
		;
4528 4530

  
4529
f_arglist	: '(' f_args rparen
4531
f_arglist	: '(' f_def_args rparen
4530 4532
		    {
4531 4533
		    /*%%%*/
4532 4534
			$$ = $2;
......
4541 4543
			parser->in_kwarg = 1;
4542 4544
			lex_state |= EXPR_LABEL; /* force for args */
4543 4545
		    }
4544
		    f_args term
4546
		    f_def_args term
4545 4547
		    {
4546 4548
			parser->in_kwarg = !!$<num>1;
4547 4549
			$$ = $2;
......
4641 4643
		    }
4642 4644
		;
4643 4645

  
4644
f_bad_arg	: tCONSTANT
4646
f_def_args	:   {
4647
			parser->in_def_args = 1;
4648
			parser->ivar_arg_seen = 0;
4649
		    }
4650
		f_args
4645 4651
		    {
4646
		    /*%%%*/
4647
			yyerror("formal argument cannot be a constant");
4648
			$$ = 0;
4649
		    /*%
4650
			$$ = dispatch1(param_error, $1);
4651
			ripper_error();
4652
		    %*/
4652
			parser->in_def_args = 0;
4653
			$$ = $2;
4653 4654
		    }
4654
		| tIVAR
4655

  
4656
f_bad_arg	: tCONSTANT
4655 4657
		    {
4656 4658
		    /*%%%*/
4657
			yyerror("formal argument cannot be an instance variable");
4659
			yyerror("formal argument cannot be a constant");
4658 4660
			$$ = 0;
4659 4661
		    /*%
4660 4662
			$$ = dispatch1(param_error, $1);
......
4689 4691
			formal_argument(get_id($1));
4690 4692
			$$ = $1;
4691 4693
		    }
4694
		| tIVAR
4695
		    {
4696
		    /*%%%*/
4697
			if (!parser->in_def_args) {
4698
			    yyerror("formal argument cannot be an instance variable in a block");
4699
			    $$ = 0;
4700
			}
4701
			else {
4702
			    parser->ivar_arg_seen = 1;
4703
			    formal_argument(get_id($1));
4704
			    $$ = $1;
4705
			}
4706
		    /*%
4707
			if (!parser->in_def_args) {
4708
			    $$ = dispatch1(param_error, $1);
4709
			    ripper_error();
4710
			}
4711
			else {
4712
			    parser->ivar_arg_seen = 1;
4713
			    formal_argument(get_id($1));
4714
			    $$ = $1;
4715
			}
4716
		    %*/
4717
		    }
4692 4718
		;
4693 4719

  
4694 4720
f_arg_asgn	: f_norm_arg
......
6942 6968
{
6943 6969
    switch (id_type(lhs)) {
6944 6970
      case ID_LOCAL:
6971
	shadowing_lvar(lhs);
6972
	break;
6973
      case ID_INSTANCE:
6945 6974
	break;
6946 6975
#ifndef RIPPER
6947 6976
      case ID_CONST:
6948 6977
	yyerror("formal argument cannot be a constant");
6949 6978
	return 0;
6950
      case ID_INSTANCE:
6951
	yyerror("formal argument cannot be an instance variable");
6952
	return 0;
6953 6979
      case ID_GLOBAL:
6954 6980
	yyerror("formal argument cannot be a global variable");
6955 6981
	return 0;
......
6966 6992
	return 0;
6967 6993
#endif
6968 6994
    }
6969
    shadowing_lvar(lhs);
6970 6995
    return lhs;
6971 6996
}
6972 6997

  
......
7963 7988
    if (tokadd_ident(parser, c)) return 0;
7964 7989
    SET_LEX_STATE(EXPR_END);
7965 7990
    tokenize_ident(parser, last_state);
7991

  
7992
    if (parser->in_def_args && result == tIVAR && peek(':')) {
7993
	nextc();
7994
	SET_LEX_STATE(EXPR_ARG|EXPR_LABELED);
7995
	return tLABEL;
7996
    }
7997

  
7966 7998
    return result;
7967 7999
}
7968 8000

  
......
10044 10076

  
10045 10077
    args->opt_args       = o;
10046 10078

  
10079
    args->has_ivars      = parser->ivar_arg_seen;
10080

  
10047 10081
    ruby_sourceline = saved_line;
10048 10082

  
10049 10083
    return tail;
test/ruby/test_method.rb
953 953
    assert_equal('1', obj.foo(1))
954 954
    assert_equal('1', obj.bar(1))
955 955
  end
956

  
957
  class IvarArgumentExample
958
    def initialize(local, @ivar, @def = 9, kwarg:, @ikwarg:, @idkwarg: 10)
959
      @stored_local = local
960
      @stored_kwarg = kwarg
961
    end
962

  
963
    def ivar_values
964
      [@ivar, @def, @ikwarg, @idkwarg]
965
    end
966

  
967
    def local_values
968
      [@local, @kwarg]
969
    end
970

  
971
    def stored_local_values
972
      [@stored_local, @stored_kwarg]
973
    end
974
  end
975

  
976
  def ivar_argument_example
977
    IvarArgumentExample.new(1, 2, kwarg: 4, ikwarg: 5)
978
  end
979

  
980
  def test_ivar_arguments_basic
981
    assert_equal [2, 9, 5, 10], ivar_argument_example.ivar_values
982
  end
983

  
984
  def test_ivar_arguments_supplying_optionals
985
    values = IvarArgumentExample.new(1, 2, 3, kwarg: 4, ikwarg: 5, idkwarg: 6).ivar_values
986
    assert_equal [2, 3, 5, 6], values
987
  end
988

  
989
  def test_local_arguments_are_not_set_as_ivars
990
    assert_equal [nil, nil], ivar_argument_example.local_values
991
  end
992

  
993
  def test_local_arguments_were_stored
994
    assert_equal [1, 4], ivar_argument_example.stored_local_values
995
  end
996

  
997
  def test_ivar_arguments_are_not_accepted_in_a_block
998
    assert_raise(SyntaxError) do
999
      eval "[].each { |@x| true }"
1000
    end
1001
  end
1002

  
1003
  def test_ivar_arguments_are_not_accepted_in_a_lambda
1004
    assert_raise(SyntaxError) do
1005
      eval "-> (@x) { true }"
1006
    end
1007
  end
1008

  
1009
  def accessing_ivar_as_local(@ivar)
1010
    ivar # this should raise
1011
  end
1012

  
1013
  def test_ivar_arguments_are_not_exposed_as_local_variables
1014
    assert_raise(NameError) { accessing_ivar_as_local(10) }
1015
  end
956 1016
end
vm_args.c
386 386
args_setup_kw_parameters(VALUE* const passed_values, const int passed_keyword_len, const VALUE *const passed_keywords,
387 387
			 const rb_iseq_t * const iseq, VALUE * const locals)
388 388
{
389
    const ID *acceptable_keywords = iseq->body->param.keyword->table;
389
    const ID *acceptable_keywords;
390
    ID modified_acceptable_keywords[iseq->body->param.keyword->num];
390 391
    const int req_key_num = iseq->body->param.keyword->required_num;
391 392
    const int key_num = iseq->body->param.keyword->num;
392 393
    const VALUE * const default_values = iseq->body->param.keyword->default_values;
......
395 396
    int unspecified_bits = 0;
396 397
    VALUE unspecified_bits_value = Qnil;
397 398

  
399
    if (iseq->body->param.flags.has_ivars) {
400
	const ID *pid = iseq->body->param.keyword->table;
401
	for (i=0; i<iseq->body->param.keyword->num; i++, pid++) {
402
	    modified_acceptable_keywords[i] = rb_is_instance_id(*pid) ? rb_intern(rb_id2name(*pid) + 1) : *pid;
403
	}
404
	acceptable_keywords = (const ID *)&modified_acceptable_keywords;
405
    }
406
    else {
407
	acceptable_keywords = iseq->body->param.keyword->table;
408
    }
409

  
398 410
    for (i=0; i<req_key_num; i++) {
399 411
	ID key = acceptable_keywords[i];
400 412
	if (args_setup_kw_parameters_lookup(key, &locals[i], passed_keywords, passed_values, passed_keyword_len)) {
......
487 499
    *locals = blockval;
488 500
}
489 501

  
502
static inline void
503
args_setup_ivar_parameters(const rb_iseq_t * const iseq, struct rb_calling_info *calling, VALUE *locals)
504
{
505
    unsigned int i;
506
    ID id;
507

  
508
    for (i=0; i<iseq->body->local_table_size; i++) {
509
	id = iseq->body->local_table[i];
510

  
511
	if (rb_is_instance_id(id)) {
512
	    rb_ivar_set(calling->recv, id, locals[i]);
513
	    locals[i] = Qnil;
514
	}
515
    }
516
}
517

  
490 518
struct fill_values_arg {
491 519
    VALUE *keys;
492 520
    VALUE *vals;
......
678 706
	args_setup_block_parameter(th, calling, locals + iseq->body->param.block_start);
679 707
    }
680 708

  
709
    if (iseq->body->param.flags.has_ivars) {
710
	args_setup_ivar_parameters(iseq, calling, locals);
711
    }
712

  
681 713
#if 0
682 714
    {
683 715
	int i;
vm_core.h
316 316
	    unsigned int has_kw     : 1;
317 317
	    unsigned int has_kwrest : 1;
318 318
	    unsigned int has_block  : 1;
319
	    unsigned int has_ivars  : 1;
319 320

  
320 321
	    unsigned int ambiguous_param0 : 1; /* {|a|} */
321 322
	} flags;
vm_insnhelper.c
1344 1344
	   iseq->body->param.flags.has_post == FALSE &&
1345 1345
	   iseq->body->param.flags.has_kw == FALSE &&
1346 1346
	   iseq->body->param.flags.has_kwrest == FALSE &&
1347
	   iseq->body->param.flags.has_block == FALSE;
1347
	   iseq->body->param.flags.has_block == FALSE &&
1348
	   iseq->body->param.flags.has_ivars == FALSE;
1348 1349
}
1349 1350

  
1350 1351
static inline int