1
0
Fork 0
mirror of https://github.com/ruby/ruby.git synced 2022-11-09 12:17:21 -05:00

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:
nobu 2015-09-27 06:44:02 +00:00
parent 71730b4243
commit 859337b17b
9 changed files with 112 additions and 26 deletions

View file

@ -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

3
NEWS
View file

@ -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

View file

@ -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:{

56
iseq.c
View file

@ -344,6 +344,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)
{
@ -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");

1
iseq.h
View file

@ -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

4
node.c
View file

@ -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:

2
node.h
View file

@ -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

38
parse.y
View file

@ -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)) {

View file

@ -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')