diff options
author | Wojtek Kosior <koszko@koszko.org> | 2024-02-14 23:13:36 +0100 |
---|---|---|
committer | Wojtek Kosior <koszko@koszko.org> | 2024-02-14 23:19:19 +0100 |
commit | d761f509f2e0ea2503f7f75014e396cb6bb26802 (patch) | |
tree | b36d65029f5305781e73ce629517103a2cbd179d /trace-preamble.js | |
download | tracifyjs-d761f509f2e0ea2503f7f75014e396cb6bb26802.tar.gz tracifyjs-d761f509f2e0ea2503f7f75014e396cb6bb26802.zip |
Initial commit.HEADapprentice
Diffstat (limited to 'trace-preamble.js')
-rw-r--r-- | trace-preamble.js | 146 |
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 + ); + } + }; +})(); |