aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authornobu <nobu@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>2015-09-27 06:44:02 +0000
committernobu <nobu@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>2015-09-27 06:44:02 +0000
commit859337b17b5e1f9ee9ebeb0ac9e3ed7d73691ca2 (patch)
tree2f87021f6f7a577c1ab65ed51191bd448e39570e
parent71730b42434e3c2dce7e7f6488bd54fae52cae51 (diff)
downloadruby-859337b17b5e1f9ee9ebeb0ac9e3ed7d73691ca2.tar.gz
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] git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@51953 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
-rw-r--r--ChangeLog14
-rw-r--r--NEWS3
-rw-r--r--compile.c7
-rw-r--r--iseq.c56
-rw-r--r--iseq.h1
-rw-r--r--node.c4
-rw-r--r--node.h2
-rw-r--r--parse.y38
-rw-r--r--test/ruby/test_literal.rb13
9 files changed, 112 insertions, 26 deletions
diff --git a/ChangeLog b/ChangeLog
index 91f0672943..f9be417990 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,17 @@
+Sun Sep 27 15:43:59 2015 Nobuyoshi Nakada <nobu@ruby-lang.org>
+
+ * 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@ruby-lang.org>
* lib/ostruct.rb (delete_field): do not raise NameError for
diff --git a/NEWS b/NEWS
index 73f4a65a22..032a095f56 100644
--- a/NEWS
+++ b/NEWS
@@ -13,6 +13,9 @@ with all sufficient information, see the ChangeLog file.
=== Language changes
+* frozen-string-literal pragma:
+ * new pragma, frozen-string-literal has been experimentally introduced.
+
=== Core classes updates (outstanding ones only)
* ARGF
diff --git a/compile.c b/compile.c
index 9f9fcb66d8..9928aaa44c 100644
--- a/compile.c
+++ b/compile.c
@@ -5588,8 +5588,15 @@ iseq_compile_each(rb_iseq_t *iseq, LINK_ANCHOR *ret, NODE * node, int poped)
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:{
diff --git a/iseq.c b/iseq.c
index cbf78ff377..0fd95f04cc 100644
--- a/iseq.c
+++ b/iseq.c
@@ -345,6 +345,39 @@ static rb_compile_option_t COMPILE_OPTION_DEFAULT = {
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 *option, VALUE opt)
}
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");
diff --git a/iseq.h b/iseq.h
index 9d544fdbe7..13d2f2630c 100644
--- a/iseq.h
+++ b/iseq.h
@@ -173,6 +173,7 @@ enum defined_type {
};
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
diff --git a/node.c b/node.c
index 79f65ec684..1153431206 100644
--- a/node.c
+++ b/node.c
@@ -804,8 +804,10 @@ dump_node(VALUE buf, VALUE indent, int comment, NODE *node)
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:
diff --git a/node.h b/node.h
index c639176ef7..30e65ae52b 100644
--- a/node.h
+++ b/node.h
@@ -452,7 +452,7 @@ typedef struct RNode {
#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
diff --git a/parse.y b/parse.y
index c72aacf9b4..798645abed 100644
--- a/parse.y
+++ b/parse.y
@@ -288,6 +288,7 @@ struct parser_params {
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 {
VALUE coverage;
token_info *token_info;
+
+ VALUE compile_option;
#else
/* Ripper only */
unsigned int toplevel_p: 1;
@@ -5506,8 +5509,8 @@ yycompile0(VALUE arg)
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_params *parser, const char *name, const char
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_comments[] = {
{"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 *parser)
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 *parser)
}
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 *parser)
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 *parser)
#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)) {
diff --git a/test/ruby/test_literal.rb b/test/ruby/test_literal.rb
index 302f487bb6..0d65ebac2c 100644
--- a/test/ruby/test_literal.rb
+++ b/test/ruby/test_literal.rb
@@ -121,6 +121,19 @@ class TestRubyLiteral < Test::Unit::TestCase
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')