mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
667efac1a4
dumbContext ends up with jsdc->glob as its default global, so we have to be very careful to audit for any places where the code might be assuming that its cx is in the compartment of jsdc->glob. Luckily, the code already seems pretty explicit about its compartments.
967 lines
23 KiB
C++
967 lines
23 KiB
C++
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
|
* vim: set ts=8 sts=4 et sw=4 tw=99:
|
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
|
|
/*
|
|
* JavaScript Debugging support - Script support
|
|
*/
|
|
|
|
#include "jsd.h"
|
|
#include "jsfriendapi.h"
|
|
#include "nsCxPusher.h"
|
|
|
|
using mozilla::AutoSafeJSContext;
|
|
|
|
/* Comment this out to disable (NT specific) dumping as we go */
|
|
/*
|
|
** #ifdef DEBUG
|
|
** #define JSD_DUMP 1
|
|
** #endif
|
|
*/
|
|
|
|
#define NOT_SET_YET -1
|
|
|
|
/***************************************************************************/
|
|
|
|
#ifdef DEBUG
|
|
void JSD_ASSERT_VALID_SCRIPT(JSDScript* jsdscript)
|
|
{
|
|
JS_ASSERT(jsdscript);
|
|
JS_ASSERT(jsdscript->script);
|
|
}
|
|
void JSD_ASSERT_VALID_EXEC_HOOK(JSDExecHook* jsdhook)
|
|
{
|
|
JS_ASSERT(jsdhook);
|
|
JS_ASSERT(jsdhook->hook);
|
|
}
|
|
#endif
|
|
|
|
#ifdef LIVEWIRE
|
|
static JSBool
|
|
HasFileExtention(const char* name, const char* ext)
|
|
{
|
|
int i;
|
|
int len = strlen(ext);
|
|
const char* p = strrchr(name,'.');
|
|
if( !p )
|
|
return JS_FALSE;
|
|
p++;
|
|
for(i = 0; i < len; i++ )
|
|
{
|
|
JS_ASSERT(islower(ext[i]));
|
|
if( 0 == p[i] || tolower(p[i]) != ext[i] )
|
|
return JS_FALSE;
|
|
}
|
|
if( 0 != p[i] )
|
|
return JS_FALSE;
|
|
return JS_TRUE;
|
|
}
|
|
#endif /* LIVEWIRE */
|
|
|
|
static JSDScript*
|
|
_newJSDScript(JSDContext* jsdc,
|
|
JSContext *cx,
|
|
JSScript *script_)
|
|
{
|
|
JS::RootedScript script(cx, script_);
|
|
if ( JS_GetScriptIsSelfHosted(script) )
|
|
return NULL;
|
|
|
|
JSDScript* jsdscript;
|
|
unsigned lineno;
|
|
const char* raw_filename;
|
|
|
|
JS_ASSERT(JSD_SCRIPTS_LOCKED(jsdc));
|
|
|
|
/* these are inlined javascript: urls and we can't handle them now */
|
|
lineno = (unsigned) JS_GetScriptBaseLineNumber(cx, script);
|
|
if( lineno == 0 )
|
|
return NULL;
|
|
|
|
jsdscript = (JSDScript*) calloc(1, sizeof(JSDScript));
|
|
if( ! jsdscript )
|
|
return NULL;
|
|
|
|
raw_filename = JS_GetScriptFilename(cx,script);
|
|
|
|
JS_HashTableAdd(jsdc->scriptsTable, (void *)script, (void *)jsdscript);
|
|
JS_APPEND_LINK(&jsdscript->links, &jsdc->scripts);
|
|
jsdscript->jsdc = jsdc;
|
|
jsdscript->script = script;
|
|
jsdscript->lineBase = lineno;
|
|
jsdscript->lineExtent = (unsigned)NOT_SET_YET;
|
|
jsdscript->data = NULL;
|
|
#ifndef LIVEWIRE
|
|
jsdscript->url = (char*) jsd_BuildNormalizedURL(raw_filename);
|
|
#else
|
|
jsdscript->app = LWDBG_GetCurrentApp();
|
|
if( jsdscript->app && raw_filename )
|
|
{
|
|
jsdscript->url = jsdlw_BuildAppRelativeFilename(jsdscript->app, raw_filename);
|
|
if( function )
|
|
{
|
|
JSString* funid = JS_GetFunctionId(function);
|
|
char* funbytes;
|
|
const char* funnanme;
|
|
if( fuinid )
|
|
{
|
|
funbytes = JS_EncodeString(cx, funid);
|
|
funname = funbytes ? funbytes : "";
|
|
}
|
|
else
|
|
{
|
|
funbytes = NULL;
|
|
funname = "anonymous";
|
|
}
|
|
jsdscript->lwscript =
|
|
LWDBG_GetScriptOfFunction(jsdscript->app,funname);
|
|
JS_Free(cx, funbytes);
|
|
|
|
/* also, make sure this file is added to filelist if is .js file */
|
|
if( HasFileExtention(raw_filename,"js") ||
|
|
HasFileExtention(raw_filename,"sjs") )
|
|
{
|
|
jsdlw_PreLoadSource(jsdc, jsdscript->app, raw_filename, JS_FALSE);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
jsdscript->lwscript = LWDBG_GetCurrentTopLevelScript();
|
|
}
|
|
}
|
|
#endif
|
|
|
|
JS_INIT_CLIST(&jsdscript->hooks);
|
|
|
|
return jsdscript;
|
|
}
|
|
|
|
static void
|
|
_destroyJSDScript(JSDContext* jsdc,
|
|
JSDScript* jsdscript)
|
|
{
|
|
JS_ASSERT(JSD_SCRIPTS_LOCKED(jsdc));
|
|
|
|
/* destroy all hooks */
|
|
jsd_ClearAllExecutionHooksForScript(jsdc, jsdscript);
|
|
|
|
JS_REMOVE_LINK(&jsdscript->links);
|
|
if(jsdscript->url)
|
|
free(jsdscript->url);
|
|
|
|
if (jsdscript->profileData)
|
|
free(jsdscript->profileData);
|
|
|
|
free(jsdscript);
|
|
}
|
|
|
|
/***************************************************************************/
|
|
|
|
#ifdef JSD_DUMP
|
|
#ifndef XP_WIN
|
|
void
|
|
OutputDebugString (char *buf)
|
|
{
|
|
fprintf (stderr, "%s", buf);
|
|
}
|
|
#endif
|
|
|
|
static void
|
|
_dumpJSDScript(JSDContext* jsdc, JSDScript* jsdscript, const char* leadingtext)
|
|
{
|
|
const char* name;
|
|
JSString* fun;
|
|
unsigned base;
|
|
unsigned extent;
|
|
char Buf[256];
|
|
size_t n;
|
|
|
|
name = jsd_GetScriptFilename(jsdc, jsdscript);
|
|
fun = jsd_GetScriptFunctionId(jsdc, jsdscript);
|
|
base = jsd_GetScriptBaseLineNumber(jsdc, jsdscript);
|
|
extent = jsd_GetScriptLineExtent(jsdc, jsdscript);
|
|
n = size_t(snprintf(Buf, sizeof(Buf), "%sscript=%08X, %s, ",
|
|
leadingtext, (unsigned) jsdscript->script,
|
|
name ? name : "no URL"));
|
|
if (n + 1 < sizeof(Buf)) {
|
|
if (fun) {
|
|
n += size_t(snprintf(Buf + n, sizeof(Buf) - n, "%s", "no fun"));
|
|
} else {
|
|
n += JS_PutEscapedFlatString(Buf + n, sizeof(Buf) - n,
|
|
JS_ASSERT_STRING_IS_FLAT(fun), 0);
|
|
Buf[sizeof(Buf) - 1] = '\0';
|
|
}
|
|
if (n + 1 < sizeof(Buf))
|
|
snprintf(Buf + n, sizeof(Buf) - n, ", %d-%d\n", base, base + extent - 1);
|
|
}
|
|
OutputDebugString( Buf );
|
|
}
|
|
|
|
static void
|
|
_dumpJSDScriptList( JSDContext* jsdc )
|
|
{
|
|
JSDScript* iterp = NULL;
|
|
JSDScript* jsdscript = NULL;
|
|
|
|
OutputDebugString( "*** JSDScriptDump\n" );
|
|
while( NULL != (jsdscript = jsd_IterateScripts(jsdc, &iterp)) )
|
|
_dumpJSDScript( jsdc, jsdscript, " script: " );
|
|
}
|
|
#endif /* JSD_DUMP */
|
|
|
|
/***************************************************************************/
|
|
static JSHashNumber
|
|
jsd_hash_script(const void *key)
|
|
{
|
|
return ((JSHashNumber)(ptrdiff_t) key) >> 2; /* help lame MSVC1.5 on Win16 */
|
|
}
|
|
|
|
static void *
|
|
jsd_alloc_script_table(void *priv, size_t size)
|
|
{
|
|
return malloc(size);
|
|
}
|
|
|
|
static void
|
|
jsd_free_script_table(void *priv, void *item, size_t size)
|
|
{
|
|
free(item);
|
|
}
|
|
|
|
static JSHashEntry *
|
|
jsd_alloc_script_entry(void *priv, const void *item)
|
|
{
|
|
return (JSHashEntry*) malloc(sizeof(JSHashEntry));
|
|
}
|
|
|
|
static void
|
|
jsd_free_script_entry(void *priv, JSHashEntry *he, unsigned flag)
|
|
{
|
|
if (flag == HT_FREE_ENTRY)
|
|
{
|
|
_destroyJSDScript((JSDContext*) priv, (JSDScript*) he->value);
|
|
free(he);
|
|
}
|
|
}
|
|
|
|
static JSHashAllocOps script_alloc_ops = {
|
|
jsd_alloc_script_table, jsd_free_script_table,
|
|
jsd_alloc_script_entry, jsd_free_script_entry
|
|
};
|
|
|
|
#ifndef JSD_SCRIPT_HASH_SIZE
|
|
#define JSD_SCRIPT_HASH_SIZE 1024
|
|
#endif
|
|
|
|
JSBool
|
|
jsd_InitScriptManager(JSDContext* jsdc)
|
|
{
|
|
JS_INIT_CLIST(&jsdc->scripts);
|
|
jsdc->scriptsTable = JS_NewHashTable(JSD_SCRIPT_HASH_SIZE, jsd_hash_script,
|
|
JS_CompareValues, JS_CompareValues,
|
|
&script_alloc_ops, (void*) jsdc);
|
|
return !!jsdc->scriptsTable;
|
|
}
|
|
|
|
void
|
|
jsd_DestroyScriptManager(JSDContext* jsdc)
|
|
{
|
|
JSD_LOCK_SCRIPTS(jsdc);
|
|
if (jsdc->scriptsTable)
|
|
JS_HashTableDestroy(jsdc->scriptsTable);
|
|
JSD_UNLOCK_SCRIPTS(jsdc);
|
|
}
|
|
|
|
JSDScript*
|
|
jsd_FindJSDScript( JSDContext* jsdc,
|
|
JSScript *script )
|
|
{
|
|
JS_ASSERT(JSD_SCRIPTS_LOCKED(jsdc));
|
|
return (JSDScript*) JS_HashTableLookup(jsdc->scriptsTable, (void *)script);
|
|
}
|
|
|
|
JSDScript *
|
|
jsd_FindOrCreateJSDScript(JSDContext *jsdc,
|
|
JSContext *cx,
|
|
JSScript *script_,
|
|
JSAbstractFramePtr frame)
|
|
{
|
|
JS::RootedScript script(cx, script_);
|
|
JSDScript *jsdscript;
|
|
JS_ASSERT(JSD_SCRIPTS_LOCKED(jsdc));
|
|
|
|
jsdscript = jsd_FindJSDScript(jsdc, script);
|
|
if (jsdscript)
|
|
return jsdscript;
|
|
|
|
/* Fallback for unknown scripts: create a new script. */
|
|
if (!frame) {
|
|
JSBrokenFrameIterator iter(cx);
|
|
if (!iter.done())
|
|
frame = iter.abstractFramePtr();
|
|
}
|
|
if (frame)
|
|
jsdscript = _newJSDScript(jsdc, cx, script);
|
|
|
|
return jsdscript;
|
|
}
|
|
|
|
JSDProfileData*
|
|
jsd_GetScriptProfileData(JSDContext* jsdc, JSDScript *script)
|
|
{
|
|
if (!script->profileData)
|
|
script->profileData = (JSDProfileData*)calloc(1, sizeof(JSDProfileData));
|
|
|
|
return script->profileData;
|
|
}
|
|
|
|
uint32_t
|
|
jsd_GetScriptFlags(JSDContext *jsdc, JSDScript *script)
|
|
{
|
|
return script->flags;
|
|
}
|
|
|
|
void
|
|
jsd_SetScriptFlags(JSDContext *jsdc, JSDScript *script, uint32_t flags)
|
|
{
|
|
script->flags = flags;
|
|
}
|
|
|
|
unsigned
|
|
jsd_GetScriptCallCount(JSDContext* jsdc, JSDScript *script)
|
|
{
|
|
if (script->profileData)
|
|
return script->profileData->callCount;
|
|
|
|
return 0;
|
|
}
|
|
|
|
unsigned
|
|
jsd_GetScriptMaxRecurseDepth(JSDContext* jsdc, JSDScript *script)
|
|
{
|
|
if (script->profileData)
|
|
return script->profileData->maxRecurseDepth;
|
|
|
|
return 0;
|
|
}
|
|
|
|
double
|
|
jsd_GetScriptMinExecutionTime(JSDContext* jsdc, JSDScript *script)
|
|
{
|
|
if (script->profileData)
|
|
return script->profileData->minExecutionTime;
|
|
|
|
return 0.0;
|
|
}
|
|
|
|
double
|
|
jsd_GetScriptMaxExecutionTime(JSDContext* jsdc, JSDScript *script)
|
|
{
|
|
if (script->profileData)
|
|
return script->profileData->maxExecutionTime;
|
|
|
|
return 0.0;
|
|
}
|
|
|
|
double
|
|
jsd_GetScriptTotalExecutionTime(JSDContext* jsdc, JSDScript *script)
|
|
{
|
|
if (script->profileData)
|
|
return script->profileData->totalExecutionTime;
|
|
|
|
return 0.0;
|
|
}
|
|
|
|
double
|
|
jsd_GetScriptMinOwnExecutionTime(JSDContext* jsdc, JSDScript *script)
|
|
{
|
|
if (script->profileData)
|
|
return script->profileData->minOwnExecutionTime;
|
|
|
|
return 0.0;
|
|
}
|
|
|
|
double
|
|
jsd_GetScriptMaxOwnExecutionTime(JSDContext* jsdc, JSDScript *script)
|
|
{
|
|
if (script->profileData)
|
|
return script->profileData->maxOwnExecutionTime;
|
|
|
|
return 0.0;
|
|
}
|
|
|
|
double
|
|
jsd_GetScriptTotalOwnExecutionTime(JSDContext* jsdc, JSDScript *script)
|
|
{
|
|
if (script->profileData)
|
|
return script->profileData->totalOwnExecutionTime;
|
|
|
|
return 0.0;
|
|
}
|
|
|
|
void
|
|
jsd_ClearScriptProfileData(JSDContext* jsdc, JSDScript *script)
|
|
{
|
|
if (script->profileData)
|
|
{
|
|
free(script->profileData);
|
|
script->profileData = NULL;
|
|
}
|
|
}
|
|
|
|
JSScript *
|
|
jsd_GetJSScript (JSDContext *jsdc, JSDScript *script)
|
|
{
|
|
return script->script;
|
|
}
|
|
|
|
JSFunction *
|
|
jsd_GetJSFunction (JSDContext *jsdc, JSDScript *script)
|
|
{
|
|
AutoSafeJSContext cx; // NB: Actually unused.
|
|
return JS_GetScriptFunction(cx, script->script);
|
|
}
|
|
|
|
JSDScript*
|
|
jsd_IterateScripts(JSDContext* jsdc, JSDScript **iterp)
|
|
{
|
|
JSDScript *jsdscript = *iterp;
|
|
|
|
JS_ASSERT(JSD_SCRIPTS_LOCKED(jsdc));
|
|
|
|
if( !jsdscript )
|
|
jsdscript = (JSDScript *)jsdc->scripts.next;
|
|
if( jsdscript == (JSDScript *)&jsdc->scripts )
|
|
return NULL;
|
|
*iterp = (JSDScript*) jsdscript->links.next;
|
|
return jsdscript;
|
|
}
|
|
|
|
void *
|
|
jsd_SetScriptPrivate(JSDScript *jsdscript, void *data)
|
|
{
|
|
void *rval = jsdscript->data;
|
|
jsdscript->data = data;
|
|
return rval;
|
|
}
|
|
|
|
void *
|
|
jsd_GetScriptPrivate(JSDScript *jsdscript)
|
|
{
|
|
return jsdscript->data;
|
|
}
|
|
|
|
JSBool
|
|
jsd_IsActiveScript(JSDContext* jsdc, JSDScript *jsdscript)
|
|
{
|
|
JSDScript *current;
|
|
|
|
JS_ASSERT(JSD_SCRIPTS_LOCKED(jsdc));
|
|
|
|
for( current = (JSDScript *)jsdc->scripts.next;
|
|
current != (JSDScript *)&jsdc->scripts;
|
|
current = (JSDScript *)current->links.next )
|
|
{
|
|
if(jsdscript == current)
|
|
return JS_TRUE;
|
|
}
|
|
return JS_FALSE;
|
|
}
|
|
|
|
const char*
|
|
jsd_GetScriptFilename(JSDContext* jsdc, JSDScript *jsdscript)
|
|
{
|
|
return jsdscript->url;
|
|
}
|
|
|
|
JSString*
|
|
jsd_GetScriptFunctionId(JSDContext* jsdc, JSDScript *jsdscript)
|
|
{
|
|
JSString* str;
|
|
JSFunction *fun = jsd_GetJSFunction(jsdc, jsdscript);
|
|
|
|
if( ! fun )
|
|
return NULL;
|
|
str = JS_GetFunctionId(fun);
|
|
|
|
/* For compatibility we return "anonymous", not an empty string here. */
|
|
return str ? str : JS_GetAnonymousString(jsdc->jsrt);
|
|
}
|
|
|
|
unsigned
|
|
jsd_GetScriptBaseLineNumber(JSDContext* jsdc, JSDScript *jsdscript)
|
|
{
|
|
return jsdscript->lineBase;
|
|
}
|
|
|
|
unsigned
|
|
jsd_GetScriptLineExtent(JSDContext* jsdc, JSDScript *jsdscript)
|
|
{
|
|
AutoSafeJSContext cx;
|
|
JSAutoCompartment ac(cx, jsdc->glob); // Just in case.
|
|
if( NOT_SET_YET == (int)jsdscript->lineExtent )
|
|
jsdscript->lineExtent = JS_GetScriptLineExtent(cx, jsdscript->script);
|
|
return jsdscript->lineExtent;
|
|
}
|
|
|
|
uintptr_t
|
|
jsd_GetClosestPC(JSDContext* jsdc, JSDScript* jsdscript, unsigned line)
|
|
{
|
|
uintptr_t pc;
|
|
|
|
if( !jsdscript )
|
|
return 0;
|
|
#ifdef LIVEWIRE
|
|
if( jsdscript->lwscript )
|
|
{
|
|
unsigned newline;
|
|
jsdlw_RawToProcessedLineNumber(jsdc, jsdscript, line, &newline);
|
|
if( line != newline )
|
|
line = newline;
|
|
}
|
|
#endif
|
|
|
|
AutoSafeJSContext cx;
|
|
JSAutoCompartment ac(cx, jsdscript->script);
|
|
pc = (uintptr_t) JS_LineNumberToPC(cx, jsdscript->script, line );
|
|
return pc;
|
|
}
|
|
|
|
unsigned
|
|
jsd_GetClosestLine(JSDContext* jsdc, JSDScript* jsdscript, uintptr_t pc)
|
|
{
|
|
unsigned first = jsdscript->lineBase;
|
|
unsigned last = first + jsd_GetScriptLineExtent(jsdc, jsdscript) - 1;
|
|
unsigned line = 0;
|
|
|
|
if (pc) {
|
|
AutoSafeJSContext cx;
|
|
JSAutoCompartment ac(cx, jsdscript->script);
|
|
line = JS_PCToLineNumber(cx, jsdscript->script, (jsbytecode*)pc);
|
|
}
|
|
|
|
if( line < first )
|
|
return first;
|
|
if( line > last )
|
|
return last;
|
|
|
|
#ifdef LIVEWIRE
|
|
if( jsdscript && jsdscript->lwscript )
|
|
{
|
|
unsigned newline;
|
|
jsdlw_ProcessedToRawLineNumber(jsdc, jsdscript, line, &newline);
|
|
line = newline;
|
|
}
|
|
#endif
|
|
|
|
return line;
|
|
}
|
|
|
|
JSBool
|
|
jsd_GetLinePCs(JSDContext* jsdc, JSDScript* jsdscript,
|
|
unsigned startLine, unsigned maxLines,
|
|
unsigned* count, unsigned** retLines, uintptr_t** retPCs)
|
|
{
|
|
unsigned first = jsdscript->lineBase;
|
|
unsigned last = first + jsd_GetScriptLineExtent(jsdc, jsdscript) - 1;
|
|
JSBool ok;
|
|
jsbytecode **pcs;
|
|
unsigned i;
|
|
|
|
if (last < startLine)
|
|
return JS_TRUE;
|
|
|
|
AutoSafeJSContext cx;
|
|
JSAutoCompartment ac(cx, jsdscript->script);
|
|
|
|
ok = JS_GetLinePCs(cx, jsdscript->script,
|
|
startLine, maxLines,
|
|
count, retLines, &pcs);
|
|
|
|
if (ok) {
|
|
if (retPCs) {
|
|
for (i = 0; i < *count; ++i) {
|
|
(*retPCs)[i] = (*pcs)[i];
|
|
}
|
|
}
|
|
|
|
JS_free(cx, pcs);
|
|
}
|
|
|
|
return ok;
|
|
}
|
|
|
|
JSBool
|
|
jsd_SetScriptHook(JSDContext* jsdc, JSD_ScriptHookProc hook, void* callerdata)
|
|
{
|
|
JSD_LOCK();
|
|
jsdc->scriptHook = hook;
|
|
jsdc->scriptHookData = callerdata;
|
|
JSD_UNLOCK();
|
|
return JS_TRUE;
|
|
}
|
|
|
|
JSBool
|
|
jsd_GetScriptHook(JSDContext* jsdc, JSD_ScriptHookProc* hook, void** callerdata)
|
|
{
|
|
JSD_LOCK();
|
|
if( hook )
|
|
*hook = jsdc->scriptHook;
|
|
if( callerdata )
|
|
*callerdata = jsdc->scriptHookData;
|
|
JSD_UNLOCK();
|
|
return JS_TRUE;
|
|
}
|
|
|
|
JSBool
|
|
jsd_EnableSingleStepInterrupts(JSDContext* jsdc, JSDScript* jsdscript, JSBool enable)
|
|
{
|
|
JSBool rv;
|
|
AutoSafeJSContext cx;
|
|
JSAutoCompartment ac(cx, jsdscript->script);
|
|
JSD_LOCK();
|
|
rv = JS_SetSingleStepMode(cx, jsdscript->script, enable);
|
|
JSD_UNLOCK();
|
|
return rv;
|
|
}
|
|
|
|
|
|
/***************************************************************************/
|
|
|
|
void
|
|
jsd_NewScriptHookProc(
|
|
JSContext *cx,
|
|
const char *filename, /* URL this script loads from */
|
|
unsigned lineno, /* line where this script starts */
|
|
JSScript *script,
|
|
JSFunction *fun,
|
|
void* callerdata )
|
|
{
|
|
JSDScript* jsdscript = NULL;
|
|
JSDContext* jsdc = (JSDContext*) callerdata;
|
|
JSD_ScriptHookProc hook;
|
|
void* hookData;
|
|
|
|
JSD_ASSERT_VALID_CONTEXT(jsdc);
|
|
|
|
if( JSD_IS_DANGEROUS_THREAD(jsdc) )
|
|
return;
|
|
|
|
JSD_LOCK_SCRIPTS(jsdc);
|
|
jsdscript = _newJSDScript(jsdc, cx, script);
|
|
JSD_UNLOCK_SCRIPTS(jsdc);
|
|
if( ! jsdscript )
|
|
return;
|
|
|
|
#ifdef JSD_DUMP
|
|
JSD_LOCK_SCRIPTS(jsdc);
|
|
_dumpJSDScript(jsdc, jsdscript, "***NEW Script: ");
|
|
_dumpJSDScriptList( jsdc );
|
|
JSD_UNLOCK_SCRIPTS(jsdc);
|
|
#endif /* JSD_DUMP */
|
|
|
|
/* local in case jsdc->scriptHook gets cleared on another thread */
|
|
JSD_LOCK();
|
|
hook = jsdc->scriptHook;
|
|
if( hook )
|
|
jsdscript->flags = jsdscript->flags | JSD_SCRIPT_CALL_DESTROY_HOOK_BIT;
|
|
hookData = jsdc->scriptHookData;
|
|
JSD_UNLOCK();
|
|
|
|
if( hook )
|
|
hook(jsdc, jsdscript, JS_TRUE, hookData);
|
|
}
|
|
|
|
void
|
|
jsd_DestroyScriptHookProc(
|
|
JSFreeOp *fop,
|
|
JSScript *script_,
|
|
void* callerdata )
|
|
{
|
|
JSDScript* jsdscript = NULL;
|
|
JSDContext* jsdc = (JSDContext*) callerdata;
|
|
// NB: We're called during GC, so we can't push a cx. Root directly with
|
|
// the runtime.
|
|
JS::RootedScript script(jsdc->jsrt, script_);
|
|
JSD_ScriptHookProc hook;
|
|
void* hookData;
|
|
|
|
JSD_ASSERT_VALID_CONTEXT(jsdc);
|
|
|
|
if( JSD_IS_DANGEROUS_THREAD(jsdc) )
|
|
return;
|
|
|
|
JSD_LOCK_SCRIPTS(jsdc);
|
|
jsdscript = jsd_FindJSDScript(jsdc, script);
|
|
JSD_UNLOCK_SCRIPTS(jsdc);
|
|
|
|
if( ! jsdscript )
|
|
return;
|
|
|
|
#ifdef JSD_DUMP
|
|
JSD_LOCK_SCRIPTS(jsdc);
|
|
_dumpJSDScript(jsdc, jsdscript, "***DESTROY Script: ");
|
|
JSD_UNLOCK_SCRIPTS(jsdc);
|
|
#endif /* JSD_DUMP */
|
|
|
|
/* local in case hook gets cleared on another thread */
|
|
JSD_LOCK();
|
|
hook = (jsdscript->flags & JSD_SCRIPT_CALL_DESTROY_HOOK_BIT) ? jsdc->scriptHook : NULL;
|
|
hookData = jsdc->scriptHookData;
|
|
JSD_UNLOCK();
|
|
|
|
if( hook )
|
|
hook(jsdc, jsdscript, JS_FALSE, hookData);
|
|
|
|
JSD_LOCK_SCRIPTS(jsdc);
|
|
JS_HashTableRemove(jsdc->scriptsTable, (void *)script);
|
|
JSD_UNLOCK_SCRIPTS(jsdc);
|
|
|
|
#ifdef JSD_DUMP
|
|
JSD_LOCK_SCRIPTS(jsdc);
|
|
_dumpJSDScriptList(jsdc);
|
|
JSD_UNLOCK_SCRIPTS(jsdc);
|
|
#endif /* JSD_DUMP */
|
|
}
|
|
|
|
|
|
/***************************************************************************/
|
|
|
|
static JSDExecHook*
|
|
_findHook(JSDContext* jsdc, JSDScript* jsdscript, uintptr_t pc)
|
|
{
|
|
JSDExecHook* jsdhook;
|
|
JSCList* list = &jsdscript->hooks;
|
|
|
|
for( jsdhook = (JSDExecHook*)list->next;
|
|
jsdhook != (JSDExecHook*)list;
|
|
jsdhook = (JSDExecHook*)jsdhook->links.next )
|
|
{
|
|
if (jsdhook->pc == pc)
|
|
return jsdhook;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
static JSBool
|
|
_isActiveHook(JSDContext* jsdc, JSScript *script, JSDExecHook* jsdhook)
|
|
{
|
|
JSDExecHook* current;
|
|
JSCList* list;
|
|
JSDScript* jsdscript;
|
|
|
|
JSD_LOCK_SCRIPTS(jsdc);
|
|
jsdscript = jsd_FindJSDScript(jsdc, script);
|
|
if( ! jsdscript)
|
|
{
|
|
JSD_UNLOCK_SCRIPTS(jsdc);
|
|
return JS_FALSE;
|
|
}
|
|
|
|
list = &jsdscript->hooks;
|
|
|
|
for( current = (JSDExecHook*)list->next;
|
|
current != (JSDExecHook*)list;
|
|
current = (JSDExecHook*)current->links.next )
|
|
{
|
|
if(current == jsdhook)
|
|
{
|
|
JSD_UNLOCK_SCRIPTS(jsdc);
|
|
return JS_TRUE;
|
|
}
|
|
}
|
|
JSD_UNLOCK_SCRIPTS(jsdc);
|
|
return JS_FALSE;
|
|
}
|
|
|
|
|
|
JSTrapStatus
|
|
jsd_TrapHandler(JSContext *cx, JSScript *script_, jsbytecode *pc, jsval *rval,
|
|
jsval closure)
|
|
{
|
|
JS::RootedScript script(cx, script_);
|
|
JSDExecHook* jsdhook = (JSDExecHook*) JSVAL_TO_PRIVATE(closure);
|
|
JSD_ExecutionHookProc hook;
|
|
void* hookData;
|
|
JSDContext* jsdc;
|
|
JSDScript* jsdscript;
|
|
|
|
JSD_LOCK();
|
|
|
|
if( NULL == (jsdc = jsd_JSDContextForJSContext(cx)) ||
|
|
! _isActiveHook(jsdc, script, jsdhook) )
|
|
{
|
|
JSD_UNLOCK();
|
|
return JSTRAP_CONTINUE;
|
|
}
|
|
|
|
JSD_ASSERT_VALID_EXEC_HOOK(jsdhook);
|
|
JS_ASSERT(!jsdhook->pc || jsdhook->pc == (uintptr_t)pc);
|
|
JS_ASSERT(jsdhook->jsdscript->script == script);
|
|
JS_ASSERT(jsdhook->jsdscript->jsdc == jsdc);
|
|
|
|
hook = jsdhook->hook;
|
|
hookData = jsdhook->callerdata;
|
|
jsdscript = jsdhook->jsdscript;
|
|
|
|
/* do not use jsdhook-> after this point */
|
|
JSD_UNLOCK();
|
|
|
|
if( ! jsdc || ! jsdc->inited )
|
|
return JSTRAP_CONTINUE;
|
|
|
|
if( JSD_IS_DANGEROUS_THREAD(jsdc) )
|
|
return JSTRAP_CONTINUE;
|
|
|
|
#ifdef LIVEWIRE
|
|
if( ! jsdlw_UserCodeAtPC(jsdc, jsdscript, (uintptr_t)pc) )
|
|
return JSTRAP_CONTINUE;
|
|
#endif
|
|
|
|
return jsd_CallExecutionHook(jsdc, cx, JSD_HOOK_BREAKPOINT,
|
|
hook, hookData, rval);
|
|
}
|
|
|
|
|
|
|
|
JSBool
|
|
jsd_SetExecutionHook(JSDContext* jsdc,
|
|
JSDScript* jsdscript,
|
|
uintptr_t pc,
|
|
JSD_ExecutionHookProc hook,
|
|
void* callerdata)
|
|
{
|
|
JSDExecHook* jsdhook;
|
|
JSBool rv;
|
|
|
|
JSD_LOCK();
|
|
if( ! hook )
|
|
{
|
|
jsd_ClearExecutionHook(jsdc, jsdscript, pc);
|
|
JSD_UNLOCK();
|
|
return JS_TRUE;
|
|
}
|
|
|
|
jsdhook = _findHook(jsdc, jsdscript, pc);
|
|
if( jsdhook )
|
|
{
|
|
jsdhook->hook = hook;
|
|
jsdhook->callerdata = callerdata;
|
|
JSD_UNLOCK();
|
|
return JS_TRUE;
|
|
}
|
|
/* else... */
|
|
|
|
jsdhook = (JSDExecHook*)calloc(1, sizeof(JSDExecHook));
|
|
if( ! jsdhook ) {
|
|
JSD_UNLOCK();
|
|
return JS_FALSE;
|
|
}
|
|
jsdhook->jsdscript = jsdscript;
|
|
jsdhook->pc = pc;
|
|
jsdhook->hook = hook;
|
|
jsdhook->callerdata = callerdata;
|
|
|
|
{
|
|
AutoSafeJSContext cx;
|
|
JSAutoCompartment ac(cx, jsdscript->script);
|
|
rv = JS_SetTrap(cx, jsdscript->script,
|
|
(jsbytecode*)pc, jsd_TrapHandler,
|
|
PRIVATE_TO_JSVAL(jsdhook));
|
|
}
|
|
|
|
if ( ! rv ) {
|
|
free(jsdhook);
|
|
JSD_UNLOCK();
|
|
return JS_FALSE;
|
|
}
|
|
|
|
JS_APPEND_LINK(&jsdhook->links, &jsdscript->hooks);
|
|
JSD_UNLOCK();
|
|
|
|
return JS_TRUE;
|
|
}
|
|
|
|
JSBool
|
|
jsd_ClearExecutionHook(JSDContext* jsdc,
|
|
JSDScript* jsdscript,
|
|
uintptr_t pc)
|
|
{
|
|
JSDExecHook* jsdhook;
|
|
|
|
JSD_LOCK();
|
|
|
|
jsdhook = _findHook(jsdc, jsdscript, pc);
|
|
if( ! jsdhook )
|
|
{
|
|
JSD_UNLOCK();
|
|
return JS_FALSE;
|
|
}
|
|
|
|
{
|
|
AutoSafeJSContext cx;
|
|
JSAutoCompartment ac(cx, jsdscript->script);
|
|
JS_ClearTrap(cx, jsdscript->script,
|
|
(jsbytecode*)pc, NULL, NULL );
|
|
}
|
|
|
|
JS_REMOVE_LINK(&jsdhook->links);
|
|
free(jsdhook);
|
|
|
|
JSD_UNLOCK();
|
|
return JS_TRUE;
|
|
}
|
|
|
|
JSBool
|
|
jsd_ClearAllExecutionHooksForScript(JSDContext* jsdc, JSDScript* jsdscript)
|
|
{
|
|
JSDExecHook* jsdhook;
|
|
JSCList* list = &jsdscript->hooks;
|
|
JSD_LOCK();
|
|
|
|
while( (JSDExecHook*)list != (jsdhook = (JSDExecHook*)list->next) )
|
|
{
|
|
JS_REMOVE_LINK(&jsdhook->links);
|
|
free(jsdhook);
|
|
}
|
|
|
|
JS_ClearScriptTraps(jsdc->jsrt, jsdscript->script);
|
|
JSD_UNLOCK();
|
|
|
|
return JS_TRUE;
|
|
}
|
|
|
|
JSBool
|
|
jsd_ClearAllExecutionHooks(JSDContext* jsdc)
|
|
{
|
|
JSDScript* jsdscript;
|
|
JSDScript* iterp = NULL;
|
|
|
|
JSD_LOCK();
|
|
while( NULL != (jsdscript = jsd_IterateScripts(jsdc, &iterp)) )
|
|
jsd_ClearAllExecutionHooksForScript(jsdc, jsdscript);
|
|
JSD_UNLOCK();
|
|
return JS_TRUE;
|
|
}
|
|
|
|
void
|
|
jsd_ScriptCreated(JSDContext* jsdc,
|
|
JSContext *cx,
|
|
const char *filename, /* URL this script loads from */
|
|
unsigned lineno, /* line where this script starts */
|
|
JSScript *script,
|
|
JSFunction *fun)
|
|
{
|
|
jsd_NewScriptHookProc(cx, filename, lineno, script, fun, jsdc);
|
|
}
|
|
|
|
void
|
|
jsd_ScriptDestroyed(JSDContext* jsdc,
|
|
JSFreeOp *fop,
|
|
JSScript *script)
|
|
{
|
|
jsd_DestroyScriptHookProc(fop, script, jsdc);
|
|
}
|