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

ruby-changes:39872

From: nobu <ko1@a...>
Date: Sun, 27 Sep 2015 15:44:21 +0900 (JST)
Subject: [ruby-changes:39872] nobu:r51953 (trunk): fronzen-string-literal pragma

nobu	2015-09-27 15:44:02 +0900 (Sun, 27 Sep 2015)

  New Revision: 51953

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

  Log:
    fronzen-string-literal pragma
    
    * compile.c (iseq_compile_each): override compile option by option
      given by pragma.
    * iseq.c (rb_iseq_make_compile_option): extract a function to
      overwrite rb_compile_option_t.
    * parse.y (parser_set_compile_option_flag): introduce pragma to
      override compile options.
    * parse.y (magic_comments): new pragma "fronzen-string-literal".
      [Feature #8976]

  Modified files:
    trunk/ChangeLog
    trunk/NEWS
    trunk/compile.c
    trunk/iseq.c
    trunk/iseq.h
    trunk/node.c
    trunk/node.h
    trunk/parse.y
    trunk/test/ruby/test_literal.rb
Index: ChangeLog
===================================================================
--- ChangeLog	(revision 51952)
+++ ChangeLog	(revision 51953)
@@ -1,3 +1,17 @@ https://github.com/ruby/ruby/blob/trunk/ChangeLog#L1
+Sun Sep 27 15:43:59 2015  Nobuyoshi Nakada  <nobu@r...>
+
+	* compile.c (iseq_compile_each): override compile option by option
+	  given by pragma.
+
+	* iseq.c (rb_iseq_make_compile_option): extract a function to
+	  overwrite rb_compile_option_t.
+
+	* parse.y (parser_set_compile_option_flag): introduce pragma to
+	  override compile options.
+
+	* parse.y (magic_comments): new pragma "fronzen-string-literal".
+	  [Feature #8976]
+
 Sun Sep 27 08:16:35 2015  Nobuyoshi Nakada  <nobu@r...>
 
 	* lib/ostruct.rb (delete_field): do not raise NameError for
Index: iseq.c
===================================================================
--- iseq.c	(revision 51952)
+++ iseq.c	(revision 51953)
@@ -345,6 +345,39 @@ static rb_compile_option_t COMPILE_OPTIO https://github.com/ruby/ruby/blob/trunk/iseq.c#L345
 static const rb_compile_option_t COMPILE_OPTION_FALSE = {0};
 
 static void
+set_compile_option_from_hash(rb_compile_option_t *option, VALUE opt)
+{
+#define SET_COMPILE_OPTION(o, h, mem) \
+  { VALUE flag = rb_hash_aref((h), ID2SYM(rb_intern(#mem))); \
+      if (flag == Qtrue)  { (o)->mem = 1; } \
+      else if (flag == Qfalse)  { (o)->mem = 0; } \
+  }
+#define SET_COMPILE_OPTION_NUM(o, h, mem) \
+  { VALUE num = rb_hash_aref(opt, ID2SYM(rb_intern(#mem))); \
+      if (!NIL_P(num)) (o)->mem = NUM2INT(num); \
+  }
+    SET_COMPILE_OPTION(option, opt, inline_const_cache);
+    SET_COMPILE_OPTION(option, opt, peephole_optimization);
+    SET_COMPILE_OPTION(option, opt, tailcall_optimization);
+    SET_COMPILE_OPTION(option, opt, specialized_instruction);
+    SET_COMPILE_OPTION(option, opt, operands_unification);
+    SET_COMPILE_OPTION(option, opt, instructions_unification);
+    SET_COMPILE_OPTION(option, opt, stack_caching);
+    SET_COMPILE_OPTION(option, opt, trace_instruction);
+    SET_COMPILE_OPTION(option, opt, frozen_string_literal);
+    SET_COMPILE_OPTION_NUM(option, opt, debug_level);
+#undef SET_COMPILE_OPTION
+#undef SET_COMPILE_OPTION_NUM
+}
+
+void
+rb_iseq_make_compile_option(rb_compile_option_t *option, VALUE opt)
+{
+    Check_Type(opt, T_HASH);
+    set_compile_option_from_hash(option, opt);
+}
+
+static void
 make_compile_option(rb_compile_option_t *option, VALUE opt)
 {
     if (opt == Qnil) {
@@ -360,28 +393,7 @@ make_compile_option(rb_compile_option_t https://github.com/ruby/ruby/blob/trunk/iseq.c#L393
     }
     else if (CLASS_OF(opt) == rb_cHash) {
 	*option = COMPILE_OPTION_DEFAULT;
-
-#define SET_COMPILE_OPTION(o, h, mem) \
-  { VALUE flag = rb_hash_aref((h), ID2SYM(rb_intern(#mem))); \
-      if (flag == Qtrue)  { (o)->mem = 1; } \
-      else if (flag == Qfalse)  { (o)->mem = 0; } \
-  }
-#define SET_COMPILE_OPTION_NUM(o, h, mem) \
-  { VALUE num = rb_hash_aref(opt, ID2SYM(rb_intern(#mem))); \
-      if (!NIL_P(num)) (o)->mem = NUM2INT(num); \
-  }
-	SET_COMPILE_OPTION(option, opt, inline_const_cache);
-	SET_COMPILE_OPTION(option, opt, peephole_optimization);
-	SET_COMPILE_OPTION(option, opt, tailcall_optimization);
-	SET_COMPILE_OPTION(option, opt, specialized_instruction);
-	SET_COMPILE_OPTION(option, opt, operands_unification);
-	SET_COMPILE_OPTION(option, opt, instructions_unification);
-	SET_COMPILE_OPTION(option, opt, stack_caching);
-	SET_COMPILE_OPTION(option, opt, trace_instruction);
-	SET_COMPILE_OPTION(option, opt, frozen_string_literal);
-	SET_COMPILE_OPTION_NUM(option, opt, debug_level);
-#undef SET_COMPILE_OPTION
-#undef SET_COMPILE_OPTION_NUM
+	set_compile_option_from_hash(option, opt);
     }
     else {
 	rb_raise(rb_eTypeError, "Compile option must be Hash/true/false/nil");
Index: iseq.h
===================================================================
--- iseq.h	(revision 51952)
+++ iseq.h	(revision 51953)
@@ -173,6 +173,7 @@ enum defined_type { https://github.com/ruby/ruby/blob/trunk/iseq.h#L173
 };
 
 VALUE rb_iseq_defined_string(enum defined_type type);
+void rb_iseq_make_compile_option(struct rb_compile_option_struct *option, VALUE opt);
 
 RUBY_SYMBOL_EXPORT_END
 
Index: compile.c
===================================================================
--- compile.c	(revision 51952)
+++ compile.c	(revision 51953)
@@ -5588,8 +5588,15 @@ iseq_compile_each(rb_iseq_t *iseq, LINK_ https://github.com/ruby/ruby/blob/trunk/compile.c#L5588
 	break;
       }
       case NODE_PRELUDE:{
+	const rb_compile_option_t *orig_opt = iseq->compile_data->option;
+	if (node->nd_orig) {
+	    rb_compile_option_t new_opt = *orig_opt;
+	    rb_iseq_make_compile_option(&new_opt, node->nd_orig);
+	    iseq->compile_data->option = &new_opt;
+	}
 	COMPILE_POPED(ret, "prelude", node->nd_head);
 	COMPILE_(ret, "body", node->nd_body, poped);
+	iseq->compile_data->option = orig_opt;
 	break;
       }
       case NODE_LAMBDA:{
Index: parse.y
===================================================================
--- parse.y	(revision 51952)
+++ parse.y	(revision 51953)
@@ -288,6 +288,7 @@ struct parser_params { https://github.com/ruby/ruby/blob/trunk/parse.y#L288
     unsigned int past_scope_enabled: 1;
 # endif
     unsigned int has_err: 1;
+    unsigned int token_seen: 1;
 
     NODE *eval_tree_begin;
     NODE *eval_tree;
@@ -295,6 +296,8 @@ struct parser_params { https://github.com/ruby/ruby/blob/trunk/parse.y#L296
     VALUE coverage;
 
     token_info *token_info;
+
+    VALUE compile_option;
 #else
     /* Ripper only */
     unsigned int toplevel_p: 1;
@@ -5506,8 +5509,8 @@ yycompile0(VALUE arg) https://github.com/ruby/ruby/blob/trunk/parse.y#L5509
     if (!tree) {
 	tree = NEW_NIL();
     }
-    else if (ruby_eval_tree_begin) {
-	tree->nd_body = NEW_PRELUDE(ruby_eval_tree_begin, tree->nd_body);
+    else {
+	tree->nd_body = NEW_PRELUDE(ruby_eval_tree_begin, tree->nd_body, parser->compile_option);
     }
     return (VALUE)tree;
 }
@@ -6887,6 +6890,25 @@ parser_set_token_info(struct parser_para https://github.com/ruby/ruby/blob/trunk/parse.y#L6890
     if (b >= 0) parser->token_info_enabled = b;
 }
 
+static void
+parser_set_compile_option_flag(struct parser_params *parser, const char *name, const char *val)
+{
+    int b;
+
+    if (parser->token_seen) {
+	rb_warningS("`%s' is ignored after any tokens", name);
+	return;
+    }
+
+    b = parser_get_bool(parser, name, val);
+    if (b < 0) return;
+
+    if (!parser->compile_option)
+	parser->compile_option = rb_ident_hash_new();
+    rb_hash_aset(parser->compile_option, ID2SYM(rb_intern(name)),
+		 (b ? Qtrue : Qfalse));
+}
+
 # if WARN_PAST_SCOPE
 static void
 parser_set_past_scope(struct parser_params *parser, const char *name, const char *val)
@@ -6907,6 +6929,7 @@ static const struct magic_comment magic_ https://github.com/ruby/ruby/blob/trunk/parse.y#L6929
     {"coding", magic_comment_encoding, parser_encode_length},
     {"encoding", magic_comment_encoding, parser_encode_length},
 #ifndef RIPPER
+    {"frozen_string_literal", parser_set_compile_option_flag},
     {"warn_indent", parser_set_token_info},
 # if WARN_PAST_SCOPE
     {"warn_past_scope", parser_set_past_scope},
@@ -7861,6 +7884,8 @@ parser_yylex(struct parser_params *parse https://github.com/ruby/ruby/blob/trunk/parse.y#L7884
     enum lex_state_e last_state;
 #ifdef RIPPER
     int fallthru = FALSE;
+#else
+    int token_seen = parser->token_seen;
 #endif
 
     if (lex_strterm) {
@@ -7891,6 +7916,9 @@ parser_yylex(struct parser_params *parse https://github.com/ruby/ruby/blob/trunk/parse.y#L7916
     }
     cmd_state = command_start;
     command_start = FALSE;
+#ifndef RIPPER
+    parser->token_seen = TRUE;
+#endif
   retry:
     last_state = lex_state;
     switch (c = nextc()) {
@@ -7921,6 +7949,9 @@ parser_yylex(struct parser_params *parse https://github.com/ruby/ruby/blob/trunk/parse.y#L7949
 	goto retry;
 
       case '#':		/* it's a comment */
+#ifndef RIPPER
+	parser->token_seen = token_seen;
+#endif
 	/* no magic_comment in shebang line */
 	if (!parser_magic_comment(parser, lex_p, lex_pend - lex_p)) {
 	    if (comment_at_top(parser)) {
@@ -7934,6 +7965,9 @@ parser_yylex(struct parser_params *parse https://github.com/ruby/ruby/blob/trunk/parse.y#L7965
 #endif
 	/* fall through */
       case '\n':
+#ifndef RIPPER
+	parser->token_seen = token_seen;
+#endif
 	c = (IS_lex_state(EXPR_BEG|EXPR_CLASS|EXPR_FNAME|EXPR_DOT) &&
 	     !IS_lex_state(EXPR_LABELED));
 	if (c || IS_lex_state_all(EXPR_ARG|EXPR_LABELED)) {
Index: NEWS
===================================================================
--- NEWS	(revision 51952)
+++ NEWS	(revision 51953)
@@ -13,6 +13,9 @@ with all sufficient information, see the https://github.com/ruby/ruby/blob/trunk/NEWS#L13
 
 === Language changes
 
+* frozen-string-literal pragma:
+  * new pragma, frozen-string-literal has been experimentally introduced.
+
 === Core classes updates (outstanding ones only)
 
 * ARGF
Index: test/ruby/test_literal.rb
===================================================================
--- test/ruby/test_literal.rb	(revision 51952)
+++ test/ruby/test_literal.rb	(revision 51953)
@@ -121,6 +121,19 @@ class TestRubyLiteral < Test::Unit::Test https://github.com/ruby/ruby/blob/trunk/test/ruby/test_literal.rb#L121
     assert_equal "foo\n", `echo #{s}`
   end
 
+  def test_frozen_string
+    all_assertions do |a|
+      a.for("false") do
+        str = eval("# -*- frozen-string-literal: false -*-\n""'foo'")
+        assert_not_predicate(str, :frozen?)
+      end
+      a.for("true") do
+        str = eval("# -*- frozen-string-literal: true -*-\n""'foo'")
+        assert_predicate(str, :frozen?)
+      end
+    end
+  end
+
   def test_regexp
     assert_instance_of Regexp, //
     assert_match(//, 'a')
Index: node.c
===================================================================
--- node.c	(revision 51952)
+++ node.c	(revision 51953)
@@ -804,8 +804,10 @@ dump_node(VALUE buf, VALUE indent, int c https://github.com/ruby/ruby/blob/trunk/node.c#L804
 	ANN("format: BEGIN { [nd_head] }; [nd_body]");
 	ANN("example: bar; BEGIN { foo }");
 	F_NODE(nd_head, "prelude");
-	LAST_NODE;
 	F_NODE(nd_body, "body");
+	LAST_NODE;
+#define nd_compile_option u3.value
+	F_LIT(nd_compile_option, "compile_option");
 	break;
 
       case NODE_LAMBDA:
Index: node.h
===================================================================
--- node.h	(revision 51952)
+++ node.h	(revision 51953)
@@ -452,7 +452,7 @@ typedef struct RNode { https://github.com/ruby/ruby/blob/trunk/node.h#L452
 #define NEW_POSTEXE(b) NEW_NODE(NODE_POSTEXE,0,b,0)
 #define NEW_BMETHOD(b) NEW_NODE(NODE_BMETHOD,0,0,b)
 #define NEW_ATTRASGN(r,m,a) NEW_NODE(NODE_ATTRASGN,r,m,a)
-#define NEW_PRELUDE(p,b) NEW_NODE(NODE_PRELUDE,p,b,0)
+#define NEW_PRELUDE(p,b,o) NEW_NODE(NODE_PRELUDE,p,b,o)
 
 RUBY_SYMBOL_EXPORT_BEGIN
 

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

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