/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
 *
 * ***** BEGIN LICENSE BLOCK *****
 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
 *
 * The contents of this file are subject to the Mozilla Public License Version
 * 1.1 (the "License"); you may not use this file except in compliance with
 * the License. You may obtain a copy of the License at
 * http://www.mozilla.org/MPL/
 *
 * Software distributed under the License is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
 * for the specific language governing rights and limitations under the
 * License.
 *
 * The Original Code is mozilla.org code.
 *
 * The Initial Developer of the Original Code is
 * Netscape Communications Corporation.
 * Portions created by the Initial Developer are Copyright (C) 1998
 * the Initial Developer. All Rights Reserved.
 *
 * Contributor(s):
 *
 * Alternatively, the contents of this file may be used under the terms of
 * either the GNU General Public License Version 2 or later (the "GPL"), or
 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
 * in which case the provisions of the GPL or the LGPL are applicable instead
 * of those above. If you wish to allow use of your version of this file only
 * under the terms of either the GPL or the LGPL, and not to allow others to
 * use your version of this file under the terms of the MPL, indicate your
 * decision by deleting the provisions above and replace them with the notice
 * and other provisions required by the GPL or the LGPL. If you do not delete
 * the provisions above, a recipient may use your version of this file under
 * the terms of any one of the MPL, the GPL or the LGPL.
 *
 * ***** END LICENSE BLOCK ***** */

/*
* Reflects JSD api into JavaScript
*/

#include "jsdbpriv.h"

/***************************************************************************/
/* Non-const Properties */

static const char _str_ThreadStateHandle[]  = "ThreadStateHandle";
static const char _str_InterruptSet[]       = "InterruptSet";
static const char _str_Evaluating[]         = "Evaluating";
static const char _str_DebuggerDepth[]      = "DebuggerDepth";
static const char _str_ReturnExpression[]   = "ReturnExpression";

static jsval _valFalse = JSVAL_FALSE;
static jsval _valTrue  = JSVAL_TRUE;
static jsval _valNull  = JSVAL_NULL;


enum jsd_prop_ids
{
    JSD_PROP_ID_THREADSTATE_HANDLE  = -1,
    JSD_PROP_ID_INTERRUPT_SET       = -2,
    JSD_PROP_ID_EVALUATING          = -3,
    JSD_PROP_ID_DEBUGGER_DEPTH      = -4,
    JSD_PROP_ID_RETURN_EXPRESSION   = -5,
    COMPLETELY_IGNORED_ID           = 0 /* just to avoid tracking the comma */
};

static JSPropertySpec jsd_properties[] = {
    {_str_ThreadStateHandle, JSD_PROP_ID_THREADSTATE_HANDLE, JSPROP_ENUMERATE|JSPROP_PERMANENT, 0, 0},
    {_str_InterruptSet,      JSD_PROP_ID_INTERRUPT_SET,      JSPROP_ENUMERATE|JSPROP_PERMANENT, 0, 0},
    {_str_Evaluating,        JSD_PROP_ID_EVALUATING,         JSPROP_ENUMERATE|JSPROP_PERMANENT, 0, 0},
    {_str_DebuggerDepth,     JSD_PROP_ID_DEBUGGER_DEPTH,     JSPROP_ENUMERATE|JSPROP_PERMANENT, 0, 0},
    {_str_ReturnExpression,  JSD_PROP_ID_RETURN_EXPRESSION,  JSPROP_ENUMERATE|JSPROP_PERMANENT, 0, 0},
    {0, 0, 0, 0, 0}
};

static JSBool
_defineNonConstProperties(JSDB_Data* data)
{
    JSContext* cx = data->cxDebugger;
    JSObject*  ob = data->jsdOb;
    jsval val;
    JSBool ignored;

    if(!JS_DefineProperties(cx, ob, jsd_properties))
        return JS_FALSE;

    /* set initial state of some of the properties */

    if(!JS_SetProperty(cx, ob, _str_ThreadStateHandle, &_valNull))
        return JS_FALSE;

    if(!JS_SetProperty(cx, ob, _str_InterruptSet, &_valFalse))
        return JS_FALSE;

    if(!JS_SetProperty(cx, ob, _str_Evaluating, &_valFalse))
        return JS_FALSE;

    val = INT_TO_JSVAL(data->debuggerDepth);
    if(!JS_SetProperty(cx, ob, _str_DebuggerDepth, &val))
        return JS_FALSE;
    if(!JS_SetPropertyAttributes(cx, ob, _str_DebuggerDepth,
                                 JSPROP_READONLY |
                                 JSPROP_ENUMERATE |
                                 JSPROP_PERMANENT,
                                 &ignored))
        return JS_FALSE;

    if(!JS_SetProperty(cx, ob, _str_ReturnExpression, &_valNull))
        return JS_FALSE;

    return JS_TRUE;
}

JSBool
jsdb_SetThreadState(JSDB_Data* data, JSDThreadState* jsdthreadstate)
{
    jsval val;

    data->jsdthreadstate = jsdthreadstate;
    if(jsdthreadstate)
        val = P2H_THREADSTATE(data->cxDebugger, jsdthreadstate);
    else
        val = JSVAL_NULL;

    return JS_SetProperty(data->cxDebugger, data->jsdOb,
                          _str_ThreadStateHandle, &val);
}

/***************************************************************************/
/*
*  System to store JSD_xxx handles in jsvals. This supports tracking the
*  handle's type - both for debugging and for automatic 'dropping' of
*  reference counted handle types (e.g. JSDValue).
*/

typedef struct JSDBHandle
{
    void* ptr;
    JSDBHandleType type;
} JSDBHandle;

static const char _str_HandleNameString[]   = "handle_name";

JS_STATIC_DLL_CALLBACK(void)
handle_finalize(JSContext *cx, JSObject *obj)
{
    JSDBHandle* p;
    JSObject* pobj;
    JSDB_Data* data;
    pobj = JS_GetParent(cx, obj);
    if (!pobj)
        return;
    data = (JSDB_Data*) JS_GetPrivate(cx, pobj);
    if (!data)
        return;

    p = (JSDBHandle*) JS_GetPrivate(cx, obj);
    if(p)
    {
        switch(p->type)
        {
          case JSDB_VALUE:
            JSD_DropValue(data->jsdcTarget, (JSDValue*) p->ptr);
            break;
          case JSDB_PROPERTY:
            JSD_DropProperty(data->jsdcTarget, (JSDProperty*) p->ptr);
            break;
          default:
            break;
        }
        free(p);
    }
}

JSClass jsdb_HandleClass = {
    "JSDHandle",
    JSCLASS_HAS_PRIVATE,
    JS_PropertyStub,  JS_PropertyStub,  JS_PropertyStub,  JS_PropertyStub,
    JS_EnumerateStub, JS_ResolveStub,   JS_ConvertStub,   handle_finalize,
    JSCLASS_NO_OPTIONAL_MEMBERS
};

JS_STATIC_DLL_CALLBACK(JSBool)
handle_toString(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
    if(!JS_InstanceOf(cx, obj, &jsdb_HandleClass, argv) ||
       !JS_GetProperty(cx, obj, _str_HandleNameString, rval))
    {
        JS_ASSERT(0);
        return JS_FALSE;
    }
    return JS_TRUE;
}

static JSFunctionSpec handle_methods[] = {
    {"toString",   handle_toString,        0, 0, 0},
    {0, 0, 0, 0, 0}
};

void*
jsdb_HandleValToPointer(JSContext *cx, jsval val, JSDBHandleType type)
{
    JSDBHandle* p;
    JSObject *obj;

    if(!JSVAL_IS_OBJECT(val) ||
       !(obj = JSVAL_TO_OBJECT(val)) ||
       !JS_InstanceOf(cx, obj, &jsdb_HandleClass, NULL) ||
       !(p = (JSDBHandle*) JS_GetPrivate(cx, obj)))
    {
/*         JS_ASSERT(0); */
        return NULL;
    }
    if (p->type != type)
        return NULL;
    JS_ASSERT(p->ptr);
    return p->ptr;
}

jsval
jsdb_PointerToNewHandleVal(JSContext *cx, void* ptr, JSDBHandleType type)
{
    JSDBHandle* p;
    JSObject* obj;
    char* name;
    char* type_name;
    JSString* name_str;

    /* OK to fail silently on NULL pointer */
    if(!ptr)
        return JSVAL_NULL;

    if(!(p = (JSDBHandle*)malloc(sizeof(JSDBHandle))))
    {
        JS_ASSERT(0);
        return JSVAL_NULL;
    }
    JS_ASSERT(!(((int)p) & 1));    /* must be 2-byte-aligned */

    p->ptr = ptr;
    p->type = type;

    if(!(obj = JS_NewObject(cx, &jsdb_HandleClass, NULL, NULL)) ||
       !JS_SetPrivate(cx, obj, p))
    {
        JS_ASSERT(0);
        return JSVAL_NULL;
    }

    switch(type)
    {
      case JSDB_GENERIC:
        type_name = "GENERIC";
        break;
      case JSDB_CONTEXT:
        type_name = "CONTEXT";
        break;
      case JSDB_SCRIPT:
        type_name = "SCRIPT";
        break;
      case JSDB_SOURCETEXT:
        type_name = "SOURCETEXT";
        break;
      case JSDB_THREADSTATE:
        type_name = "THREADSTATE";
        break;
      case JSDB_STACKFRAMEINFO:
        type_name = "STACKFRAMEINFO";
        break;
      case JSDB_VALUE:
        type_name = "VALUE";
        break;
      case JSDB_PROPERTY:
        type_name = "PROPERTY";
        break;
      case JSDB_OBJECT:
        type_name = "OBJECT";
        break;
      default:
        JS_ASSERT(0);
        type_name = "BOGUS";
        break;
    }

    name = JS_smprintf("%s:%x", type_name, ptr);
    if(!name ||
       !(name_str = JS_NewString(cx, name, strlen(name))) ||
       !JS_DefineProperty(cx, obj,
                          _str_HandleNameString,
                          STRING_TO_JSVAL(name_str),
                          NULL, NULL,
                          JSPROP_READONLY|JSPROP_PERMANENT))
    {
        JS_ASSERT(0);
        return JSVAL_NULL;
    }
    return OBJECT_TO_JSVAL(obj);
}

JSBool _initHandleSystem(JSDB_Data* data)
{
    return (JSBool) JS_InitClass(data->cxDebugger, data->globDebugger,
                                 NULL, &jsdb_HandleClass, NULL, 0,
                                 NULL, handle_methods, NULL, NULL);
}

/***************************************************************************/

JSBool
jsdb_EvalReturnExpression(JSDB_Data* data, jsval* rval)
{
    JSDStackFrameInfo* frame;
    jsval expressionVal;
    JSString* expressionString;
    JSContext* cx = data->cxDebugger;
    JSBool result = JS_FALSE;

    frame = JSD_GetStackFrame(data->jsdcTarget, data->jsdthreadstate);
    if(!frame)
        return JS_FALSE;

    if(!JS_GetProperty(cx, data->jsdOb, _str_ReturnExpression, &expressionVal))
        return JS_FALSE;

    if(!(expressionString = JS_ValueToString(cx, expressionVal)))
        return JS_FALSE;

    JS_SetProperty(cx, data->jsdOb, _str_Evaluating, &_valTrue);
    if(JSD_EvaluateScriptInStackFrame(data->jsdcTarget,
                                      data->jsdthreadstate,
                                      frame,
                                      JS_GetStringBytes(expressionString),
                                      JS_GetStringLength(expressionString),
                                      "ReturnExpressionEval", 1, rval))
    {
        result = JS_TRUE;
    }
    JS_SetProperty(cx, data->jsdOb, _str_Evaluating, &_valFalse);
    JS_SetProperty(cx, data->jsdOb, _str_ReturnExpression, &_valNull);
    return result;
}

/***************************************************************************/
/* High Level calls */

JS_STATIC_DLL_CALLBACK(JSBool)
GetMajorVersion(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
    *rval = INT_TO_JSVAL(JSD_GetMajorVersion());
    return JS_TRUE;
}

JS_STATIC_DLL_CALLBACK(JSBool)
GetMinorVersion(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
    *rval = INT_TO_JSVAL(JSD_GetMinorVersion());
    return JS_TRUE;
}

/***************************************************************************/
/* Script functions */

JS_STATIC_DLL_CALLBACK(JSBool)
LockScriptSubsystem(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
    JSDB_Data* data = (JSDB_Data*) JS_GetContextPrivate(cx);
    JS_ASSERT(data);
    JSD_LockScriptSubsystem(data->jsdcTarget);
    *rval = JSVAL_TRUE;
    return JS_TRUE;
}

JS_STATIC_DLL_CALLBACK(JSBool)
UnlockScriptSubsystem(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
    JSDB_Data* data = (JSDB_Data*) JS_GetContextPrivate(cx);
    JS_ASSERT(data);
    JSD_UnlockScriptSubsystem(data->jsdcTarget);
    *rval = JSVAL_TRUE;
    return JS_TRUE;
}

JS_STATIC_DLL_CALLBACK(JSBool)
IterateScripts(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
    JSDScript *iterp = NULL;
    JSDScript *script;
    JSFunction *fun;
    jsval argv0;
    int count = 0;
    JSDB_Data* data = (JSDB_Data*) JS_GetContextPrivate(cx);
    JS_ASSERT(data);

    if(argc < 1 || !(fun = JS_ValueToFunction(cx, argv[0])))
    {
        JS_ReportError(cx, "IterateScripts requires a function param");
        return JS_FALSE;
    }

    /* We pass along any additional args, so patch and leverage argv */
    argv0 = argv[0];
    JSD_LockScriptSubsystem(data->jsdcTarget);
    while(NULL != (script = JSD_IterateScripts(data->jsdcTarget, &iterp)))
    {
        jsval retval;
        JSBool another;
        argv[0] = P2H_SCRIPT(cx, script);

        if(!JS_CallFunction(cx, NULL, fun, argc, argv, &retval))
            break;
        count++ ;
        if(!JS_ValueToBoolean(cx, retval, &another) || !another)
            break;
    }
    JSD_UnlockScriptSubsystem(data->jsdcTarget);
    argv[0] = argv0;

    *rval = INT_TO_JSVAL(count);
    return JS_TRUE;
}

JS_STATIC_DLL_CALLBACK(JSBool)
IsActiveScript(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
    JSDScript *jsdscript;
    JSDB_Data* data = (JSDB_Data*) JS_GetContextPrivate(cx);
    JSBool active;

    JS_ASSERT(data);

    if(argc < 1 || !(jsdscript = H2P_SCRIPT(cx, argv[0])))
    {
        JS_ReportError(cx, "IsActiveScript requires script handle");
        return JS_FALSE;
    }

    JSD_LockScriptSubsystem(data->jsdcTarget);
    active = JSD_IsActiveScript(data->jsdcTarget, jsdscript);
    JSD_UnlockScriptSubsystem(data->jsdcTarget);

    *rval = active ? JSVAL_TRUE : JSVAL_FALSE;
    return JS_TRUE;
}

JS_STATIC_DLL_CALLBACK(JSBool)
GetScriptFilename(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
    JSDScript *jsdscript;
    JSDB_Data* data = (JSDB_Data*) JS_GetContextPrivate(cx);
    JSBool active;

    JS_ASSERT(data);

    if(argc < 1 || !(jsdscript = H2P_SCRIPT(cx, argv[0])))
    {
        JS_ReportError(cx, "GetScriptFilename requires script handle");
        return JS_FALSE;
    }

    JSD_LockScriptSubsystem(data->jsdcTarget);
    active = JSD_IsActiveScript(data->jsdcTarget, jsdscript);
    JSD_UnlockScriptSubsystem(data->jsdcTarget);
    if(! active )
    {
        printf("%d\n", (int)jsdscript);
        JS_ReportError(cx, "GetScriptFilename passed inactive script");
        return JS_FALSE;
    }

    *rval = STRING_TO_JSVAL(JS_NewStringCopyZ(cx,
                JSD_GetScriptFilename(data->jsdcTarget, jsdscript)));
    return JS_TRUE;
}

JS_STATIC_DLL_CALLBACK(JSBool)
GetScriptFunctionName(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
    JSDScript *jsdscript;
    JSDB_Data* data = (JSDB_Data*) JS_GetContextPrivate(cx);
    JSBool active;

    JS_ASSERT(data);

    if(argc < 1 || !(jsdscript = H2P_SCRIPT(cx, argv[0])))
    {
        JS_ReportError(cx, "GetScriptFunctionName requires script handle");
        return JS_FALSE;
    }

    JSD_LockScriptSubsystem(data->jsdcTarget);
    active = JSD_IsActiveScript(data->jsdcTarget, jsdscript);
    JSD_UnlockScriptSubsystem(data->jsdcTarget);
    if(! active )
    {
        JS_ReportError(cx, "GetScriptFunctionName passed inactive script");
        return JS_FALSE;
    }

    *rval = STRING_TO_JSVAL(JS_NewStringCopyZ(cx,
                JSD_GetScriptFunctionName(data->jsdcTarget, jsdscript)));
    return JS_TRUE;
}

JS_STATIC_DLL_CALLBACK(JSBool)
GetScriptBaseLineNumber(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
    JSDScript *jsdscript;
    JSDB_Data* data = (JSDB_Data*) JS_GetContextPrivate(cx);
    JSBool active;

    JS_ASSERT(data);

    if(argc < 1 || !(jsdscript = H2P_SCRIPT(cx, argv[0])))
    {
        JS_ReportError(cx, "GetScriptBaseLineNumber requires script handle");
        return JS_FALSE;
    }

    JSD_LockScriptSubsystem(data->jsdcTarget);
    active = JSD_IsActiveScript(data->jsdcTarget, jsdscript);
    JSD_UnlockScriptSubsystem(data->jsdcTarget);
    if(! active )
    {
        JS_ReportError(cx, "GetScriptBaseLineNumber passed inactive script");
        return JS_FALSE;
    }

    *rval = INT_TO_JSVAL(JSD_GetScriptBaseLineNumber(data->jsdcTarget, jsdscript));
    return JS_TRUE;
}

JS_STATIC_DLL_CALLBACK(JSBool)
GetScriptLineExtent(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
    JSDScript *jsdscript;
    JSDB_Data* data = (JSDB_Data*) JS_GetContextPrivate(cx);
    JSBool active;

    JS_ASSERT(data);

    if(argc < 1 || !(jsdscript = H2P_SCRIPT(cx, argv[0])))
    {
        JS_ReportError(cx, "GetScriptLineExtent requires script handle");
        return JS_FALSE;
    }

    JSD_LockScriptSubsystem(data->jsdcTarget);
    active = JSD_IsActiveScript(data->jsdcTarget, jsdscript);
    JSD_UnlockScriptSubsystem(data->jsdcTarget);
    if(! active )
    {
        JS_ReportError(cx, "GetScriptLineExtent passed inactive script");
        return JS_FALSE;
    }

    *rval = INT_TO_JSVAL(JSD_GetScriptLineExtent(data->jsdcTarget, jsdscript));
    return JS_TRUE;
}

JS_STATIC_DLL_CALLBACK(JSBool)
SetScriptHook(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
    jsval oldHook;
    JSDB_Data* data = (JSDB_Data*) JS_GetContextPrivate(cx);
    JS_ASSERT(data);

    if(argc < 1 ||
       !(JSVAL_IS_NULL(argv[0]) ||
         NULL != JS_ValueToFunction(cx, argv[0])))
    {
        JS_ReportError(cx, "SetScriptHook requires a function or null param");
        return JS_FALSE;
    }

    oldHook = data->jsScriptHook;
    data->jsScriptHook = argv[0];

    *rval = oldHook;
    return JS_TRUE;
}

JS_STATIC_DLL_CALLBACK(JSBool)
GetScriptHook(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
    JSDB_Data* data = (JSDB_Data*) JS_GetContextPrivate(cx);
    JS_ASSERT(data);

    *rval = data->jsScriptHook;
    return JS_TRUE;
}

JS_STATIC_DLL_CALLBACK(JSBool)
GetClosestPC(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
    int32 line;
    JSDScript *jsdscript;
    JSDB_Data* data = (JSDB_Data*) JS_GetContextPrivate(cx);
    JSBool active;

    JS_ASSERT(data);

    if(argc < 2 || !(jsdscript = H2P_SCRIPT(cx, argv[0])) ||
       ! JS_ValueToInt32(cx, argv[1], &line))
    {
        JS_ReportError(cx, "GetClosestPC requires script handle and a line number");
        return JS_FALSE;
    }

    JSD_LockScriptSubsystem(data->jsdcTarget);
    active = JSD_IsActiveScript(data->jsdcTarget, jsdscript);
    JSD_UnlockScriptSubsystem(data->jsdcTarget);
    if(! active )
    {
        JS_ReportError(cx, "GetClosestPC passed inactive script");
        return JS_FALSE;
    }

    *rval = P2H_GENERIC(cx, (void*)
                        JSD_GetClosestPC(data->jsdcTarget, jsdscript, line));
    return JS_TRUE;
}

JS_STATIC_DLL_CALLBACK(JSBool)
GetClosestLine(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
    JSDScript *jsdscript;
    JSDB_Data* data = (JSDB_Data*) JS_GetContextPrivate(cx);
    JSBool active;

    JS_ASSERT(data);

    if(argc < 2 || !(jsdscript = H2P_SCRIPT(cx, argv[0])))
    {
        JS_ReportError(cx, "GetClosestLine requires script handle and a line number");
        return JS_FALSE;
    }

    JSD_LockScriptSubsystem(data->jsdcTarget);
    active = JSD_IsActiveScript(data->jsdcTarget, jsdscript);
    JSD_UnlockScriptSubsystem(data->jsdcTarget);
    if(! active )
    {
        JS_ReportError(cx, "GetClosestLine passed inactive script");
        return JS_FALSE;
    }

    *rval = INT_TO_JSVAL(JSD_GetClosestLine(data->jsdcTarget, jsdscript,
                                 (jsuword) H2P_GENERIC(cx, argv[1])));
    return JS_TRUE;
}

JS_STATIC_DLL_CALLBACK(JSBool)
SetExecutionHook(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
    jsval oldHook;
    JSDB_Data* data = (JSDB_Data*) JS_GetContextPrivate(cx);
    JS_ASSERT(data);

    if(argc < 1 ||
       !(JSVAL_IS_NULL(argv[0]) ||
         NULL != JS_ValueToFunction(cx, argv[0])))
    {
        JS_ReportError(cx, "SetExecutionHook requires a function or null param");
        return JS_FALSE;
    }

    oldHook = data->jsExecutionHook;
    data->jsExecutionHook = argv[0];

    *rval = oldHook;
    return JS_TRUE;
}

JS_STATIC_DLL_CALLBACK(JSBool)
GetExecutionHook(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
    JSDB_Data* data = (JSDB_Data*) JS_GetContextPrivate(cx);
    JS_ASSERT(data);

    *rval = data->jsExecutionHook;
    return JS_TRUE;
}

JS_STATIC_DLL_CALLBACK(JSBool)
SendInterrupt(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
    jsval val = JSVAL_TRUE;
    JSBool success;
    JSDB_Data* data = (JSDB_Data*) JS_GetContextPrivate(cx);
    JS_ASSERT(data);


    success = JSD_SetInterruptHook(data->jsdcTarget, jsdb_ExecHookHandler, data);
    if(success)
        success = JS_SetProperty(data->cxDebugger, data->jsdOb,
                                 _str_InterruptSet, &val);
    return success;
}

JS_STATIC_DLL_CALLBACK(JSBool)
ClearInterrupt(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
    jsval val = JSVAL_FALSE;
    JSBool success;
    JSDB_Data* data = (JSDB_Data*) JS_GetContextPrivate(cx);
    JS_ASSERT(data);

    success = JSD_ClearInterruptHook(data->jsdcTarget);
    if(success)
        success = JS_SetProperty(data->cxDebugger, data->jsdOb,
                                 _str_InterruptSet, &val);
    return success;
}

JS_STATIC_DLL_CALLBACK(JSBool)
GetCountOfStackFrames(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
    JSDB_Data* data = (JSDB_Data*) JS_GetContextPrivate(cx);
    JS_ASSERT(data);

    *rval = INT_TO_JSVAL(JSD_GetCountOfStackFrames(data->jsdcTarget,
                                                   data->jsdthreadstate));
    return JS_TRUE;
}

JS_STATIC_DLL_CALLBACK(JSBool)
GetStackFrame(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
    JSDB_Data* data = (JSDB_Data*) JS_GetContextPrivate(cx);
    JS_ASSERT(data);

    *rval = P2H_STACKFRAMEINFO(cx, JSD_GetStackFrame(data->jsdcTarget,
                                                     data->jsdthreadstate));
    return JS_TRUE;
}

JS_STATIC_DLL_CALLBACK(JSBool)
GetCallingStackFrame(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
    JSDStackFrameInfo* jsdframe;
    JSDB_Data* data = (JSDB_Data*) JS_GetContextPrivate(cx);
    JS_ASSERT(data);

    if(argc < 1 || !(jsdframe = H2P_STACKFRAMEINFO(cx, argv[0])))
    {
        JS_ReportError(cx, "GetCallingStackFrame requires stackframe handle");
        return JS_FALSE;
    }

    *rval = P2H_STACKFRAMEINFO(cx, JSD_GetCallingStackFrame(data->jsdcTarget,
                                                            data->jsdthreadstate,
                                                            jsdframe));
    return JS_TRUE;
}

JS_STATIC_DLL_CALLBACK(JSBool)
GetScriptForStackFrame(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
    JSDStackFrameInfo* jsdframe;
    JSDB_Data* data = (JSDB_Data*) JS_GetContextPrivate(cx);
    JS_ASSERT(data);

    if(argc < 1 || !(jsdframe = H2P_STACKFRAMEINFO(cx, argv[0])))
    {
        JS_ReportError(cx, "GetScriptForStackFrame requires stackframe handle");
        return JS_FALSE;
    }

    *rval = P2H_SCRIPT(cx, JSD_GetScriptForStackFrame(data->jsdcTarget,
                                                      data->jsdthreadstate,
                                                      jsdframe));
    return JS_TRUE;
}

JS_STATIC_DLL_CALLBACK(JSBool)
GetPCForStackFrame(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
    JSDStackFrameInfo* jsdframe;
    JSDB_Data* data = (JSDB_Data*) JS_GetContextPrivate(cx);
    JS_ASSERT(data);

    if(argc < 1 || !(jsdframe = H2P_STACKFRAMEINFO(cx, argv[0])))
    {
        JS_ReportError(cx, "GetPCForStackFrame requires stackframe handle");
        return JS_FALSE;
    }

    *rval = P2H_GENERIC(cx, (void*) JSD_GetPCForStackFrame(data->jsdcTarget,
                                                           data->jsdthreadstate,
                                                           jsdframe));
    return JS_TRUE;
}

JS_STATIC_DLL_CALLBACK(JSBool)
EvaluateScriptInStackFrame(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
    static char default_filename[] = "jsdb_show";
    JSDStackFrameInfo* jsdframe;
    jsval foreignAnswerVal;
    JSString* foreignAnswerString;
    JSString* textJSString;
    char* filename;
    int32 lineno;
    JSBool retVal = JS_FALSE;
    JSDB_Data* data = (JSDB_Data*) JS_GetContextPrivate(cx);
    JS_ASSERT(data);

    if(argc < 1 || !(jsdframe = H2P_STACKFRAMEINFO(cx, argv[0])))
    {
        JS_ReportError(cx, "EvaluateScriptInStackFrame requires stackframe handle");
        return JS_FALSE;
    }

    if(argc < 2 || !(textJSString = JS_ValueToString(cx, argv[1])))
    {
        JS_ReportError(cx, "EvaluateScriptInStackFrame requires source text as a second param");
        return JS_FALSE;
    }

    if(argc < 3)
        filename = default_filename;
    else
    {
        JSString* filenameJSString;
        if(!(filenameJSString = JS_ValueToString(cx, argv[2])))
        {
            JS_ReportError(cx, "EvaluateScriptInStackFrame passed non-string filename as 3rd param");
            return JS_FALSE;
        }
        filename = JS_GetStringBytes(filenameJSString);
    }

    if(argc < 4)
        lineno = 1;
    else
    {
        if(!JS_ValueToInt32(cx, argv[3], &lineno))
        {
            JS_ReportError(cx, "EvaluateScriptInStackFrame passed non-int lineno as 4th param");
            return JS_FALSE;
        }
    }

    JS_SetProperty(cx, data->jsdOb, _str_Evaluating, &_valTrue);

    if(JSD_EvaluateScriptInStackFrame(data->jsdcTarget,
                                      data->jsdthreadstate,
                                      jsdframe,
                                      JS_GetStringBytes(textJSString),
                                      JS_GetStringLength(textJSString),
                                      filename, lineno, &foreignAnswerVal))
    {
        if(NULL != (foreignAnswerString =
            JSD_ValToStringInStackFrame(data->jsdcTarget,
                                        data->jsdthreadstate,
                                        jsdframe,
                                        foreignAnswerVal)))
        {
            *rval = STRING_TO_JSVAL(
                        JS_NewStringCopyZ(cx,
                                      JS_GetStringBytes(foreignAnswerString)));
            retVal = JS_TRUE;
        }

    }

    JS_SetProperty(cx, data->jsdOb, _str_Evaluating, &_valFalse);

    return retVal;
}


JS_STATIC_DLL_CALLBACK(JSBool)
EvaluateScriptInStackFrameToValue(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
    static char default_filename[] = "jsdb_show";
    JSDStackFrameInfo* jsdframe;
    jsval foreignAnswerVal;
    JSString* textJSString;
    char* filename;
    int32 lineno;
    JSBool retVal = JS_FALSE;
    JSDB_Data* data = (JSDB_Data*) JS_GetContextPrivate(cx);
    JS_ASSERT(data);

    if(argc < 1 || !(jsdframe = H2P_STACKFRAMEINFO(cx, argv[0])))
    {
        JS_ReportError(cx, "EvaluateScriptInStackFrameToValue requires stackframe handle");
        return JS_FALSE;
    }

    if(argc < 2 || !(textJSString = JS_ValueToString(cx, argv[1])))
    {
        JS_ReportError(cx, "EvaluateScriptInStackFrameToValue requires source text as a second param");
        return JS_FALSE;
    }

    if(argc < 3)
        filename = default_filename;
    else
    {
        JSString* filenameJSString;
        if(!(filenameJSString = JS_ValueToString(cx, argv[2])))
        {
            JS_ReportError(cx, "EvaluateScriptInStackFrameToValue passed non-string filename as 3rd param");
            return JS_FALSE;
        }
        filename = JS_GetStringBytes(filenameJSString);
    }

    if(argc < 4)
        lineno = 1;
    else
    {
        if(!JS_ValueToInt32(cx, argv[3], &lineno))
        {
            JS_ReportError(cx, "EvaluateScriptInStackFrameToValue passed non-int lineno as 4th param");
            return JS_FALSE;
        }
    }

    JS_SetProperty(cx, data->jsdOb, _str_Evaluating, &_valTrue);

    if(JSD_EvaluateScriptInStackFrame(data->jsdcTarget,
                                      data->jsdthreadstate,
                                      jsdframe,
                                      JS_GetStringBytes(textJSString),
                                      JS_GetStringLength(textJSString),
                                      filename, lineno, &foreignAnswerVal))
    {
        *rval = P2H_VALUE(cx, JSD_NewValue(data->jsdcTarget, foreignAnswerVal));
        retVal = JS_TRUE;
    }

    JS_SetProperty(cx, data->jsdOb, _str_Evaluating, &_valFalse);

    return retVal;
}

JS_STATIC_DLL_CALLBACK(JSBool)
SetTrap(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
    JSDScript *jsdscript;
    jsuword pc;
    JSBool success;
    JSDB_Data* data = (JSDB_Data*) JS_GetContextPrivate(cx);
    JSBool active;

    JS_ASSERT(data);

    if(argc < 1 || !(jsdscript = H2P_SCRIPT(cx, argv[0])))
    {
        JS_ReportError(cx, "SetTrap requires script handle as first arg");
        return JS_FALSE;
    }

    JSD_LockScriptSubsystem(data->jsdcTarget);
    active = JSD_IsActiveScript(data->jsdcTarget, jsdscript);
    JSD_UnlockScriptSubsystem(data->jsdcTarget);
    if(! active )
    {
        JS_ReportError(cx, "SetTrap passed inactive script");
        return JS_FALSE;
    }

    if(argc < 2 || 0 == (pc = (jsuword) H2P_GENERIC(cx, argv[1])))
    {
        JS_ReportError(cx, "SetTrap requires pc handle as second arg");
        return JS_FALSE;
    }

    success = JSD_SetExecutionHook(data->jsdcTarget, jsdscript, pc,
                                   jsdb_ExecHookHandler, data);
    *rval = success ? JSVAL_TRUE : JSVAL_FALSE;
    return JS_TRUE;
}

JS_STATIC_DLL_CALLBACK(JSBool)
ClearTrap(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
    JSDScript *jsdscript;
    jsuword pc;
    JSBool success;
    JSDB_Data* data = (JSDB_Data*) JS_GetContextPrivate(cx);
    JSBool active;

    JS_ASSERT(data);

    if(argc < 1 || !(jsdscript = H2P_SCRIPT(cx, argv[0])))
    {
        JS_ReportError(cx, "ClearTrap requires script handle as first arg");
        return JS_FALSE;
    }

    JSD_LockScriptSubsystem(data->jsdcTarget);
    active = JSD_IsActiveScript(data->jsdcTarget, jsdscript);
    JSD_UnlockScriptSubsystem(data->jsdcTarget);
    if(! active )
    {
        JS_ReportError(cx, "ClearTrap passed inactive script");
        return JS_FALSE;
    }

    if(argc < 2 || 0 == (pc = (jsuword) H2P_GENERIC(cx, argv[1])))
    {
        JS_ReportError(cx, "ClearTrap requires pc handle as second arg");
        return JS_FALSE;
    }

    success = JSD_ClearExecutionHook(data->jsdcTarget, jsdscript, pc);
    *rval = success ? JSVAL_TRUE : JSVAL_FALSE;
    return JS_TRUE;
}

JS_STATIC_DLL_CALLBACK(JSBool)
ClearAllTrapsForScript(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
    JSDScript *jsdscript;
    JSBool success;
    JSDB_Data* data = (JSDB_Data*) JS_GetContextPrivate(cx);
    JSBool active;

    JS_ASSERT(data);

    if(argc < 1 || !(jsdscript = H2P_SCRIPT(cx, argv[0])))
    {
        JS_ReportError(cx, "ClearAllTrapsForScript script handle as first arg");
        return JS_FALSE;
    }

    JSD_LockScriptSubsystem(data->jsdcTarget);
    active = JSD_IsActiveScript(data->jsdcTarget, jsdscript);
    JSD_UnlockScriptSubsystem(data->jsdcTarget);
    if(! active )
    {
        JS_ReportError(cx, "GetClosestLine passed inactive script");
        return JS_FALSE;
    }

    success = JSD_ClearAllExecutionHooksForScript(data->jsdcTarget, jsdscript);
    *rval = success ? JSVAL_TRUE : JSVAL_FALSE;
    return JS_TRUE;
}

JS_STATIC_DLL_CALLBACK(JSBool)
ClearAllTraps(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
    JSBool success;
    JSDB_Data* data = (JSDB_Data*) JS_GetContextPrivate(cx);
    JS_ASSERT(data);

    success = JSD_ClearAllExecutionHooks(data->jsdcTarget);
    *rval = success ? JSVAL_TRUE : JSVAL_FALSE;
    return JS_TRUE;
}

JS_STATIC_DLL_CALLBACK(JSBool)
SetErrorReporterHook(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
    jsval oldHook;
    JSDB_Data* data = (JSDB_Data*) JS_GetContextPrivate(cx);
    JS_ASSERT(data);

    if(argc < 1 ||
       !(JSVAL_IS_NULL(argv[0]) ||
         NULL != JS_ValueToFunction(cx, argv[0])))
    {
        JS_ReportError(cx, "SetErrorReporterHook requires a function or null param");
        return JS_FALSE;
    }

    oldHook = data->jsErrorReporterHook;
    data->jsErrorReporterHook = argv[0];

    *rval = oldHook;
    return JS_TRUE;
}

JS_STATIC_DLL_CALLBACK(JSBool)
GetException(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
    JSDB_Data* data = (JSDB_Data*) JS_GetContextPrivate(cx);
    JS_ASSERT(data);

    *rval = P2H_VALUE(cx, JSD_GetException(data->jsdcTarget,
                                           data->jsdthreadstate));
    return JS_TRUE;
}

JS_STATIC_DLL_CALLBACK(JSBool)
SetException(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
    JSDValue* jsdval;
    JSDB_Data* data = (JSDB_Data*) JS_GetContextPrivate(cx);
    JS_ASSERT(data);

    if(argc < 1 || !(jsdval = H2P_VALUE(cx, argv[0])))
    {
        JS_ReportError(cx, "SetException requires value handle");
        return JS_FALSE;
    }

    *rval = BOOLEAN_TO_JSVAL(JSD_SetException(data->jsdcTarget,
                                              data->jsdthreadstate,
                                              jsdval));
    return JS_TRUE;
}

JS_STATIC_DLL_CALLBACK(JSBool)
ClearException(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
    JSDB_Data* data = (JSDB_Data*) JS_GetContextPrivate(cx);
    JS_ASSERT(data);

    *rval = BOOLEAN_TO_JSVAL(JSD_SetException(data->jsdcTarget,
                                              data->jsdthreadstate,
                                              NULL));
    return JS_TRUE;
}

/***************************************************************************/
/* Source Text functions */

JS_STATIC_DLL_CALLBACK(JSBool)
LockSourceTextSubsystem(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
    JSDB_Data* data = (JSDB_Data*) JS_GetContextPrivate(cx);
    JS_ASSERT(data);
    JSD_LockSourceTextSubsystem(data->jsdcTarget);
    *rval = JSVAL_TRUE;
    return JS_TRUE;
}

JS_STATIC_DLL_CALLBACK(JSBool)
UnlockSourceTextSubsystem(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
    JSDB_Data* data = (JSDB_Data*) JS_GetContextPrivate(cx);
    JS_ASSERT(data);
    JSD_UnlockSourceTextSubsystem(data->jsdcTarget);
    *rval = JSVAL_TRUE;
    return JS_TRUE;
}

JS_STATIC_DLL_CALLBACK(JSBool)
IterateSources(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
    JSDSourceText *iterp = NULL;
    JSDSourceText *jsdsrc;
    JSFunction *fun;
    jsval argv0;
    int count = 0;
    JSDB_Data* data = (JSDB_Data*) JS_GetContextPrivate(cx);
    JS_ASSERT(data);

    if(argc < 1 || !(fun = JS_ValueToFunction(cx, argv[0])))
    {
        JS_ReportError(cx, "IterateSources requires a function param");
        return JS_FALSE;
    }

    /* We pass along any additional args, so patch and leverage argv */
    argv0 = argv[0];
    while(NULL != (jsdsrc = JSD_IterateSources(data->jsdcTarget, &iterp)))
    {
        jsval retval;
        JSBool another;

        argv[0] = P2H_SOURCETEXT(cx, jsdsrc);

        if(!JS_CallFunction(cx, NULL, fun, argc, argv, &retval))
            break;
        count++ ;
        if(!JS_ValueToBoolean(cx, retval, &another) || !another)
            break;
    }
    argv[0] = argv0;

    *rval = INT_TO_JSVAL(count);
    return JS_TRUE;
}

JS_STATIC_DLL_CALLBACK(JSBool)
FindSourceForURL(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
    JSString* jsstr;
    JSDB_Data* data = (JSDB_Data*) JS_GetContextPrivate(cx);
    JS_ASSERT(data);

    if(argc < 1 || !(jsstr = JS_ValueToString(cx, argv[0])))
    {
        JS_ReportError(cx, "FindSourceForURL requires a URL (filename) string");
        return JS_FALSE;
    }
    *rval = P2H_SOURCETEXT(cx, JSD_FindSourceForURL(data->jsdcTarget,
                                                    JS_GetStringBytes(jsstr)));
    return JS_TRUE;
}

JS_STATIC_DLL_CALLBACK(JSBool)
GetSourceURL(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
    JSDSourceText* jsdsrc;
    JSDB_Data* data = (JSDB_Data*) JS_GetContextPrivate(cx);
    JS_ASSERT(data);

    if(argc < 1 || !(jsdsrc = H2P_SOURCETEXT(cx, argv[0])))
    {
        JS_ReportError(cx, "GetSourceURL requires sourcetext handle");
        return JS_FALSE;
    }

    *rval = STRING_TO_JSVAL(JS_NewStringCopyZ(cx,
                JSD_GetSourceURL(data->jsdcTarget, jsdsrc)));
    return JS_TRUE;
}

JS_STATIC_DLL_CALLBACK(JSBool)
GetSourceText(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
    JSDSourceText* jsdsrc;
    const char* ptr;
    int len;

    JSDB_Data* data = (JSDB_Data*) JS_GetContextPrivate(cx);
    JS_ASSERT(data);

    if(argc < 1 || !(jsdsrc = H2P_SOURCETEXT(cx, argv[0])))
    {
        JS_ReportError(cx, "GetSourceText requires sourcetext handle");
        return JS_FALSE;
    }

    if(JSD_GetSourceText(data->jsdcTarget, jsdsrc, &ptr, &len))
        *rval = STRING_TO_JSVAL(JS_NewStringCopyN(cx, ptr, len));
    else
        *rval = JSVAL_NULL;
    return JS_TRUE;
}

JS_STATIC_DLL_CALLBACK(JSBool)
GetSourceStatus(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
    JSDSourceText* jsdsrc;

    JSDB_Data* data = (JSDB_Data*) JS_GetContextPrivate(cx);
    JS_ASSERT(data);

    if(argc < 1 || !(jsdsrc = H2P_SOURCETEXT(cx, argv[0])))
    {
        JS_ReportError(cx, "GetSourceStatus requires sourcetext handle");
        return JS_FALSE;
    }

    *rval = INT_TO_JSVAL(JSD_GetSourceStatus(data->jsdcTarget, jsdsrc));
    return JS_TRUE;
}

JS_STATIC_DLL_CALLBACK(JSBool)
GetSourceAlterCount(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
    JSDSourceText* jsdsrc;

    JSDB_Data* data = (JSDB_Data*) JS_GetContextPrivate(cx);
    JS_ASSERT(data);

    if(argc < 1 || !(jsdsrc = H2P_SOURCETEXT(cx, argv[0])))
    {
        JS_ReportError(cx, "GetSourceAlterCount requires sourcetext handle");
        return JS_FALSE;
    }

    *rval = INT_TO_JSVAL(JSD_GetSourceAlterCount(data->jsdcTarget, jsdsrc));
    return JS_TRUE;
}
/***************************************************************************/
/* Value and Property functions */

JS_STATIC_DLL_CALLBACK(JSBool)
GetCallObjectForStackFrame(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
    JSDStackFrameInfo* jsdframe;
    JSDB_Data* data = (JSDB_Data*) JS_GetContextPrivate(cx);
    JS_ASSERT(data);

    if(argc < 1 || !(jsdframe = H2P_STACKFRAMEINFO(cx, argv[0])))
    {
        JS_ReportError(cx, "GetCallObjectForStackFrame requires stackframe handle");
        return JS_FALSE;
    }

    *rval = P2H_VALUE(cx, JSD_GetCallObjectForStackFrame(data->jsdcTarget,
                                                         data->jsdthreadstate,
                                                         jsdframe));
    return JS_TRUE;
}

JS_STATIC_DLL_CALLBACK(JSBool)
GetScopeChainForStackFrame(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
    JSDStackFrameInfo* jsdframe;
    JSDB_Data* data = (JSDB_Data*) JS_GetContextPrivate(cx);
    JS_ASSERT(data);

    if(argc < 1 || !(jsdframe = H2P_STACKFRAMEINFO(cx, argv[0])))
    {
        JS_ReportError(cx, "GetScopeChainForStackFrame requires stackframe handle");
        return JS_FALSE;
    }

    *rval = P2H_VALUE(cx, JSD_GetScopeChainForStackFrame(data->jsdcTarget,
                                                         data->jsdthreadstate,
                                                         jsdframe));
    return JS_TRUE;
}

JS_STATIC_DLL_CALLBACK(JSBool)
GetThisForStackFrame(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
    JSDStackFrameInfo* jsdframe;
    JSDB_Data* data = (JSDB_Data*) JS_GetContextPrivate(cx);
    JS_ASSERT(data);

    if(argc < 1 || !(jsdframe = H2P_STACKFRAMEINFO(cx, argv[0])))
    {
        JS_ReportError(cx, "GetThisForStackFrame requires stackframe handle");
        return JS_FALSE;
    }

    *rval = P2H_VALUE(cx, JSD_GetThisForStackFrame(data->jsdcTarget,
                                                   data->jsdthreadstate,
                                                   jsdframe));
    return JS_TRUE;
}

JS_STATIC_DLL_CALLBACK(JSBool)
RefreshValue(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
    JSDValue* jsdval;
    JSDB_Data* data = (JSDB_Data*) JS_GetContextPrivate(cx);
    JS_ASSERT(data);

    if(argc < 1 || !(jsdval = H2P_VALUE(cx, argv[0])))
    {
        JS_ReportError(cx, "RefreshValue requires value handle");
        return JS_FALSE;
    }

    JSD_RefreshValue(data->jsdcTarget, jsdval);
    return JS_TRUE;
}

JS_STATIC_DLL_CALLBACK(JSBool)
IsValueObject(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
    JSDValue* jsdval;
    JSDB_Data* data = (JSDB_Data*) JS_GetContextPrivate(cx);
    JS_ASSERT(data);

    if(argc < 1 || !(jsdval = H2P_VALUE(cx, argv[0])))
    {
        JS_ReportError(cx, "IsValueObject requires value handle");
        return JS_FALSE;
    }

    *rval = BOOLEAN_TO_JSVAL(JSD_IsValueObject(data->jsdcTarget, jsdval));
    return JS_TRUE;
}

JS_STATIC_DLL_CALLBACK(JSBool)
IsValueNumber(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
    JSDValue* jsdval;
    JSDB_Data* data = (JSDB_Data*) JS_GetContextPrivate(cx);
    JS_ASSERT(data);

    if(argc < 1 || !(jsdval = H2P_VALUE(cx, argv[0])))
    {
        JS_ReportError(cx, "IsValueNumber requires value handle");
        return JS_FALSE;
    }

    *rval = BOOLEAN_TO_JSVAL(JSD_IsValueNumber(data->jsdcTarget, jsdval));
    return JS_TRUE;
}

JS_STATIC_DLL_CALLBACK(JSBool)
IsValueInt(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
    JSDValue* jsdval;
    JSDB_Data* data = (JSDB_Data*) JS_GetContextPrivate(cx);
    JS_ASSERT(data);

    if(argc < 1 || !(jsdval = H2P_VALUE(cx, argv[0])))
    {
        JS_ReportError(cx, "IsValueInt requires value handle");
        return JS_FALSE;
    }

    *rval = BOOLEAN_TO_JSVAL(JSD_IsValueInt(data->jsdcTarget, jsdval));
    return JS_TRUE;
}

JS_STATIC_DLL_CALLBACK(JSBool)
IsValueDouble(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
    JSDValue* jsdval;
    JSDB_Data* data = (JSDB_Data*) JS_GetContextPrivate(cx);
    JS_ASSERT(data);

    if(argc < 1 || !(jsdval = H2P_VALUE(cx, argv[0])))
    {
        JS_ReportError(cx, "IsValueDouble requires value handle");
        return JS_FALSE;
    }

    *rval = BOOLEAN_TO_JSVAL(JSD_IsValueDouble(data->jsdcTarget, jsdval));
    return JS_TRUE;
}

JS_STATIC_DLL_CALLBACK(JSBool)
IsValueString(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
    JSDValue* jsdval;
    JSDB_Data* data = (JSDB_Data*) JS_GetContextPrivate(cx);
    JS_ASSERT(data);

    if(argc < 1 || !(jsdval = H2P_VALUE(cx, argv[0])))
    {
        JS_ReportError(cx, "IsValueString requires value handle");
        return JS_FALSE;
    }

    *rval = BOOLEAN_TO_JSVAL(JSD_IsValueString(data->jsdcTarget, jsdval));
    return JS_TRUE;
}

JS_STATIC_DLL_CALLBACK(JSBool)
IsValueBoolean(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
    JSDValue* jsdval;
    JSDB_Data* data = (JSDB_Data*) JS_GetContextPrivate(cx);
    JS_ASSERT(data);

    if(argc < 1 || !(jsdval = H2P_VALUE(cx, argv[0])))
    {
        JS_ReportError(cx, "IsValueBoolean requires value handle");
        return JS_FALSE;
    }

    *rval = BOOLEAN_TO_JSVAL(JSD_IsValueBoolean(data->jsdcTarget, jsdval));
    return JS_TRUE;
}

JS_STATIC_DLL_CALLBACK(JSBool)
IsValueNull(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
    JSDValue* jsdval;
    JSDB_Data* data = (JSDB_Data*) JS_GetContextPrivate(cx);
    JS_ASSERT(data);

    if(argc < 1 || !(jsdval = H2P_VALUE(cx, argv[0])))
    {
        JS_ReportError(cx, "IsValueNull requires value handle");
        return JS_FALSE;
    }

    *rval = BOOLEAN_TO_JSVAL(JSD_IsValueNull(data->jsdcTarget, jsdval));
    return JS_TRUE;
}

JS_STATIC_DLL_CALLBACK(JSBool)
IsValueVoid(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
    JSDValue* jsdval;
    JSDB_Data* data = (JSDB_Data*) JS_GetContextPrivate(cx);
    JS_ASSERT(data);

    if(argc < 1 || !(jsdval = H2P_VALUE(cx, argv[0])))
    {
        JS_ReportError(cx, "IsValueVoid requires value handle");
        return JS_FALSE;
    }

    *rval = BOOLEAN_TO_JSVAL(JSD_IsValueVoid(data->jsdcTarget, jsdval));
    return JS_TRUE;
}

JS_STATIC_DLL_CALLBACK(JSBool)
IsValuePrimitive(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
    JSDValue* jsdval;
    JSDB_Data* data = (JSDB_Data*) JS_GetContextPrivate(cx);
    JS_ASSERT(data);

    if(argc < 1 || !(jsdval = H2P_VALUE(cx, argv[0])))
    {
        JS_ReportError(cx, "IsValuePrimitive requires value handle");
        return JS_FALSE;
    }

    *rval = BOOLEAN_TO_JSVAL(JSD_IsValuePrimitive(data->jsdcTarget, jsdval));
    return JS_TRUE;
}

JS_STATIC_DLL_CALLBACK(JSBool)
IsValueFunction(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
    JSDValue* jsdval;
    JSDB_Data* data = (JSDB_Data*) JS_GetContextPrivate(cx);
    JS_ASSERT(data);

    if(argc < 1 || !(jsdval = H2P_VALUE(cx, argv[0])))
    {
        JS_ReportError(cx, "IsValueFunction requires value handle");
        return JS_FALSE;
    }

    *rval = BOOLEAN_TO_JSVAL(JSD_IsValueFunction(data->jsdcTarget, jsdval));
    return JS_TRUE;
}

JS_STATIC_DLL_CALLBACK(JSBool)
IsValueNative(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
    JSDValue* jsdval;
    JSDB_Data* data = (JSDB_Data*) JS_GetContextPrivate(cx);
    JS_ASSERT(data);

    if(argc < 1 || !(jsdval = H2P_VALUE(cx, argv[0])))
    {
        JS_ReportError(cx, "IsValueNative requires value handle");
        return JS_FALSE;
    }

    *rval = BOOLEAN_TO_JSVAL(JSD_IsValueNative(data->jsdcTarget, jsdval));
    return JS_TRUE;
}


JS_STATIC_DLL_CALLBACK(JSBool)
GetValueBoolean(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
    JSDValue* jsdval;
    JSDB_Data* data = (JSDB_Data*) JS_GetContextPrivate(cx);
    JS_ASSERT(data);

    if(argc < 1 || !(jsdval = H2P_VALUE(cx, argv[0])))
    {
        JS_ReportError(cx, "GetValueBoolean requires value handle");
        return JS_FALSE;
    }

    *rval = BOOLEAN_TO_JSVAL(JSD_GetValueBoolean(data->jsdcTarget, jsdval));
    return JS_TRUE;
}

JS_STATIC_DLL_CALLBACK(JSBool)
GetValueInt(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
    JSDValue* jsdval;
    JSDB_Data* data = (JSDB_Data*) JS_GetContextPrivate(cx);
    JS_ASSERT(data);

    if(argc < 1 || !(jsdval = H2P_VALUE(cx, argv[0])))
    {
        JS_ReportError(cx, "GetValueInt requires value handle");
        return JS_FALSE;
    }

    *rval = INT_TO_JSVAL(JSD_GetValueInt(data->jsdcTarget, jsdval));
    return JS_TRUE;
}

JS_STATIC_DLL_CALLBACK(JSBool)
GetValueDouble(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
    JSDValue* jsdval;
    JSDB_Data* data = (JSDB_Data*) JS_GetContextPrivate(cx);
    JS_ASSERT(data);

    if(argc < 1 || !(jsdval = H2P_VALUE(cx, argv[0])))
    {
        JS_ReportError(cx, "GetValueDouble requires value handle");
        return JS_FALSE;
    }

    *rval = DOUBLE_TO_JSVAL(JSD_GetValueDouble(data->jsdcTarget, jsdval));
    return JS_TRUE;
}

JS_STATIC_DLL_CALLBACK(JSBool)
GetValueString(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
    JSDValue* jsdval;
    JSDB_Data* data = (JSDB_Data*) JS_GetContextPrivate(cx);
    JS_ASSERT(data);

    if(argc < 1 || !(jsdval = H2P_VALUE(cx, argv[0])))
    {
        JS_ReportError(cx, "GetValueString requires value handle");
        return JS_FALSE;
    }

    *rval = STRING_TO_JSVAL(JSD_GetValueString(data->jsdcTarget, jsdval));
    return JS_TRUE;
}

JS_STATIC_DLL_CALLBACK(JSBool)
GetValueFunctionName(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
    JSDValue* jsdval;
    JSDB_Data* data = (JSDB_Data*) JS_GetContextPrivate(cx);
    JS_ASSERT(data);

    if(argc < 1 || !(jsdval = H2P_VALUE(cx, argv[0])))
    {
        JS_ReportError(cx, "GetValueFunctionName requires value handle");
        return JS_FALSE;
    }

    *rval = STRING_TO_JSVAL(JSD_GetValueFunctionName(data->jsdcTarget, jsdval));
    return JS_TRUE;
}

JS_STATIC_DLL_CALLBACK(JSBool)
GetCountOfProperties(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
    JSDValue* jsdval;
    JSDB_Data* data = (JSDB_Data*) JS_GetContextPrivate(cx);
    JS_ASSERT(data);

    if(argc < 1 || !(jsdval = H2P_VALUE(cx, argv[0])))
    {
        JS_ReportError(cx, "GetCountOfProperties requires value handle");
        return JS_FALSE;
    }

    *rval = INT_TO_JSVAL(JSD_GetCountOfProperties(data->jsdcTarget, jsdval));
    return JS_TRUE;
}

JS_STATIC_DLL_CALLBACK(JSBool)
IterateProperties(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
    JSDProperty* iterp = NULL;
    JSDProperty* jsdprop;
    JSDValue* jsdval;
    JSFunction *fun;
    jsval argv1;
    int count = 0;
    JSDB_Data* data = (JSDB_Data*) JS_GetContextPrivate(cx);
    JS_ASSERT(data);

    if(argc < 1 || !(jsdval = H2P_VALUE(cx, argv[0])))
    {
        JS_ReportError(cx, "IterateProperties requires value handle first param");
        return JS_FALSE;
    }

    if(argc < 2 || !(fun = JS_ValueToFunction(cx, argv[1])))
    {
        JS_ReportError(cx, "IterateProperties requires a function second param");
        return JS_FALSE;
    }

    /* We pass along any additional args, so patch and leverage argv */
    argv1 = argv[1];
    while(NULL != (jsdprop = JSD_IterateProperties(data->jsdcTarget, jsdval, &iterp)))
    {
        jsval retval;
        JSBool another;

        argv[1] = P2H_PROPERTY(cx, jsdprop);

        if(!JS_CallFunction(cx, NULL, fun, argc, argv, &retval))
            break;
        count++ ;
        if(!JS_ValueToBoolean(cx, retval, &another) || !another)
            break;
    }
    argv[1] = argv1;

    *rval = INT_TO_JSVAL(count);
    return JS_TRUE;
}

JS_STATIC_DLL_CALLBACK(JSBool)
GetValueProperty(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
    JSDValue* jsdval;
    JSString* name;
    JSDB_Data* data = (JSDB_Data*) JS_GetContextPrivate(cx);
    JS_ASSERT(data);

    if(argc < 1 || !(jsdval = H2P_VALUE(cx, argv[0])))
    {
        JS_ReportError(cx, "GetValueProperty requires value handle first param");
        return JS_FALSE;
    }

    if(argc < 2 || !(name = JS_ValueToString(cx, argv[1])))
    {
        JS_ReportError(cx, "GetValueProperty requires name string second param");
        return JS_FALSE;
    }

    *rval = P2H_PROPERTY(cx, JSD_GetValueProperty(data->jsdcTarget, jsdval, name));
    return JS_TRUE;
}

JS_STATIC_DLL_CALLBACK(JSBool)
GetValuePrototype(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
    JSDValue* jsdval;
    JSDB_Data* data = (JSDB_Data*) JS_GetContextPrivate(cx);
    JS_ASSERT(data);

    if(argc < 1 || !(jsdval = H2P_VALUE(cx, argv[0])))
    {
        JS_ReportError(cx, "GetValuePrototype requires value handle");
        return JS_FALSE;
    }

    *rval = P2H_VALUE(cx, JSD_GetValuePrototype(data->jsdcTarget, jsdval));
    return JS_TRUE;
}

JS_STATIC_DLL_CALLBACK(JSBool)
GetValueParent(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
    JSDValue* jsdval;
    JSDB_Data* data = (JSDB_Data*) JS_GetContextPrivate(cx);
    JS_ASSERT(data);

    if(argc < 1 || !(jsdval = H2P_VALUE(cx, argv[0])))
    {
        JS_ReportError(cx, "GetValueParent requires value handle");
        return JS_FALSE;
    }

    *rval = P2H_VALUE(cx, JSD_GetValueParent(data->jsdcTarget, jsdval));
    return JS_TRUE;
}

JS_STATIC_DLL_CALLBACK(JSBool)
GetValueConstructor(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
    JSDValue* jsdval;
    JSDB_Data* data = (JSDB_Data*) JS_GetContextPrivate(cx);
    JS_ASSERT(data);

    if(argc < 1 || !(jsdval = H2P_VALUE(cx, argv[0])))
    {
        JS_ReportError(cx, "GetValueConstructor requires value handle");
        return JS_FALSE;
    }

    *rval = P2H_VALUE(cx, JSD_GetValueConstructor(data->jsdcTarget, jsdval));
    return JS_TRUE;
}

JS_STATIC_DLL_CALLBACK(JSBool)
GetValueClassName(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
    JSDValue* jsdval;
    const char* name;
    JSString* nameStr;
    JSDB_Data* data = (JSDB_Data*) JS_GetContextPrivate(cx);
    JS_ASSERT(data);

    if(argc < 1 || !(jsdval = H2P_VALUE(cx, argv[0])))
    {
        JS_ReportError(cx, "GetValueClassName requires value handle");
        return JS_FALSE;
    }

    if(!(name = JSD_GetValueClassName(data->jsdcTarget, jsdval)) ||
       !(nameStr = JS_NewStringCopyZ(cx, name)))
        *rval = JSVAL_NULL;
    else
        *rval = STRING_TO_JSVAL(nameStr);
    return JS_TRUE;
}

JS_STATIC_DLL_CALLBACK(JSBool)
GetPropertyName(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
    JSDProperty* jsdprop;
    JSDB_Data* data = (JSDB_Data*) JS_GetContextPrivate(cx);
    JS_ASSERT(data);

    if(argc < 1 || !(jsdprop = H2P_PROPERTY(cx, argv[0])))
    {
        JS_ReportError(cx, "GetPropertyName requires property handle");
        return JS_FALSE;
    }

    *rval = P2H_VALUE(cx, JSD_GetPropertyName(data->jsdcTarget, jsdprop));
    return JS_TRUE;
}

JS_STATIC_DLL_CALLBACK(JSBool)
GetPropertyValue(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
    JSDProperty* jsdprop;
    JSDB_Data* data = (JSDB_Data*) JS_GetContextPrivate(cx);
    JS_ASSERT(data);

    if(argc < 1 || !(jsdprop = H2P_PROPERTY(cx, argv[0])))
    {
        JS_ReportError(cx, "GetPropertyValue requires property handle");
        return JS_FALSE;
    }

    *rval = P2H_VALUE(cx, JSD_GetPropertyValue(data->jsdcTarget, jsdprop));
    return JS_TRUE;
}

JS_STATIC_DLL_CALLBACK(JSBool)
GetPropertyAlias(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
    JSDProperty* jsdprop;
    JSDB_Data* data = (JSDB_Data*) JS_GetContextPrivate(cx);
    JS_ASSERT(data);

    if(argc < 1 || !(jsdprop = H2P_PROPERTY(cx, argv[0])))
    {
        JS_ReportError(cx, "GetPropertyAlias requires property handle");
        return JS_FALSE;
    }

    *rval = P2H_VALUE(cx, JSD_GetPropertyAlias(data->jsdcTarget, jsdprop));
    return JS_TRUE;
}

JS_STATIC_DLL_CALLBACK(JSBool)
GetPropertyFlags(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
    JSDProperty* jsdprop;
    JSDB_Data* data = (JSDB_Data*) JS_GetContextPrivate(cx);
    JS_ASSERT(data);

    if(argc < 1 || !(jsdprop = H2P_PROPERTY(cx, argv[0])))
    {
        JS_ReportError(cx, "GetPropertyFlags requires property handle");
        return JS_FALSE;
    }

    *rval = INT_TO_JSVAL(JSD_GetPropertyFlags(data->jsdcTarget, jsdprop));
    return JS_TRUE;
}

JS_STATIC_DLL_CALLBACK(JSBool)
GetPropertyVarArgSlot(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
    JSDProperty* jsdprop;
    JSDB_Data* data = (JSDB_Data*) JS_GetContextPrivate(cx);
    JS_ASSERT(data);

    if(argc < 1 || !(jsdprop = H2P_PROPERTY(cx, argv[0])))
    {
        JS_ReportError(cx, "GetPropertyVarArgSlot requires property handle");
        return JS_FALSE;
    }

    *rval = INT_TO_JSVAL(JSD_GetPropertyVarArgSlot(data->jsdcTarget, jsdprop));
    return JS_TRUE;
}
/***************************************************************************/
/* Object functions */

JS_STATIC_DLL_CALLBACK(JSBool)
LockObjectSubsystem(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
    JSDB_Data* data = (JSDB_Data*) JS_GetContextPrivate(cx);
    JS_ASSERT(data);
    JSD_LockObjectSubsystem(data->jsdcTarget);
    *rval = JSVAL_TRUE;
    return JS_TRUE;
}

JS_STATIC_DLL_CALLBACK(JSBool)
UnlockObjectSubsystem(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
    JSDB_Data* data = (JSDB_Data*) JS_GetContextPrivate(cx);
    JS_ASSERT(data);
    JSD_UnlockObjectSubsystem(data->jsdcTarget);
    *rval = JSVAL_TRUE;
    return JS_TRUE;
}

JS_STATIC_DLL_CALLBACK(JSBool)
IterateObjects(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
    JSDObject* iterp = NULL;
    JSDObject* jsdobj;
    JSFunction *fun;
    jsval argv0;
    int count = 0;
    JSDB_Data* data = (JSDB_Data*) JS_GetContextPrivate(cx);
    JS_ASSERT(data);

    if(argc < 1 || !(fun = JS_ValueToFunction(cx, argv[0])))
    {
        JS_ReportError(cx, "IterateObjects requires a function param");
        return JS_FALSE;
    }

    /* We pass along any additional args, so patch and leverage argv */
    argv0 = argv[0];
    while(NULL != (jsdobj = JSD_IterateObjects(data->jsdcTarget, &iterp)))
    {
        jsval retval;
        JSBool another;
        argv[0] = P2H_OBJECT(cx, jsdobj);

        if(!JS_CallFunction(cx, NULL, fun, argc, argv, &retval))
            break;
        count++ ;
        if(!JS_ValueToBoolean(cx, retval, &another) || !another)
            break;
    }
    argv[0] = argv0;

    *rval = INT_TO_JSVAL(count);
    return JS_TRUE;
}

JS_STATIC_DLL_CALLBACK(JSBool)
GetObjectNewURL(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
    JSDObject* jsdobj;
    const char* str;
    JSString* strStr;
    JSDB_Data* data = (JSDB_Data*) JS_GetContextPrivate(cx);
    JS_ASSERT(data);

    if(argc < 1 || !(jsdobj = H2P_OBJECT(cx, argv[0])))
    {
        JS_ReportError(cx, "GetObjectNewURL requires object handle");
        return JS_FALSE;
    }

    if(!(str = JSD_GetObjectNewURL(data->jsdcTarget, jsdobj)) ||
       !(strStr = JS_NewStringCopyZ(cx, str)))
        *rval = JSVAL_NULL;
    else
        *rval = STRING_TO_JSVAL(strStr);
    return JS_TRUE;
}

JS_STATIC_DLL_CALLBACK(JSBool)
GetObjectNewLineNumber(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
    JSDObject* jsdobj;
    JSDB_Data* data = (JSDB_Data*) JS_GetContextPrivate(cx);
    JS_ASSERT(data);

    if(argc < 1 || !(jsdobj = H2P_OBJECT(cx, argv[0])))
    {
        JS_ReportError(cx, "GetObjectNewLineNumber requires object handle");
        return JS_FALSE;
    }

    *rval = INT_TO_JSVAL(JSD_GetObjectNewLineNumber(data->jsdcTarget, jsdobj));
    return JS_TRUE;
}

JS_STATIC_DLL_CALLBACK(JSBool)
GetObjectConstructorURL(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
    JSDObject* jsdobj;
    const char* str;
    JSString* strStr;
    JSDB_Data* data = (JSDB_Data*) JS_GetContextPrivate(cx);
    JS_ASSERT(data);

    if(argc < 1 || !(jsdobj = H2P_OBJECT(cx, argv[0])))
    {
        JS_ReportError(cx, "GetObjectConstructorURL requires object handle");
        return JS_FALSE;
    }

    if(!(str = JSD_GetObjectConstructorURL(data->jsdcTarget, jsdobj)) ||
       !(strStr = JS_NewStringCopyZ(cx, str)))
        *rval = JSVAL_NULL;
    else
        *rval = STRING_TO_JSVAL(strStr);
    return JS_TRUE;
}

JS_STATIC_DLL_CALLBACK(JSBool)
GetObjectConstructorLineNumber(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
    JSDObject* jsdobj;
    JSDB_Data* data = (JSDB_Data*) JS_GetContextPrivate(cx);
    JS_ASSERT(data);

    if(argc < 1 || !(jsdobj = H2P_OBJECT(cx, argv[0])))
    {
        JS_ReportError(cx, "GetObjectConstructorLineNumber requires object handle");
        return JS_FALSE;
    }

    *rval = INT_TO_JSVAL(JSD_GetObjectConstructorLineNumber(data->jsdcTarget, jsdobj));
    return JS_TRUE;
}

JS_STATIC_DLL_CALLBACK(JSBool)
GetObjectConstructorName(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
    JSDObject* jsdobj;
    const char* str;
    JSString* strStr;
    JSDB_Data* data = (JSDB_Data*) JS_GetContextPrivate(cx);
    JS_ASSERT(data);

    if(argc < 1 || !(jsdobj = H2P_OBJECT(cx, argv[0])))
    {
        JS_ReportError(cx, "GetObjectConstructorName requires object handle");
        return JS_FALSE;
    }

    if(!(str = JSD_GetObjectConstructorName(data->jsdcTarget, jsdobj)) ||
       !(strStr = JS_NewStringCopyZ(cx, str)))
        *rval = JSVAL_NULL;
    else
        *rval = STRING_TO_JSVAL(strStr);
    return JS_TRUE;
}

JS_STATIC_DLL_CALLBACK(JSBool)
GetObjectForValue(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
    JSDValue* jsdval;
    JSDB_Data* data = (JSDB_Data*) JS_GetContextPrivate(cx);
    JS_ASSERT(data);

    if(argc < 1 || !(jsdval = H2P_VALUE(cx, argv[0])))
    {
        JS_ReportError(cx, "GetObjectForValue requires value handle");
        return JS_FALSE;
    }

    *rval = P2H_OBJECT(cx, JSD_GetObjectForValue(data->jsdcTarget, jsdval));
    return JS_TRUE;
}

JS_STATIC_DLL_CALLBACK(JSBool)
GetValueForObject(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
    JSDObject* jsdobj;
    JSDB_Data* data = (JSDB_Data*) JS_GetContextPrivate(cx);
    JS_ASSERT(data);

    if(argc < 1 || !(jsdobj = H2P_OBJECT(cx, argv[0])))
    {
        JS_ReportError(cx, "GetValueForObject requires object handle");
        return JS_FALSE;
    }

    *rval = P2H_VALUE(cx, JSD_GetValueForObject(data->jsdcTarget, jsdobj));
    return JS_TRUE;
}

/***************************************************************************/

/* expands to {"x",x,n JSPROP_ENUMERATE},
 * e.g....
 * FUN_SPEC(GetMajorVersion,0) ->
 *      {"GetMajorVersion",GetMajorVersion,0,JSPROP_ENUMERATE},
 */
#define FUN_SPEC(x,n) {#x,x,n,JSPROP_ENUMERATE,0},

static JSFunctionSpec jsd_functions[] = {
/* High Level calls */
    FUN_SPEC(GetMajorVersion, 0)
    FUN_SPEC(GetMinorVersion, 0)
/* Script functions */
    FUN_SPEC(LockScriptSubsystem, 0)
    FUN_SPEC(UnlockScriptSubsystem, 0)
    FUN_SPEC(IterateScripts, 1)
    FUN_SPEC(IsActiveScript, 1)
    FUN_SPEC(GetScriptFilename, 1)
    FUN_SPEC(GetScriptFunctionName, 1)
    FUN_SPEC(GetScriptBaseLineNumber, 1)
    FUN_SPEC(GetScriptLineExtent, 1)
    FUN_SPEC(SetScriptHook, 1)
    FUN_SPEC(GetScriptHook, 0)
    FUN_SPEC(GetClosestPC, 2)
    FUN_SPEC(GetClosestLine, 2)
/* Execution/Interrupt Hook functions */
    FUN_SPEC(SetExecutionHook, 1)
    FUN_SPEC(GetExecutionHook, 0)
    FUN_SPEC(SendInterrupt, 0)
    FUN_SPEC(ClearInterrupt, 0)
    FUN_SPEC(GetCountOfStackFrames, 0)
    FUN_SPEC(GetStackFrame, 0)
    FUN_SPEC(GetCallingStackFrame, 1)
    FUN_SPEC(GetScriptForStackFrame, 1)
    FUN_SPEC(GetPCForStackFrame, 1)
    FUN_SPEC(GetCallObjectForStackFrame, 1)
    FUN_SPEC(GetScopeChainForStackFrame, 1)
    FUN_SPEC(GetThisForStackFrame, 1)
    FUN_SPEC(EvaluateScriptInStackFrame, 4)
    FUN_SPEC(EvaluateScriptInStackFrameToValue, 4)
    FUN_SPEC(SetTrap, 2)
    FUN_SPEC(ClearTrap, 2)
    FUN_SPEC(ClearAllTrapsForScript, 1)
    FUN_SPEC(ClearAllTraps, 0)
    FUN_SPEC(SetErrorReporterHook, 1)
    FUN_SPEC(GetException, 0)
    FUN_SPEC(SetException, 1)
    FUN_SPEC(ClearException, 0)
/* Source Text functions */
    FUN_SPEC(LockSourceTextSubsystem, 0)
    FUN_SPEC(UnlockSourceTextSubsystem, 0)
    FUN_SPEC(IterateSources, 1)
    FUN_SPEC(FindSourceForURL, 1)
    FUN_SPEC(GetSourceURL, 1)
    FUN_SPEC(GetSourceText, 1)
    FUN_SPEC(GetSourceStatus, 1)
    FUN_SPEC(GetSourceAlterCount, 1)
/* Value and Property functions */
    FUN_SPEC(RefreshValue, 1)
    FUN_SPEC(IsValueObject, 1)
    FUN_SPEC(IsValueNumber, 1)
    FUN_SPEC(IsValueInt, 1)
    FUN_SPEC(IsValueDouble, 1)
    FUN_SPEC(IsValueString, 1)
    FUN_SPEC(IsValueBoolean, 1)
    FUN_SPEC(IsValueNull, 1)
    FUN_SPEC(IsValueVoid, 1)
    FUN_SPEC(IsValuePrimitive, 1)
    FUN_SPEC(IsValueFunction, 1)
    FUN_SPEC(IsValueNative, 1)
    FUN_SPEC(GetValueBoolean, 1)
    FUN_SPEC(GetValueInt, 1)
    FUN_SPEC(GetValueDouble, 1)
    FUN_SPEC(GetValueString, 1)
    FUN_SPEC(GetValueFunctionName, 1)
    FUN_SPEC(GetCountOfProperties, 1)
    FUN_SPEC(IterateProperties, 2)
    FUN_SPEC(GetValueProperty, 2)
    FUN_SPEC(GetValuePrototype, 1)
    FUN_SPEC(GetValueParent, 1)
    FUN_SPEC(GetValueConstructor, 1)
    FUN_SPEC(GetValueClassName, 1)
    FUN_SPEC(GetPropertyName, 1)
    FUN_SPEC(GetPropertyValue, 1)
    FUN_SPEC(GetPropertyAlias, 1)
    FUN_SPEC(GetPropertyFlags, 1)
    FUN_SPEC(GetPropertyVarArgSlot, 1)
/* Object functions */
    FUN_SPEC(LockObjectSubsystem, 0)
    FUN_SPEC(UnlockObjectSubsystem, 0)
    FUN_SPEC(IterateObjects, 1)
    FUN_SPEC(GetObjectNewURL, 1)
    FUN_SPEC(GetObjectNewLineNumber, 1)
    FUN_SPEC(GetObjectConstructorURL, 1)
    FUN_SPEC(GetObjectConstructorLineNumber, 1)
    FUN_SPEC(GetObjectConstructorName, 1)
    FUN_SPEC(GetObjectForValue, 1)
    FUN_SPEC(GetValueForObject, 1)
    {0,0,0,0,0}
};

/***************************************************************************/
/***************************************************************************/
/* Constant Properties */
typedef struct ConstProp
{
    char* name;
    int   val;
} ConstProp;

/* expands to {"x",x}, e.g. {"JSD_HOOK_INTERRUPTED",JSD_HOOK_INTERRUPTED}, */
#define CONST_PROP(x) {#x,x},

/* these are used for constants defined in jsdebug.h - they must match! */
static ConstProp const_props[] = {

    CONST_PROP(JSD_HOOK_INTERRUPTED)
    CONST_PROP(JSD_HOOK_BREAKPOINT)
    CONST_PROP(JSD_HOOK_DEBUG_REQUESTED)
    CONST_PROP(JSD_HOOK_DEBUGGER_KEYWORD)
    CONST_PROP(JSD_HOOK_THROW)

    CONST_PROP(JSD_HOOK_RETURN_HOOK_ERROR)
    CONST_PROP(JSD_HOOK_RETURN_CONTINUE)
    CONST_PROP(JSD_HOOK_RETURN_ABORT)
    CONST_PROP(JSD_HOOK_RETURN_RET_WITH_VAL)
    CONST_PROP(JSD_HOOK_RETURN_THROW_WITH_VAL)
    CONST_PROP(JSD_HOOK_RETURN_CONTINUE_THROW)

    CONST_PROP(JSD_SOURCE_INITED)
    CONST_PROP(JSD_SOURCE_PARTIAL)
    CONST_PROP(JSD_SOURCE_COMPLETED)
    CONST_PROP(JSD_SOURCE_ABORTED)
    CONST_PROP(JSD_SOURCE_FAILED)
    CONST_PROP(JSD_SOURCE_CLEARED)

    CONST_PROP(JSD_ERROR_REPORTER_PASS_ALONG)
    CONST_PROP(JSD_ERROR_REPORTER_RETURN)
    CONST_PROP(JSD_ERROR_REPORTER_DEBUG)
    CONST_PROP(JSD_ERROR_REPORTER_CLEAR_RETURN)

    CONST_PROP(JSDPD_ENUMERATE)
    CONST_PROP(JSDPD_READONLY)
    CONST_PROP(JSDPD_PERMANENT)
    CONST_PROP(JSDPD_ALIAS)
    CONST_PROP(JSDPD_ARGUMENT)
    CONST_PROP(JSDPD_VARIABLE)
    CONST_PROP(JSDPD_HINTED)

    {0,0}
};

JSBool
_defineConstProperties(JSDB_Data* data)
{
    int i;
    for(i = 0; const_props[i].name;i++)
    {
        if(!JS_DefineProperty(data->cxDebugger, data->jsdOb,
                              const_props[i].name,
                              INT_TO_JSVAL(const_props[i].val),
                              NULL, NULL,
                              JSPROP_ENUMERATE|JSPROP_READONLY|JSPROP_PERMANENT))
            return JS_FALSE;
    }
    return JS_TRUE;
}
/**************************************/

static JSClass jsd_class = {
    "JSD", 0,
    JS_PropertyStub,  JS_PropertyStub,  JS_PropertyStub,  JS_PropertyStub,
    JS_EnumerateStub, JS_ResolveStub,   JS_ConvertStub,   JS_FinalizeStub,
    JSCLASS_NO_OPTIONAL_MEMBERS
};

/***************************************************************************/

JSBool
jsdb_ReflectJSD(JSDB_Data* data)
{
    if(!(data->jsdOb = JS_DefineObject(data->cxDebugger, data->globDebugger,
                   "jsd", &jsd_class, NULL,
                    JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT)))
        return JS_FALSE;

    if(!JS_DefineFunctions(data->cxDebugger, data->jsdOb, jsd_functions))
        return JS_FALSE;

    if (!JS_DefineProperties(data->cxDebugger, data->jsdOb, jsd_properties))
        return JS_FALSE;

    if(!_defineConstProperties(data))
        return JS_FALSE;

    if(!_defineNonConstProperties(data))
        return JS_FALSE;

    if(!_initHandleSystem(data))
        return JS_FALSE;

    return JS_TRUE;
}


