From d35595fd7d8244c51cfbaf357f1d4214ca7bfeda Mon Sep 17 00:00:00 2001 From: "Kartik K. Agaram" Date: Mon, 20 Mar 2017 18:03:21 -0700 Subject: 3805 --- 101run_sandboxed.cc | 48 ++++- html/101run_sandboxed.cc.html | 450 +++++++++++++++++++++++------------------- 2 files changed, 291 insertions(+), 207 deletions(-) diff --git a/101run_sandboxed.cc b/101run_sandboxed.cc index b048b321..1904942f 100644 --- a/101run_sandboxed.cc +++ b/101run_sandboxed.cc @@ -350,6 +350,42 @@ def foo [ # no product should have been tracked +mem: storing 0 in location 10 +:(scenario "run_interactive_ignores_products_in_previous_instructions") +def main [ + 1:text <- new [ + add 1, 1 # generates a product + foo] # no products + 2:text <- run-sandboxed 1:text + 10:@:char <- copy *2:text +] +def foo [ + 20:num <- copy 1234 + { + break + reply 5678 + } +] +# no product should have been tracked ++mem: storing 0 in location 10 + +:(scenario "run_interactive_remembers_products_before_final_label") +def main [ + 1:text <- new [ + add 1, 1 # generates a product + +foo] # no products + 2:text <- run-sandboxed 1:text + 10:@:char <- copy *2:text +] +def foo [ + 20:num <- copy 1234 + { + break + reply 5678 + } +] +# product tracked ++mem: storing 50 in location 11 + :(scenario "run_interactive_returns_text") def main [ # try to interactively add 2 and 2 @@ -391,12 +427,18 @@ b:num <- copy 0 # no errors +mem: storing 0 in location 3 +:(after "Running One Instruction") +if (Track_most_recent_products && SIZE(Current_routine->calls) == Call_depth_to_track_most_recent_products_at + && !current_instruction().is_label + && current_instruction().name != "$stop-tracking-products") { + Most_recent_products = ""; +} :(before "End Running One Instruction") if (Track_most_recent_products && SIZE(Current_routine->calls) == Call_depth_to_track_most_recent_products_at) { - track_most_recent_products(current_instruction(), products); + Most_recent_products = track_most_recent_products(current_instruction(), products); } :(code) -void track_most_recent_products(const instruction& instruction, const vector >& products) { +string track_most_recent_products(const instruction& instruction, const vector >& products) { ostringstream out; for (int i = 0; i < SIZE(products); ++i) { // A sandbox can print a string result, but only if it is actually saved @@ -420,7 +462,7 @@ void track_most_recent_products(const instruction& instruction, const vector 56 if (!new_code_pushed_to_stack) { 57 ¦ products.resize(5); 58 ¦ products.at(0).push_back(0); - 59 ¦ products.at(1).push_back(trace_error_contents()); + 59 ¦ products.at(1).push_back(trace_error_contents()); 60 ¦ products.at(2).push_back(0); - 61 ¦ products.at(3).push_back(trace_app_contents()); + 61 ¦ products.at(3).push_back(trace_app_contents()); 62 ¦ products.at(4).push_back(1); // completed 63 ¦ run_code_end(); 64 ¦ break; // done with this instruction @@ -161,7 +161,7 @@ if ('onhashchange' in window) { 97 ¦ for (int i = 1; i < Reserved_for_tests; ++i) 98 ¦ ¦ Memory.erase(i); 99 } -100 string command = trim(strip_comments(read_mu_text(address))); +100 string command = trim(strip_comments(read_mu_text(address))); 101 Name[get(Recipe_ordinal, "interactive")].clear(); 102 run_code_begin(/*should_stash_snapshots*/true); 103 if (command.empty()) return false; @@ -355,7 +355,7 @@ if ('onhashchange' in window) { 291 :(before "End Primitive Recipe Implementations") 292 case SAVE_ERRORS: { 293 products.resize(1); -294 products.at(0).push_back(trace_error_contents()); +294 products.at(0).push_back(trace_error_contents()); 295 break; 296 } 297 @@ -370,7 +370,7 @@ if ('onhashchange' in window) { 306 :(before "End Primitive Recipe Implementations") 307 case SAVE_APP_TRACE: { 308 products.resize(1); -309 products.at(0).push_back(trace_app_contents()); +309 products.at(0).push_back(trace_app_contents()); 310 break; 311 } 312 @@ -414,208 +414,250 @@ if ('onhashchange' in window) { 350 # no product should have been tracked 351 +mem: storing 0 in location 10 352 -353 :(scenario "run_interactive_returns_text") +353 :(scenario "run_interactive_ignores_products_in_previous_instructions") 354 def main [ -355 # try to interactively add 2 and 2 -356 1:text <- new [ -357 ¦ x:text <- new [a] -358 ¦ y:text <- new [b] -359 ¦ z:text <- append x:text, y:text -360 ] -361 2:text <- run-sandboxed 1:text -362 10:@:char <- copy *2:text -363 ] -364 # output contains "ab" -365 +mem: storing 97 in location 11 -366 +mem: storing 98 in location 12 -367 -368 :(scenario "run_interactive_returns_errors") -369 def main [ -370 # run a command that generates an error -371 1:text <- new [x:num <- copy 34 -372 get x:num, foo:offset] -373 2:text, 3:text <- run-sandboxed 1:text -374 10:@:char <- copy *3:text -375 ] -376 # error should be "unknown element foo in container number" -377 +mem: storing 117 in location 11 -378 +mem: storing 110 in location 12 -379 +mem: storing 107 in location 13 -380 +mem: storing 110 in location 14 -381 # ... -382 -383 :(scenario run_interactive_with_comment) -384 def main [ -385 # 2 instructions, with a comment after the first -386 1:&:@:num <- new [a:num <- copy 0 # abc -387 b:num <- copy 0 -388 ] -389 2:text, 3:text <- run-sandboxed 1:text -390 ] -391 # no errors -392 +mem: storing 0 in location 3 -393 -394 :(before "End Running One Instruction") -395 if (Track_most_recent_products && SIZE(Current_routine->calls) == Call_depth_to_track_most_recent_products_at) { -396 track_most_recent_products(current_instruction(), products); -397 } -398 :(code) -399 void track_most_recent_products(const instruction& instruction, const vector<vector<double> >& products) { -400 ostringstream out; -401 for (int i = 0; i < SIZE(products); ++i) { -402 ¦ // A sandbox can print a string result, but only if it is actually saved -403 ¦ // to a variable in the sandbox, because otherwise the results are -404 ¦ // reclaimed before the sandbox sees them. So you get these interactions -405 ¦ // in the sandbox: -406 ¦ // -407 ¦ // new [abc] -408 ¦ // => <address> -409 ¦ // -410 ¦ // x:text <- new [abc] -411 ¦ // => abc -412 ¦ if (i < SIZE(instruction.products)) { -413 ¦ ¦ if (is_mu_text(instruction.products.at(i))) { -414 ¦ ¦ ¦ if (!scalar(products.at(i))) continue; // error handled elsewhere -415 ¦ ¦ ¦ out << read_mu_text(products.at(i).at(0)) << '\n'; -416 ¦ ¦ ¦ continue; -417 ¦ ¦ } -418 ¦ } -419 ¦ for (int j = 0; j < SIZE(products.at(i)); ++j) -420 ¦ ¦ out << no_scientific(products.at(i).at(j)) << ' '; -421 ¦ out << '\n'; -422 } -423 Most_recent_products = out.str(); -424 } -425 -426 :(code) -427 string strip_comments(string in) { -428 ostringstream result; -429 for (int i = 0; i < SIZE(in); ++i) { -430 ¦ if (in.at(i) != '#') { -431 ¦ ¦ result << in.at(i); -432 ¦ } -433 ¦ else { -434 ¦ ¦ while (i+1 < SIZE(in) && in.at(i+1) != '\n') -435 ¦ ¦ ¦ ++i; -436 ¦ } -437 } -438 return result.str(); +355 1:text <- new [ +356 ¦ add 1, 1 # generates a product +357 ¦ foo] # no products +358 2:text <- run-sandboxed 1:text +359 10:@:char <- copy *2:text +360 ] +361 def foo [ +362 20:num <- copy 1234 +363 { +364 ¦ break +365 ¦ reply 5678 +366 } +367 ] +368 # no product should have been tracked +369 +mem: storing 0 in location 10 +370 +371 :(scenario "run_interactive_remembers_products_before_final_label") +372 def main [ +373 1:text <- new [ +374 ¦ add 1, 1 # generates a product +375 ¦ +foo] # no products +376 2:text <- run-sandboxed 1:text +377 10:@:char <- copy *2:text +378 ] +379 def foo [ +380 20:num <- copy 1234 +381 { +382 ¦ break +383 ¦ reply 5678 +384 } +385 ] +386 # product tracked +387 +mem: storing 50 in location 11 +388 +389 :(scenario "run_interactive_returns_text") +390 def main [ +391 # try to interactively add 2 and 2 +392 1:text <- new [ +393 ¦ x:text <- new [a] +394 ¦ y:text <- new [b] +395 ¦ z:text <- append x:text, y:text +396 ] +397 2:text <- run-sandboxed 1:text +398 10:@:char <- copy *2:text +399 ] +400 # output contains "ab" +401 +mem: storing 97 in location 11 +402 +mem: storing 98 in location 12 +403 +404 :(scenario "run_interactive_returns_errors") +405 def main [ +406 # run a command that generates an error +407 1:text <- new [x:num <- copy 34 +408 get x:num, foo:offset] +409 2:text, 3:text <- run-sandboxed 1:text +410 10:@:char <- copy *3:text +411 ] +412 # error should be "unknown element foo in container number" +413 +mem: storing 117 in location 11 +414 +mem: storing 110 in location 12 +415 +mem: storing 107 in location 13 +416 +mem: storing 110 in location 14 +417 # ... +418 +419 :(scenario run_interactive_with_comment) +420 def main [ +421 # 2 instructions, with a comment after the first +422 1:&:@:num <- new [a:num <- copy 0 # abc +423 b:num <- copy 0 +424 ] +425 2:text, 3:text <- run-sandboxed 1:text +426 ] +427 # no errors +428 +mem: storing 0 in location 3 +429 +430 :(after "Running One Instruction") +431 if (Track_most_recent_products && SIZE(Current_routine->calls) == Call_depth_to_track_most_recent_products_at +432 ¦ && !current_instruction().is_label +433 ¦ && current_instruction().name != "$stop-tracking-products") { +434 Most_recent_products = ""; +435 } +436 :(before "End Running One Instruction") +437 if (Track_most_recent_products && SIZE(Current_routine->calls) == Call_depth_to_track_most_recent_products_at) { +438 Most_recent_products = track_most_recent_products(current_instruction(), products); 439 } -440 -441 int stringified_value_of_location(int address) { -442 // convert to string -443 ostringstream out; -444 out << no_scientific(get_or_insert(Memory, address)); -445 return new_mu_text(out.str()); -446 } -447 -448 int trace_error_contents() { -449 if (!Trace_stream) return 0; -450 ostringstream out; -451 for (vector<trace_line>::iterator p = Trace_stream->past_lines.begin(); p != Trace_stream->past_lines.end(); ++p) { -452 ¦ if (p->label != "error") continue; -453 ¦ out << p->contents; -454 ¦ if (*--p->contents.end() != '\n') out << '\n'; -455 } -456 string result = out.str(); -457 truncate(result); -458 if (result.empty()) return 0; -459 return new_mu_text(result); -460 } -461 -462 int trace_app_contents() { -463 if (!Trace_stream) return 0; -464 ostringstream out; -465 for (vector<trace_line>::iterator p = Trace_stream->past_lines.begin(); p != Trace_stream->past_lines.end(); ++p) { -466 ¦ if (p->depth != App_depth) continue; -467 ¦ out << p->contents; -468 ¦ if (*--p->contents.end() != '\n') out << '\n'; -469 } -470 string result = out.str(); -471 if (result.empty()) return 0; -472 truncate(result); -473 return new_mu_text(result); -474 } -475 -476 void truncate(string& x) { -477 if (SIZE(x) > 1024) { -478 ¦ x.erase(1024); -479 ¦ *x.rbegin() = '\n'; -480 ¦ *++x.rbegin() = '.'; -481 ¦ *++++x.rbegin() = '.'; -482 } -483 } -484 -485 //: simpler version of run-sandboxed: doesn't do any running, just loads -486 //: recipes and reports errors. -487 -488 :(before "End Primitive Recipe Declarations") -489 RELOAD, -490 :(before "End Primitive Recipe Numbers") -491 put(Recipe_ordinal, "reload", RELOAD); -492 :(before "End Primitive Recipe Checks") -493 case RELOAD: { -494 if (SIZE(inst.ingredients) != 1) { -495 ¦ raise << maybe(get(Recipe, r).name) << "'reload' requires exactly one ingredient, but got '" << inst.original_string << "'\n" << end(); -496 ¦ break; +440 :(code) +441 string track_most_recent_products(const instruction& instruction, const vector<vector<double> >& products) { +442 ostringstream out; +443 for (int i = 0; i < SIZE(products); ++i) { +444 ¦ // A sandbox can print a string result, but only if it is actually saved +445 ¦ // to a variable in the sandbox, because otherwise the results are +446 ¦ // reclaimed before the sandbox sees them. So you get these interactions +447 ¦ // in the sandbox: +448 ¦ // +449 ¦ // new [abc] +450 ¦ // => <address> +451 ¦ // +452 ¦ // x:text <- new [abc] +453 ¦ // => abc +454 ¦ if (i < SIZE(instruction.products)) { +455 ¦ ¦ if (is_mu_text(instruction.products.at(i))) { +456 ¦ ¦ ¦ if (!scalar(products.at(i))) continue; // error handled elsewhere +457 ¦ ¦ ¦ out << read_mu_text(products.at(i).at(0)) << '\n'; +458 ¦ ¦ ¦ continue; +459 ¦ ¦ } +460 ¦ } +461 ¦ for (int j = 0; j < SIZE(products.at(i)); ++j) +462 ¦ ¦ out << no_scientific(products.at(i).at(j)) << ' '; +463 ¦ out << '\n'; +464 } +465 return out.str(); +466 } +467 +468 :(code) +469 string strip_comments(string in) { +470 ostringstream result; +471 for (int i = 0; i < SIZE(in); ++i) { +472 ¦ if (in.at(i) != '#') { +473 ¦ ¦ result << in.at(i); +474 ¦ } +475 ¦ else { +476 ¦ ¦ while (i+1 < SIZE(in) && in.at(i+1) != '\n') +477 ¦ ¦ ¦ ++i; +478 ¦ } +479 } +480 return result.str(); +481 } +482 +483 int stringified_value_of_location(int address) { +484 // convert to string +485 ostringstream out; +486 out << no_scientific(get_or_insert(Memory, address)); +487 return new_mu_text(out.str()); +488 } +489 +490 int trace_error_contents() { +491 if (!Trace_stream) return 0; +492 ostringstream out; +493 for (vector<trace_line>::iterator p = Trace_stream->past_lines.begin(); p != Trace_stream->past_lines.end(); ++p) { +494 ¦ if (p->label != "error") continue; +495 ¦ out << p->contents; +496 ¦ if (*--p->contents.end() != '\n') out << '\n'; 497 } -498 if (!is_mu_text(inst.ingredients.at(0))) { -499 ¦ raise << maybe(get(Recipe, r).name) << "first ingredient of 'reload' should be a string, but got '" << inst.ingredients.at(0).original_string << "'\n" << end(); -500 ¦ break; -501 } -502 break; -503 } -504 :(before "End Primitive Recipe Implementations") -505 case RELOAD: { -506 restore_non_recipe_snapshots(); -507 string code = read_mu_text(ingredients.at(0).at(0)); -508 run_code_begin(/*should_stash_snapshots*/false); -509 routine* save_current_routine = Current_routine; -510 Current_routine = NULL; -511 Sandbox_mode = true; -512 vector<recipe_ordinal> recipes_reloaded = load(code); -513 transform_all(); -514 Trace_stream->newline(); // flush trace -515 Sandbox_mode = false; -516 Current_routine = save_current_routine; -517 products.resize(1); -518 products.at(0).push_back(trace_error_contents()); -519 run_code_end(); // wait until we're done with the trace contents -520 break; -521 } -522 -523 :(scenario reload_continues_past_error) -524 def main [ -525 local-scope -526 x:text <- new [recipe foo [ -527 get 1234:num, foo:offset -528 ]] -529 reload x -530 1:num/raw <- copy 34 -531 ] -532 +mem: storing 34 in location 1 -533 -534 :(scenario reload_can_repeatedly_load_container_definitions) -535 # define a container and try to create it (merge requires knowing container size) -536 def main [ -537 local-scope -538 x:text <- new [ -539 ¦ container foo [ -540 ¦ ¦ x:num -541 ¦ ¦ y:num -542 ¦ ] -543 ¦ recipe bar [ -544 ¦ ¦ local-scope -545 ¦ ¦ x:foo <- merge 34, 35 -546 ¦ ] -547 ] -548 # save warning addresses in locations of type 'number' to avoid spurious changes to them due to 'abandon' -549 1:num/raw <- reload x -550 2:num/raw <- reload x -551 ] -552 # no errors on either load -553 +mem: storing 0 in location 1 -554 +mem: storing 0 in location 2 +498 string result = out.str(); +499 truncate(result); +500 if (result.empty()) return 0; +501 return new_mu_text(result); +502 } +503 +504 int trace_app_contents() { +505 if (!Trace_stream) return 0; +506 ostringstream out; +507 for (vector<trace_line>::iterator p = Trace_stream->past_lines.begin(); p != Trace_stream->past_lines.end(); ++p) { +508 ¦ if (p->depth != App_depth) continue; +509 ¦ out << p->contents; +510 ¦ if (*--p->contents.end() != '\n') out << '\n'; +511 } +512 string result = out.str(); +513 if (result.empty()) return 0; +514 truncate(result); +515 return new_mu_text(result); +516 } +517 +518 void truncate(string& x) { +519 if (SIZE(x) > 1024) { +520 ¦ x.erase(1024); +521 ¦ *x.rbegin() = '\n'; +522 ¦ *++x.rbegin() = '.'; +523 ¦ *++++x.rbegin() = '.'; +524 } +525 } +526 +527 //: simpler version of run-sandboxed: doesn't do any running, just loads +528 //: recipes and reports errors. +529 +530 :(before "End Primitive Recipe Declarations") +531 RELOAD, +532 :(before "End Primitive Recipe Numbers") +533 put(Recipe_ordinal, "reload", RELOAD); +534 :(before "End Primitive Recipe Checks") +535 case RELOAD: { +536 if (SIZE(inst.ingredients) != 1) { +537 ¦ raise << maybe(get(Recipe, r).name) << "'reload' requires exactly one ingredient, but got '" << inst.original_string << "'\n" << end(); +538 ¦ break; +539 } +540 if (!is_mu_text(inst.ingredients.at(0))) { +541 ¦ raise << maybe(get(Recipe, r).name) << "first ingredient of 'reload' should be a string, but got '" << inst.ingredients.at(0).original_string << "'\n" << end(); +542 ¦ break; +543 } +544 break; +545 } +546 :(before "End Primitive Recipe Implementations") +547 case RELOAD: { +548 restore_non_recipe_snapshots(); +549 string code = read_mu_text(ingredients.at(0).at(0)); +550 run_code_begin(/*should_stash_snapshots*/false); +551 routine* save_current_routine = Current_routine; +552 Current_routine = NULL; +553 Sandbox_mode = true; +554 vector<recipe_ordinal> recipes_reloaded = load(code); +555 transform_all(); +556 Trace_stream->newline(); // flush trace +557 Sandbox_mode = false; +558 Current_routine = save_current_routine; +559 products.resize(1); +560 products.at(0).push_back(trace_error_contents()); +561 run_code_end(); // wait until we're done with the trace contents +562 break; +563 } +564 +565 :(scenario reload_continues_past_error) +566 def main [ +567 local-scope +568 x:text <- new [recipe foo [ +569 get 1234:num, foo:offset +570 ]] +571 reload x +572 1:num/raw <- copy 34 +573 ] +574 +mem: storing 34 in location 1 +575 +576 :(scenario reload_can_repeatedly_load_container_definitions) +577 # define a container and try to create it (merge requires knowing container size) +578 def main [ +579 local-scope +580 x:text <- new [ +581 ¦ container foo [ +582 ¦ ¦ x:num +583 ¦ ¦ y:num +584 ¦ ] +585 ¦ recipe bar [ +586 ¦ ¦ local-scope +587 ¦ ¦ x:foo <- merge 34, 35 +588 ¦ ] +589 ] +590 # save warning addresses in locations of type 'number' to avoid spurious changes to them due to 'abandon' +591 1:num/raw <- reload x +592 2:num/raw <- reload x +593 ] +594 # no errors on either load +595 +mem: storing 0 in location 1 +596 +mem: storing 0 in location 2 -- cgit 1.4.1-2-gfad0