From d761f509f2e0ea2503f7f75014e396cb6bb26802 Mon Sep 17 00:00:00 2001 From: Wojtek Kosior Date: Wed, 14 Feb 2024 23:13:36 +0100 Subject: Initial commit. --- trace-preamble.js | 146 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 146 insertions(+) create mode 100644 trace-preamble.js (limited to 'trace-preamble.js') 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 + ); + } + }; +})(); -- cgit v1.2.3