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
This commit is contained in:
parent
71730b4243
commit
859337b17b
14
ChangeLog
14
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>
|
Sun Sep 27 08:16:35 2015 Nobuyoshi Nakada <nobu@ruby-lang.org>
|
||||||
|
|
||||||
* lib/ostruct.rb (delete_field): do not raise NameError for
|
* lib/ostruct.rb (delete_field): do not raise NameError for
|
||||||
|
3
NEWS
3
NEWS
@ -13,6 +13,9 @@ with all sufficient information, see the ChangeLog file.
|
|||||||
|
|
||||||
=== Language changes
|
=== Language changes
|
||||||
|
|
||||||
|
* frozen-string-literal pragma:
|
||||||
|
* new pragma, frozen-string-literal has been experimentally introduced.
|
||||||
|
|
||||||
=== Core classes updates (outstanding ones only)
|
=== Core classes updates (outstanding ones only)
|
||||||
|
|
||||||
* ARGF
|
* ARGF
|
||||||
|
@ -5588,8 +5588,15 @@ iseq_compile_each(rb_iseq_t *iseq, LINK_ANCHOR *ret, NODE * node, int poped)
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case NODE_PRELUDE:{
|
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_POPED(ret, "prelude", node->nd_head);
|
||||||
COMPILE_(ret, "body", node->nd_body, poped);
|
COMPILE_(ret, "body", node->nd_body, poped);
|
||||||
|
iseq->compile_data->option = orig_opt;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case NODE_LAMBDA:{
|
case NODE_LAMBDA:{
|
||||||
|
56
iseq.c
56
iseq.c
@ -344,6 +344,39 @@ static rb_compile_option_t COMPILE_OPTION_DEFAULT = {
|
|||||||
|
|
||||||
static const rb_compile_option_t COMPILE_OPTION_FALSE = {0};
|
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
|
static void
|
||||||
make_compile_option(rb_compile_option_t *option, VALUE opt)
|
make_compile_option(rb_compile_option_t *option, VALUE opt)
|
||||||
{
|
{
|
||||||
@ -360,28 +393,7 @@ make_compile_option(rb_compile_option_t *option, VALUE opt)
|
|||||||
}
|
}
|
||||||
else if (CLASS_OF(opt) == rb_cHash) {
|
else if (CLASS_OF(opt) == rb_cHash) {
|
||||||
*option = COMPILE_OPTION_DEFAULT;
|
*option = COMPILE_OPTION_DEFAULT;
|
||||||
|
set_compile_option_from_hash(option, 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
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
rb_raise(rb_eTypeError, "Compile option must be Hash/true/false/nil");
|
rb_raise(rb_eTypeError, "Compile option must be Hash/true/false/nil");
|
||||||
|
1
iseq.h
1
iseq.h
@ -173,6 +173,7 @@ enum defined_type {
|
|||||||
};
|
};
|
||||||
|
|
||||||
VALUE rb_iseq_defined_string(enum defined_type 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
|
RUBY_SYMBOL_EXPORT_END
|
||||||
|
|
||||||
|
4
node.c
4
node.c
@ -804,8 +804,10 @@ dump_node(VALUE buf, VALUE indent, int comment, NODE *node)
|
|||||||
ANN("format: BEGIN { [nd_head] }; [nd_body]");
|
ANN("format: BEGIN { [nd_head] }; [nd_body]");
|
||||||
ANN("example: bar; BEGIN { foo }");
|
ANN("example: bar; BEGIN { foo }");
|
||||||
F_NODE(nd_head, "prelude");
|
F_NODE(nd_head, "prelude");
|
||||||
LAST_NODE;
|
|
||||||
F_NODE(nd_body, "body");
|
F_NODE(nd_body, "body");
|
||||||
|
LAST_NODE;
|
||||||
|
#define nd_compile_option u3.value
|
||||||
|
F_LIT(nd_compile_option, "compile_option");
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case NODE_LAMBDA:
|
case NODE_LAMBDA:
|
||||||
|
2
node.h
2
node.h
@ -452,7 +452,7 @@ typedef struct RNode {
|
|||||||
#define NEW_POSTEXE(b) NEW_NODE(NODE_POSTEXE,0,b,0)
|
#define NEW_POSTEXE(b) NEW_NODE(NODE_POSTEXE,0,b,0)
|
||||||
#define NEW_BMETHOD(b) NEW_NODE(NODE_BMETHOD,0,0,b)
|
#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_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
|
RUBY_SYMBOL_EXPORT_BEGIN
|
||||||
|
|
||||||
|
38
parse.y
38
parse.y
@ -288,6 +288,7 @@ struct parser_params {
|
|||||||
unsigned int past_scope_enabled: 1;
|
unsigned int past_scope_enabled: 1;
|
||||||
# endif
|
# endif
|
||||||
unsigned int has_err: 1;
|
unsigned int has_err: 1;
|
||||||
|
unsigned int token_seen: 1;
|
||||||
|
|
||||||
NODE *eval_tree_begin;
|
NODE *eval_tree_begin;
|
||||||
NODE *eval_tree;
|
NODE *eval_tree;
|
||||||
@ -295,6 +296,8 @@ struct parser_params {
|
|||||||
VALUE coverage;
|
VALUE coverage;
|
||||||
|
|
||||||
token_info *token_info;
|
token_info *token_info;
|
||||||
|
|
||||||
|
VALUE compile_option;
|
||||||
#else
|
#else
|
||||||
/* Ripper only */
|
/* Ripper only */
|
||||||
unsigned int toplevel_p: 1;
|
unsigned int toplevel_p: 1;
|
||||||
@ -5506,8 +5509,8 @@ yycompile0(VALUE arg)
|
|||||||
if (!tree) {
|
if (!tree) {
|
||||||
tree = NEW_NIL();
|
tree = NEW_NIL();
|
||||||
}
|
}
|
||||||
else if (ruby_eval_tree_begin) {
|
else {
|
||||||
tree->nd_body = NEW_PRELUDE(ruby_eval_tree_begin, tree->nd_body);
|
tree->nd_body = NEW_PRELUDE(ruby_eval_tree_begin, tree->nd_body, parser->compile_option);
|
||||||
}
|
}
|
||||||
return (VALUE)tree;
|
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;
|
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
|
# if WARN_PAST_SCOPE
|
||||||
static void
|
static void
|
||||||
parser_set_past_scope(struct parser_params *parser, const char *name, const char *val)
|
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},
|
{"coding", magic_comment_encoding, parser_encode_length},
|
||||||
{"encoding", magic_comment_encoding, parser_encode_length},
|
{"encoding", magic_comment_encoding, parser_encode_length},
|
||||||
#ifndef RIPPER
|
#ifndef RIPPER
|
||||||
|
{"frozen_string_literal", parser_set_compile_option_flag},
|
||||||
{"warn_indent", parser_set_token_info},
|
{"warn_indent", parser_set_token_info},
|
||||||
# if WARN_PAST_SCOPE
|
# if WARN_PAST_SCOPE
|
||||||
{"warn_past_scope", parser_set_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;
|
enum lex_state_e last_state;
|
||||||
#ifdef RIPPER
|
#ifdef RIPPER
|
||||||
int fallthru = FALSE;
|
int fallthru = FALSE;
|
||||||
|
#else
|
||||||
|
int token_seen = parser->token_seen;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (lex_strterm) {
|
if (lex_strterm) {
|
||||||
@ -7891,6 +7916,9 @@ parser_yylex(struct parser_params *parser)
|
|||||||
}
|
}
|
||||||
cmd_state = command_start;
|
cmd_state = command_start;
|
||||||
command_start = FALSE;
|
command_start = FALSE;
|
||||||
|
#ifndef RIPPER
|
||||||
|
parser->token_seen = TRUE;
|
||||||
|
#endif
|
||||||
retry:
|
retry:
|
||||||
last_state = lex_state;
|
last_state = lex_state;
|
||||||
switch (c = nextc()) {
|
switch (c = nextc()) {
|
||||||
@ -7921,6 +7949,9 @@ parser_yylex(struct parser_params *parser)
|
|||||||
goto retry;
|
goto retry;
|
||||||
|
|
||||||
case '#': /* it's a comment */
|
case '#': /* it's a comment */
|
||||||
|
#ifndef RIPPER
|
||||||
|
parser->token_seen = token_seen;
|
||||||
|
#endif
|
||||||
/* no magic_comment in shebang line */
|
/* no magic_comment in shebang line */
|
||||||
if (!parser_magic_comment(parser, lex_p, lex_pend - lex_p)) {
|
if (!parser_magic_comment(parser, lex_p, lex_pend - lex_p)) {
|
||||||
if (comment_at_top(parser)) {
|
if (comment_at_top(parser)) {
|
||||||
@ -7934,6 +7965,9 @@ parser_yylex(struct parser_params *parser)
|
|||||||
#endif
|
#endif
|
||||||
/* fall through */
|
/* fall through */
|
||||||
case '\n':
|
case '\n':
|
||||||
|
#ifndef RIPPER
|
||||||
|
parser->token_seen = token_seen;
|
||||||
|
#endif
|
||||||
c = (IS_lex_state(EXPR_BEG|EXPR_CLASS|EXPR_FNAME|EXPR_DOT) &&
|
c = (IS_lex_state(EXPR_BEG|EXPR_CLASS|EXPR_FNAME|EXPR_DOT) &&
|
||||||
!IS_lex_state(EXPR_LABELED));
|
!IS_lex_state(EXPR_LABELED));
|
||||||
if (c || IS_lex_state_all(EXPR_ARG|EXPR_LABELED)) {
|
if (c || IS_lex_state_all(EXPR_ARG|EXPR_LABELED)) {
|
||||||
|
@ -121,6 +121,19 @@ class TestRubyLiteral < Test::Unit::TestCase
|
|||||||
assert_equal "foo\n", `echo #{s}`
|
assert_equal "foo\n", `echo #{s}`
|
||||||
end
|
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
|
def test_regexp
|
||||||
assert_instance_of Regexp, //
|
assert_instance_of Regexp, //
|
||||||
assert_match(//, 'a')
|
assert_match(//, 'a')
|
||||||
|
Loading…
x
Reference in New Issue
Block a user