[前][次][番号順一覧][スレッド一覧]

ruby-changes:40153

From: nobu <ko1@a...>
Date: Fri, 23 Oct 2015 10:49:53 +0900 (JST)
Subject: [ruby-changes:40153] nobu:r52234 (trunk): safe navigation attrset

nobu	2015-10-23 10:49:38 +0900 (Fri, 23 Oct 2015)

  New Revision: 52234

  http://svn.ruby-lang.org/cgi-bin/viewvc.cgi?view=revision&revision=52234

  Log:
    safe navigation attrset
    
    * compile.c (iseq_compile_each): support safe navigation of simple
      attribute assignment.  [Feature #11537]
    * parse.y (mlhs_node, lhs, attrset_gen): ditto.  keep mid
      non-attrset as the sign of safe navigation.

  Modified files:
    trunk/ChangeLog
    trunk/compile.c
    trunk/parse.y
    trunk/test/ruby/test_call.rb
    trunk/test/ruby/test_iseq.rb
Index: ChangeLog
===================================================================
--- ChangeLog	(revision 52233)
+++ ChangeLog	(revision 52234)
@@ -1,3 +1,11 @@ https://github.com/ruby/ruby/blob/trunk/ChangeLog#L1
+Fri Oct 23 10:49:36 2015  Nobuyoshi Nakada  <nobu@r...>
+
+	* compile.c (iseq_compile_each): support safe navigation of simple
+	  attribute assignment.  [Feature #11537]
+
+	* parse.y (mlhs_node, lhs, attrset_gen): ditto.  keep mid
+	  non-attrset as the sign of safe navigation.
+
 Fri Oct 23 07:17:11 2015  Eric Wong  <e@8...>
 
 	* test/io/wait/test_io_wait.rb (test_wait_eof): test return value
Index: compile.c
===================================================================
--- compile.c	(revision 52233)
+++ compile.c	(revision 52234)
@@ -5588,12 +5588,14 @@ iseq_compile_each(rb_iseq_t *iseq, LINK_ https://github.com/ruby/ruby/blob/trunk/compile.c#L5588
 	DECL_ANCHOR(recv);
 	DECL_ANCHOR(args);
 	unsigned int flag = 0;
+	ID mid = node->nd_mid;
+	LABEL *lskip = 0;
 	VALUE argc;
 
 	/* optimization shortcut
 	 *   obj["literal"] = value -> opt_aset_with(obj, "literal", value)
 	 */
-	if (node->nd_mid == idASET && !private_recv_p(node) && node->nd_args &&
+	if (mid == idASET && !private_recv_p(node) && node->nd_args &&
 	    nd_type(node->nd_args) == NODE_ARRAY && node->nd_args->nd_alen == 2 &&
 	    nd_type(node->nd_args->nd_head) == NODE_STR &&
 	    iseq->compile_data->current_block == NULL &&
@@ -5622,8 +5624,15 @@ iseq_compile_each(rb_iseq_t *iseq, LINK_ https://github.com/ruby/ruby/blob/trunk/compile.c#L5624
 	flag |= COMPILE_RECV(recv, "recv", node);
 
 	debugp_param("argc", argc);
-	debugp_param("nd_mid", ID2SYM(node->nd_mid));
+	debugp_param("nd_mid", ID2SYM(mid));
 
+	if (!rb_is_attrset_id(mid)) {
+	    /* safe nav attr */
+	    mid = rb_id_attrset(mid);
+	    ADD_INSN(recv, line, dup);
+	    lskip = NEW_LABEL(line);
+	    ADD_INSNL(recv, line, branchnil, lskip);
+	}
 	if (!poped) {
 	    ADD_INSN(ret, line, putnil);
 	    ADD_SEQ(ret, recv);
@@ -5653,7 +5662,8 @@ iseq_compile_each(rb_iseq_t *iseq, LINK_ https://github.com/ruby/ruby/blob/trunk/compile.c#L5662
 	    ADD_SEQ(ret, recv);
 	    ADD_SEQ(ret, args);
 	}
-	ADD_SEND_WITH_FLAG(ret, line, node->nd_mid, argc, INT2FIX(flag));
+	ADD_SEND_WITH_FLAG(ret, line, mid, argc, INT2FIX(flag));
+	if (lskip) ADD_LABEL(ret, lskip);
 	ADD_INSN(ret, line, pop);
 
 	break;
Index: parse.y
===================================================================
--- parse.y	(revision 52233)
+++ parse.y	(revision 52234)
@@ -451,8 +451,8 @@ static NODE *assignable_gen(struct parse https://github.com/ruby/ruby/blob/trunk/parse.y#L451
 
 static NODE *aryset_gen(struct parser_params*,NODE*,NODE*);
 #define aryset(node1,node2) aryset_gen(parser, (node1), (node2))
-static NODE *attrset_gen(struct parser_params*,NODE*,ID);
-#define attrset(node,id) attrset_gen(parser, (node), (id))
+static NODE *attrset_gen(struct parser_params*,NODE*,ID,ID);
+#define attrset(node,q,id) attrset_gen(parser, (node), (q), (id))
 
 static void rb_backref_error_gen(struct parser_params*,NODE*);
 #define rb_backref_error(n) rb_backref_error_gen(parser,(n))
@@ -1720,7 +1720,7 @@ mlhs_node	: user_variable https://github.com/ruby/ruby/blob/trunk/parse.y#L1720
 		| primary_value call_op tIDENTIFIER
 		    {
 		    /*%%%*/
-			$$ = attrset($1, $3);
+			$$ = attrset($1, $2, $3);
 		    /*%
 			$$ = dispatch3(field, $1, ripper_id2sym($2), $3);
 		    %*/
@@ -1728,7 +1728,7 @@ mlhs_node	: user_variable https://github.com/ruby/ruby/blob/trunk/parse.y#L1728
 		| primary_value tCOLON2 tIDENTIFIER
 		    {
 		    /*%%%*/
-			$$ = attrset($1, $3);
+			$$ = attrset($1, idCOLON2, $3);
 		    /*%
 			$$ = dispatch2(const_path_field, $1, $3);
 		    %*/
@@ -1736,7 +1736,7 @@ mlhs_node	: user_variable https://github.com/ruby/ruby/blob/trunk/parse.y#L1736
 		| primary_value call_op tCONSTANT
 		    {
 		    /*%%%*/
-			$$ = attrset($1, $3);
+			$$ = attrset($1, $2, $3);
 		    /*%
 			$$ = dispatch3(field, $1, ripper_id2sym($2), $3);
 		    %*/
@@ -1811,7 +1811,7 @@ lhs		: user_variable https://github.com/ruby/ruby/blob/trunk/parse.y#L1811
 		| primary_value call_op tIDENTIFIER
 		    {
 		    /*%%%*/
-			$$ = attrset($1, $3);
+			$$ = attrset($1, $2, $3);
 		    /*%
 			$$ = dispatch3(field, $1, ripper_id2sym($2), $3);
 		    %*/
@@ -1819,7 +1819,7 @@ lhs		: user_variable https://github.com/ruby/ruby/blob/trunk/parse.y#L1819
 		| primary_value tCOLON2 tIDENTIFIER
 		    {
 		    /*%%%*/
-			$$ = attrset($1, $3);
+			$$ = attrset($1, idCOLON2, $3);
 		    /*%
 			$$ = dispatch3(field, $1, ID2SYM(idCOLON2), $3);
 		    %*/
@@ -1827,7 +1827,7 @@ lhs		: user_variable https://github.com/ruby/ruby/blob/trunk/parse.y#L1827
 		| primary_value call_op tCONSTANT
 		    {
 		    /*%%%*/
-			$$ = attrset($1, $3);
+			$$ = attrset($1, $2, $3);
 		    /*%
 			$$ = dispatch3(field, $1, ripper_id2sym($2), $3);
 		    %*/
@@ -9187,9 +9187,10 @@ block_dup_check_gen(struct parser_params https://github.com/ruby/ruby/blob/trunk/parse.y#L9187
 }
 
 static NODE *
-attrset_gen(struct parser_params *parser, NODE *recv, ID id)
+attrset_gen(struct parser_params *parser, NODE *recv, ID atype, ID id)
 {
-    return NEW_ATTRASGN(recv, rb_id_attrset(id), 0);
+    if (atype != tDOTQ) id = rb_id_attrset(id);
+    return NEW_ATTRASGN(recv, id, 0);
 }
 
 static void
Index: test/ruby/test_call.rb
===================================================================
--- test/ruby/test_call.rb	(revision 52233)
+++ test/ruby/test_call.rb	(revision 52234)
@@ -42,5 +42,10 @@ class TestCall < Test::Unit::TestCase https://github.com/ruby/ruby/blob/trunk/test/ruby/test_call.rb#L42
     assert_equal(6, o.x)
     o.?x *= 7
     assert_equal(42, o.x)
+
+    o = nil
+    assert_nil(o.?x)
+    assert_nothing_raised(NoMethodError) {o.?x = 6}
+    assert_nothing_raised(NoMethodError) {o.?x *= 7}
   end
 end
Index: test/ruby/test_iseq.rb
===================================================================
--- test/ruby/test_iseq.rb	(revision 52233)
+++ test/ruby/test_iseq.rb	(revision 52234)
@@ -149,7 +149,7 @@ class TestISeq < Test::Unit::TestCase https://github.com/ruby/ruby/blob/trunk/test/ruby/test_iseq.rb#L149
     src = "a['foo'] = a['bar']; 'a'.freeze"
     _,_,_,_,_,_,_,_,_,_,_,_,_,body= RubyVM::InstructionSequence.compile(src, __FILE__, __FILE__, __LINE__, false).to_a
     body.each{|insn|
-      next if Integer === insn
+      next unless Array === insn
       op = insn.first
       assert(!op.to_s.match(/^opt_/), "#{op}")
     }

--
ML: ruby-changes@q...
Info: http://www.atdot.net/~ko1/quickml/

[前][次][番号順一覧][スレッド一覧]