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:
		
							parent
							
								
									71730b4243
								
							
						
					
					
						commit
						859337b17b
					
				
					 9 changed files with 112 additions and 26 deletions
				
			
		
							
								
								
									
										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>
 | 
			
		||||
 | 
			
		||||
	* 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
 | 
			
		||||
 | 
			
		||||
* frozen-string-literal pragma:
 | 
			
		||||
  * new pragma, frozen-string-literal has been experimentally introduced.
 | 
			
		||||
 | 
			
		||||
=== Core classes updates (outstanding ones only)
 | 
			
		||||
 | 
			
		||||
* ARGF
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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
									
										
									
									
									
								
							
							
						
						
									
										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 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
									
										
									
									
									
								
							
							
						
						
									
										1
									
								
								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
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										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("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
									
										
									
									
									
								
							
							
						
						
									
										2
									
								
								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
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										38
									
								
								parse.y
									
										
									
									
									
								
							
							
						
						
									
										38
									
								
								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)) {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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')
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue