From fcf35c1315773edb2061b6bbd87b0963eecbd097 Mon Sep 17 00:00:00 2001 From: "Kartik K. Agaram" Date: Thu, 27 Oct 2016 11:26:42 -0700 Subject: 3604 --- html/020run.cc.html | 3 ++ html/035lookup.cc.html | 20 +++++++++++-- html/036refcount.cc.html | 2 +- html/042name.cc.html | 4 +-- html/050scenario.cc.html | 3 +- html/053recipe_header.cc.html | 11 ++++++- html/057immutable.cc.html | 8 ----- html/080display.cc.html | 8 ++--- html/091socket.cc.html | 68 ++++++++++++++++++++++++++----------------- html/092socket.mu.html | 38 ++++++++++++------------ html/edit/011-errors.mu.html | 6 ++-- html/http-client.mu.html | 3 -- html/http-server.mu.html | 4 +-- 13 files changed, 108 insertions(+), 70 deletions(-) diff --git a/html/020run.cc.html b/html/020run.cc.html index cb39df0e..57ea81ac 100644 --- a/html/020run.cc.html +++ b/html/020run.cc.html @@ -90,12 +90,15 @@ routine* Current_routine = NULL, int> Instructions_running; map<string, int> Locations_read; map<string, int> Locations_read_by_instruction; +:(before "End Setup") +Current_routine = NULL; :(code) void run(const recipe_ordinal r) { routine rr(r); Current_routine = &rr; run_current_routine(); + Current_routine = NULL; } void run_current_routine() { diff --git a/html/035lookup.cc.html b/html/035lookup.cc.html index 277d71ed..07545857 100644 --- a/html/035lookup.cc.html +++ b/html/035lookup.cc.html @@ -104,6 +104,15 @@ canonize(x); -mem: storing 34 in location 0 +error: can't write to location 0 in '1:address:num/lookup <- copy 34' +//: attempts to /lookup address 0 always loudly fail +:(scenario lookup_0_fails) +% Hide_errors = true; +def main [ + 1:address:num <- copy 0 + 2:num <- copy 1:address:num/lookup +] ++error: tried to /lookup 0 in '2:num <- copy 1:address:num/lookup' + :(code) void canonize(reagent& x) { if (is_literal(x)) return; @@ -122,10 +131,10 @@ canonize(x); raise << maybe(current_recipe_name()) << "tried to /lookup 0\n" << end(); return; } - lookup_memory_core(x); + lookup_memory_core(x, /*check_for_null*/true); } -void lookup_memory_core(reagent& x) { +void lookup_memory_core(reagent& x, bool check_for_null) { if (x.value == 0) return; trace(9999, "mem") << "location " << x.value << " is " << no_scientific(get_or_insert(Memory, x.value)) << end(); x.set_value(get_or_insert(Memory, x.value)); @@ -134,6 +143,12 @@ canonize(x); trace(9999, "mem") << "skipping refcount at " << x.value << end(); x.set_value(x.value+1); // skip refcount } + else if (check_for_null) { + if (Current_routine) + raise << "tried to /lookup 0 in '" << to_original_string(current_instruction()) << "'\n" << end(); + else + raise << "tried to /lookup 0\n" << end(); + } drop_one_lookup(x); } @@ -147,6 +162,7 @@ canonize(x); } void test_lookup_zero_address_does_not_skip_refcount() { + Hide_errors = true; reagent x("*x:address:num"); x.set_value(34); // unsafe put(Memory, 34, 0); diff --git a/html/036refcount.cc.html b/html/036refcount.cc.html index 40547a4d..95e1bf03 100644 --- a/html/036refcount.cc.html +++ b/html/036refcount.cc.html @@ -119,7 +119,7 @@ body { font-size: 12pt; font-family: monospace; color: #eeeeee; background-color int payload_size(reagent/*copy*/ x) { x.properties.push_back(pair<string, string_tree*>("lookup", NULL)); - lookup_memory_core(x); + lookup_memory_core(x, /*check for nulls*/false); return size_of(x) + /*refcount*/1; } diff --git a/html/042name.cc.html b/html/042name.cc.html index 098fee41..d6133a59 100644 --- a/html/042name.cc.html +++ b/html/042name.cc.html @@ -52,7 +52,7 @@ body { font-size: 12pt; font-family: monospace; color: #eeeeee; background-color def main [ x:num <- copy y:num ] -+error: main: use before set: 'y' ++error: main: tried to read ingredient 'y' in 'x:num <- copy y:num' but it hasn't been written to yet # todo: detect conditional defines :(after "Transform.push_back(compute_container_sizes)") @@ -92,7 +92,7 @@ Name = Name_snapshot; if (is_named_location(ingredient)) names_used = true; if (is_integer(ingredient.name)) continue; if (!already_transformed(ingredient, names)) { - raise << maybe(caller.name) << "use before set: '" << ingredient.name << "'\n" << end(); + raise << maybe(caller.name) << "tried to read ingredient '" << ingredient.name << "' in '" << to_original_string(inst) << "' but it hasn't been written to yet\n" << end(); // use-before-set Error return; } diff --git a/html/050scenario.cc.html b/html/050scenario.cc.html index 3ca291e8..d44504a0 100644 --- a/html/050scenario.cc.html +++ b/html/050scenario.cc.html @@ -160,13 +160,14 @@ vector<scenario> Scenarios; Num_core_mu_scenarios = SIZE(Scenarios); :(before "End Tests") Hide_missing_default_space_errors = false; -if (Num_core_mu_scenarios) { +if (Num_core_mu_scenarios > 0) { time(&t); cerr << "Mu tests: " << ctime(&t); for (int i = 0; i < Num_core_mu_scenarios; ++i) { //? cerr << '\n' << i << ": " << Scenarios.at(i).name; run_mu_scenario(Scenarios.at(i)); if (Passed) cerr << "."; + else ++Num_failures; } cerr << "\n"; } diff --git a/html/053recipe_header.cc.html b/html/053recipe_header.cc.html index 021db5f4..a922f9e2 100644 --- a/html/053recipe_header.cc.html +++ b/html/053recipe_header.cc.html @@ -266,7 +266,7 @@ put(Recipe_ordinal,b:num <- add a:num, 1 ] -+error: foo: use before set: 'a' ++error: foo: tried to read ingredient 'a' in 'b:num <- add a:num, 1' but it hasn't been written to yet +error: did you forget 'load-ingredients'? :(after "use-before-set Error") @@ -286,6 +286,15 @@ put(Recipe_ordinal,if (is_present_in_ingredients(get(Recipe, get(Recipe_ordinal, recipe_name)), x.name)) raise << " did you forget 'load-ingredients'?\n" << end(); +:(code) +bool is_present_in_ingredients(const recipe& callee, const string& ingredient_name) { + for (int i = 0; i < SIZE(callee.ingredients); ++i) { + if (callee.ingredients.at(i).name == ingredient_name) + return true; + } + return false; +} + //:: Check all calls against headers. :(scenario show_clear_error_on_bad_call) diff --git a/html/057immutable.cc.html b/html/057immutable.cc.html index f1fbb793..5f8ad767 100644 --- a/html/057immutable.cc.html +++ b/html/057immutable.cc.html @@ -538,14 +538,6 @@ set<int> scan_contained_in_product_indicesreturn false; } -bool is_present_in_ingredients(const recipe& callee, const string& ingredient_name) { - for (int i = 0; i < SIZE(callee.ingredients); ++i) { - if (callee.ingredients.at(i).name == ingredient_name) - return true; - } - return false; -} - set<int> ingredient_indices(const instruction& inst, const set<reagent>& ingredient_names) { set<int> result; for (int i = 0; i < SIZE(inst.ingredients); ++i) { diff --git a/html/080display.cc.html b/html/080display.cc.html index 56533131..a5f85757 100644 --- a/html/080display.cc.html +++ b/html/080display.cc.html @@ -47,17 +47,17 @@ body { font-size: 12pt; font-family: monospace; color: #eeeeee; background-color #define CHECK_SCREEN \ if (!tb_is_active()) { \ if (Run_tests) \ - raise << maybe(current_recipe_name()) << "tried to print to real screen before 'open-console'\n" << end(); \ - else \ raise << maybe(current_recipe_name()) << "tried to print to real screen in a test!\n" << end(); \ + else \ + raise << maybe(current_recipe_name()) << "tried to print to real screen before 'open-console' or after 'close-console'\n" << end(); \ break; \ } #define CHECK_CONSOLE \ if (!tb_is_active()) { \ if (Run_tests) \ - raise << maybe(current_recipe_name()) << "tried to read event from real keyboard/mouse before 'open-console'\n" << end(); \ - else \ raise << maybe(current_recipe_name()) << "tried to read event from real keyboard/mouse in a test!\n" << end(); \ + else \ + raise << maybe(current_recipe_name()) << "tried to read event from real keyboard/mouse before 'open-console' or after 'close-console'\n" << end(); \ break; \ } diff --git a/html/091socket.cc.html b/html/091socket.cc.html index e205508f..2c75978a 100644 --- a/html/091socket.cc.html +++ b/html/091socket.cc.html @@ -14,6 +14,7 @@ pre { white-space: pre-wrap; font-family: monospace; color: #eeeeee; background- body { font-size: 12pt; font-family: monospace; color: #eeeeee; background-color: #080808; } * { font-size: 12pt; font-size: 1em; } .Constant { color: #00a0a0; } +.CommentedCode { color: #6c6c6c; } .cSpecial { color: #008000; } .Delimiter { color: #800080; } .Identifier { color: #c0a020; } @@ -155,7 +156,7 @@ socket_t* server_socket(int< int dummy = 0; setsockopt(result->fd, SOL_SOCKET, SO_REUSEADDR, &dummy, sizeof(dummy)); result->addr.sin_family = AF_INET; - result->addr.sin_addr.s_addr = INADDR_ANY; + result->addr.sin_addr.s_addr = Current_scenario ? htonl(INADDR_LOOPBACK) : INADDR_ANY; // run tests without running afoul of any firewall result->addr.sin_port = htons(port); if (bind(result->fd, reinterpret_cast<sockaddr*>(&result->addr), sizeof(result->addr)) >= 0) { listen(result->fd, /*queue length*/5); @@ -223,16 +224,12 @@ _READ_FROM_SOCKET, put(Recipe_ordinal, "$read-from-socket", _READ_FROM_SOCKET); :(before "End Primitive Recipe Checks") case _READ_FROM_SOCKET: { - if (SIZE(inst.ingredients) != 2) { - raise << maybe(get(Recipe, r).name) << "'$read-from-socket' requires exactly two ingredients, but got '" << inst.original_string << "'\n" << end(); + if (SIZE(inst.ingredients) != 1) { + raise << maybe(get(Recipe, r).name) << "'$read-from-socket' requires exactly one ingredient, but got '" << inst.original_string << "'\n" << end(); break; } if (!is_mu_number(inst.ingredients.at(0))) { - raise << maybe(get(Recipe, r).name) << "first ingredient of '$read-from-socket' should be a number, but got '" << to_string(inst.ingredients.at(0)) << "'\n" << end(); - break; - } - if (!is_mu_number(inst.ingredients.at(1))) { - raise << maybe(get(Recipe, r).name) << "second ingredient of '$read-from-socket' should be a number, but got '" << to_string(inst.ingredients.at(0)) << "'\n" << end(); + raise << maybe(get(Recipe, r).name) << "first ingredient of '$read-from-socket' should be a number (socket), but got '" << to_string(inst.ingredients.at(0)) << "'\n" << end(); break; } int nprod = SIZE(inst.products); @@ -240,8 +237,8 @@ put(Recipe_ordinal,(get(Recipe, r).name) << "'$read-from-socket' requires 1-4 products, but got '" << inst.original_string << "'\n" << end(); break; } - if (!is_mu_text(inst.products.at(0))) { - raise << maybe(get(Recipe, r).name) << "first product of '$read-from-socket' should be a text (address array character), but got '" << to_string(inst.products.at(0)) << "'\n" << end(); + if (!is_mu_character(inst.products.at(0))) { + raise << maybe(get(Recipe, r).name) << "first product of '$read-from-socket' should be a character, but got '" << to_string(inst.products.at(0)) << "'\n" << end(); break; } if (nprod > 1 && !is_mu_boolean(inst.products.at(1))) { @@ -269,20 +266,25 @@ put(Recipe_ordinal,// so use poll() in the beginning to wait for data before calling recv() // 3. but poll() will block on EOF, so only use poll() on the very first // $read-from-socket on a socket + // + // Also, there was an unresolved issue where attempts to read() a small + // number of bytes (less than 447 on Linux and Mac) would cause browsers to + // prematurely close the connection. See commit 3403. That seems to be gone + // after moving to recv()+poll(). It was never observed on OpenBSD. if (!socket->polled) { pollfd p; bzero(&p, sizeof(p)); p.fd = socket->fd; p.events = POLLIN | POLLHUP; - int status = poll(&p, /*num pollfds*/1, /*timeout*/100/*ms*/); - if (status == 0) { + int poll_result = poll(&p, /*num pollfds*/1, /*timeout*/100/*ms*/); + if (poll_result == 0) { products.at(0).push_back(/*no data*/0); products.at(1).push_back(/*found*/false); products.at(2).push_back(/*eof*/false); products.at(3).push_back(/*error*/0); break; } - else if (status < 0) { + else if (poll_result < 0) { int error_code = errno; raise << maybe(current_recipe_name()) << "error in $read-from-socket\n" << end(); products.at(0).push_back(/*no data*/0); @@ -293,15 +295,19 @@ put(Recipe_ordinal,} socket->polled = true; } - int bytes = static_cast<int>(ingredients.at(1).at(0)); - char* contents = new char[bytes]; - bzero(contents, bytes); - int bytes_read = recv(socket->fd, contents, bytes-/*terminal null*/1, MSG_DONTWAIT); - products.at(0).push_back(new_mu_text(contents)); + char c = '\0'; + int error_code = 0; + int bytes_read = recv(socket->fd, &c, /*single byte*/1, MSG_DONTWAIT); + if (bytes_read < 0) error_code = errno; +//? if (error_code) { +//? ostringstream out; +//? out << "error in $read-from-socket " << socket->fd; +//? perror(out.str().c_str()); +//? } + products.at(0).push_back(c); products.at(1).push_back(/*found*/true); products.at(2).push_back(/*eof*/bytes_read <= 0); - products.at(3).push_back(/*error*/0); - delete contents; + products.at(3).push_back(error_code); break; } @@ -320,17 +326,16 @@ put(Recipe_ordinal,:(before "End Primitive Recipe Implementations") case _WRITE_TO_SOCKET: { long long int x = static_cast<long long int>(ingredients.at(0).at(0)); - socket_t* session = reinterpret_cast<socket_t*>(x); - // write just one character at a time to the session socket + socket_t* socket = reinterpret_cast<socket_t*>(x); + // write just one character at a time to the socket long long int y = static_cast<long long int>(ingredients.at(1).at(0)); char c = static_cast<char>(y); - if (write(session->fd, &c, 1) != 1) { + if (write(socket->fd, &c, 1) != 1) { raise << maybe(current_recipe_name()) << "failed to write to socket\n" << end(); exit(0); } - long long int result = reinterpret_cast<long long int>(session); products.resize(1); - products.at(0).push_back(result); + products.at(0).push_back(ingredients.at(0).at(0)); break; } @@ -345,7 +350,15 @@ put(Recipe_ordinal,break; } if (!is_mu_number(inst.ingredients.at(0))) { - raise << maybe(get(Recipe, r).name) << "first ingredient of '$close-socket' should be a character, but got '" << to_string(inst.ingredients.at(0)) << "'\n" << end(); + raise << maybe(get(Recipe, r).name) << "first ingredient of '$close-socket' should be a number, but got '" << to_string(inst.ingredients.at(0)) << "'\n" << end(); + break; + } + if (SIZE(inst.products) != 1) { + raise << maybe(get(Recipe, r).name) << "'$close-socket' requires exactly one product, but got '" << inst.original_string << "'\n" << end(); + break; + } + if (inst.products.at(0).name != inst.ingredients.at(0).name) { + raise << maybe(get(Recipe, r).name) << "product of '$close-socket' must be first ingredient '" << inst.ingredients.at(0).original_string << "', but got '" << inst.products.at(0).original_string << "'\n" << end(); break; } break; @@ -355,6 +368,9 @@ put(Recipe_ordinal,long long int x = static_cast<long long int>(ingredients.at(0).at(0)); socket_t* socket = reinterpret_cast<socket_t*>(x); close(socket->fd); + delete socket; + products.resize(1); + products.at(0).push_back(0); // make sure we can't reuse the socket break; } diff --git a/html/092socket.mu.html b/html/092socket.mu.html index 78321183..e3c179eb 100644 --- a/html/092socket.mu.html +++ b/html/092socket.mu.html @@ -54,6 +54,7 @@ body { font-size: 12pt; font-family: monospace; color: #eeeeee; background-color memory-should-contain [ 10:array:character <- [abc] ] + socket <- $close-socket socket ] # helper just for this scenario def example-handler query:text -> response:text [ @@ -85,18 +86,18 @@ body { font-size: 12pt; font-family: monospace; color: #eeeeee; background-color type request-handler = (recipe text -> text) -def serve-one-request socket:num, request-handler:request-handler [ +def serve-one-request socket:num, request-handler:request-handler -> socket:num [ local-scope load-ingredients session:num <- $accept socket assert session, [ F - example-server-test: $accept failed] contents:&:source:char, sink:&:sink:char <- new-channel 30 - sink <- start-running receive-from-socket session, sink + start-running receive-from-socket session, sink query:text <- drain contents response:text <- call request-handler, query write-to-socket session, response - $close-socket session + session <- $close-socket session ] def start-reading-from-network resources:&:resources, uri:text -> contents:&:source:char [ @@ -120,7 +121,7 @@ body { font-size: 12pt; font-family: monospace; color: #eeeeee; background-color req:text <- interpolate [GET _ HTTP/1.1], path request-socket socket, req contents:&:source:char, sink:&:sink:char <- new-channel 10000 - start-running receive-from-socket socket, sink + start-running receive-from-client-socket-and-close socket, sink ] def request-socket socket:num, s:text -> socket:num [ @@ -134,33 +135,34 @@ body { font-size: 12pt; font-family: monospace; color: #eeeeee; background-color $write-to-socket socket, 10/lf ] -def receive-from-socket socket:num, sink:&:sink:char -> sink:&:sink:char [ +def receive-from-socket socket:num, sink:&:sink:char -> sink:&:sink:char, socket:num [ local-scope load-ingredients { +next-attempt - req:text, found?:bool, eof?:bool, error:num <- $read-from-socket socket, 4096/bytes + c:char, found?:bool, eof?:bool, error:num <- $read-from-socket socket + break-if eof? break-if error { - break-if found? - switch - loop +next-attempt + break-unless found? + sink <- write sink, c } - bytes-read:num <- length *req - i:num <- copy 0 { - done?:bool <- greater-or-equal i, bytes-read - break-if done? - c:char <- index *req, i # todo: unicode - sink <- write sink, c - i <- add i, 1 - loop + break-if found? + switch } - loop-unless eof? + loop } sink <- close sink ] +def receive-from-client-socket-and-close socket:num, sink:&:sink:char -> sink:&:sink:char, socket:num [ + local-scope + load-ingredients + sink <- receive-from-socket socket, sink + socket <- $close-socket socket +] + def write-to-socket socket:num, s:text [ local-scope load-ingredients diff --git a/html/edit/011-errors.mu.html b/html/edit/011-errors.mu.html index 2bdf811a..91e48c57 100644 --- a/html/edit/011-errors.mu.html +++ b/html/edit/011-errors.mu.html @@ -521,7 +521,8 @@ body { font-size: 12pt; font-family: monospace; color: #eeeeee; background-color . local-scope ╎ . . x:num <- copy y:num ╎ . .] ╎ . - .foo: use before set: 'y' ╎ . + .foo: tried to read ingredient 'y' in 'x:num <- co↩╎ . + .py y:num' but it hasn't been written to yet ╎ . .╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╎ . . ╎ . ] @@ -539,7 +540,8 @@ body { font-size: 12pt; font-family: monospace; color: #eeeeee; background-color . local-scope ╎ . . x:num <- copy y:num ╎ . .] ╎ . - .foo: use before set: 'y' ╎ . + .foo: tried to read ingredient 'y' in 'x:num <- co↩╎ . + .py y:num' but it hasn't been written to yet ╎ . .╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╎ . . ╎ . ] diff --git a/html/http-client.mu.html b/html/http-client.mu.html index 264f56ed..8421fc60 100644 --- a/html/http-client.mu.html +++ b/html/http-client.mu.html @@ -18,7 +18,6 @@ body { font-size: 12pt; font-family: monospace; color: #eeeeee; background-color .Comment { color: #9090ff; } .Constant { color: #00a0a0; } .Special { color: #c00000; } -.CommentedCode { color: #6c6c6c; } .muRecipe { color: #ff8700; } --> @@ -43,8 +42,6 @@ body { font-size: 12pt; font-family: monospace; color: #eeeeee; background-color break-if done? n <- add n, 1 buf <- append buf, c -#? trunc?:bool <- greater-or-equal n, 10000 -#? loop-unless trunc? loop } result:text <- buffer-to-array buf diff --git a/html/http-server.mu.html b/html/http-server.mu.html index 97220e9b..6ed0ffe6 100644 --- a/html/http-server.mu.html +++ b/html/http-server.mu.html @@ -54,8 +54,8 @@ body { font-size: 12pt; font-family: monospace; color: #eeeeee; background-color SUCCESS! ] $print 10/newline, [Wrote to and closing socket...], 10/newline - $close-socket session - $close-socket socket + session <- $close-socket session + socket <- $close-socket socket ] -- cgit 1.4.1-2-gfad0