summaryrefslogtreecommitdiff
path: root/js/jsd/jsd_high.c
diff options
context:
space:
mode:
Diffstat (limited to 'js/jsd/jsd_high.c')
-rw-r--r--js/jsd/jsd_high.c439
1 files changed, 439 insertions, 0 deletions
diff --git a/js/jsd/jsd_high.c b/js/jsd/jsd_high.c
new file mode 100644
index 0000000..fab896b
--- /dev/null
+++ b/js/jsd/jsd_high.c
@@ -0,0 +1,439 @@
+/* -*- 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 ***** */
+
+/*
+ * JavaScript Debugging support - 'High Level' functions
+ */
+
+#include "jsd.h"
+
+/***************************************************************************/
+
+/* XXX not 'static' because of old Mac CodeWarrior bug */
+JSCList _jsd_context_list = JS_INIT_STATIC_CLIST(&_jsd_context_list);
+
+/* these are used to connect JSD_SetUserCallbacks() with JSD_DebuggerOn() */
+static JSD_UserCallbacks _callbacks;
+static void* _user = NULL;
+static JSRuntime* _jsrt = NULL;
+
+#ifdef JSD_HAS_DANGEROUS_THREAD
+static void* _dangerousThread = NULL;
+#endif
+
+#ifdef JSD_THREADSAFE
+void* _jsd_global_lock = NULL;
+#endif
+
+#ifdef DEBUG
+void JSD_ASSERT_VALID_CONTEXT(JSDContext* jsdc)
+{
+ JS_ASSERT(jsdc->inited);
+ JS_ASSERT(jsdc->jsrt);
+ JS_ASSERT(jsdc->dumbContext);
+ JS_ASSERT(jsdc->glob);
+}
+#endif
+
+static JSClass global_class = {
+ "JSDGlobal", JSCLASS_GLOBAL_FLAGS,
+ JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub,
+ JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub,
+ JSCLASS_NO_OPTIONAL_MEMBERS
+};
+
+static JSBool
+_validateUserCallbacks(JSD_UserCallbacks* callbacks)
+{
+ return !callbacks ||
+ (callbacks->size && callbacks->size <= sizeof(JSD_UserCallbacks));
+}
+
+static JSDContext*
+_newJSDContext(JSRuntime* jsrt,
+ JSD_UserCallbacks* callbacks,
+ void* user,
+ JSObject* scopeobj)
+{
+ JSDContext* jsdc = NULL;
+ JSCrossCompartmentCall *call = NULL;
+
+ if( ! jsrt )
+ return NULL;
+
+ if( ! _validateUserCallbacks(callbacks) )
+ return NULL;
+
+ jsdc = (JSDContext*) calloc(1, sizeof(JSDContext));
+ if( ! jsdc )
+ goto label_newJSDContext_failure;
+
+ if( ! JSD_INIT_LOCKS(jsdc) )
+ goto label_newJSDContext_failure;
+
+ JS_INIT_CLIST(&jsdc->links);
+
+ jsdc->jsrt = jsrt;
+
+ if( callbacks )
+ memcpy(&jsdc->userCallbacks, callbacks, callbacks->size);
+
+ jsdc->user = user;
+
+#ifdef JSD_HAS_DANGEROUS_THREAD
+ jsdc->dangerousThread = _dangerousThread;
+#endif
+
+ JS_INIT_CLIST(&jsdc->threadsStates);
+ JS_INIT_CLIST(&jsdc->sources);
+ JS_INIT_CLIST(&jsdc->removedSources);
+
+ jsdc->sourceAlterCount = 1;
+
+ if( ! jsd_CreateAtomTable(jsdc) )
+ goto label_newJSDContext_failure;
+
+ if( ! jsd_InitObjectManager(jsdc) )
+ goto label_newJSDContext_failure;
+
+ if( ! jsd_InitScriptManager(jsdc) )
+ goto label_newJSDContext_failure;
+
+ jsdc->dumbContext = JS_NewContext(jsdc->jsrt, 256);
+ if( ! jsdc->dumbContext )
+ goto label_newJSDContext_failure;
+
+ JS_BeginRequest(jsdc->dumbContext);
+
+ jsdc->glob = JS_NewCompartmentAndGlobalObject(jsdc->dumbContext, &global_class, NULL);
+
+ if( ! jsdc->glob )
+ goto label_newJSDContext_failure;
+
+ call = JS_EnterCrossCompartmentCall(jsdc->dumbContext, jsdc->glob);
+ if( ! call )
+ goto label_newJSDContext_failure;
+
+ if( ! JS_InitStandardClasses(jsdc->dumbContext, jsdc->glob) )
+ goto label_newJSDContext_failure;
+
+ if( call )
+ JS_LeaveCrossCompartmentCall(call);
+
+ JS_EndRequest(jsdc->dumbContext);
+
+ jsdc->data = NULL;
+ jsdc->inited = JS_TRUE;
+
+ JSD_LOCK();
+ JS_INSERT_LINK(&jsdc->links, &_jsd_context_list);
+ JSD_UNLOCK();
+
+ return jsdc;
+
+label_newJSDContext_failure:
+ if( jsdc ) {
+ jsd_DestroyObjectManager(jsdc);
+ jsd_DestroyAtomTable(jsdc);
+ JS_EndRequest(jsdc->dumbContext);
+ free(jsdc);
+ }
+ return NULL;
+}
+
+static void
+_destroyJSDContext(JSDContext* jsdc)
+{
+ JSD_ASSERT_VALID_CONTEXT(jsdc);
+
+ JSD_LOCK();
+ JS_REMOVE_LINK(&jsdc->links);
+ JSD_UNLOCK();
+
+ jsd_DestroyObjectManager(jsdc);
+ jsd_DestroyAtomTable(jsdc);
+
+ jsdc->inited = JS_FALSE;
+
+ /*
+ * We should free jsdc here, but we let it leak in case there are any
+ * asynchronous hooks calling into the system using it as a handle
+ *
+ * XXX we also leak the locks
+ */
+ JS_DestroyContext(jsdc->dumbContext);
+ jsdc->dumbContext = NULL;
+}
+
+/***************************************************************************/
+
+JSDContext*
+jsd_DebuggerOnForUser(JSRuntime* jsrt,
+ JSD_UserCallbacks* callbacks,
+ void* user,
+ JSObject* scopeobj)
+{
+ JSDContext* jsdc;
+ JSContext* iter = NULL;
+
+ jsdc = _newJSDContext(jsrt, callbacks, user, scopeobj);
+ if( ! jsdc )
+ return NULL;
+
+ /*
+ * Set hooks here. The new/destroy script hooks are on even when
+ * the debugger is paused. The destroy hook so we'll clean up
+ * internal data structures when scripts are destroyed, and the
+ * newscript hook for backwards compatibility for now. We'd like
+ * to stop doing that.
+ */
+ JS_SetNewScriptHookProc(jsdc->jsrt, jsd_NewScriptHookProc, jsdc);
+ JS_SetDestroyScriptHookProc(jsdc->jsrt, jsd_DestroyScriptHookProc, jsdc);
+ jsd_DebuggerUnpause(jsdc);
+#ifdef LIVEWIRE
+ LWDBG_SetNewScriptHookProc(jsd_NewScriptHookProc, jsdc);
+#endif
+ if( jsdc->userCallbacks.setContext )
+ jsdc->userCallbacks.setContext(jsdc, jsdc->user);
+ return jsdc;
+}
+
+JSDContext*
+jsd_DebuggerOn(void)
+{
+ JS_ASSERT(_jsrt);
+ JS_ASSERT(_validateUserCallbacks(&_callbacks));
+ return jsd_DebuggerOnForUser(_jsrt, &_callbacks, _user, NULL);
+}
+
+void
+jsd_DebuggerOff(JSDContext* jsdc)
+{
+ jsd_DebuggerPause(jsdc, JS_TRUE);
+ /* clear hooks here */
+ JS_SetNewScriptHookProc(jsdc->jsrt, NULL, NULL);
+ JS_SetDestroyScriptHookProc(jsdc->jsrt, NULL, NULL);
+#ifdef LIVEWIRE
+ LWDBG_SetNewScriptHookProc(NULL,NULL);
+#endif
+
+ /* clean up */
+ JSD_LockScriptSubsystem(jsdc);
+ jsd_DestroyScriptManager(jsdc);
+ JSD_UnlockScriptSubsystem(jsdc);
+ jsd_DestroyAllSources(jsdc);
+
+ _destroyJSDContext(jsdc);
+
+ if( jsdc->userCallbacks.setContext )
+ jsdc->userCallbacks.setContext(NULL, jsdc->user);
+}
+
+void
+jsd_DebuggerPause(JSDContext* jsdc, JSBool forceAllHooksOff)
+{
+ JS_SetDebuggerHandler(jsdc->jsrt, NULL, NULL);
+ if (forceAllHooksOff || !(jsdc->flags & JSD_COLLECT_PROFILE_DATA)) {
+ JS_SetExecuteHook(jsdc->jsrt, NULL, NULL);
+ JS_SetCallHook(jsdc->jsrt, NULL, NULL);
+ }
+ JS_SetThrowHook(jsdc->jsrt, NULL, NULL);
+ JS_SetDebugErrorHook(jsdc->jsrt, NULL, NULL);
+}
+
+void
+jsd_DebuggerUnpause(JSDContext* jsdc)
+{
+ JS_SetDebuggerHandler(jsdc->jsrt, jsd_DebuggerHandler, jsdc);
+ JS_SetExecuteHook(jsdc->jsrt, jsd_TopLevelCallHook, jsdc);
+ JS_SetCallHook(jsdc->jsrt, jsd_FunctionCallHook, jsdc);
+ JS_SetThrowHook(jsdc->jsrt, jsd_ThrowHandler, jsdc);
+ JS_SetDebugErrorHook(jsdc->jsrt, jsd_DebugErrorHook, jsdc);
+}
+
+void
+jsd_SetUserCallbacks(JSRuntime* jsrt, JSD_UserCallbacks* callbacks, void* user)
+{
+ _jsrt = jsrt;
+ _user = user;
+
+#ifdef JSD_HAS_DANGEROUS_THREAD
+ _dangerousThread = JSD_CURRENT_THREAD();
+#endif
+
+ if( callbacks )
+ memcpy(&_callbacks, callbacks, sizeof(JSD_UserCallbacks));
+ else
+ memset(&_callbacks, 0 , sizeof(JSD_UserCallbacks));
+}
+
+void*
+jsd_SetContextPrivate(JSDContext* jsdc, void *data)
+{
+ void *rval = jsdc->data;
+ jsdc->data = data;
+ return data;
+}
+
+void*
+jsd_GetContextPrivate(JSDContext* jsdc)
+{
+ return jsdc->data;
+}
+
+void
+jsd_ClearAllProfileData(JSDContext* jsdc)
+{
+ JSDScript *current;
+
+ JSD_LOCK_SCRIPTS(jsdc);
+ current = (JSDScript *)jsdc->scripts.next;
+ while (current != (JSDScript *)&jsdc->scripts)
+ {
+ jsd_ClearScriptProfileData(jsdc, current);
+ current = (JSDScript *)current->links.next;
+ }
+
+ JSD_UNLOCK_SCRIPTS(jsdc);
+}
+
+JSDContext*
+jsd_JSDContextForJSContext(JSContext* context)
+{
+ JSDContext* iter;
+ JSDContext* jsdc = NULL;
+ JSRuntime* runtime = JS_GetRuntime(context);
+
+ JSD_LOCK();
+ for( iter = (JSDContext*)_jsd_context_list.next;
+ iter != (JSDContext*)&_jsd_context_list;
+ iter = (JSDContext*)iter->links.next )
+ {
+ if( runtime == iter->jsrt )
+ {
+ jsdc = iter;
+ break;
+ }
+ }
+ JSD_UNLOCK();
+ return jsdc;
+}
+
+static JSBool
+jsd_DebugErrorHook(JSContext *cx, const char *message,
+ JSErrorReport *report, void *closure)
+{
+ JSDContext* jsdc = (JSDContext*) closure;
+ JSD_ErrorReporter errorReporter;
+ void* errorReporterData;
+
+ if( ! jsdc )
+ {
+ JS_ASSERT(0);
+ return JS_TRUE;
+ }
+ if( JSD_IS_DANGEROUS_THREAD(jsdc) )
+ return JS_TRUE;
+
+ /* local in case hook gets cleared on another thread */
+ JSD_LOCK();
+ errorReporter = jsdc->errorReporter;
+ errorReporterData = jsdc->errorReporterData;
+ JSD_UNLOCK();
+
+ if(!errorReporter)
+ return JS_TRUE;
+
+ switch(errorReporter(jsdc, cx, message, report, errorReporterData))
+ {
+ case JSD_ERROR_REPORTER_PASS_ALONG:
+ return JS_TRUE;
+ case JSD_ERROR_REPORTER_RETURN:
+ return JS_FALSE;
+ case JSD_ERROR_REPORTER_DEBUG:
+ {
+ jsval rval;
+ JSD_ExecutionHookProc hook;
+ void* hookData;
+
+ /* local in case hook gets cleared on another thread */
+ JSD_LOCK();
+ hook = jsdc->debugBreakHook;
+ hookData = jsdc->debugBreakHookData;
+ JSD_UNLOCK();
+
+ jsd_CallExecutionHook(jsdc, cx, JSD_HOOK_DEBUG_REQUESTED,
+ hook, hookData, &rval);
+ /* XXX Should make this dependent on ExecutionHook retval */
+ return JS_TRUE;
+ }
+ case JSD_ERROR_REPORTER_CLEAR_RETURN:
+ if(report && JSREPORT_IS_EXCEPTION(report->flags))
+ JS_ClearPendingException(cx);
+ return JS_FALSE;
+ default:
+ JS_ASSERT(0);
+ break;
+ }
+ return JS_TRUE;
+}
+
+JSBool
+jsd_SetErrorReporter(JSDContext* jsdc,
+ JSD_ErrorReporter reporter,
+ void* callerdata)
+{
+ JSD_LOCK();
+ jsdc->errorReporter = reporter;
+ jsdc->errorReporterData = callerdata;
+ JSD_UNLOCK();
+ return JS_TRUE;
+}
+
+JSBool
+jsd_GetErrorReporter(JSDContext* jsdc,
+ JSD_ErrorReporter* reporter,
+ void** callerdata)
+{
+ JSD_LOCK();
+ if( reporter )
+ *reporter = jsdc->errorReporter;
+ if( callerdata )
+ *callerdata = jsdc->errorReporterData;
+ JSD_UNLOCK();
+ return JS_TRUE;
+}