C = require('iced-runtime').const

exports.skip = true

ipush = (e, msg) ->
  if msg?
    # Note: `e.istack = []` will be a noop if e is a primitive type
    # like number or string. We have to check if `e.istack` exists
    # before pushing so we don't crash.
    e.istack = [] unless e.istack?
    e.istack?.push msg

copy_trace = (src, dst) ->
  dst[C.trace] = src[C.trace]
  dst

# Error short-circuit connector with more verbose istack.

# When resulting `esc` function is called, an istack entry will be constructed
# based on iced trace object. This way istack will be populated by not only esc
# identifiers (usually call site function names) but also filenames and line
# numbers of call sites of individual esc() calls. If an error happens and
# short-circuited back from deep in some test, it should be easier to identify
# where exactly it originates from using the new istack.

exports.make_esc = make_esc = (gcb, where) ->
  # Create "global where" based on make_esc() call site.
  where = make_esc.caller?.name unless where?
  return (lcb) ->
    # Create "local where" based on esc() call site. So every
    # esc(defer(...)) will have its own istack entry, if iced
    # trace object is available.
    sb = [ where ]
    if (tr = lcb[C.trace])
      sb.push "Trace:"
      sb.push tr[C.funcname]
      sb.push "(#{tr[C.filename]}:#{tr[C.lineno] + 1})"
    local_where = sb.filter((x) -> x).join(' ')
    copy_trace lcb, (err, args...) ->
      if not err? then lcb args...
      else if not gcb.__esc
        gcb.__esc = true
        ipush err, local_where
        gcb err

exports.patch_make_esc = () ->
  iced_error = require 'iced-error'
  iced_error.make_esc = make_esc
