1 //: Update refcounts when copying addresses.
   2 //: The top of the address layer has more on refcounts.
   3 
   4 :(scenario refcounts)
   5 def main [
   6   1:address:num <- copy 1000/unsafe
   7   2:address:num <- copy 1:address:num
   8   1:address:num <- copy 0
   9   2:address:num <- copy 0
  10 ]
  11 +run: {1: ("address" "number")} <- copy {1000: "literal", "unsafe": ()}
  12 +mem: incrementing refcount of 1000: 0 -> 1
  13 +run: {2: ("address" "number")} <- copy {1: ("address" "number")}
  14 +mem: incrementing refcount of 1000: 1 -> 2
  15 +run: {1: ("address" "number")} <- copy {0: "literal"}
  16 +mem: decrementing refcount of 1000: 2 -> 1
  17 +run: {2: ("address" "number")} <- copy {0: "literal"}
  18 +mem: decrementing refcount of 1000: 1 -> 0
  19 
  20 :(before "End Globals")
  21 //: escape hatch for a later layer
  22 bool Update_refcounts_in_write_memory = true;
  23 
  24 :(before "End write_memory(x) Special-cases")
  25 if (Update_refcounts_in_write_memory)
  26   update_any_refcounts(x, data);
  27 
  28 :(code)
  29 void update_any_refcounts(const reagent& canonized_x, const vector<double>& data) {
  30   increment_any_refcounts(canonized_x, data);  // increment first so we don't reclaim on x <- copy x
  31   decrement_any_refcounts(canonized_x);
  32 }
  33 
  34 void increment_any_refcounts(const reagent& canonized_x, const vector<double>& data) {
  35   if (is_mu_address(canonized_x)) {
  36     assert(scalar(data));
  37     assert(!canonized_x.metadata.size);
  38     increment_refcount(data.at(0));
  39   }
  40   // End Increment Refcounts(canonized_x)
  41 }
  42 
  43 void increment_refcount(int new_address) {
  44   assert(new_address >= 0);
  45   if (new_address == 0) return;
  46   int new_refcount = get_or_insert(Memory, new_address);
  47   trace(9999, "mem") << "incrementing refcount of " << new_address << ": " << new_refcount << " -> " << new_refcount+1 << end();
  48   put
#include <stdarg.h>
#include <stddef.h>
#include <setjmp.h>
#include <cmocka.h>
#include <stdlib.h>
#include <string.h>
#include <glib.h>

#include "config/preferences.h"

#include "ui/ui.h"
#include "ui/stub_ui.h"

#include "command/cmd_funcs.h"

#define CMD_PRESENCE "/presence"

void
cmd_presence_shows_usage_when_bad_subcmd(void** state)
{
    gchar* args[] = { "badcmd", NULL };

    expect_string(cons_bad_cmd_usage, cmd, CMD_PRESENCE);

    gboolean result = cmd_presence(NULL, CMD_PRESENCE, args);
    assert_true(result);
}

void
cmd_presence_shows_usage_when_bad_console_setting(void** state)
{
    gchar* args[] = { "console", "badsetting", NULL };

    expect_string(cons_bad_cmd_usage, cmd, CMD_PRESENCE);

    gboolean result = cmd_presence(NULL, CMD_PRESENCE, args);
    assert_true(result);
}

void
cmd_presence_shows_usage_when_bad_chat_setting(void** state)
{
    gchar* args[] = { "chat", "badsetting", NULL };

    expect_string(cons_bad_cmd_usage, cmd, CMD_PRESENCE);

    gboolean result = cmd_presence(NULL, CMD_PRESENCE, args);
    assert_true(result);
}

void
cmd_presence_shows_usage_when_bad_muc_setting(void** state)
{
    gchar* args[] = { "muc", "badsetting", NULL };

    expect_string(cons_bad_cmd_usage, cmd, CMD_PRESENCE);

    gboolean result = cmd_presence(NULL, CMD_PRESENCE, args);
    assert_true(result);
}

void
cmd_presence_console_sets_all(void** state)
{
    gchar* args[] = { "console", "all", NULL };

    expect_cons_show("All presence updates will appear in the console.");

    gboolean result = cmd_presence(NULL, CMD_PRESENCE, args);

    char* setting = prefs_get_string(PREF_STATUSES_CONSOLE);
    assert_non_null(setting);
    assert_string_equal("all", setting);
    assert_true(result);
    g_free(setting);
}

void
cmd_presence_console_sets_online(void** state)
{
    gchar* args[] = { "console", "online", NULL };

    expect_cons_show("Only online/offline presence updates will appear in the console.");

    gboolean result = cmd_presence(NULL, CMD_PRESENCE, args);

    char* setting = prefs_get_string(PREF_STATUSES_CONSOLE);
    assert_non_null(setting);
    assert_string_equal("online", setting);
    assert_true(result);
    g_free(setting);
}

void
cmd_presence_console_sets_none(void** state)
{
    gchar* args[] = { "console", "none", NULL };

    expect_cons_show("Presence updates will not appear in the console.");

    gboolean result = cmd_presence(NULL, CMD_PRESENCE, args);

    char* setting = prefs_get_string(PREF_STATUSES_CONSOLE);
    assert_non_null(setting);
    assert_string_equal("none", setting);
    assert_true(result);
    g_free(setting);
}

void
cmd_presence_chat_sets_all(void** state)
{
    gchar* args[] = { "chat", "all", NULL };

    expect_cons_show("All presence updates will appear in chat windows.");

    gboolean result = cmd_presence(NULL, CMD_PRESENCE, args);

    char* setting = prefs_get_string(PREF_STATUSES_CHAT);
    assert_non_null(setting);
    assert_string_equal("all", setting);
    assert_true(result);
    g_free(setting);
}

void
cmd_presence_chat_sets_online(void** state)
{
    gchar* args[] = { "chat", "online", NULL };

    expect_cons_show("Only online/offline presence updates will appear in chat windows.");

    gboolean result = cmd_presence(NULL, CMD_PRESENCE, args);

    char* setting = prefs_get_string(PREF_STATUSES_CHAT);
    assert_non_null(setting);
    assert_string_equal("online", setting);
    assert_true(result);
    g_free(setting);
}

void
cmd_presence_chat_sets_none(void** state)
{
    gchar* args[] = { "chat", "none", NULL };

    expect_cons_show("Presence updates will not appear in chat windows.");

    gboolean result = cmd_presence(NULL, CMD_PRESENCE, args);

    char* setting = prefs_get_string(PREF_STATUSES_CHAT);
    assert_non_null(setting);
    assert_string_equal("none", setting);
    assert_true(result);
    g_free(setting);
}

void
cmd_presence_room_sets_all(void** state)
{
    gchar* args[] = { "room", "all", NULL };

    expect_cons_show("All presence updates will appear in chat room windows.");

    gboolean result = cmd_presence(NULL, CMD_PRESENCE, args);

    char* setting = prefs_get_string(PREF_STATUSES_MUC);
    assert_non_null(setting);
    assert_string_equal("all", setting);
    assert_true(result);
    g_free(setting);
}

void
cmd_presence_room_sets_online(void** state)
{
    gchar* args[] = { "room", "online", NULL };

    expect_cons_show("Only join/leave presence updates will appear in chat room windows.");

    gboolean result = cmd_presence(NULL, CMD_PRESENCE, args);

    char* setting = prefs_get_string(PREF_STATUSES_MUC);
    assert_non_null(setting);
    assert_string_equal("online", setting);
    assert_true(result);
    g_free(setting);
}

void
cmd_presence_room_sets_none(void** state)
{
    gchar* args[] = { "room", "none", NULL };

    expect_cons_show("Presence updates will not appear in chat room windows.");

    gboolean result = cmd_presence(NULL, CMD_PRESENCE, args);

    char* setting = prefs_get_string(PREF_STATUSES_MUC);
    assert_non_null(setting);
    assert_string_equal("none", setting);
    assert_true(result);
    g_free(setting);
}
lass="LineNr"> 259 for (set<tag_condition_info>::const_iterator pa = a.begin(), pb = b.begin(); pa != a.end(); ++pa, ++pb) { 260 if (pa->offset != pb->offset) return pa->offset < pb->offset; 261 if (pa->tag != pb->tag) return pa->tag < pb->tag; 262 } 263 return false; // equal 264 } 265 bool operator<(const tag_condition_info& a, const tag_condition_info& b) { 266 if (a.offset != b.offset) return a.offset < b.offset; 267 if (a.tag != b.tag) return a.tag < b.tag; 268 return false; // equal 269 } 270 bool operator<(const set<address_element_info>& a, const set<address_element_info>& b) { 271 if (a.size() != b.size()) return a.size() < b.size(); 272 for (set<address_element_info>::const_iterator pa = a.begin(), pb = b.begin(); pa != a.end(); ++pa, ++pb) { 273 if (pa->offset != pb->offset) return pa->offset < pb->offset; 274 } 275 return false; // equal 276 } 277 bool operator<(const address_element_info& a, const address_element_info& b) { 278 if (a.offset != b.offset) return a.offset < b.offset; 279 return false; // equal 280 } 281 282 //: populate metadata.address in a separate transform, because it requires 283 //: already knowing the sizes of all types 284 285 :(after "Transform.push_back(compute_container_sizes)") 286 Transform.push_back(compute_container_address_offsets); // idempotent 287 :(code) 288 void compute_container_address_offsets(const recipe_ordinal r) { 289 recipe& caller = get(Recipe, r); 290 trace(9992, "transform") << "--- compute address offsets for " << caller.name << end(); 291 for (int i = 0; i < SIZE(caller.steps); ++i) { 292 instruction& inst = caller.steps.at(i); 293 trace(9993, "transform") << "- compute address offsets for " << to_string(inst) << end(); 294 for (int i = 0; i < SIZE(inst.ingredients); ++i) 295 compute_container_address_offsets(inst.ingredients.at(i), " in '"+to_original_string(inst)+"'"); 296 for (int i = 0; i < SIZE(inst.products); ++i) 297 compute_container_address_offsets(inst.products.at(i), " in '"+to_original_string(inst)+"'"); 298 } 299 } 300 301 void compute_container_address_offsets(reagent& r, const string& location_for_error_messages) { 302 if (is_literal(r) || is_dummy(r)) return; 303 compute_container_address_offsets(r.type, location_for_error_messages); 304 if (contains_key(Container_metadata, r.type)) 305 r.metadata = get(Container_metadata, r.type); 306 } 307 308 // the recursive structure of this function needs to exactly match 309 // compute_container_sizes 310 void compute_container_address_offsets(const type_tree* type, const string& location_for_error_messages) { 311 if (!type) return; 312 if (!type->atom) { 313 if (!type->left->atom) { 314 raise << "invalid type " << to_string(type) << location_for_error_messages << '\n' << end(); 315 return; 316 } 317 if (type->left->name == "address") 318 compute_container_address_offsets(payload_type(type), location_for_error_messages); 319 else if (type->left->name == "array") 320 compute_container_address_offsets(array_element(type), location_for_error_messages); 321 // End compute_container_address_offsets Non-atom Special-cases 322 } 323 const type_tree* base_type = type; 324 // Update base_type in compute_container_address_offsets 325 if (!contains_key(Type, base_type->value)) return; // error raised elsewhere 326 type_info& info = get(Type, base_type->value); 327 if (info.kind == CONTAINER) { 328 compute_container_address_offsets(info, type, location_for_error_messages); 329 } 330 if (info.kind == EXCLUSIVE_CONTAINER) { 331 compute_exclusive_container_address_offsets(info, type, location_for_error_messages); 332 } 333 } 334 335 void compute_container_address_offsets(const type_info& container_info, const type_tree* full_type, const string& location_for_error_messages) { 336 container_metadata& metadata = get(Container_metadata, full_type); 337 if (!metadata.address.empty()) return; 338 trace(9994, "transform") << "compute address offsets for container " << container_info.name << end(); 339 append_addresses(0, full_type, metadata.address, set<tag_condition_info>(), location_for_error_messages); 340 } 341 342 void compute_exclusive_container_address_offsets(const type_info& exclusive_container_info, const type_tree* full_type, const string& location_for_error_messages) { 343 container_metadata& metadata = get(Container_metadata, full_type); 344 trace(9994, "transform") << "compute address offsets for exclusive container " << exclusive_container_info.name << end(); 345 for (int tag = 0; tag < SIZE(exclusive_container_info.elements); ++tag) { 346 set<tag_condition_info> key; 347 key.insert(tag_condition_info(/*tag is at offset*/0, tag)); 348 append_addresses(/*skip tag offset*/1, variant_type(full_type, tag).type, metadata.address, key, location_for_error_messages); 349 } 350 } 351 352 void append_addresses(int base_offset, const type_tree* type, map<set<tag_condition_info>, set<address_element_info> >& out, const set<tag_condition_info>& key, const string& location_for_error_messages) { 353 if (is_mu_address(type)) { 354 get_or_insert(out, key).insert(address_element_info(base_offset, new type_tree(*payload_type(type)))); 355 return; 356 } 357 const type_tree* base_type = type; 358 // Update base_type in append_container_address_offsets 359 const type_info& info = get(Type, base_type->value); 360 if (info.kind == CONTAINER) { 361 for (int curr_index = 0, curr_offset = base_offset; curr_index < SIZE(info.elements); ++curr_index) { 362 trace(9993, "transform") << "checking container " << base_type->name << ", element " << curr_index << end(); 363 reagent/*copy*/ element = element_type(type, curr_index); // not base_type 364 // Compute Container Address Offset(element) 365 if (is_mu_address(element)) { 366 trace(9993, "transform") << "address at offset " << curr_offset << end(); 367 get_or_insert(out, key).insert(address_element_info(curr_offset, new type_tree(*payload_type(element.type)))); 368 ++curr_offset; 369 } 370 else if (is_mu_array(element)) { 371 curr_offset += /*array length*/1; 372 const type_tree* array_element_type = array_element(element.type); 373 int array_element_size = size_of(array_element_type); 374 for (int i = 0; i < static_array_length(element.type); ++i) { 375 append_addresses(curr_offset, array_element_type, out, key, location_for_error_messages); 376 curr_offset += array_element_size; 377 } 378 } 379 else if (is_mu_container(element)) { 380 append_addresses(curr_offset, element.type, out, key, location_for_error_messages); 381 curr_offset += size_of(element); 382 } 383 else if (is_mu_exclusive_container(element)) { 384 const type_tree* element_base_type = element.type; 385 // Update element_base_type For Exclusive Container in append_addresses 386 const type_info& element_info = get(Type, element_base_type->value); 387 for (int tag = 0; tag < SIZE(element_info.elements); ++tag) { 388 set<tag_condition_info> new_key = key; 389 new_key.insert(tag_condition_info(curr_offset, tag)); 390 if (!contains_key(out, new_key)) 391 append_addresses(curr_offset+/*skip tag*/1, variant_type(element.type, tag).type, out, new_key, location_for_error_messages); 392 } 393 curr_offset += size_of(element); 394 } 395 else { 396 // non-address primitive 397 ++curr_offset; 398 } 399 } 400 } 401 else if (info.kind == EXCLUSIVE_CONTAINER) { 402 for (int tag = 0; tag < SIZE(info.elements); ++tag) { 403 set<tag_condition_info> new_key = key; 404 new_key.insert(tag_condition_info(base_offset, tag)); 405 if (!contains_key(out, new_key)) 406 append_addresses(base_offset+/*skip tag*/1, variant_type(type, tag).type, out, new_key, location_for_error_messages); 407 } 408 } 409 } 410 411 //: for the following unit tests we'll do the work of the transform by hand 412 413 :(before "End Unit Tests") 414 void test_container_address_offsets_empty() { 415 int old_size = SIZE(Container_metadata); 416 // define a container with no addresses 417 reagent r("x:point"); 418 compute_container_sizes(r, ""); // need to first pre-populate the metadata 419 // scan 420 compute_container_address_offsets(r, ""); 421 // global metadata contains just the entry for foo 422 // no entries for non-container types or other junk 423 CHECK_EQ(SIZE(Container_metadata)-old_size, 1); 424 // the reagent we scanned knows it has no addresses 425 CHECK(r.metadata.address.empty()); 426 // the global table contains an identical entry 427 CHECK(contains_key(Container_metadata, r.type)); 428 CHECK(get(Container_metadata, r.type).address.empty()); 429 // compute_container_address_offsets creates no new entries 430 CHECK_EQ(SIZE(Container_metadata)-old_size, 1); 431 } 432 433 void test_container_address_offsets() { 434 int old_size = SIZE(Container_metadata); 435 // define a container with an address at offset 0 that we have the size for 436 run("container foo [\n" 437 " x:address:num\n" 438 "]\n"); 439 reagent r("x:foo"); 440 compute_container_sizes(r, ""); // need to first pre-populate the metadata 441 // scan 442 compute_container_address_offsets(r, ""); 443 // global metadata contains just the entry for foo 444 // no entries for non-container types or other junk 445 CHECK_EQ(SIZE(Container_metadata)-old_size, 1); 446 // the reagent we scanned knows it has an address at offset 0 447 CHECK_EQ(SIZE(r.metadata.address), 1); 448 CHECK(contains_key(r.metadata.address, set<tag_condition_info>())); 449 const set<address_element_info>& address_offsets = get(r.metadata.address, set<tag_condition_info>()); // unconditional for containers 450 CHECK_EQ(SIZE(address_offsets), 1); 451 CHECK_EQ(address_offsets.begin()->offset, 0); 452 CHECK(address_offsets.begin()->payload_type->atom); 453 CHECK_EQ(address_offsets.begin()->payload_type->name, "number"); 454 // the global table contains an identical entry 455 CHECK(contains_key(Container_metadata, r.type)); 456 const set<address_element_info>& address_offsets2 = get(get(Container_metadata, r.type).address, set<tag_condition_info>()); 457 CHECK_EQ(SIZE(address_offsets2), 1); 458 CHECK_EQ(address_offsets2.begin()->offset, 0); 459 CHECK(address_offsets2.begin()->payload_type->atom); 460 CHECK_EQ(address_offsets2.begin()->payload_type->name, "number"); 461 // compute_container_address_offsets creates no new entries 462 CHECK_EQ(SIZE(Container_metadata)-old_size, 1); 463 } 464 465 void test_container_address_offsets_2() { 466 int old_size = SIZE(Container_metadata); 467 // define a container with an address at offset 1 that we have the size for 468 run("container foo [\n" 469 " x:num\n" 470 " y:address:num\n" 471 "]\n"); 472 reagent r("x:foo"); 473 compute_container_sizes(r, ""); // need to first pre-populate the metadata 474 // global metadata contains just the entry for foo 475 // no entries for non-container types or other junk 476 CHECK_EQ(SIZE(Container_metadata)-old_size, 1); 477 // scan 478 compute_container_address_offsets(r, ""); 479 // compute_container_address_offsets creates no new entries 480 CHECK_EQ(SIZE(Container_metadata)-old_size, 1); 481 // the reagent we scanned knows it has an address at offset 1 482 CHECK_EQ(SIZE(r.metadata.address), 1); 483 CHECK(contains_key(r.metadata.address, set<tag_condition_info>())); 484 const set<address_element_info>& address_offsets = get(r.metadata.address, set<tag_condition_info>()); 485 CHECK_EQ(SIZE(address_offsets), 1); 486 CHECK_EQ(address_offsets.begin()->offset, 1); // 487 CHECK(address_offsets.begin()->payload_type->atom); 488 CHECK_EQ(address_offsets.begin()->payload_type->name, "number"); 489 // the global table contains an identical entry 490 CHECK(contains_key(Container_metadata, r.type)); 491 const set<address_element_info>& address_offsets2 = get(get(Container_metadata, r.type).address, set<tag_condition_info>()); 492 CHECK_EQ(SIZE(address_offsets2), 1); 493 CHECK_EQ(address_offsets2.begin()->offset, 1); // 494 CHECK(address_offsets2.begin()->payload_type->atom); 495 CHECK_EQ(address_offsets2.begin()->payload_type->name, "number"); 496 } 497 498 void test_container_address_offsets_nested() { 499 int old_size = SIZE(Container_metadata); 500 // define a container with a nested container containing an address 501 run("container foo [\n" 502 " x:address:num\n" 503 " y:num\n" 504 "]\n" 505 "container bar [\n" 506 " p:point\n" 507 " f:foo\n" // nested container containing address 508 "]\n"); 509 reagent r("x:bar"); 510 compute_container_sizes(r, ""); // need to first pre-populate the metadata 511 // global metadata contains entries for bar and included types: point and foo 512 // no entries for non-container types or other junk 513 CHECK_EQ(SIZE(Container_metadata)-old_size, 3); 514 // scan 515 compute_container_address_offsets(r, ""); 516 // the reagent we scanned knows it has an address at offset 2 517 CHECK_EQ(SIZE(r.metadata.address), 1); 518 CHECK(contains_key(r.metadata.address, set<tag_condition_info>())); 519 const set<address_element_info>& address_offsets = get(r.metadata.address, set<tag_condition_info>()); 520 CHECK_EQ(SIZE(address_offsets), 1); 521 CHECK_EQ(address_offsets.begin()->offset, 2); // 522 CHECK(address_offsets.begin()->payload_type->atom); 523 CHECK_EQ(address_offsets.begin()->payload_type->name, "number"); 524 // the global table also knows its address offset 525 CHECK(contains_key(Container_metadata, r.type)); 526 const set<address_element_info>& address_offsets2 = get(get(Container_metadata, r.type).address, set<tag_condition_info>()); 527 CHECK_EQ(SIZE(address_offsets2), 1); 528 CHECK_EQ(address_offsets2.begin()->offset, 2); // 529 CHECK(address_offsets2.begin()->payload_type->atom); 530 CHECK_EQ(address_offsets2.begin()->payload_type->name, "number"); 531 // compute_container_address_offsets creates no new entries 532 CHECK_EQ(SIZE(Container_metadata)-old_size, 3); 533 } 534 535 void test_container_address_offsets_from_address() { 536 int old_size = SIZE(Container_metadata); 537 // define a container with an address at offset 0 538 run("container foo [\n" 539 " x:address:num\n" 540 "]\n"); 541 reagent r("x:address:foo"); 542 compute_container_sizes(r, ""); // need to first pre-populate the metadata 543 // global metadata contains just the entry for foo 544 // no entries for non-container types or other junk 545 CHECK_EQ(SIZE(Container_metadata)-old_size, 1); 546 // scan an address to the container 547 compute_container_address_offsets(r, ""); 548 // compute_container_address_offsets creates no new entries 549 CHECK_EQ(SIZE(Container_metadata)-old_size, 1); 550 // scanning precomputed metadata for the container 551 reagent container("x:foo"); 552 CHECK(contains_key(Container_metadata, container.type)); 553 const set<address_element_info>& address_offsets2 = get(get(Container_metadata, container.type).address, set<tag_condition_info>()); 554 CHECK_EQ(SIZE(address_offsets2), 1); 555 CHECK_EQ(address_offsets2.begin()->offset, 0); 556 CHECK(address_offsets2.begin()->payload_type->atom); 557 CHECK_EQ(address_offsets2.begin()->payload_type->name, "number"); 558 } 559 560 void test_container_address_offsets_from_array() { 561 int old_size = SIZE(Container_metadata); 562 // define a container with an address at offset 0 563 run("container foo [\n" 564 " x:address:num\n" 565 "]\n"); 566 reagent r("x:array:foo"); 567 compute_container_sizes(r, ""); // need to first pre-populate the metadata 568 // global metadata contains just the entry for foo 569 // no entries for non-container types or other junk 570 CHECK_EQ(SIZE(Container_metadata)-old_size, 1); 571 // scan an array of the container 572 compute_container_address_offsets(r, ""); 573 // compute_container_address_offsets creates no new entries 574 CHECK_EQ(SIZE(Container_metadata)-old_size, 1); 575 // scanning precomputed metadata for the container 576 reagent container("x:foo"); 577 CHECK(contains_key(Container_metadata, container.type)); 578 const set<address_element_info>& address_offsets2 = get(get(Container_metadata, container.type).address, set<tag_condition_info>()); 579 CHECK_EQ(SIZE(address_offsets2), 1); 580 CHECK_EQ(address_offsets2.begin()->offset, 0); 581 CHECK(address_offsets2.begin()->payload_type->atom); 582 CHECK_EQ(address_offsets2.begin()->payload_type->name, "number"); 583 } 584 585 void test_container_address_offsets_from_address_to_array() { 586 int old_size = SIZE(Container_metadata); 587 // define a container with an address at offset 0 588 run("container foo [\n" 589 " x:address:num\n" 590 "]\n"); 591 reagent r("x:address:array:foo"); 592 compute_container_sizes(r, ""); // need to first pre-populate the metadata 593 // global metadata contains just the entry for foo 594 // no entries for non-container types or other junk 595 CHECK_EQ(SIZE(Container_metadata)-old_size, 1); 596 // scan an address to an array of the container 597 compute_container_address_offsets(r, ""); 598 // compute_container_address_offsets creates no new entries 599 CHECK_EQ(SIZE(Container_metadata)-old_size, 1); 600 // scanning precomputed metadata for the container 601 reagent container("x:foo"); 602 CHECK(contains_key(Container_metadata, container.type)); 603 const set<address_element_info>& address_offsets2 = get(get(Container_metadata, container.type).address, set<tag_condition_info>()); 604 CHECK_EQ(SIZE(address_offsets2), 1); 605 CHECK_EQ(address_offsets2.begin()->offset, 0); 606 CHECK(address_offsets2.begin()->payload_type->atom); 607 CHECK_EQ(address_offsets2.begin()->payload_type->name, "number"); 608 } 609 610 void test_container_address_offsets_from_static_array() { 611 int old_size = SIZE(Container_metadata); 612 // define a container with an address at offset 0 613 run("container foo [\n" 614 " x:address:num\n" 615 "]\n"); 616 reagent r("x:array:foo:10"); 617 compute_container_sizes(r, ""); // need to first pre-populate the metadata 618 // global metadata contains just the entry for foo 619 // no entries for non-container types or other junk 620 CHECK_EQ(SIZE(Container_metadata)-old_size, 1); 621 // scan a static array of the container 622 compute_container_address_offsets(r, ""); 623 // compute_container_address_offsets creates no new entries 624 CHECK_EQ(SIZE(Container_metadata)-old_size, 1); 625 // scanning precomputed metadata for the container 626 reagent container("x:foo"); 627 CHECK(contains_key(Container_metadata, container.type)); 628 const set<address_element_info>& address_offsets2 = get(get(Container_metadata, container.type).address, set<tag_condition_info>()); 629 CHECK_EQ(SIZE(address_offsets2), 1); 630 CHECK_EQ(address_offsets2.begin()->offset, 0); 631 CHECK(address_offsets2.begin()->payload_type->atom); 632 CHECK_EQ(address_offsets2.begin()->payload_type->name, "number"); 633 } 634 635 void test_container_address_offsets_from_address_to_static_array() { 636 int old_size = SIZE(Container_metadata); 637 // define a container with an address at offset 0 638 run("container foo [\n" 639 " x:address:num\n" 640 "]\n"); 641 reagent r("x:address:array:foo:10"); 642 compute_container_sizes(r, ""); // need to first pre-populate the metadata 643 // global metadata contains just the entry for foo 644 // no entries for non-container types or other junk 645 CHECK_EQ(SIZE(Container_metadata)-old_size, 1); 646 // scan an address to a static array of the container 647 compute_container_address_offsets(r, ""); 648 // compute_container_address_offsets creates no new entries 649 CHECK_EQ(SIZE(Container_metadata)-old_size, 1); 650 // scanning precomputed metadata for the container 651 reagent container("x:foo"); 652 CHECK(contains_key(Container_metadata, container.type)); 653 const set<address_element_info>& address_offsets2 = get(get(Container_metadata, container.type).address, set<tag_condition_info>()); 654 CHECK_EQ(SIZE(address_offsets2), 1); 655 CHECK_EQ(address_offsets2.begin()->offset, 0); 656 CHECK(address_offsets2.begin()->payload_type->atom); 657 CHECK_EQ(address_offsets2.begin()->payload_type->name, "number"); 658 } 659 660 void test_container_address_offsets_from_repeated_address_and_array_types() { 661 int old_size = SIZE(Container_metadata); 662 // define a container with an address at offset 0 663 run("container foo [\n" 664 " x:address:num\n" 665 "]\n"); 666 // scan a deep nest of 'address' and 'array' types modifying a container 667 reagent r("x:address:array:address:address:array:foo:10"); 668 compute_container_sizes(r, ""); // need to first pre-populate the metadata 669 // global metadata contains just the entry for foo 670 // no entries for non-container types or other junk 671 CHECK_EQ(SIZE(Container_metadata)-old_size, 1); 672 compute_container_address_offsets(r, ""); 673 // compute_container_address_offsets creates no new entries 674 CHECK_EQ(SIZE(Container_metadata)-old_size, 1); 675 // scanning precomputed metadata for the container 676 reagent container("x:foo"); 677 CHECK(contains_key(Container_metadata, container.type)); 678 const set<address_element_info>& address_offsets2 = get(get(Container_metadata, container.type).address, set<tag_condition_info>()); 679 CHECK_EQ(SIZE(address_offsets2), 1); 680 CHECK_EQ(address_offsets2.begin()->offset, 0); 681 CHECK(address_offsets2.begin()->payload_type->atom); 682 CHECK_EQ(address_offsets2.begin()->payload_type->name, "number"); 683 } 684 685 //: use metadata.address to update refcounts within containers, arrays and 686 //: exclusive containers 687 688 :(before "End Increment Refcounts(canonized_x)") 689 if (is_mu_container(canonized_x) || is_mu_exclusive_container(canonized_x)) { 690 const container_metadata& metadata = get(Container_metadata, canonized_x.type); 691 for (map<set<tag_condition_info>, set<address_element_info> >::const_iterator p = metadata.address.begin(); p != metadata.address.end(); ++p) { 692 if (!all_match(data, p->first)) continue; 693 for (set<address_element_info>::const_iterator info = p->second.begin(); info != p->second.end(); ++info) 694 increment_refcount(data.at(info->offset)); 695 } 696 } 697 698 :(before "End Decrement Refcounts(canonized_x)") 699 if (is_mu_container(canonized_x) || is_mu_exclusive_container(canonized_x)) { 700 trace(9999, "mem") << "need to read old value of '" << to_string(canonized_x) << "' to figure out what refcounts to decrement" << end(); 701 // read from canonized_x but without canonizing again 702 // todo: inline without running canonize all over again 703 reagent/*copy*/ tmp = canonized_x; 704 tmp.properties.push_back(pair<string, string_tree*>("raw", NULL)); 705 vector<double> data = read_memory(tmp); 706 trace(9999, "mem") << "done reading old value of '" << to_string(canonized_x) << "'" << end(); 707 const container_metadata& metadata = get(Container_metadata, canonized_x.type); 708 for (map<set<tag_condition_info>, set<address_element_info> >::const_iterator p = metadata.address.begin(); p != metadata.address.end(); ++p) { 709 if (!all_match(data, p->first)) continue; 710 for (set<address_element_info>::const_iterator info = p->second.begin(); info != p->second.end(); ++info) { 711 int element_address = get_or_insert(Memory, canonized_x.value + info->offset); 712 reagent/*local*/ element; 713 element.set_value(element_address+/*skip refcount*/1); 714 element.type = new type_tree(*info->payload_type); 715 decrement_refcount(element_address, info->payload_type, size_of(element)+/*refcount*/1); 716 } 717 } 718 } 719 720 :(code) 721 bool all_match(const vector<double>& data, const set<tag_condition_info>& conditions) { 722 for (set<tag_condition_info>::const_iterator p = conditions.begin(); p != conditions.end(); ++p) { 723 if (data.at(p->offset) != p->tag) 724 return false; 725 } 726 return true; 727 } 728 729 :(scenario refcounts_put_container) 730 container foo [ 731 a:bar # contains an address 732 ] 733 container bar [ 734 x:address:num 735 ] 736 def main [ 737 1:address:num <- new number:type 738 2:bar <- merge 1:address:num 739 3:address:foo <- new foo:type 740 *3:address:foo <- put *3:address:foo, a:offset, 2:bar 741 ] 742 +run: {1: ("address" "number")} <- new {number: "type"} 743 +mem: incrementing refcount of 1000: 0 -> 1 744 +run: {2: "bar"} <- merge {1: ("address" "number")} 745 +mem: incrementing refcount of 1000: 1 -> 2 746 +run: {3: ("address" "foo"), "lookup": ()} <- put {3: ("address" "foo"), "lookup": ()}, {a: "offset"}, {2: "bar"} 747 # put increments refcount inside container 748 +mem: incrementing refcount of 1000: 2 -> 3 749 750 :(scenario refcounts_put_index_array) 751 container bar [ 752 x:address:num 753 ] 754 def main [ 755 1:address:num <- new number:type 756 2:bar <- merge 1:address:num 757 3:address:array:bar <- new bar:type, 3 758 *3:address:array:bar <- put-index *3:address:array:bar, 0, 2:bar 759 ] 760 +run: {1: ("address" "number")} <- new {number: "type"} 761 +mem: incrementing refcount of 1000: 0 -> 1 762 +run: {2: "bar"} <- merge {1: ("address" "number")} 763 +mem: incrementing refcount of 1000: 1 -> 2 764 +run: {3: ("address" "array" "bar"), "lookup": ()} <- put-index {3: ("address" "array" "bar"), "lookup": ()}, {0: "literal"}, {2: "bar"} 765 # put-index increments refcount inside container 766 +mem: incrementing refcount of 1000: 2 -> 3 767 768 :(scenario refcounts_maybe_convert_container) 769 exclusive-container foo [ 770 a:num 771 b:bar # contains an address 772 ] 773 container bar [ 774 x:address:num 775 ] 776 def main [ 777 1:address:num <- new number:type 778 2:bar <- merge 1:address:num 779 3:foo <- merge 1/b, 2:bar 780 5:bar, 6:bool <- maybe-convert 3:foo, 1:variant/b 781 ] 782 +run: {1: ("address" "number")} <- new {number: "type"} 783 +mem: incrementing refcount of 1000: 0 -> 1 784 +run: {2: "bar"} <- merge {1: ("address" "number")} 785 +mem: incrementing refcount of 1000: 1 -> 2 786 +run: {3: "foo"} <- merge {1: "literal", "b": ()}, {2: "bar"} 787 +mem: incrementing refcount of 1000: 2 -> 3 788 +run: {5: "bar"}, {6: "boolean"} <- maybe-convert {3: "foo"}, {1: "variant", "b": ()} 789 +mem: incrementing refcount of 1000: 3 -> 4 790 791 :(scenario refcounts_copy_doubly_nested) 792 container foo [ 793 a:bar # no addresses 794 b:curr # contains addresses 795 ] 796 container bar [ 797 x:num 798 y:num 799 ] 800 container curr [ 801 x:num 802 y:address:num # address inside container inside container 803 ] 804 def main [ 805 1:address:num <- new number:type 806 2:address:curr <- new curr:type 807 *2:address:curr <- put *2:address:curr, 1:offset/y, 1:address:num 808 3:address:foo <- new foo:type 809 *3:address:foo <- put *3:address:foo, 1:offset/b, *2:address:curr 810 4:foo <- copy *3:address:foo 811 ] 812 +transform: compute address offsets for container foo 813 +transform: checking container foo, element 1 814 +transform: address at offset 3 815 +run: {1: ("address" "number")} <- new {number: "type"} 816 +mem: incrementing refcount of 1000: 0 -> 1 817 # storing an address in a container updates its refcount 818 +run: {2: ("address" "curr"), "lookup": ()} <- put {2: ("address" "curr"), "lookup": ()}, {1: "offset", "y": ()}, {1: ("address" "number")} 819 +mem: incrementing refcount of 1000: 1 -> 2 820 # storing a container in a container updates refcounts of any contained addresses 821 +run: {3: ("address" "foo"), "lookup": ()} <- put {3: ("address" "foo"), "lookup": ()}, {1: "offset", "b": ()}, {2: ("address" "curr"), "lookup": ()} 822 +mem: incrementing refcount of 1000: 2 -> 3 823 # copying a container containing a container containing an address updates refcount 824 +run: {4: "foo"} <- copy {3: ("address" "foo"), "lookup": ()} 825 +mem: incrementing refcount of 1000: 3 -> 4 826 827 :(scenario refcounts_copy_exclusive_container_within_container) 828 container foo [ 829 a:num 830 b:bar 831 ] 832 exclusive-container bar [ 833 x:num 834 y:num 835 z:address:num 836 ] 837 def main [ 838 1:address:num <- new number:type 839 2:bar <- merge 0/x, 34 840 3:foo <- merge 12, 2:bar 841 5:bar <- merge 1/y, 35 842 6:foo <- merge 13, 5:bar 843 8:bar <- merge 2/z, 1:address:num 844 9:foo <- merge 14, 8:bar 845 11:foo <- copy 9:foo 846 ] 847 +run: {1: ("address" "number")} <- new {number: "type"} 848 +mem: incrementing refcount of 1000: 0 -> 1 849 # no change while merging items of other types 850 +run: {8: "bar"} <- merge {2: "literal", "z": ()}, {1: ("address" "number")} 851 +mem: incrementing refcount of 1000: 1 -> 2 852 +run: {9: "foo"} <- merge {14: "literal"}, {8: "bar"} 853 +mem: incrementing refcount of 1000: 2 -> 3 854 +run: {11: "foo"} <- copy {9: "foo"} 855 +mem: incrementing refcount of 1000: 3 -> 4 856 857 :(scenario refcounts_copy_container_within_exclusive_container) 858 exclusive-container foo [ 859 a:num 860 b:bar 861 ] 862 container bar [ 863 x:num 864 y:num 865 z:address:num 866 ] 867 def main [ 868 1:address:num <- new number:type 869 2:foo <- merge 0/a, 34 870 6:foo <- merge 0/a, 35 871 10:bar <- merge 2/x, 15/y, 1:address:num 872 13:foo <- merge 1/b, 10:bar 873 17:foo <- copy 13:foo 874 ] 875 +run: {1: ("address" "number")} <- new {number: "type"} 876 +mem: incrementing refcount of 1000: 0 -> 1 877 # no change while merging items of other types 878 +run: {10: "bar"} <- merge {2: "literal", "x": ()}, {15: "literal", "y": ()}, {1: ("address" "number")} 879 +mem: incrementing refcount of 1000: 1 -> 2 880 +run: {13: "foo"} <- merge {1: "literal", "b": ()}, {10: "bar"} 881 +mem: incrementing refcount of 1000: 2 -> 3 882 +run: {17: "foo"} <- copy {13: "foo"} 883 +mem: incrementing refcount of 1000: 3 -> 4 884 885 :(scenario refcounts_copy_exclusive_container_within_exclusive_container) 886 exclusive-container foo [ 887 a:num 888 b:bar 889 ] 890 exclusive-container bar [ 891 x:num 892 y:address:num 893 ] 894 def main [ 895 1:address:num <- new number:type 896 10:foo <- merge 1/b, 1/y, 1:address:num 897 20:foo <- copy 10:foo 898 ] 899 +run: {1: ("address" "number")} <- new {number: "type"} 900 +mem: incrementing refcount of 1000: 0 -> 1 901 # no change while merging items of other types 902 +run: {10: "foo"} <- merge {1: "literal", "b": ()}, {1: "literal", "y": ()}, {1: ("address" "number")} 903 +mem: incrementing refcount of 1000: 1 -> 2 904 +run: {20: "foo"} <- copy {10: "foo"} 905 +mem: incrementing refcount of 1000: 2 -> 3 906 907 :(scenario refcounts_copy_array_within_container) 908 container foo [ 909 x:address:array:num 910 ] 911 def main [ 912 1:address:array:num <- new number:type, 3 913 2:foo <- merge 1:address:array:num 914 3:address:array:num <- new number:type, 5 915 2:foo <- merge 3:address:array:num 916 ] 917 +run: {1: ("address" "array" "number")} <- new {number: "type"}, {3: "literal"} 918 +mem: incrementing refcount of 1000: 0 -> 1 919 +run: {2: "foo"} <- merge {1: ("address" "array" "number")} 920 +mem: incrementing refcount of 1000: 1 -> 2 921 +run: {2: "foo"} <- merge {3: ("address" "array" "number")} 922 +mem: decrementing refcount of 1000: 2 -> 1 923 924 :(scenario refcounts_copy_address_within_static_array_within_container) 925 container foo [ 926 a:array:bar:3 927 b:address:num 928 ] 929 container bar [ 930 y:num 931 z:address:num 932 ] 933 def main [ 934 1:address:num <- new number:type 935 2:bar <- merge 34, 1:address:num 936 10:array:bar:3 <- create-array 937 put-index 10:array:bar:3, 1, 2:bar 938 20:foo <- merge 10:array:bar:3, 1:address:num 939 1:address:num <- copy 0 940 2:bar <- merge 34, 1:address:num 941 put-index 10:array:bar:3, 1, 2:bar 942 20:foo <- merge 10:array:bar:3, 1:address:num 943 ] 944 +run: {1: ("address" "number")} <- new {number: "type"} 945 +mem: incrementing refcount of 1000: 0 -> 1 946 +run: {2: "bar"} <- merge {34: "literal"}, {1: ("address" "number")} 947 +mem: incrementing refcount of 1000: 1 -> 2 948 +run: put-index {10: ("array" "bar" "3")}, {1: "literal"}, {2: "bar"} 949 +mem: incrementing refcount of 1000: 2 -> 3 950 +run: {20: "foo"} <- merge {10: ("array" "bar" "3")}, {1: ("address" "number")} 951 +mem: incrementing refcount of 1000: 3 -> 4 952 +mem: incrementing refcount of 1000: 4 -> 5 953 +run: {1: ("address" "number")} <- copy {0: "literal"} 954 +mem: decrementing refcount of 1000: 5 -> 4 955 +run: {2: "bar"} <- merge {34: "literal"}, {1: ("address" "number")} 956 +mem: decrementing refcount of 1000: 4 -> 3 957 +run: put-index {10: ("array" "bar" "3")}, {1: "literal"}, {2: "bar"} 958 +mem: decrementing refcount of 1000: 3 -> 2 959 +run: {20: "foo"} <- merge {10: ("array" "bar" "3")}, {1: ("address" "number")} 960 +mem: decrementing refcount of 1000: 2 -> 1 961 +mem: decrementing refcount of 1000: 1 -> 0 962 963 :(scenario refcounts_handle_exclusive_containers_with_different_tags) 964 container foo1 [ 965 x:address:num 966 y:num 967 ] 968 container foo2 [ 969 x:num 970 y:address:num 971 ] 972 exclusive-container bar [ 973 a:foo1 974 b:foo2 975 ] 976 def main [ 977 1:address:num <- copy 12000/unsafe # pretend allocation 978 *1:address:num <- copy 34 979 2:bar <- merge 0/foo1, 1:address:num, 97 980 5:address:num <- copy 13000/unsafe # pretend allocation 981 *5:address:num <- copy 35 982 6:bar <- merge 1/foo2, 98, 5:address:num 983 2:bar <- copy 6:bar 984 ] 985 +run: {2: "bar"} <- merge {0: "literal", "foo1": ()}, {1: ("address" "number")}, {97: "literal"} 986 +mem: incrementing refcount of 12000: 1 -> 2 987 +run: {6: "bar"} <- merge {1: "literal", "foo2": ()}, {98: "literal"}, {5: ("address" "number")} 988 +mem: incrementing refcount of 13000: 1 -> 2 989 +run: {2: "bar"} <- copy {6: "bar"} 990 +mem: incrementing refcount of 13000: 2 -> 3 991 +mem: decrementing refcount of 12000: 2 -> 1 992 993 :(code) 994 bool is_mu_container(const reagent& r) { 995 return is_mu_container(r.type); 996 } 997 bool is_mu_container(const type_tree* type) { 998 if (!type) return false; 999 // End is_mu_container(type) Special-cases 1000 if (type->value == 0) return false; 1001 type_info& info = get(Type, type->value); 1002 return info.kind == CONTAINER; 1003 } 1004 1005 bool is_mu_exclusive_container(const reagent& r) { 1006 return is_mu_exclusive_container(r.type); 1007 } 1008 bool is_mu_exclusive_container(const type_tree* type) { 1009 if (!type) return false; 1010 // End is_mu_exclusive_container(type) Special-cases 1011 if (type->value == 0) return false; 1012 type_info& info = get(Type, type->value); 1013 return info.kind == EXCLUSIVE_CONTAINER; 1014 }