Great! This seems like a general memory/internals test regarding cloning and
destruct:
==
user@elli:~/Downloads/Remote/LPC/GEM$ cat tests/clone_object.c
int x; object tmp, tmp2;
void create () {
x = 42;
}
void set_x (int i) { x
= i; } int query_x () { return x; }
void lpc_main (array argv)
{
object ob;
printf ("query_x(): %d
", query_x ());
ob = clone_object (this_object ());
printf ("Cloned object: %s
", ob);
printf ("OB->query_x (): %d
", ob->query_x ());
ob->set_x (999);
printf ("query_x(): %d
", query_x ());
printf ("OB->query_x (): %d
", ob->query_x ());
destruct (ob);
foo ();
printf (">>> out of foo()
");
ob = 0;
ob = clone_object (this_object ());
printf ("Cloned object: %s
", ob);
printf ("OB->query_x (): %d
", ob->query_x ());
printf ("End
");
tmp = clone_object
(this_object ());
tmp2 = tmp;
printf ("Cloned object: %s
", tmp);
printf ("OB->query_x (): %d
", tmp->query_x ()); destruct (tmp);
call_out ("out", 1);
}
void foo ()
{ object ob;
ob = clone_object
(this_object ());
printf (">>> cloned %s
", ob);
destruct (ob);
printf (">>> destructed
");
}
void out ()
{ printf ("Exiting
"); }
user@elli:~/Downloads/Remote/LPC/GEM$
==
Original/reference run output:
==
query_x(): 42
Cloned object: <object:tests/clone_object.c#1>
OB->query_x (): 42
query_x(): 42
OB->query_x (): 999
>>> cloned <object:tests/clone_object.c#2>
>>> destructed
>>> out of foo()
Cloned object: <object:tests/clone_object.c#3>
OB->query_x (): 42
End
Cloned object: <object:tests/clone_object.c#4>
OB->query_x (): 42
==
This test perfectly exercises the final major pillar of any MUD or Virtual
Machine: Object Lifecycle Management.
In a C-based LPC engine, destroying an object wipes the memory struct. If
another variable (like tmp2) was pointing to it, the engine typically caught
the dangling pointer and evaluated it to 0.
In JavaScript, we cannot forcibly delete an object from RAM if another
variable still holds a reference to it. However, we can semantically mimic
memory destruction by introducing a "Tombstone" pattern. When destruct(ob) is
called, we will flag ob.__destructed = true.
Our call_other (->) will check for this flag and instantly return 0, safely
defusing method calls.
Our printf will check for this flag and format the object string as "0"
instead of its file name!
We also need to update our VM so that it perfectly formats the object strings
(like <object:tests/clone_object.c#1>) and tracks clone ID counters.
Here is the two-step update to perfectly mimic C-style memory destruction.
Step 1: Update efuns.js
Replace your efuns.js with this code. I've updated the formatValue helper
inside printf to output <object:filename> format, and to handle destructed
objects by printing "0"!
==
Step 2: Update harness.js
We need to add the destruct efun to our AST routing, and implement the
Master/Clone ID separation inside the JSCodeGenerator. Note that we now keep
the actual file path .c as the object blueprint name to identically match the
GD formatting output!
==
Run node harness.js tests/clone_object.c.
It will perfectly match your C output string-for-string, properly invalidating
tmp and tmp2 via the tombstone check. You will also see an additional Exiting
statement print at the very end because our Node VM correctly waits for the
asynchronous call_out to evaluate before terminating!