From 5fe060d582d4a82444243a28b18085c971a85628 Mon Sep 17 00:00:00 2001 From: Kartik Agaram Date: Fri, 27 Jul 2018 17:07:52 -0700 Subject: 4447 --- html/101run_sandboxed.cc.html | 1203 +++++++++++++++++++++-------------------- 1 file changed, 619 insertions(+), 584 deletions(-) (limited to 'html/101run_sandboxed.cc.html') diff --git a/html/101run_sandboxed.cc.html b/html/101run_sandboxed.cc.html index 59f27f05..9cd09613 100644 --- a/html/101run_sandboxed.cc.html +++ b/html/101run_sandboxed.cc.html @@ -17,15 +17,15 @@ a:hover { text-decoration: underline; } * { font-size: 12pt; font-size: 1em; } .Constant { color: #00a0a0; } .Special { color: #c00000; } -.CommentedCode { color: #6c6c6c; } .muRecipe { color: #ff8700; } .Comment { color: #9090ff; } .Comment a { color:#0000ee; text-decoration:underline; } .Delimiter { color: #800080; } .LineNr { color: #444444; } -.Identifier { color: #c0a020; } +.CommentedCode { color: #6c6c6c; } .Normal { color: #aaaaaa; background-color: #080808; padding-bottom: 1px; } .traceContains { color: #008000; } +.Identifier { color: #c0a020; } .cSpecial { color: #008000; } --> @@ -66,602 +66,637 @@ if ('onhashchange' in window) { 3 4 :(scenario run_interactive_code) 5 def main [ - 6 1:num <- copy 0 - 7 2:text <- new [1:num/raw <- copy 34] - 8 run-sandboxed 2:text - 9 3:num <- copy 1:num - 10 ] - 11 +mem: storing 34 in location 3 - 12 - 13 :(scenario run_interactive_empty) - 14 def main [ - 15 1:text <- copy 0/unsafe - 16 2:text <- run-sandboxed 1:text - 17 ] - 18 # result is null - 19 +mem: storing 0 in location 2 - 20 - 21 //: As the name suggests, 'run-sandboxed' will prevent certain operations that - 22 //: regular Mu code can perform. - 23 :(before "End Globals") - 24 bool Sandbox_mode = false; - 25 //: for starters, users can't override 'main' when the environment is running - 26 :(before "End Load Recipe Name") - 27 if (Sandbox_mode && result.name == "main") { - 28 slurp_balanced_bracket(in); - 29 return -1; - 30 } - 31 - 32 //: run code in 'interactive mode', i.e. with errors off and return: - 33 //: stringified output in case we want to print it to screen - 34 //: any errors encountered - 35 //: simulated screen any prints went to - 36 //: any 'app' layer traces generated - 37 :(before "End Primitive Recipe Declarations") - 38 RUN_SANDBOXED, - 39 :(before "End Primitive Recipe Numbers") - 40 put(Recipe_ordinal, "run-sandboxed", RUN_SANDBOXED); - 41 :(before "End Primitive Recipe Checks") - 42 case RUN_SANDBOXED: { - 43 if (SIZE(inst.ingredients) != 1) { - 44 raise << maybe(get(Recipe, r).name) << "'run-sandboxed' requires exactly one ingredient, but got '" << inst.original_string << "'\n" << end(); - 45 break; - 46 } - 47 if (!is_mu_text(inst.ingredients.at(0))) { - 48 raise << maybe(get(Recipe, r).name) << "first ingredient of 'run-sandboxed' should be a string, but got '" << to_string(inst.ingredients.at(0)) << "'\n" << end(); - 49 break; - 50 } - 51 break; - 52 } - 53 :(before "End Primitive Recipe Implementations") - 54 case RUN_SANDBOXED: { - 55 bool new_code_pushed_to_stack = run_interactive(ingredients.at(0).at(0)); - 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()); - 60 products.at(2).push_back(0); - 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 - 65 } - 66 else { - 67 continue; // not done with caller; don't increment current_step_index() - 68 } - 69 } - 70 - 71 //: To show results in the sandbox Mu uses a hack: it saves the products - 72 //: returned by each instruction while Track_most_recent_products is true, and - 73 //: keeps the most recent such result around so that it can be returned as the - 74 //: result of a sandbox. - 75 - 76 :(before "End Globals") - 77 bool Track_most_recent_products = false; - 78 int Call_depth_to_track_most_recent_products_at = 0; - 79 string Most_recent_products; - 80 :(before "End Reset") - 81 Track_most_recent_products = false; - 82 Call_depth_to_track_most_recent_products_at = 0; - 83 Most_recent_products = ""; - 84 - 85 :(before "End Globals") - 86 trace_stream* Save_trace_stream = NULL; - 87 string Save_trace_file; - 88 :(code) - 89 // reads a string, tries to call it as code (treating it as a test), saving - 90 // all errors. - 91 // returns true if successfully called (no errors found during load and transform) - 92 bool run_interactive(int address) { - 93 assert(contains_key(Recipe_ordinal, "interactive") && get(Recipe_ordinal, "interactive") != 0); - 94 // try to sandbox the run as best you can - 95 // todo: test this - 96 if (!Current_scenario) { - 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))); -101 Name[get(Recipe_ordinal, "interactive")].clear(); -102 run_code_begin(/*should_stash_snapshots*/true); -103 if (command.empty()) return false; -104 // don't kill the current routine on parse errors -105 routine* save_current_routine = Current_routine; -106 Current_routine = NULL; -107 // call run(string) but without the scheduling -108 load(string("recipe! interactive [\n") + -109 "local-scope\n" + -110 "screen:&:screen <- next-ingredient\n" + -111 "$start-tracking-products\n" + -112 command + "\n" + -113 "$stop-tracking-products\n" + -114 "return screen\n" + -115 "]\n"); -116 transform_all(); -117 Current_routine = save_current_routine; -118 if (trace_count("error") > 0) return false; -119 // now call 'sandbox' which will run 'interactive' in a separate routine, -120 // and wait for it -121 if (Save_trace_stream) { -122 ++Save_trace_stream->callstack_depth; -123 trace(9999, "trace") << "run-sandboxed: incrementing callstack depth to " << Save_trace_stream->callstack_depth << end(); -124 assert(Save_trace_stream->callstack_depth < 9000); // 9998-101 plus cushion -125 } -126 Current_routine->calls.push_front(call(get(Recipe_ordinal, "sandbox"))); -127 return true; -128 } -129 -130 //: Carefully update all state to exactly how it was -- including snapshots. -131 -132 :(before "End Globals") -133 bool Run_profiler_stash = false; -134 map<string, recipe_ordinal> Recipe_ordinal_snapshot_stash; -135 map<recipe_ordinal, recipe> Recipe_snapshot_stash; -136 map<string, type_ordinal> Type_ordinal_snapshot_stash; -137 map<type_ordinal, type_info> Type_snapshot_stash; -138 map<recipe_ordinal, map<string, int> > Name_snapshot_stash; -139 map<string, vector<recipe_ordinal> > Recipe_variants_snapshot_stash; -140 map<string, type_tree*> Type_abbreviations_snapshot_stash; -141 vector<scenario> Scenarios_snapshot_stash; -142 set<string> Scenario_names_snapshot_stash; -143 -144 :(code) -145 void run_code_begin(bool should_stash_snapshots) { -146 // stuff to undo later, in run_code_end() -147 Hide_errors = true; -148 Disable_redefine_checks = true; -149 Run_profiler_stash = Run_profiler; -150 Run_profiler = false; -151 if (should_stash_snapshots) -152 stash_snapshots(); -153 Save_trace_stream = Trace_stream; -154 Trace_stream = new trace_stream; -155 Trace_stream->collect_depth = App_depth; -156 } -157 -158 void run_code_end() { -159 Hide_errors = false; -160 Disable_redefine_checks = false; -161 Run_profiler = Run_profiler_stash; -162 Run_profiler_stash = false; -163 //? ofstream fout("sandbox.log"); -164 //? fout << Trace_stream->readable_contents(""); -165 //? fout.close(); -166 delete Trace_stream; -167 Trace_stream = Save_trace_stream; -168 Save_trace_stream = NULL; -169 Save_trace_file.clear(); -170 Recipe.erase(get(Recipe_ordinal, "interactive")); // keep past sandboxes from inserting errors -171 if (!Recipe_snapshot_stash.empty()) -172 unstash_snapshots(); -173 } -174 -175 // keep sync'd with save_snapshots and restore_snapshots -176 void stash_snapshots() { -177 assert(Recipe_ordinal_snapshot_stash.empty()); -178 Recipe_ordinal_snapshot_stash = Recipe_ordinal_snapshot; -179 assert(Recipe_snapshot_stash.empty()); -180 Recipe_snapshot_stash = Recipe_snapshot; -181 assert(Type_ordinal_snapshot_stash.empty()); -182 Type_ordinal_snapshot_stash = Type_ordinal_snapshot; -183 assert(Type_snapshot_stash.empty()); -184 Type_snapshot_stash = Type_snapshot; -185 assert(Name_snapshot_stash.empty()); -186 Name_snapshot_stash = Name_snapshot; -187 assert(Recipe_variants_snapshot_stash.empty()); -188 Recipe_variants_snapshot_stash = Recipe_variants_snapshot; -189 assert(Type_abbreviations_snapshot_stash.empty()); -190 Type_abbreviations_snapshot_stash = Type_abbreviations_snapshot; -191 assert(Scenarios_snapshot_stash.empty()); -192 Scenarios_snapshot_stash = Scenarios_snapshot; -193 assert(Scenario_names_snapshot_stash.empty()); -194 Scenario_names_snapshot_stash = Scenario_names_snapshot; -195 save_snapshots(); -196 } -197 void unstash_snapshots() { -198 restore_snapshots(); -199 Recipe_ordinal_snapshot = Recipe_ordinal_snapshot_stash; Recipe_ordinal_snapshot_stash.clear(); -200 Recipe_snapshot = Recipe_snapshot_stash; Recipe_snapshot_stash.clear(); -201 Type_ordinal_snapshot = Type_ordinal_snapshot_stash; Type_ordinal_snapshot_stash.clear(); -202 Type_snapshot = Type_snapshot_stash; Type_snapshot_stash.clear(); -203 Name_snapshot = Name_snapshot_stash; Name_snapshot_stash.clear(); -204 Recipe_variants_snapshot = Recipe_variants_snapshot_stash; Recipe_variants_snapshot_stash.clear(); -205 Type_abbreviations_snapshot = Type_abbreviations_snapshot_stash; Type_abbreviations_snapshot_stash.clear(); -206 Scenarios_snapshot = Scenarios_snapshot_stash; Scenarios_snapshot_stash.clear(); -207 Scenario_names_snapshot = Scenario_names_snapshot_stash; Scenario_names_snapshot_stash.clear(); -208 } -209 -210 :(before "End Load Recipes") -211 load(string( -212 "recipe interactive [\n") + // just a dummy version to initialize the Recipe_ordinal and so on -213 "]\n" + -214 "recipe sandbox [\n" + -215 "local-scope\n" + -216 "screen:&:screen <- new-fake-screen 30, 5\n" + -217 "routine-id:num <- start-running interactive, screen\n" + -218 "limit-time routine-id, 100000/instructions\n" + -219 "wait-for-routine routine-id\n" + -220 "instructions-run:num <- number-of-instructions routine-id\n" + -221 "stash instructions-run [instructions run]\n" + -222 "sandbox-state:num <- routine-state routine-id\n" + -223 "completed?:bool <- equal sandbox-state, 1/completed\n" + -224 "output:text <- $most-recent-products\n" + -225 "errors:text <- save-errors\n" + -226 "stashes:text <- save-app-trace\n" + -227 "$cleanup-run-sandboxed\n" + -228 "return output, errors, screen, stashes, completed?\n" + -229 "]\n"); -230 -231 //: adjust errors in the sandbox -232 :(before "End maybe(recipe_name) Special-cases") -233 if (recipe_name == "interactive") return ""; -234 -235 :(scenario run_interactive_comments) -236 def main [ -237 1:text <- new [# ab -238 add 2, 2] -239 2:text <- run-sandboxed 1:text -240 3:@:char <- copy *2:text -241 ] -242 +mem: storing 52 in location 4 + 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 //: As the name suggests, 'run-sandboxed' will prevent certain operations that + 24 //: regular Mu code can perform. + 25 :(before "End Globals") + 26 bool Sandbox_mode = false; + 27 //: for starters, users can't override 'main' when the environment is running + 28 :(before "End Load Recipe Name") + 29 if (Sandbox_mode && result.name == "main") { + 30 slurp_balanced_bracket(in); + 31 return -1; + 32 } + 33 + 34 //: run code in 'interactive mode', i.e. with errors off and return: + 35 //: stringified output in case we want to print it to screen + 36 //: any errors encountered + 37 //: simulated screen any prints went to + 38 //: any 'app' layer traces generated + 39 :(before "End Primitive Recipe Declarations") + 40 RUN_SANDBOXED, + 41 :(before "End Primitive Recipe Numbers") + 42 put(Recipe_ordinal, "run-sandboxed", RUN_SANDBOXED); + 43 :(before "End Primitive Recipe Checks") + 44 case RUN_SANDBOXED: { + 45 if (SIZE(inst.ingredients) != 1) { + 46 raise << maybe(get(Recipe, r).name) << "'run-sandboxed' requires exactly one ingredient, but got '" << inst.original_string << "'\n" << end(); + 47 break; + 48 } + 49 if (!is_mu_text(inst.ingredients.at(0))) { + 50 raise << maybe(get(Recipe, r).name) << "first ingredient of 'run-sandboxed' should be a string, but got '" << to_string(inst.ingredients.at(0)) << "'\n" << end(); + 51 break; + 52 } + 53 break; + 54 } + 55 :(before "End Primitive Recipe Implementations") + 56 case RUN_SANDBOXED: { + 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 :(before "End Primitive Recipe Declarations") -245 _START_TRACKING_PRODUCTS, -246 :(before "End Primitive Recipe Numbers") -247 put(Recipe_ordinal, "$start-tracking-products", _START_TRACKING_PRODUCTS); -248 :(before "End Primitive Recipe Checks") -249 case _START_TRACKING_PRODUCTS: { -250 break; -251 } -252 :(before "End Primitive Recipe Implementations") -253 case _START_TRACKING_PRODUCTS: { -254 Track_most_recent_products = true; -255 Call_depth_to_track_most_recent_products_at = SIZE(Current_routine->calls); -256 break; -257 } -258 -259 :(before "End Primitive Recipe Declarations") -260 _STOP_TRACKING_PRODUCTS, -261 :(before "End Primitive Recipe Numbers") -262 put(Recipe_ordinal, "$stop-tracking-products", _STOP_TRACKING_PRODUCTS); -263 :(before "End Primitive Recipe Checks") -264 case _STOP_TRACKING_PRODUCTS: { -265 break; -266 } -267 :(before "End Primitive Recipe Implementations") -268 case _STOP_TRACKING_PRODUCTS: { -269 Track_most_recent_products = false; -270 break; -271 } -272 -273 :(before "End Primitive Recipe Declarations") -274 _MOST_RECENT_PRODUCTS, -275 :(before "End Primitive Recipe Numbers") -276 put(Recipe_ordinal, "$most-recent-products", _MOST_RECENT_PRODUCTS); -277 :(before "End Primitive Recipe Checks") -278 case _MOST_RECENT_PRODUCTS: { -279 break; -280 } -281 :(before "End Primitive Recipe Implementations") -282 case _MOST_RECENT_PRODUCTS: { -283 products.resize(1); -284 products.at(0).push_back(new_mu_text(Most_recent_products)); -285 break; -286 } -287 -288 :(before "End Primitive Recipe Declarations") -289 SAVE_ERRORS, -290 :(before "End Primitive Recipe Numbers") -291 put(Recipe_ordinal, "save-errors", SAVE_ERRORS); -292 :(before "End Primitive Recipe Checks") -293 case SAVE_ERRORS: { -294 break; -295 } -296 :(before "End Primitive Recipe Implementations") -297 case SAVE_ERRORS: { -298 products.resize(1); -299 products.at(0).push_back(trace_error_contents()); -300 break; -301 } -302 -303 :(before "End Primitive Recipe Declarations") -304 SAVE_APP_TRACE, -305 :(before "End Primitive Recipe Numbers") -306 put(Recipe_ordinal, "save-app-trace", SAVE_APP_TRACE); -307 :(before "End Primitive Recipe Checks") -308 case SAVE_APP_TRACE: { -309 break; -310 } -311 :(before "End Primitive Recipe Implementations") -312 case SAVE_APP_TRACE: { -313 products.resize(1); -314 products.at(0).push_back(trace_app_contents()); +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 _CLEANUP_RUN_SANDBOXED, +319 SAVE_APP_TRACE, 320 :(before "End Primitive Recipe Numbers") -321 put(Recipe_ordinal, "$cleanup-run-sandboxed", _CLEANUP_RUN_SANDBOXED); +321 put(Recipe_ordinal, "save-app-trace", SAVE_APP_TRACE); 322 :(before "End Primitive Recipe Checks") -323 case _CLEANUP_RUN_SANDBOXED: { +323 case SAVE_APP_TRACE: { 324 break; 325 } 326 :(before "End Primitive Recipe Implementations") -327 case _CLEANUP_RUN_SANDBOXED: { -328 run_code_end(); -329 break; -330 } -331 -332 :(scenario "run_interactive_converts_result_to_text") -333 def main [ -334 # try to interactively add 2 and 2 -335 1:text <- new [add 2, 2] -336 2:text <- run-sandboxed 1:text -337 10:@:char <- copy *2:text -338 ] -339 # first letter in the output should be '4' in unicode -340 +mem: storing 52 in location 11 -341 -342 :(scenario "run_interactive_ignores_products_in_nested_functions") -343 def main [ -344 1:text <- new [foo] -345 2:text <- run-sandboxed 1:text -346 10:@:char <- copy *2:text -347 ] -348 def foo [ -349 20:num <- copy 1234 -350 { -351 break -352 reply 5678 -353 } +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 # no product should have been tracked -356 +mem: storing 0 in location 10 +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_previous_instructions") +358 :(scenario "run_interactive_ignores_products_in_nested_functions") 359 def main [ -360 1:text <- new [ -361 add 1, 1 # generates a product -362 foo] # no products -363 2:text <- run-sandboxed 1:text -364 10:@:char <- copy *2:text -365 ] -366 def foo [ -367 20:num <- copy 1234 -368 { -369 break -370 reply 5678 -371 } -372 ] -373 # no product should have been tracked -374 +mem: storing 0 in location 10 -375 -376 :(scenario "run_interactive_remembers_products_before_final_label") -377 def main [ -378 1:text <- new [ -379 add 1, 1 # generates a product -380 +foo] # no products -381 2:text <- run-sandboxed 1:text -382 10:@:char <- copy *2:text -383 ] -384 def foo [ -385 20:num <- copy 1234 -386 { -387 break -388 reply 5678 -389 } -390 ] -391 # product tracked -392 +mem: storing 50 in location 11 -393 -394 :(scenario "run_interactive_returns_text") -395 def main [ -396 # try to interactively add 2 and 2 -397 1:text <- new [ -398 x:text <- new [a] -399 y:text <- new [b] -400 z:text <- append x:text, y:text -401 ] -402 2:text <- run-sandboxed 1:text -403 10:@:char <- copy *2:text -404 ] -405 # output contains "ab" -406 +mem: storing 97 in location 11 -407 +mem: storing 98 in location 12 -408 -409 :(scenario "run_interactive_returns_errors") -410 def main [ -411 # run a command that generates an error -412 1:text <- new [x:num <- copy 34 -413 get x:num, foo:offset] -414 2:text, 3:text <- run-sandboxed 1:text -415 10:@:char <- copy *3:text -416 ] -417 # error should be "unknown element foo in container number" -418 +mem: storing 117 in location 11 -419 +mem: storing 110 in location 12 -420 +mem: storing 107 in location 13 -421 +mem: storing 110 in location 14 -422 # ... -423 -424 :(scenario run_interactive_with_comment) -425 def main [ -426 # 2 instructions, with a comment after the first -427 1:&:@:num <- new [a:num <- copy 0 # abc -428 b:num <- copy 0 -429 ] -430 2:text, 3:text <- run-sandboxed 1:text -431 ] -432 # no errors -433 +mem: storing 0 in location 3 -434 -435 :(after "Running One Instruction") -436 if (Track_most_recent_products && SIZE(Current_routine->calls) == Call_depth_to_track_most_recent_products_at -437 && !current_instruction().is_label -438 && current_instruction().name != "$stop-tracking-products") { -439 Most_recent_products = ""; -440 } -441 :(before "End Running One Instruction") -442 if (Track_most_recent_products && SIZE(Current_routine->calls) == Call_depth_to_track_most_recent_products_at) { -443 Most_recent_products = track_most_recent_products(current_instruction(), products); -444 } -445 :(code) -446 string track_most_recent_products(const instruction& instruction, const vector<vector<double> >& products) { -447 ostringstream out; -448 for (int i = 0; i < SIZE(products); ++i) { -449 // A sandbox can print a string result, but only if it is actually saved -450 // to a variable in the sandbox, because otherwise the results are -451 // reclaimed before the sandbox sees them. So you get these interactions -452 // in the sandbox: -453 // -454 // new [abc] -455 // => <address> -456 // -457 // x:text <- new [abc] -458 // => abc -459 if (i < SIZE(instruction.products)) { -460 if (is_mu_text(instruction.products.at(i))) { -461 if (!scalar(products.at(i))) continue; // error handled elsewhere -462 out << read_mu_text(products.at(i).at(0)) << '\n'; -463 continue; -464 } -465 } -466 for (int j = 0; j < SIZE(products.at(i)); ++j) -467 out << no_scientific(products.at(i).at(j)) << ' '; -468 out << '\n'; -469 } -470 return out.str(); -471 } -472 -473 :(code) -474 string strip_comments(string in) { -475 ostringstream result; -476 for (int i = 0; i < SIZE(in); ++i) { -477 if (in.at(i) != '#') { -478 result << in.at(i); -479 } -480 else { -481 while (i+1 < SIZE(in) && in.at(i+1) != '\n') -482 ++i; -483 } -484 } -485 return result.str(); -486 } -487 -488 int stringified_value_of_location(int address) { -489 // convert to string -490 ostringstream out; -491 out << no_scientific(get_or_insert(Memory, address)); -492 return new_mu_text(out.str()); -493 } -494 -495 int trace_error_contents() { -496 if (!Trace_stream) return 0; -497 ostringstream out; -498 for (vector<trace_line>::iterator p = Trace_stream->past_lines.begin(); p != Trace_stream->past_lines.end(); ++p) { -499 if (p->label != "error") continue; -500 out << p->contents; -501 if (*--p->contents.end() != '\n') out << '\n'; -502 } -503 string result = out.str(); -504 truncate(result); -505 if (result.empty()) return 0; -506 return new_mu_text(result); -507 } -508 -509 int trace_app_contents() { -510 if (!Trace_stream) return 0; -511 ostringstream out; -512 for (vector<trace_line>::iterator p = Trace_stream->past_lines.begin(); p != Trace_stream->past_lines.end(); ++p) { -513 if (p->depth != App_depth) continue; -514 out << p->contents; -515 if (*--p->contents.end() != '\n') out << '\n'; -516 } -517 string result = out.str(); -518 if (result.empty()) return 0; -519 truncate(result); -520 return new_mu_text(result); -521 } -522 -523 void truncate(string& x) { -524 if (SIZE(x) > 1024) { -525 x.erase(1024); -526 *x.rbegin() = '\n'; -527 *++x.rbegin() = '.'; -528 *++++x.rbegin() = '.'; -529 } -530 } -531 -532 //: simpler version of run-sandboxed: doesn't do any running, just loads -533 //: recipes and reports errors. -534 -535 :(before "End Primitive Recipe Declarations") -536 RELOAD, -537 :(before "End Primitive Recipe Numbers") -538 put(Recipe_ordinal, "reload", RELOAD); -539 :(before "End Primitive Recipe Checks") -540 case RELOAD: { -541 if (SIZE(inst.ingredients) != 1) { -542 raise << maybe(get(Recipe, r).name) << "'reload' requires exactly one ingredient, but got '" << inst.original_string << "'\n" << end(); -543 break; -544 } -545 if (!is_mu_text(inst.ingredients.at(0))) { -546 raise << maybe(get(Recipe, r).name) << "first ingredient of 'reload' should be a string, but got '" << inst.ingredients.at(0).original_string << "'\n" << end(); -547 break; -548 } -549 break; +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 :(before "End Primitive Recipe Implementations") -552 case RELOAD: { -553 restore_non_recipe_snapshots(); -554 string code = read_mu_text(ingredients.at(0).at(0)); -555 run_code_begin(/*should_stash_snapshots*/false); -556 routine* save_current_routine = Current_routine; -557 Current_routine = NULL; -558 Sandbox_mode = true; -559 vector<recipe_ordinal> recipes_reloaded = load(code); -560 transform_all(); -561 Trace_stream->newline(); // flush trace -562 Sandbox_mode = false; -563 Current_routine = save_current_routine; -564 products.resize(1); -565 products.at(0).push_back(trace_error_contents()); -566 run_code_end(); // wait until we're done with the trace contents -567 break; -568 } -569 -570 :(scenario reload_continues_past_error) -571 def main [ -572 local-scope -573 x:text <- new [recipe foo [ -574 get 1234:num, foo:offset -575 ]] -576 reload x -577 1:num/raw <- copy 34 -578 ] -579 +mem: storing 34 in location 1 -580 -581 :(scenario reload_can_repeatedly_load_container_definitions) -582 # define a container and try to create it (merge requires knowing container size) -583 def main [ -584 local-scope -585 x:text <- new [ -586 container foo [ -587 x:num -588 y:num -589 ] -590 recipe bar [ -591 local-scope -592 x:foo <- merge 34, 35 -593 ] -594 ] -595 # save warning addresses in locations of type 'number' to avoid spurious changes to them due to 'abandon' -596 1:num/raw <- reload x -597 2:num/raw <- reload x -598 ] -599 # no errors on either load -600 +mem: storing 0 in location 1 -601 +mem: storing 0 in location 2 +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 -- cgit 1.4.1-2-gfad0