https://github.com/akkartik/mu/blob/master/101run_sandboxed.cc
  1 //: Helper for various programming environments: run arbitrary Mu code and
  2 //: return some result in text form.
  3 
  4 :(scenario run_interactive_code)
  5 def main [
  6   1:num <- copy 0  # reserve space for the sandbox
  7   10:text <- new [1:num/raw <- copy 34]
  8 #?   $print 10:num [|] 11:num [: ] 1000:num [|] *10:text [ (] 10:text [)] 10/newline
  9   run-sandboxed 10:text
 10   20:num <- copy 1:num
 11 ]
 12 +mem: storing 34 in location 20
 13 
 14 :(scenario run_interactive_empty)
 15 def main [
 16   10:text <- copy null
 17   20:text <- run-sandboxed 10:text
 18 ]
 19 # result is null
 20 +mem: storing 0 in location 20
 21 +mem: storing 0 in location 21
 22 
 23 pre { line-height: 125%; }
td.linenos .normal { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; }
span.linenos { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; }
td.linenos .special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; }
span.linenos.special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; }
.highlight .hll { background-color: #ffffcc }
.highlight .c { color: #888888 } /* Comment */
.highlight .err { color: #a61717; background-color: #e3d2d2 } /* Error */
.highlight .k { color: #008800; font-weight: bold } /* Keyword */
.highlight .ch { color: #888888 } /* Comment.Hashbang */
.highlight .cm { color: #888888 } /* Comment.Multiline */
.highlight .cp { color: #cc0000; font-weight: bold } /* Comment.Preproc */
.highlight .cpf { color: #888888 } /* Comment.PreprocFile */
.highlight .c1 { color: #888888 } /* Comment.Single */
.highlight .cs { color: #cc0000; font-weight: bold; background-color: #fff0f0 } /* Comment.Special */
.highlight .gd { color: #000000; background-color: #ffdddd } /* Generic.Deleted */
.highlight .ge { font-style: italic } /* Generic.Emph */
.highlight .ges { font-weight: bold; font-style: italic } /* Generic.EmphStrong */
.highlight .gr { color: #aa0000 } /* Generic.Error */
.highlight .gh { color: #333333 } /* Generic.Heading */
.highlight .gi { color: #000000; background-color: #ddffdd } /* Generic.Inserted */
.highlight .go { color: #888888 } /* Generic.Output */
.highlight .gp { color: #555555 } /* Generic.Prompt */
.highlight .gs { font-weight: bold } /* Generic.Strong */
.highlight .gu { color: #666666 } /* Generic.Subheading */
.highlight .gt { color: #aa0000 } /* Generic.Traceback */
.highlight .kc { color: #008800; font-weight: bold } /* Keyword.Constant */
.highlight .kd { color: #008800; font-weight: bold } /* Keyword.Declaration */
.highlight .kn { color: #008800; font-weight: bold } /* Keyword.Namespace */
.highlight .kp { color: #008800 } /* Keyword.Pseudo */
.highlight .kr { color: #008800; font-weight: bold } /* Keyword.Reserved */
.highlight .kt { color: #888888; font-weight: bold } /* Keyword.Type */
.highlight .m { color: #0000DD; font-weight: bold } /* Literal.Number */
.highlight .s { color: #dd2200; background-color: #fff0f0 } /* Literal.String */
.highlight .na { color: #336699 } /* Name.Attribute */
.highlight .nb { color: #003388 } /* Name.Builtin */
.highlight .nc { color: #bb0066; font-weight: bold } /* Name.Class */
.highlight .no { color: #003366; font-weight: bold } /* Name.Constant */
.highlight .nd { color: #555555 } /* Name.Decorator */
.highlight .ne { color: #bb0066; font-weight: bold } /* Name.Exception */
.highlight .nf { color: #0066bb; font-weight: bold } /* Name.Function */
.highlight .nl { color: #336699; font-style: italic } /* Name.Label */
.highlight .nn { color: #bb0066; font-weight: bold } /* Name.Namespace */
.highlight .py { color: #336699; font-weight: bold } /* Name.Property */
.highlight .nt { color: #bb0066; font-weight: bold } /* Name.Tag */
.highlight .nv { color: #336699 } /* Name.Variable */
.highlight .ow { color: #008800 } /* Operator.Word */
.highlight .w { color: #bbbbbb } /* Text.Whitespace */
.highlight .mb { color: #0000DD; font-weight: bold } /* Literal.Number.Bin */
.highlight .mf { color: #0000DD; font-weight: bold } /* Literal.Number.Float */
.highlight .mh { color: #0000DD; font-weight: bold } /* Literal.Number.Hex */
.highlight .mi { color: #0000DD; font-weight: bold } /* Literal.Number.Integer */
.highlight .mo { color: #0000DD; font-weight: bold } /* Literal.Number.Oct */
.highlight .sa { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Affix */
.highlight .sb { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Backtick */
.highlight .sc { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Char */
.highlight .dl { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Delimiter */
.highlight .sd { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Doc */
.highlight .s2 { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Double */
.highlight .se { color: #0044dd; background-color: #fff0f0 } /* Literal.String.Escape */
.highlight .sh { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Heredoc */
.highlight .si { color: #3333bb; background-color: #fff0f0 } /* Literal.String.Interpol */
.highlight .sx { color: #22bb22; background-color: #f0fff0 } /* Literal.String.Other */
.highlight .sr { color: #008800; background-color: #fff0ff } /* Literal.String.Regex */
.highlight .s1 { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Single */
.highlight .ss { color: #aa6600; background-color: #fff0f0 } /* Literal.String.Symbol */
.highlight .bp { color: #003388 } /* Name.Builtin.Pseudo */
.highlight .fm { color: #0066bb; font-weight: bold } /* Name.Function.Magic */
.highlight .vc { color: #336699 } /* Name.Variable.Class */
.highlight .vg { color: #dd7700 } /* Name.Variable.Global */
.highlight .vi { color: #3333bb } /* Name.Variable.Instance */
.highlight .vm { color: #336699 } /* Name.Variable.Magic */
.highlight .il { color: #0000DD; font-weight: bold } /* Literal.Number.Integer.Long */
====== Povrnitev okvarjene vsebine ======

Na tej strani je mogoče povrniti vsebino wiki strani na izvorne vrednosti po napadu na stran in vpisu neželenih vsebin. Za iskanje strani z neželeno vsebino, uporabite iskalnik z ustreznim nizom (npr. naslov URL), potem pa potrdite, da so najdene strani res z neželeno vsebino in nato povrnite stanje na zadnjo pravo različico.
57 bool new_code_pushed_to_stack = run_interactive(ingredients.at(0).at(/*skip alloc id*/1)); 58 if (!new_code_pushed_to_stack) { 59 products.resize(5); 60 products.at(0).push_back(/*alloc id*/0); 61 products.at(0).push_back(0); 62 products.at(1).push_back(/*alloc id*/0); 63 products.at(1).push_back(trace_error_contents()); 64 products.at(2).push_back(/*alloc id*/0); 65 products.at(2).push_back(0); 66 products.at(3).push_back(/*alloc id*/0); 67 products.at(3).push_back(trace_app_contents()); 68 products.at(4).push_back(1); // completed 69 run_code_end(); 70 break; // done with this instruction 71 } 72 else { 73 continue; // not done with caller; don't increment current_step_index() 74 } 75 } 76 77 //: To show results in the sandbox Mu uses a hack: it saves the products 78 //: returned by each instruction while Track_most_recent_products is true, and 79 //: keeps the most recent such result around so that it can be returned as the 80 //: result of a sandbox. 81 82 :(before "End Globals") 83 bool Track_most_recent_products = false; 84 int Call_depth_to_track_most_recent_products_at = 0; 85 string Most_recent_products; 86 :(before "End Reset") 87 Track_most_recent_products = false; 88 Call_depth_to_track_most_recent_products_at = 0; 89 Most_recent_products = ""; 90 91 :(before "End Globals") 92 trace_stream* Save_trace_stream = NULL; 93 string Save_trace_file; 94 :(code) 95 // reads a string, tries to call it as code (treating it as a test), saving 96 // all errors. 97 // returns true if successfully called (no errors found during load and transform) 98 bool run_interactive(int address) { 99 //? cerr << "run_interactive: " << address << '\n'; 100 assert(contains_key(Recipe_ordinal, "interactive") && get(Recipe_ordinal, "interactive") != 0); 101 // try to sandbox the run as best you can 102 // todo: test this 103 if (!Current_scenario) { 104 for (int i = 1; i < Reserved_for_tests; ++i) 105 Memory.erase(i); 106 } 107 string command = trim(strip_comments(read_mu_text(address))); 108 //? cerr << "command: " << command << '\n'; 109 Name[get(Recipe_ordinal, "interactive")].clear(); 110 run_code_begin(/*should_stash_snapshots*/true); 111 if (command.empty()) return false; 112 // don't kill the current routine on parse errors 113 routine* save_current_routine = Current_routine; 114 Current_routine = NULL; 115 // call run(string) but without the scheduling 116 load(string("recipe! interactive [\n") + 117 "local-scope\n" + 118 "screen:&:screen <- next-ingredient\n" + 119 "$start-tracking-products\n" + 120 command + "\n" + 121 "$stop-tracking-products\n" + 122 "return screen\n" + 123 "]\n"); 124 transform_all(); 125 Current_routine = save_current_routine; 126 if (trace_count("error") > 0) return false; 127 // now call 'sandbox' which will run 'interactive' in a separate routine, 128 // and wait for it 129 if (Save_trace_stream) { 130 ++Save_trace_stream->callstack_depth; 131 trace(9999, "trace") << "run-sandboxed: incrementing callstack depth to " << Save_trace_stream->callstack_depth << end(); 132 assert(Save_trace_stream->callstack_depth < 9000); // 9998-101 plus cushion 133 } 134 Current_routine->calls.push_front(call(get(Recipe_ordinal, "sandbox"))); 135 return true; 136 } 137 138 //: Carefully update all state to exactly how it was -- including snapshots. 139 140 :(before "End Globals") 141 bool Run_profiler_stash = false; 142 map<string, recipe_ordinal> Recipe_ordinal_snapshot_stash; 143 map<recipe_ordinal, recipe> Recipe_snapshot_stash; 144 map<string, type_ordinal> Type_ordinal_snapshot_stash; 145 map<type_ordinal, type_info> Type_snapshot_stash; 146 map<recipe_ordinal, map<string, int> > Name_snapshot_stash; 147 map<string, vector<recipe_ordinal> > Recipe_variants_snapshot_stash; 148 map<string, type_tree*> Type_abbreviations_snapshot_stash; 149 vector<scenario> Scenarios_snapshot_stash; 150 set<string> Scenario_names_snapshot_stash; 151 152 :(code) 153 void run_code_begin(bool should_stash_snapshots) { 154 // stuff to undo later, in run_code_end() 155 Hide_errors = true; 156 Disable_redefine_checks = true; 157 Run_profiler_stash = Run_profiler; 158 Run_profiler = false; 159 if (should_stash_snapshots) 160 stash_snapshots(); 161 Save_trace_stream = Trace_stream; 162 Trace_stream = new trace_stream; 163 Trace_stream->collect_depth = App_depth; 164 } 165 166 void run_code_end() { 167 Hide_errors = false; 168 Disable_redefine_checks = false; 169 Run_profiler = Run_profiler_stash; 170 Run_profiler_stash = false; 171 //? ofstream fout("sandbox.log"); 172 //? fout << Trace_stream->readable_contents(""); 173 //? fout.close(); 174 delete Trace_stream; 175 Trace_stream = Save_trace_stream; 176 Save_trace_stream = NULL; 177 Save_trace_file.clear(); 178 Recipe.erase(get(Recipe_ordinal, "interactive")); // keep past sandboxes from inserting errors 179 if (!Recipe_snapshot_stash.empty()) 180 unstash_snapshots(); 181 } 182 183 // keep sync'd with save_snapshots and restore_snapshots 184 void stash_snapshots() { 185 assert(Recipe_ordinal_snapshot_stash.empty()); 186 Recipe_ordinal_snapshot_stash = Recipe_ordinal_snapshot; 187 assert(Recipe_snapshot_stash.empty()); 188 Recipe_snapshot_stash = Recipe_snapshot; 189 assert(Type_ordinal_snapshot_stash.empty()); 190 Type_ordinal_snapshot_stash = Type_ordinal_snapshot; 191 assert(Type_snapshot_stash.empty()); 192 Type_snapshot_stash = Type_snapshot; 193 assert(Name_snapshot_stash.empty()); 194 Name_snapshot_stash = Name_snapshot; 195 assert(Recipe_variants_snapshot_stash.empty()); 196 Recipe_variants_snapshot_stash = Recipe_variants_snapshot; 197 assert(Type_abbreviations_snapshot_stash.empty()); 198 Type_abbreviations_snapshot_stash = Type_abbreviations_snapshot; 199 assert(Scenarios_snapshot_stash.empty()); 200 Scenarios_snapshot_stash = Scenarios_snapshot; 201 assert(Scenario_names_snapshot_stash.empty()); 202 Scenario_names_snapshot_stash = Scenario_names_snapshot; 203 save_snapshots(); 204 } 205 void unstash_snapshots() { 206 restore_snapshots(); 207 Recipe_ordinal_snapshot = Recipe_ordinal_snapshot_stash; Recipe_ordinal_snapshot_stash.clear(); 208 Recipe_snapshot = Recipe_snapshot_stash; Recipe_snapshot_stash.clear(); 209 Type_ordinal_snapshot = Type_ordinal_snapshot_stash; Type_ordinal_snapshot_stash.clear(); 210 Type_snapshot = Type_snapshot_stash; Type_snapshot_stash.clear(); 211 Name_snapshot = Name_snapshot_stash; Name_snapshot_stash.clear(); 212 Recipe_variants_snapshot = Recipe_variants_snapshot_stash; Recipe_variants_snapshot_stash.clear(); 213 Type_abbreviations_snapshot = Type_abbreviations_snapshot_stash; Type_abbreviations_snapshot_stash.clear(); 214 Scenarios_snapshot = Scenarios_snapshot_stash; Scenarios_snapshot_stash.clear(); 215 Scenario_names_snapshot = Scenario_names_snapshot_stash; Scenario_names_snapshot_stash.clear(); 216 } 217 218 :(before "End Mu Prelude") 219 load(string( 220 "recipe interactive [\n") + // just a dummy version to initialize the Recipe_ordinal and so on 221 "]\n" + 222 "recipe sandbox [\n" + 223 "local-scope\n" + 224 //? "$print [aaa] 10/newline\n" + 225 "screen:&:screen <- new-fake-screen 30, 5\n" + 226 "routine-id:num <- start-running interactive, screen\n" + 227 "limit-time routine-id, 100000/instructions\n" + 228 "wait-for-routine routine-id\n" + 229 //? "$print [bbb] 10/newline\n" + 230 "instructions-run:num <- number-of-instructions routine-id\n" + 231 "stash instructions-run [instructions run]\n" + 232 "sandbox-state:num <- routine-state routine-id\n" + 233 "completed?:bool <- equal sandbox-state, 1/completed\n" + 234 //? "$print [completed: ] completed? 10/newline\n" + 235 "output:text <- $most-recent-products\n" + 236 //? "$print [zzz] 10/newline\n" + 237 //? "$print output\n" + 238 "errors:text <- save-errors\n" + 239 "stashes:text <- save-app-trace\n" + 240 "$cleanup-run-sandboxed\n" + 241 "return output, errors, screen, stashes, completed?\n" + 242 "]\n"); 243 244 //: adjust errors in the sandbox 245 :(before "End maybe(recipe_name) Special-cases") 246 if (recipe_name == "interactive") return ""; 247 248 :(scenario run_interactive_comments) 249 def main [ 250 1:text <- new [# ab 251 add 2, 2] 252 2:text <- run-sandboxed 1:text 253 3:@:char <- copy *2:text 254 ] 255 +mem: storing 52 in location 4 256 257 :(before "End Primitive Recipe Declarations") 258 _START_TRACKING_PRODUCTS, 259 :(before "End Primitive Recipe Numbers") 260 put(Recipe_ordinal, "$start-tracking-products", _START_TRACKING_PRODUCTS); 261 :(before "End Primitive Recipe Checks") 262 case _START_TRACKING_PRODUCTS: { 263 break; 264 } 265 :(before "End Primitive Recipe Implementations") 266 case _START_TRACKING_PRODUCTS: { 267 Track_most_recent_products = true; 268 Call_depth_to_track_most_recent_products_at = SIZE(Current_routine->calls); 269 break; 270 } 271 272 :(before "End Primitive Recipe Declarations") 273 _STOP_TRACKING_PRODUCTS, 274 :(before "End Primitive Recipe Numbers") 275 put(Recipe_ordinal, "$stop-tracking-products", _STOP_TRACKING_PRODUCTS); 276 :(before "End Primitive Recipe Checks") 277 case _STOP_TRACKING_PRODUCTS: { 278 break; 279 } 280 :(before "End Primitive Recipe Implementations") 281 case _STOP_TRACKING_PRODUCTS: { 282 Track_most_recent_products = false; 283 break; 284 } 285 286 :(before "End Primitive Recipe Declarations") 287 _MOST_RECENT_PRODUCTS, 288 :(before "End Primitive Recipe Numbers") 289 put(Recipe_ordinal, "$most-recent-products", _MOST_RECENT_PRODUCTS); 290 :(before "End Primitive Recipe Checks") 291 case _MOST_RECENT_PRODUCTS: { 292 break; 293 } 294 :(before "End Primitive Recipe Implementations") 295 case _MOST_RECENT_PRODUCTS: { 296 products.resize(1); 297 products.at(0).push_back(/*alloc id*/0); 298 products.at(0).push_back(new_mu_text(Most_recent_products)); 299 break; 300 } 301 302 :(before "End Primitive Recipe Declarations") 303 SAVE_ERRORS, 304 :(before "End Primitive Recipe Numbers") 305 put(Recipe_ordinal, "save-errors", SAVE_ERRORS); 306 :(before "End Primitive Recipe Checks") 307 case SAVE_ERRORS: { 308 break; 309 } 310 :(before "End Primitive Recipe Implementations") 311 case SAVE_ERRORS: { 312 products.resize(1); 313 products.at(0).push_back(/*alloc id*/0); 314 products.at(0).push_back(trace_error_contents()); 315 break; 316 } 317 318 :(before "End Primitive Recipe Declarations") 319 SAVE_APP_TRACE, 320 :(before "End Primitive Recipe Numbers") 321 put(Recipe_ordinal, "save-app-trace", SAVE_APP_TRACE); 322 :(before "End Primitive Recipe Checks") 323 case SAVE_APP_TRACE: { 324 break; 325 } 326 :(before "End Primitive Recipe Implementations") 327 case SAVE_APP_TRACE: { 328 products.resize(1); 329 products.at(0).push_back(/*alloc id*/0); 330 products.at(0).push_back(trace_app_contents()); 331 break; 332 } 333 334 :(before "End Primitive Recipe Declarations") 335 _CLEANUP_RUN_SANDBOXED, 336 :(before "End Primitive Recipe Numbers") 337 put(Recipe_ordinal, "$cleanup-run-sandboxed", _CLEANUP_RUN_SANDBOXED); 338 :(before "End Primitive Recipe Checks") 339 case _CLEANUP_RUN_SANDBOXED: { 340 break; 341 } 342 :(before "End Primitive Recipe Implementations") 343 case _CLEANUP_RUN_SANDBOXED: { 344 run_code_end(); 345 break; 346 } 347 348 :(scenario "run_interactive_converts_result_to_text") 349 def main [ 350 # try to interactively add 2 and 2 351 10:text <- new [add 2, 2] 352 20:text <- run-sandboxed 10:text 353 30:@:char <- copy *20:text 354 ] 355 # first letter in the output should be '4' in unicode 356 +mem: storing 52 in location 31 357 358 :(scenario "run_interactive_ignores_products_in_nested_functions") 359 def main [ 360 10:text <- new [foo] 361 20:text <- run-sandboxed 10:text 362 30:@:char <- copy *20:text 363 ] 364 def foo [ 365 40:num <- copy 1234 366 { 367 break 368 reply 5678 369 } 370 ] 371 # no product should have been tracked 372 +mem: storing 0 in location 30 373 374 :(scenario "run_interactive_ignores_products_in_previous_instructions") 375 def main [ 376 10:text <- new [ 377 add 1, 1 # generates a product 378 foo] # no products 379 20:text <- run-sandboxed 10:text 380 30:@:char <- copy *20:text 381 ] 382 def foo [ 383 40:num <- copy 1234 384 { 385 break 386 reply 5678 387 } 388 ] 389 # no product should have been tracked 390 +mem: storing 0 in location 30 391 392 :(scenario "run_interactive_remembers_products_before_final_label") 393 def main [ 394 10:text <- new [ 395 add 1, 1 # generates a product 396 +foo] # no products 397 20:text <- run-sandboxed 10:text 398 30:@:char <- copy *20:text 399 ] 400 def foo [ 401 40:num <- copy 1234 402 { 403 break 404 reply 5678 405 } 406 ] 407 # product tracked 408 +mem: storing 50 in location 31 409 410 :(scenario "run_interactive_returns_text") 411 def main [ 412 # try to interactively add 2 and 2 413 1:text <- new [ 414 x:text <- new [a] 415 y:text <- new [b] 416 z:text <- append x:text, y:text 417 ] 418 10:text <- run-sandboxed 1:text 419 #? $print 10:text 10/newline 420 20:@:char <- copy *10:text 421 ] 422 # output contains "ab" 423 +mem: storing 97 in location 21 424 +mem: storing 98 in location 22 425 426 :(scenario "run_interactive_returns_errors") 427 def main [ 428 # run a command that generates an error 429 10:text <- new [x:num <- copy 34 430 get x:num, foo:offset] 431 20:text, 30:text <- run-sandboxed 10:text 432 40:@:char <- copy *30:text 433 ] 434 # error should be "unknown element foo in container number" 435 +mem: storing 117 in location 41 436 +mem: storing 110 in location 42 437 +mem: storing 107 in location 43 438 +mem: storing 110 in location 44 439 # ... 440 441 :(scenario run_interactive_with_comment) 442 def main [ 443 # 2 instructions, with a comment after the first 444 10:text <- new [a:num <- copy 0 # abc 445 b:num <- copy 0 446 ] 447 20:text, 30:text <- run-sandboxed 10:text 448 ] 449 # no errors 450 # skip alloc id 451 +mem: storing 0 in location 30 452 +mem: storing 0 in location 31 453 454 :(after "Running One Instruction") 455 if (Track_most_recent_products && SIZE(Current_routine->calls) == Call_depth_to_track_most_recent_products_at 456 && !current_instruction().is_label 457 && current_instruction().name != "$stop-tracking-products") { 458 Most_recent_products = ""; 459 } 460 :(before "End Running One Instruction") 461 if (Track_most_recent_products && SIZE(Current_routine->calls) == Call_depth_to_track_most_recent_products_at) { 462 Most_recent_products = track_most_recent_products(current_instruction(), products); 463 //? cerr << "most recent products: " << Most_recent_products << '\n'; 464 } 465 :(code) 466 string track_most_recent_products(const instruction& instruction, const vector<vector<double> >& products) { 467 ostringstream out; 468 for (int i = 0; i < SIZE(products); ++i) { 469 // A sandbox can print a string result, but only if it is actually saved 470 // to a variable in the sandbox, because otherwise the results are 471 // reclaimed before the sandbox sees them. So you get these interactions 472 // in the sandbox: 473 // 474 // new [abc] 475 // => <address> 476 // 477 // x:text <- new [abc] 478 // => abc 479 if (i < SIZE(instruction.products)) { 480 if (is_mu_text(instruction.products.at(i))) { 481 if (SIZE(products.at(i)) != 2) continue; // weak silent check for address 482 out << read_mu_text(products.at(i).at(/*skip alloc id*/1)) << '\n'; 483 continue; 484 } 485 } 486 for (int j = 0; j < SIZE(products.at(i)); ++j) 487 out << no_scientific(products.at(i).at(j)) << ' '; 488 out << '\n'; 489 } 490 return out.str(); 491 } 492 493 :(code) 494 string strip_comments(string in) { 495 ostringstream result; 496 for (int i = 0; i < SIZE(in); ++i) { 497 if (in.at(i) != '#') { 498 result << in.at(i); 499 } 500 else { 501 while (i+1 < SIZE(in) && in.at(i+1) != '\n') 502 ++i; 503 } 504 } 505 return result.str(); 506 } 507 508 int stringified_value_of_location(int address) { 509 // convert to string 510 ostringstream out; 511 out << no_scientific(get_or_insert(Memory, address)); 512 return new_mu_text(out.str()); 513 } 514 515 int trace_error_contents() { 516 if (!Trace_stream) return 0; 517 ostringstream out; 518 for (vector<trace_line>::iterator p = Trace_stream->past_lines.begin(); p != Trace_stream->past_lines.end(); ++p) { 519 if (p->label != "error") continue; 520 out << p->contents; 521 if (*--p->contents.end() != '\n') out << '\n'; 522 } 523 string result = out.str(); 524 truncate(result); 525 if (result.empty()) return 0; 526 return new_mu_text(result); 527 } 528 529 int trace_app_contents() { 530 if (!Trace_stream) return 0; 531 ostringstream out; 532 for (vector<trace_line>::iterator p = Trace_stream->past_lines.begin(); p != Trace_stream->past_lines.end(); ++p) { 533 if (p->depth != App_depth) continue; 534 out << p->contents; 535 if (*--p->contents.end() != '\n') out << '\n'; 536 } 537 string result = out.str(); 538 if (result.empty()) return 0; 539 truncate(result); 540 return new_mu_text(result); 541 } 542 543 void truncate(string& x) { 544 if (SIZE(x) > 1024) { 545 x.erase(1024); 546 *x.rbegin() = '\n'; 547 *++x.rbegin() = '.'; 548 *++++x.rbegin() = '.'; 549 } 550 } 551 552 //: simpler version of run-sandboxed: doesn't do any running, just loads 553 //: recipes and reports errors. 554 555 :(before "End Primitive Recipe Declarations") 556 RELOAD, 557 :(before "End Primitive Recipe Numbers") 558 put(Recipe_ordinal, "reload", RELOAD); 559 :(before "End Primitive Recipe Checks") 560 case RELOAD: { 561 if (SIZE(inst.ingredients) != 1) { 562 raise << maybe(get(Recipe, r).name) << "'reload' requires exactly one ingredient, but got '" << inst.original_string << "'\n" << end(); 563 break; 564 } 565 if (!is_mu_text(inst.ingredients.at(0))) { 566 raise << maybe(get(Recipe, r).name) << "first ingredient of 'reload' should be a string, but got '" << inst.ingredients.at(0).original_string << "'\n" << end(); 567 break; 568 } 569 break; 570 } 571 :(before "End Primitive Recipe Implementations") 572 case RELOAD: { 573 restore_non_recipe_snapshots(); 574 string code = read_mu_text(ingredients.at(0).at(/*skip alloc id*/1)); 575 run_code_begin(/*should_stash_snapshots*/false); 576 routine* save_current_routine = Current_routine; 577 Current_routine = NULL; 578 Sandbox_mode = true; 579 vector<recipe_ordinal> recipes_reloaded = load(code); 580 transform_all(); 581 Trace_stream->newline(); // flush trace 582 Sandbox_mode = false; 583 Current_routine = save_current_routine; 584 products.resize(1); 585 products.at(0).push_back(/*alloc id*/0); 586 products.at(0).push_back(trace_error_contents()); 587 run_code_end(); // wait until we're done with the trace contents 588 break; 589 } 590 591 :(scenario reload_loads_function_definitions) 592 def main [ 593 local-scope 594 x:text <- new [recipe foo [ 595 1:num/raw <- copy 34 596 ]] 597 reload x 598 run-sandboxed [foo] 599 2:num/raw <- copy 1:num/raw 600 ] 601 +mem: storing 34 in location 2 602 603 :(scenario reload_continues_past_error) 604 def main [ 605 local-scope 606 x:text <- new [recipe foo [ 607 get 1234:num, foo:offset 608 ]] 609 reload x 610 1:num/raw <- copy 34 611 ] 612 +mem: storing 34 in location 1 613 614 :(scenario reload_can_repeatedly_load_container_definitions) 615 # define a container and try to create it (merge requires knowing container size) 616 def main [ 617 local-scope 618 x:text <- new [ 619 container foo [ 620 x:num 621 y:num 622 ] 623 recipe bar [ 624 local-scope 625 x:foo <- merge 34, 35 626 ] 627 ] 628 # save warning addresses in locations of type 'number' to avoid spurious changes to them due to 'abandon' 629 10:text/raw <- reload x 630 20:text/raw <- reload x 631 ] 632 # no errors on either load 633 +mem: storing 0 in location 10 634 +mem: storing 0 in location 11 635 +mem: storing 0 in location 20 636 +mem: storing 0 in location 21