The current execution context has a "custom stack" context with two internal slots [[Custom Stack Start]] and [[Custom Stack Override]].
console.stack - Assert that the first argument is an array. Store the current stack frame context at the VM level (same as new Error().stack) in the current execution context's [[Custom Stack Start]] slot. Store the first argument array in the current execution context's [[Custom Stack Override]] slot.
console.endStack - Clear the [[Custom Stack Start]] and [[Custom Stack Override]] stacks.
console.trace, console.warn, console.error etc. - For any function that logs stacks associated with the call. If there is something in the [[Custom Stack Start]] slot. Find the current stack from the current execution context. Find the common ancestor between [[Custom Stack Start]] and the current stack. Remove all the common ancestors. Replace them with [[Custom Stack Override]] in the root of the stack.
If a break point happens, e.g. due to an error being thrown, debugger call or custom break points. If there is something in the [[Custom Stack Start]] slot, use the same mechanism as console.log to determine the current stack to visualize in a debugger instead of the native stack. The scope property contains an object. Every "own" property is considered to be a variable with that name in scope of that stack frame. Useful for debugging.
TODO: Consider what should happen to new Error().stack if the error is created within a custom scope.
We could have
console.stack(new Error().stack)accept a string of the same format as the engine specific stack format. That would requirenew Error().stackto be overridden byconsole.stackso that they can be chained like this.Another alternative would be to have a different way of extracting a stack. E.g.
console.snapshotStack()that would give you the same thing asnew Error().stackbut it would be override aware.