aboutsummaryrefslogtreecommitdiff
path: root/trace-preamble.js
diff options
context:
space:
mode:
authorWojtek Kosior <koszko@koszko.org>2024-02-14 23:13:36 +0100
committerWojtek Kosior <koszko@koszko.org>2024-02-14 23:19:19 +0100
commitd761f509f2e0ea2503f7f75014e396cb6bb26802 (patch)
treeb36d65029f5305781e73ce629517103a2cbd179d /trace-preamble.js
downloadtracifyjs-d761f509f2e0ea2503f7f75014e396cb6bb26802.tar.gz
tracifyjs-d761f509f2e0ea2503f7f75014e396cb6bb26802.zip
Initial commit.HEADapprentice
Diffstat (limited to 'trace-preamble.js')
-rw-r--r--trace-preamble.js146
1 files changed, 146 insertions, 0 deletions
diff --git a/trace-preamble.js b/trace-preamble.js
new file mode 100644
index 00000000..75c899d0
--- /dev/null
+++ b/trace-preamble.js
@@ -0,0 +1,146 @@
+const tracing = (() => {
+ const log = [];
+ const objects = new Map();
+
+ let call_stack_top = null;
+
+ function record_value(log_entry, name, value, as_array=false) {
+ log_entry[name] = value;
+
+ for (const _value of as_array? value : [value]) {
+ if (_value !== null && _value !== undefined
+ && _value !== false && _value !== true) {
+ const relevant_log_entries = objects.get(_value) || [];
+
+ relevant_log_entries.push(log_entry);
+ objects.set(_value, relevant_log_entries);
+ }
+ }
+
+ return value;
+ }
+
+ function with_log_entry(op_name, line, column, cb) {
+ const log_entry = {
+ op_name, line, column,
+ id: log.length,
+ parent_call: call_stack_top
+ };
+ const saved_stack_top = call_stack_top;
+
+ log.push(log_entry);
+
+ call_stack_top = log_entry;
+
+ try {
+ return cb(log_entry);
+ } catch(ex) {
+ record_value(log_entry, "error", ex);
+
+ throw ex;
+ } finally {
+ call_stack_top = saved_stack_top;
+ }
+ }
+
+ return {
+ get_objects: () => objects,
+
+ get_log: () => log,
+
+ record_binary: function(line, column, operation_name, operation,
+ left_producer, right_producer) {
+ function go(log_entry) {
+ const left = record_value(log_entry, "left", left_producer());
+
+ const right = record_value(log_entry,
+ "right",
+ right_producer());
+
+ const result = operation(left, right);
+
+ return record_value(log_entry, "result", result);
+ }
+
+ return with_log_entry(operation_name, line, column, go);
+ },
+
+ record_lazy_binary: function(line, column, operation_name, operation,
+ left_producer, right_producer) {
+ function go(log_entry) {
+ const left = record_value(log_entry, "left", left_producer());
+
+ const result = operation(left, right_producer);
+
+ return record_value(log_entry, "result", result);
+ }
+
+ return with_log_entry(operation_name, line, column, go);
+ },
+
+ record_call: function(line, column, function_producer, optional,
+ args_producer) {
+ function go(log_entry) {
+ const function_object = record_value(log_entry,
+ "function_object",
+ function_producer());
+
+ const record_args = () => record_value(log_entry,
+ "args",
+ args_producer(),
+ true);
+
+ const result = optional ?
+ function_object?.(...record_args()) :
+ function_object(...record_args());
+
+ return record_value(log_entry, "result", result);
+ }
+
+ return with_log_entry("call", line, column, go);
+ },
+
+ record_prop_call: function(line, column, this_producer,
+ property_optional, property_producer,
+ optional, args_producer) {
+ function go(log_entry) {
+ const bound_this = record_value(log_entry,
+ "bound_this",
+ this_producer());
+
+ const record_property = () => record_value(
+ log_entry, "property", property_producer()
+ );
+
+ const function_object =
+ record_value(log_entry,
+ "function_object",
+ property_optional ?
+ bound_this?.[record_property()] :
+ bound_this[record_property()]);
+
+ if ((function_object === null ||
+ function_object === undefined) &&
+ optional)
+ return undefined;
+
+ const record_args = () => record_value(log_entry,
+ "args",
+ args_producer(),
+ true);
+
+ const result = Function.apply.call(function_object,
+ bound_this,
+ record_args());
+
+ return record_value(log_entry, "result", result);
+ }
+
+ return with_log_entry(
+ `obj${property_optional}[prop]${optional}() call`,
+ line, column,
+ go
+ );
+ }
+ };
+})();