From 3196645aee3add70a33f3d926d1d6e3820d39377 Mon Sep 17 00:00:00 2001 From: matz Date: Fri, 15 Oct 1999 08:52:18 +0000 Subject: [PATCH] -r debug, -s, etc. git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@544 b2dd03c8-39d4-4d8f-98ff-823fe69b080e --- ChangeLog | 26 ++++++ ToDo | 5 +- eval.c | 20 +--- ext/Win32API/Win32API.c | 16 +++- intern.h | 1 - lib/debug.rb | 30 ++---- lib/tracer.rb | 9 +- parse.y | 76 ++++++++++++--- re.c | 8 +- ruby.c | 202 +++++++++++++++++++++------------------- sample/test.rb | 29 +++++- string.c | 3 +- 12 files changed, 259 insertions(+), 166 deletions(-) diff --git a/ChangeLog b/ChangeLog index 57f3af1baa..68d37ca3d6 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,25 @@ +Fri Oct 15 01:32:31 1999 WATANABE Hirofumi + + * ext/Win32API/Win32API.c (Win32API_Call): need to use NUM2ULONG, + not NUM2INT. + +Fri Oct 15 00:22:30 1999 Yukihiro Matsumoto + + * re.c (Init_Regexp): super class of the MatchingData, which was + Data, to be Object. + + * eval.c (ruby_run): evaluate required libraries before load & + compiling the script. + + * parse.y (lex_getline): retrieve a line from the stream, saving + lines in the table in debug mode. + + * eval.c (call_trace_func): treat the case ruby_sourcefile is null. + +Thu Oct 14 02:00:10 1999 Yukihiro Matsumoto + + * parse.y (string): compile time string concatenation. + Wed Oct 13 02:17:05 1999 Yukihiro Matsumoto * eval.c (block_pass): should copy block to prevent modifications. @@ -45,6 +67,10 @@ Mon Oct 11 07:27:05 1999 Yukihiro Matsumoto * signal.c (Init_signal): ignore SIGPIPE by default. +Wed Oct 6 17:13:19 1999 Nobuyoshi Nakada + + * ruby.c (addpath): rubylib_mangled_path() modified. + Mon Oct 4 12:42:32 1999 Kazuhiko Izawa * pack.c (pack_unpack): % in printf format should be %%. diff --git a/ToDo b/ToDo index 6866da7936..b3e3d80cab 100644 --- a/ToDo +++ b/ToDo @@ -1,7 +1,7 @@ Language Spec. - def foo; .. rescue .. end -* compile time string concatenation, "hello" "world" => "helloworld" +- compile time string concatenation, "hello" "world" => "helloworld" * ../... outside condition invokes operator method too. * %w(a\ b\ c abc) => ["a b c", "abc"] * package or access control for global variables @@ -17,7 +17,7 @@ Language Spec. Hacking Interpreter * RUBYOPT environment variable -* non-blocking open (e.g. named pipe) for thread +* non-blocking open (e.g. for named pipe) for thread * avoid blocking with gethostbyname/gethostbyaddr * objectify interpreters * remove rb_eval() recursions @@ -28,6 +28,7 @@ Hacking Interpreter Standard Libraries - hash[key] = nil may not remove entry; hashes may have nil as the value. +- hash.fetch(key) raises exception if key is not found. - Array#{first,last,at} - Dir.glob(pat){|f|...} * Struct::new([name,]member,...) ?? diff --git a/eval.c b/eval.c index fe449d1307..588d668038 100644 --- a/eval.c +++ b/eval.c @@ -966,8 +966,6 @@ ruby_init() ruby_scope = top_scope; } -static int ext_init = 0; - void ruby_options(argc, argv) int argc; @@ -978,7 +976,6 @@ ruby_options(argc, argv) PUSH_TAG(PROT_NONE) if ((state = EXEC_TAG()) == 0) { ruby_process_options(argc, argv); - ext_init = 1; /* Init_ext() called in ruby_process_options */ } POP_TAG(); if (state) { @@ -1014,18 +1011,6 @@ static void rb_thread_wait_other_threads _((void)); static int exit_status; -static void -call_required_libraries() -{ - NODE *save; - - ruby_sourcefile = 0; - if (!ext_init) Init_ext(); - save = ruby_eval_tree; - ruby_require_libraries(); - ruby_eval_tree = save; -} - void ruby_run() { @@ -1039,7 +1024,6 @@ ruby_run() PUSH_TAG(PROT_NONE); PUSH_ITER(ITER_NOT); if ((state = EXEC_TAG()) == 0) { - call_required_libraries(); eval_node(ruby_top_self); } POP_ITER(); @@ -1698,6 +1682,7 @@ call_trace_func(event, file, line, self, id, klass) struct FRAME *prev; char *file_save = ruby_sourcefile; int line_save = ruby_sourceline; + VALUE srcfile; if (!trace_func) return; @@ -1721,8 +1706,9 @@ call_trace_func(event, file, line, self, id, klass) } PUSH_TAG(PROT_NONE); if ((state = EXEC_TAG()) == 0) { + srcfile = rb_str_new2(ruby_sourcefile?ruby_sourcefile:"(ruby)"); proc_call(trace, rb_ary_new3(6, rb_str_new2(event), - rb_str_new2(ruby_sourcefile), + srcfile, INT2FIX(ruby_sourceline), INT2FIX(id), self?rb_f_binding(self):Qnil, diff --git a/ext/Win32API/Win32API.c b/ext/Win32API/Win32API.c index 9f75653132..8efb3ddad6 100644 --- a/ext/Win32API/Win32API.c +++ b/ext/Win32API/Win32API.c @@ -140,7 +140,7 @@ Win32API_Call(argc, argv, obj) obj_proc = rb_iv_get(obj, "__proc__"); - ApiFunction = (FARPROC)NUM2INT(obj_proc); + ApiFunction = (FARPROC)NUM2ULONG(obj_proc); obj_import = rb_iv_get(obj, "__import__"); obj_export = rb_iv_get(obj, "__export__"); @@ -159,7 +159,7 @@ Win32API_Call(argc, argv, obj) switch (timport) { case _T_NUMBER: case _T_INTEGER: - lParam = NUM2INT(rb_ary_entry(args, i)); + lParam = NUM2ULONG(rb_ary_entry(args, i)); #if defined(_MSC_VER) || defined(__LCC__) _asm { mov eax, lParam @@ -173,9 +173,15 @@ Win32API_Call(argc, argv, obj) break; case _T_POINTER: str = rb_ary_entry(args, i); - Check_Type(str, T_STRING); - rb_str_modify(str); - pParam = RSTRING(str)->ptr; + if (NIL_P(str)) { + pParam = 0; + } else if (FIXNUM_P(str)){ + pParam = (char *)NUM2ULONG(str); + } else { + Check_Type(str, T_STRING); + rb_str_modify(str); + pParam = RSTRING(str)->ptr; + } #if defined(_MSC_VER) || defined(__LCC__) _asm { mov eax, dword ptr pParam diff --git a/intern.h b/intern.h index 4bfefa2fd1..5dda14481c 100644 --- a/intern.h +++ b/intern.h @@ -249,7 +249,6 @@ void ruby_script _((char*)); void ruby_prog_init _((void)); void ruby_set_argv _((int, char**)); void ruby_process_options _((int, char**)); -void ruby_require_libraries _((void)); void ruby_load_script _((void)); /* signal.c */ VALUE rb_f_kill _((int, VALUE*)); diff --git a/lib/debug.rb b/lib/debug.rb index d2f1da83ff..1209f60ee2 100644 --- a/lib/debug.rb +++ b/lib/debug.rb @@ -1,3 +1,5 @@ +LINES__ = {} unless defined? LINES__ + class DEBUGGER__ begin require 'readline' @@ -24,7 +26,6 @@ class DEBUGGER__ @frames = [nil] @last_file = nil @last = [nil, nil] - @scripts = {} end DEBUG_LAST_CMD = [] @@ -206,7 +207,7 @@ class DEBUGGER__ previus_line = b STDOUT.printf "[%d, %d] in %s\n", b, e, binding_file line_at(binding_file, binding_line) - if lines = @scripts[binding_file] and lines != true + if lines = LINES__[binding_file] and lines != true n = 0 b.upto(e) do |n| if n > 0 && lines[n-1] @@ -289,26 +290,14 @@ class DEBUGGER__ end def line_at(file, line) - lines = @scripts[file] + lines = LINES__[file] if lines return "\n" if lines == true line = lines[line-1] return "\n" unless line return line end - save = $DEBUG - begin - $DEBUG = false - f = open(file) - lines = @scripts[file] = f.readlines - rescue - $DEBUG = save - @scripts[file] = true - return "\n" - end - line = lines[line-1] - return "\n" unless line - return line + return "\n" end def debug_funcname(id) @@ -396,8 +385,9 @@ class DEBUGGER__ end CONTEXT = new -end -set_trace_func proc{|event, file, line, id, binding,*rest| - DEBUGGER__::CONTEXT.trace_func event, file, line, id, binding -} + + set_trace_func proc{|event, file, line, id, binding,*rest| + DEBUGGER__::CONTEXT.trace_func event, file, line, id, binding + } +end diff --git a/lib/tracer.rb b/lib/tracer.rb index a8dc2a104d..095173e53b 100644 --- a/lib/tracer.rb +++ b/lib/tracer.rb @@ -40,7 +40,6 @@ class Tracer end @get_line_procs = {} - @sources = {} @filters = [] end @@ -79,17 +78,17 @@ class Tracer return p.call line end - unless list = @sources[file] + unless list = LINES__[file] # print file if $DEBUG begin f = open(file) begin - @sources[file] = list = f.readlines + LINES__[file] = list = f.readlines ensure f.close end rescue - @sources[file] = list = [] + LINES__[file] = list = [] end end if l = list[line - 1] @@ -148,6 +147,8 @@ class Tracer end +LINES__ = {} unless defined? LINES__ + if caller(0).size == 1 if $0 == Tracer::MY_FILE_NAME # direct call diff --git a/parse.y b/parse.y index 3365df4676..b971b21ac7 100644 --- a/parse.y +++ b/parse.y @@ -167,7 +167,7 @@ static void top_local_setup(); %token tINTEGER tFLOAT tSTRING tXSTRING tREGEXP %token tDSTRING tDXSTRING tDREGEXP tNTH_REF tBACK_REF -%type singleton +%type singleton string %type literal numeric %type compstmt stmts stmt expr arg primary command_call method_call %type if_tail opt_else case_body cases rescue ensure @@ -963,11 +963,7 @@ primary : literal { $$ = NEW_COLON3($2); } - | tSTRING - { - $$ = NEW_STR($1); - } - | tDSTRING + | string | tXSTRING { $$ = NEW_XSTR($1); @@ -1432,6 +1428,34 @@ literal : numeric } | tREGEXP +string : tSTRING + { + $$ = NEW_STR($1); + } + | tDSTRING + | string tSTRING + { + if (nd_type($1) == NODE_DSTR) { + list_append($1, NEW_STR($2)); + } + else { + rb_str_concat($1->nd_lit, $2); + } + $$ = $1; + } + | string tDSTRING + { + if (nd_type($1) == NODE_STR) { + $$ = NEW_DSTR($1->nd_lit); + } + else { + $$ = $1; + } + $2->nd_head = NEW_STR($2->nd_lit); + nd_set_type($2, NODE_ARRAY); + list_concat($$, $2); + } + symbol : tSYMBEG sym { lex_state = EXPR_END; @@ -1690,7 +1714,7 @@ char *strdup(); static char *tokenbuf = NULL; static int tokidx, toksiz = 0; -static NODE *rb_str_extend(); +static NODE *str_extend(); #define LEAVE_BS 1 @@ -1749,18 +1773,36 @@ static int heredoc_end; int ruby_in_compile = 0; int ruby__end__seen; +static VALUE ruby_debug_lines; + static NODE* yycompile(f) char *f; { int n; + if (!ruby_in_eval && rb_safe_level() == 0 && + rb_const_defined(rb_cObject, rb_intern("LINES__"))) { + VALUE hash, fname; + + hash = rb_const_get(rb_cObject, rb_intern("LINES__")); + if (TYPE(hash) == T_HASH) { + fname = rb_str_new2(f); + ruby_debug_lines = rb_hash_aref(hash, fname); + if (NIL_P(ruby_debug_lines)) { + ruby_debug_lines = rb_ary_new(); + rb_hash_aset(hash, fname, ruby_debug_lines); + } + } + } + ruby__end__seen = 0; ruby_eval_tree = 0; heredoc_end = 0; ruby_sourcefile = f; ruby_in_compile = 1; n = yyparse(); + ruby_debug_lines = 0; compile_for_eval = 0; ruby_in_compile = 0; cond_nest = 0; @@ -1794,6 +1836,16 @@ lex_get_str(s) return rb_str_new(beg, end - beg); } +static VALUE +lex_getline() +{ + VALUE line = (*lex_gets)(lex_input); + if (ruby_debug_lines && !NIL_P(line)) { + rb_ary_push(ruby_debug_lines, line); + } + return line; +} + NODE* rb_compile_string(f, s, line) const char *f; @@ -1842,7 +1894,7 @@ nextc() if (lex_p == lex_pend) { if (lex_input) { - VALUE v = (*lex_gets)(lex_input); + VALUE v = lex_getline(); if (NIL_P(v)) return -1; if (heredoc_end > 0) { @@ -2057,7 +2109,7 @@ parse_regx(term, paren) break; case '#': - list = rb_str_extend(list, term); + list = str_extend(list, term); if (list == (NODE*)-1) return 0; continue; @@ -2219,7 +2271,7 @@ parse_string(func, term, paren) } } else if (c == '#') { - list = rb_str_extend(list, term); + list = str_extend(list, term); if (list == (NODE*)-1) goto unterm_str; continue; } @@ -2382,7 +2434,7 @@ here_document(term, indent) str = rb_str_new(0,0); for (;;) { - lex_lastline = line = (*lex_gets)(lex_input); + lex_lastline = line = lex_getline(); if (NIL_P(line)) { error: ruby_sourceline = linesave; @@ -3271,7 +3323,7 @@ yylex() } static NODE* -rb_str_extend(list, term) +str_extend(list, term) NODE *list; char term; { diff --git a/re.c b/re.c index ac73732d39..8bc15c1cfb 100644 --- a/re.c +++ b/re.c @@ -261,7 +261,7 @@ rb_reg_source(re) VALUE re; { VALUE str = rb_str_new(0,0); - rb_reg_expr_str(str, RREGEXP(re)->str,RREGEXP(re)->len); + rb_reg_expr_str(str, RREGEXP(re)->str, RREGEXP(re)->len); return str; } @@ -710,7 +710,7 @@ static VALUE match_string(match) VALUE match; { - return rb_str_dup(RMATCH(match)->str); + return RMATCH(match)->str; /* str is frozen */ } VALUE rb_cRegexp; @@ -1263,7 +1263,9 @@ Init_Regexp() rb_global_variable(®_cache); rb_global_variable(&matchcache); - rb_cMatch = rb_define_class("MatchingData", rb_cData); + rb_cMatch = rb_define_class("MatchingData", rb_cObject); + rb_undef_method(CLASS_OF(rb_cMatch), "new"); + rb_define_method(rb_cMatch, "clone", match_clone, 0); rb_define_method(rb_cMatch, "size", match_size, 0); rb_define_method(rb_cMatch, "length", match_size, 0); diff --git a/ruby.c b/ruby.c index 0b4d486ec8..488c9639e0 100644 --- a/ruby.c +++ b/ruby.c @@ -120,8 +120,7 @@ NULL extern VALUE rb_load_path; -static FILE *e_fp; -static char *e_tmpname; +static VALUE e_script; #define STATIC_FILE_LENGTH 255 @@ -176,8 +175,11 @@ rubylib_mangle(s, l) strcpy(ret + newl, s + oldl); return ret; } +#define rubylib_mangled_path(s, l) rb_str_new2(rubylib_mangle((s), (l))) +#define rubylib_mangled_path2(s) rb_str_new2(rubylib_mangle((s), 0)) #else -#define rubylib_mangle(s, l) (s) +#define rubylib_mangled_path(s, l) rb_str_new((s), (l)) +#define rubylib_mangled_path2(s) rb_str_new2(s) #endif static void @@ -202,18 +204,18 @@ addpath(path) while (*p) { while (*p == sep) p++; if (s = strchr(p, sep)) { - rb_ary_push(ary, rb_str_new(rubylib_mangle(p, (int)(s-p)), (int)(s-p))); + rb_ary_push(ary, rubylib_mangled_path(p, (int)(s-p))); p = s + 1; } else { - rb_ary_push(ary, rb_str_new2(rubylib_mangle(p, 0))); + rb_ary_push(ary, rubylib_mangled_path2(p)); break; } } rb_load_path = rb_ary_plus(ary, rb_load_path); } else { - rb_ary_unshift(rb_load_path, rb_str_new2(rubylib_mangle(path, 0))); + rb_ary_unshift(rb_load_path, rubylib_mangled_path2(path)); } } @@ -237,11 +239,15 @@ add_modules(mod) } void -ruby_require_libraries() +require_libraries() { + extern NODE *ruby_eval_tree; + NODE *save; struct req_list *list = req_list_head.next; struct req_list *tmp; + ruby_sourcefile = 0; + save = ruby_eval_tree; req_list_last = 0; while (list) { rb_require(list->name); @@ -249,26 +255,57 @@ ruby_require_libraries() free(list); list = tmp; } + ruby_eval_tree = save; } extern void Init_ext _((void)); static void -proc_options(argcp, argvp) - int *argcp; - char ***argvp; +process_sflag() { - int argc = *argcp; - char **argv = *argvp; - int script_given, do_search; + if (sflag) { + int n; + VALUE *args; + + n = RARRAY(rb_argv)->len; + args = RARRAY(rb_argv)->ptr; + while (n--) { + char *s = STR2CSTR(*args); + char *p; + + if (s[0] != '-') continue; + if (s[1] == '-' && s[2] == '\0') break; + + s[0] = '$'; + if (p = strchr(s, '=')) { + *p++ = '\0'; + rb_gvar_set2(s, rb_str_new2(p)); + } + else { + rb_gvar_set2(s, Qtrue); + } + s[0] = '-'; + } + n = RARRAY(rb_argv)->len - n; + while (n--) { + rb_ary_shift(rb_argv); + } + } +} + +static void +proc_options(argc, argv) + int argc; + char **argv; +{ + char *argv0 = argv[0]; + int do_search; char *s; if (argc == 0) return; version = Qfalse; do_search = Qfalse; - script_given = 0; - e_tmpname = NULL; for (argc--,argv++; argc > 0; argc--,argv++) { if (argv[0][0] != '-' || !argv[0][1]) break; @@ -345,17 +382,12 @@ proc_options(argcp, argvp) fprintf(stderr, "%s: no code specified for -e\n", origargv[0]); exit(2); } - if (!e_fp) { - e_tmpname = ruby_mktemp(); - if (!e_tmpname) rb_fatal("Can't mktemp"); - e_fp = fopen(e_tmpname, "w"); - if (!e_fp) { - rb_fatal("Cannot open temporary file: %s", e_tmpname); - } - if (script == 0) script = e_tmpname; + if (!e_script) { + e_script = rb_str_new(0,0); + if (script == 0) script = "-e"; } - fputs(s, e_fp); - putc('\n', e_fp); + rb_str_cat(e_script, s, strlen(s)); + rb_str_cat(e_script, "\n", 1); break; case 'r': @@ -489,14 +521,11 @@ proc_options(argcp, argvp) } switch_end: - if (*argvp[0] == 0) return; + if (argv0 == 0) return; - if (e_fp) { - if (fflush(e_fp) || ferror(e_fp) || fclose(e_fp)) - rb_fatal("Cannot write to temp file for -e"); - e_fp = NULL; + if (e_script) { argc++, argv--; - argv[0] = e_tmpname; + argv[0] = script; } if (version) { @@ -507,66 +536,55 @@ proc_options(argcp, argvp) ruby_show_copyright(); } - Init_ext(); /* should be called here for some reason :-( */ - if (script_given == Qfalse) { - if (argc == 0) { /* no more args */ - if (ruby_verbose == 3) exit(0); - script = "-"; - load_stdin(); - } - else { - script = argv[0]; - if (script[0] == '\0') { - script = "-"; - load_stdin(); - } - else { - if (do_search) { - char *path = getenv("RUBYPATH"); - - script = 0; - if (path) { - script = dln_find_file(argv[0], path); - } - if (!script) { - script = dln_find_file(argv[0], getenv("PATH")); - } - if (!script) script = argv[0]; - } - load_file(script, 1); - } - argc--; argv++; - } - } if (ruby_verbose) ruby_verbose = Qtrue; if (ruby_debug) ruby_debug = Qtrue; - xflag = Qfalse; - *argvp = argv; - *argcp = argc; - - if (sflag) { - char *s; - - argc = *argcp; argv = *argvp; - for (; argc > 0 && argv[0][0] == '-'; argc--,argv++) { - if (argv[0][1] == '-') { - argc--,argv++; - break; - } - argv[0][0] = '$'; - if (s = strchr(argv[0], '=')) { - *s++ = '\0'; - rb_gvar_set2(argv[0], rb_str_new2(s)); - } - else { - rb_gvar_set2(argv[0], Qtrue); - } - argv[0][0] = '-'; + if (!e_script && argc == 0) { /* no more args */ + if (ruby_verbose == 3) exit(0); + script = "-"; + } + else { + script = argv[0]; + if (script[0] == '\0') { + script = "-"; } - *argcp = argc; *argvp = argv; + else { + if (do_search) { + char *path = getenv("RUBYPATH"); + + script = 0; + if (path) { + script = dln_find_file(argv[0], path); + } + if (!script) { + script = dln_find_file(argv[0], getenv("PATH")); + } + if (!script) script = argv[0]; + } + } + argc--; argv++; } + process_sflag(); + ruby_script(script); + ruby_set_argv(argc, argv); + + Init_ext(); /* should be called here for some reason :-( */ + require_libraries(); + + if (e_script) { + rb_compile_string(script, e_script, 1); + } + else if (strlen(script) == 1 && script[0] == '-') { + load_stdin(); + } + else { + load_file(script, 1); + } + + process_sflag(); + sflag = Qfalse; + xflag = Qfalse; } extern int ruby__end__seen; @@ -669,7 +687,7 @@ load_file(fname, script) s++; *s = '\0'; argv[1] = p; - proc_options(&argc, &argvp); + proc_options(argc, argv); p = ++s; while (*p && ISSPACE(*p)) p++; @@ -930,9 +948,7 @@ ruby_process_options(argc, argv) #if defined(USE_DLN_A_OUT) dln_argv0 = argv[0]; #endif - proc_options(&argc, &argv); - ruby_script(script); - ruby_set_argv(argc, argv); + proc_options(argc, argv); if (do_check && ruby_nerrs == 0) { printf("Syntax OK\n"); @@ -944,12 +960,4 @@ ruby_process_options(argc, argv) if (do_loop) { rb_parser_while_loop(do_line, do_split); } - if (e_fp) { - fclose(e_fp); - e_fp = NULL; - } - if (e_tmpname) { - unlink(e_tmpname); - e_tmpname = NULL; - } } diff --git a/sample/test.rb b/sample/test.rb index a849a42fc2..e23c39f5e7 100644 --- a/sample/test.rb +++ b/sample/test.rb @@ -413,6 +413,23 @@ end tt{|i| break if i == 5} ok(i == 5) +def tt2(dummy) + yield 1 +end + +def tt3(&block) + tt2(raise(ArgumentError,""),&block) +end + +$x = false +begin + tt3{} +rescue ArgumentError + $x = true +rescue Exception +end +ok($x) + # iterator break/redo/next/retry done = true loop{ @@ -592,6 +609,10 @@ check "string & char" ok("abcd" == "abcd") ok("abcd" =~ "abcd") ok("abcd" === "abcd") +# compile time string concatenation +ok("ab" "cd" == "abcd") +ok("#{22}aa" "cd#{44}" == "22aacd44") +ok("#{22}aa" "cd#{44}" "55" "#{66}" == "22aacd445566") ok("abc" !~ /^$/) ok("abc\n" !~ /^$/) ok("abc" !~ /^d*$/) @@ -604,10 +625,6 @@ ok("abcabc" =~ /.*c/ && $& == "abcabc") ok("abcabc" =~ /.*?a/ && $& == "a") ok("abcabc" =~ /.*?c/ && $& == "abc") ok(/(.|\n)*?\n(b|\n)/ =~ "a\nb\n\n" && $& == "a\nb") -$x = <ptr = RSTRING(orig)->ptr; RSTRING(orig)->orig = (VALUE)str; str->orig = 0; - if (FL_TEST(str, FL_TAINT)) { + if (FL_TEST(orig, FL_TAINT)) { FL_SET(str, FL_TAINT); } + FL_SET(str, STR_FREEZE); return (VALUE)str; } }