import sys
import time
import threading
import traceback
import pydermonkey

rt = pydermonkey.Runtime()
cx = rt.new_context()
globalobj = cx.new_object()
cx.init_standard_classes(globalobj)

def foo(cx, this, args):
    return cx.call_function(this, args[0], ())

cx.define_property(globalobj,
                   'foo',
                   cx.new_function(foo, 'foo'))

def jsprint(cx, this, args):
    if len(args) > 0:
        print args[0]

cx.define_property(globalobj,
                   'print',
                   cx.new_function(jsprint, 'print'))

def opcb(cx):
    # Don't do anything; if a keyboard interrupt was triggered,
    # it'll get raised here automatically.
    pass

cx.set_operation_callback(opcb)

class State(object):
    def __init__(self):
        self.curr_exc = None
        self.curr_tb = None
        self.curr_js_stack = None

state = State()

def throwhook(cx):
    curr_exc = cx.get_pending_exception()
    if state.curr_exc != curr_exc:
        state.curr_exc = curr_exc
        state.py_stack = traceback.extract_stack()
        state.js_stack = cx.get_stack()

cx.set_throw_hook(throwhook)

def watchdog():
    while 1:
        time.sleep(0.25)
        cx.trigger_operation_callback()

thread = threading.Thread(target=watchdog)
thread.setDaemon(True)
thread.start()

filename = 'test.js'

def make_stack(js_stack):
    lines = []
    while js_stack:
        if js_stack['script']:
            script = js_stack['script']
            thing = dict(filename = script.filename,
                         lineno = js_stack['lineno'],
                         name = '<module>')
        elif js_stack['function'] and not js_stack['function'].is_python:
            func = js_stack['function']
            thing = dict(filename = func.filename,
                         lineno = js_stack['lineno'],
                         name = func.name)
        else:
            thing = None
        if thing:
            lines.insert(0, "  File \"%(filename)s\", line %(lineno)d, in %(name)s" % thing)
        js_stack = js_stack['caller']
    lines.insert(0, "Traceback (most recent call last):")
    return '\n'.join(lines)

try:
    cx.evaluate_script(globalobj, open(filename).read(), filename, 1)
except pydermonkey.error, e:
    print make_stack(state.js_stack)
    print e.args[1]
