https://github.com/akkartik/mu/blob/master/101run_sandboxed.cc
1
2
3
4 :(scenario run_interactive_code)
5 def main [
6 1:num <- copy 0
7 10:text <- new [1:num/raw <- copy 34]
8
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
20 +mem: storing 0 in location 20
21 +mem: storing 0 in location 21
22
23
24
25 :(before "End Globals")
26 bool Sandbox_mode = false;
27
28 :(before "End Load Recipe Name")
29 if (Sandbox_mode && result.name == "main") {
30 slurp_balanced_bracket(in);
31 return -1;
32 }
33
34
35
36
37
38
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(1));
58 if (!new_code_pushed_to_stack) {
59 products.resize(5);
60 products.at(0).push_back(0);
61 products.at(0).push_back(0);
62 products.at(1).push_back(0);
63 products.at(1).push_back(trace_error_contents());
64 products.at(2).push_back(0);
65 products.at(2).push_back(0);
66 products.at(3).push_back(0);
67 products.at(3).push_back(trace_app_contents());
68 products.at(4).push_back(1);
69 run_code_end();
70 break;
71 }
72 else {
73 continue;
74 }
75 }
76
77
78
79
80
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
96
97
98 bool run_interactive(int address) {
99
100 assert(contains_key(Recipe_ordinal, "interactive") && get(Recipe_ordinal, "interactive") != 0);
101
102
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
109 Name[get(Recipe_ordinal, "interactive")].clear();
110 run_code_begin(true);
111 if (command.empty()) return false;
112
113 routine* save_current_routine = Current_routine;
114 Current_routine = NULL;
115
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
128
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);
133 }
134 Current_routine->calls.push_front(call(get(Recipe_ordinal, "sandbox")));
135 return true;
136 }
137
138
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
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
172
173
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"));
179 if (!Recipe_snapshot_stash.empty())
180 unstash_snapshots();
181 }
182
183
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") +
221 "]\n" +
222 "recipe sandbox [\n" +
223 "local-scope\n" +
224
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
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
235 "output:text <- $most-recent-products\n" +
236
237
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
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 [
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(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(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(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
351 10:text <- new [add 2, 2]
352 20:text <- run-sandboxed 10:text
353 30:@:char <- copy *20:text
354 ]
355
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
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
378 foo]
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
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
396 +foo]
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
408 +mem: storing 50 in location 31
409
410 :(scenario "run_interactive_returns_text")
411 def main [
412
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
420 20:@:char <- copy *10:text
421 ]
422
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
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
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
444 10:text <- new [a:num <- copy 0
445 b:num <- copy 0
446 ]
447 20:text, 30:text <- run-sandboxed 10:text
448 ]
449
450
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
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
470
471
472
473
474
475
476
477
478
479 if (i < SIZE(instruction.products)) {
480 if (is_mu_text(instruction.products.at(i))) {
481 if (SIZE(products.at(i)) != 2) continue;
482 out << read_mu_text(products.at(i).at(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
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
553
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(1));
575 run_code_begin(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();
582 Sandbox_mode = false;
583 Current_routine = save_current_routine;
584 products.resize(1);
585 products.at(0).push_back(0);
586 products.at(0).push_back(trace_error_contents());
587 run_code_end();
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
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
629 10:text/raw <- reload x
630 20:text/raw <- reload x
631 ]
632
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