home *** CD-ROM | disk | FTP | other *** search
- /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
- *
- * The contents of this file are subject to the Netscape Public License
- * Version 1.0 (the "NPL"); you may not use this file except in
- * compliance with the NPL. You may obtain a copy of the NPL at
- * http://www.mozilla.org/NPL/
- *
- * Software distributed under the NPL is distributed on an "AS IS" basis,
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
- * for the specific language governing rights and limitations under the
- * NPL.
- *
- * The Initial Developer of this code under the NPL is Netscape
- * Communications Corporation. Portions created by Netscape are
- * Copyright (C) 1998 Netscape Communications Corporation. All Rights
- * Reserved.
- */
-
- /*
- * JS debugging API.
- */
- #include <string.h>
- #include "prtypes.h"
- #include "prlog.h"
- #include "prclist.h"
- #include "jsapi.h"
- #include "jsdbgapi.h"
- #include "jsgc.h"
- #include "jsinterp.h"
- #include "jsobj.h"
- #include "jsopcode.h"
- #include "jsscope.h"
- #include "jsscript.h"
- #include "jsstr.h"
- #include "jscntxt.h"
- #include "jsfun.h"
-
- typedef struct JSTrap {
- PRCList links;
- JSScript *script;
- jsbytecode *pc;
- JSOp op;
- JSTrapHandler handler;
- void *closure;
- } JSTrap;
-
- static JSTrap *
- FindTrap(JSRuntime *rt, JSScript *script, jsbytecode *pc)
- {
- JSTrap *trap;
-
- for (trap = (JSTrap *)rt->trapList.next;
- trap != (JSTrap *)&rt->trapList;
- trap = (JSTrap *)trap->links.next) {
- if (trap->script == script && trap->pc == pc)
- return trap;
- }
- return NULL;
- }
-
- void
- js_PatchOpcode(JSContext *cx, JSScript *script, jsbytecode *pc, JSOp op)
- {
- JSTrap *trap;
-
- trap = FindTrap(cx->runtime, script, pc);
- if (trap)
- trap->op = op;
- else
- *pc = (jsbytecode)op;
- }
-
- PR_IMPLEMENT(JSBool)
- JS_SetTrap(JSContext *cx, JSScript *script, jsbytecode *pc,
- JSTrapHandler handler, void *closure)
- {
- JSRuntime *rt;
- JSTrap *trap;
-
- rt = cx->runtime;
- trap = FindTrap(rt, script, pc);
- if (trap) {
- /* Restore opcode at pc so it can be saved again. */
- *pc = (jsbytecode)trap->op;
- } else {
- trap = JS_malloc(cx, sizeof *trap);
- if (!trap || !js_AddRoot(cx, &trap->closure)) {
- if (trap)
- JS_free(cx, trap);
- return JS_FALSE;
- }
- }
- PR_APPEND_LINK(&trap->links, &rt->trapList);
- trap->script = script;
- trap->pc = pc;
- trap->op = *pc;
- trap->handler = handler;
- trap->closure = closure;
- *pc = JSOP_TRAP;
- return JS_TRUE;
- }
-
- PR_IMPLEMENT(JSOp)
- JS_GetTrapOpcode(JSContext *cx, JSScript *script, jsbytecode *pc)
- {
- JSTrap *trap;
-
- trap = FindTrap(cx->runtime, script, pc);
- if (!trap) {
- PR_ASSERT(0); /* XXX can't happen */
- return JSOP_LIMIT;
- }
- return trap->op;
- }
-
- static void
- DestroyTrap(JSContext *cx, JSTrap *trap)
- {
- PR_REMOVE_LINK(&trap->links);
- *trap->pc = (jsbytecode)trap->op;
- js_RemoveRoot(cx, &trap->closure);
- JS_free(cx, trap);
- }
-
- PR_IMPLEMENT(void)
- JS_ClearTrap(JSContext *cx, JSScript *script, jsbytecode *pc,
- JSTrapHandler *handlerp, void **closurep)
- {
- JSTrap *trap;
-
- trap = FindTrap(cx->runtime, script, pc);
- if (handlerp)
- *handlerp = trap ? trap->handler : NULL;
- if (closurep)
- *closurep = trap ? trap->closure : NULL;
- if (trap)
- DestroyTrap(cx, trap);
- }
-
- PR_IMPLEMENT(void)
- JS_ClearScriptTraps(JSContext *cx, JSScript *script)
- {
- JSRuntime *rt;
- JSTrap *trap, *next;
-
- rt = cx->runtime;
- for (trap = (JSTrap *)rt->trapList.next;
- trap != (JSTrap *)&rt->trapList;
- trap = next) {
- next = (JSTrap *)trap->links.next;
- if (trap->script == script)
- DestroyTrap(cx, trap);
- }
- }
-
- PR_IMPLEMENT(void)
- JS_ClearAllTraps(JSContext *cx)
- {
- JSRuntime *rt;
- JSTrap *trap, *next;
-
- rt = cx->runtime;
- for (trap = (JSTrap *)rt->trapList.next;
- trap != (JSTrap *)&rt->trapList;
- trap = next) {
- next = (JSTrap *)trap->links.next;
- DestroyTrap(cx, trap);
- }
- }
-
- PR_IMPLEMENT(JSTrapStatus)
- JS_HandleTrap(JSContext *cx, JSScript *script, jsbytecode *pc, jsval *rval)
- {
- JSTrap *trap;
- JSTrapStatus status;
-
- trap = FindTrap(cx->runtime, script, pc);
- if (!trap) {
- PR_ASSERT(0); /* XXX can't happen */
- return JSTRAP_ERROR;
- }
- status = trap->handler(cx, script, pc, rval, trap->closure);
- if (status == JSTRAP_CONTINUE) {
- /* By convention, return the true op to the interpreter in rval. */
- *rval = INT_TO_JSVAL((jsint)trap->op);
- }
- return status;
- }
-
- PR_IMPLEMENT(JSBool)
- JS_SetInterrupt(JSRuntime *rt, JSTrapHandler handler, void *closure)
- {
- rt->interruptHandler = handler;
- rt->interruptHandlerData = closure;
- return JS_TRUE;
- }
-
- PR_IMPLEMENT(JSBool)
- JS_ClearInterrupt(JSRuntime *rt, JSTrapHandler *handlerp, void **closurep)
- {
- if (handlerp)
- *handlerp = rt->interruptHandler;
- if (closurep)
- *closurep = rt->interruptHandlerData;
- rt->interruptHandler = 0;
- rt->interruptHandlerData = 0;
- return JS_TRUE;
- }
-
-
- typedef struct JSWatchPoint {
- PRCList links;
- JSObject *object; /* weak link, see js_FinalizeObject */
- jsval userid;
- JSProperty *prop;
- JSPropertyOp setter;
- JSWatchPointHandler handler;
- void *closure;
- jsrefcount nrefs;
- } JSWatchPoint;
-
- #define HoldWatchPoint(wp) ((wp)->nrefs++)
-
- static void
- DropWatchPoint(JSContext *cx, JSWatchPoint *wp)
- {
- if (--wp->nrefs != 0)
- return;
- wp->prop->setter = wp->setter;
- js_DropProperty(cx, wp->prop);
- PR_REMOVE_LINK(&wp->links);
- js_RemoveRoot(cx, &wp->closure);
- JS_free(cx, wp);
- }
-
- static JSWatchPoint *
- FindWatchPoint(JSRuntime *rt, JSObject *obj, jsval userid)
- {
- JSWatchPoint *wp;
-
- for (wp = (JSWatchPoint *)rt->watchPointList.next;
- wp != (JSWatchPoint *)&rt->watchPointList;
- wp = (JSWatchPoint *)wp->links.next) {
- if (wp->object == obj && wp->userid == userid)
- return wp;
- }
- return NULL;
- }
-
- JSProperty *
- js_FindWatchPoint(JSRuntime *rt, JSObject *obj, jsval userid)
- {
- JSWatchPoint *wp;
-
- wp = FindWatchPoint(rt, obj, userid);
- if (!wp)
- return NULL;
- return wp->prop;
- }
-
- JSBool PR_CALLBACK
- js_watch_set(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
- {
- JSRuntime *rt;
- JSWatchPoint *wp;
- JSProperty *prop;
- JSSymbol *sym;
- jsval symid, value;
- JSScope *scope;
- JSAtom *atom;
- JSBool ok;
-
- rt = cx->runtime;
- for (wp = (JSWatchPoint *)rt->watchPointList.next;
- wp != (JSWatchPoint *)&rt->watchPointList;
- wp = (JSWatchPoint *)wp->links.next) {
- prop = wp->prop;
- if (wp->object == obj && prop->id == id) {
- sym = prop->symbols;
- if (!sym) {
- symid = wp->userid;
- atom = NULL;
- if (JSVAL_IS_STRING(symid)) {
- atom = js_ValueToStringAtom(cx, symid);
- if (!atom)
- return JS_FALSE;
- symid = (jsval)atom;
- }
- scope = (JSScope *) obj->map;
- ok = obj->map->clasp->addProperty(cx, obj, prop->id, &value) &&
- scope->ops->add(cx, scope, symid, prop);
- if (atom)
- js_DropAtom(cx, atom);
- if (!ok)
- return JS_FALSE;
- sym = prop->symbols;
- }
- HoldWatchPoint(wp);
- ok = wp->handler(cx, obj, js_IdToValue(sym_id(sym)),
- obj->slots[wp->prop->slot], vp, wp->closure);
- if (ok)
- ok = wp->setter(cx, obj, id, vp);
- DropWatchPoint(cx, wp);
- return ok;
- }
- }
- PR_ASSERT(0); /* XXX can't happen */
- return JS_FALSE;
- }
-
- PR_IMPLEMENT(JSBool)
- JS_SetWatchPoint(JSContext *cx, JSObject *obj, jsval id,
- JSWatchPointHandler handler, void *closure)
- {
- JSAtom *atom;
- jsval symid;
- JSProperty *prop;
- JSRuntime *rt;
- JSWatchPoint *wp;
-
- if (JSVAL_IS_INT(id)) {
- symid = id;
- atom = NULL;
- } else {
- atom = js_ValueToStringAtom(cx, id);
- if (!atom)
- return JS_FALSE;
- symid = (jsval)atom;
- }
-
- if (!js_LookupProperty(cx, obj, symid, &obj, &prop))
- return JS_FALSE;
- rt = cx->runtime;
- if (!prop) {
- /* Check for a deleted symbol watchpoint, which holds its property. */
- prop = js_FindWatchPoint(rt, obj, id);
- if (!prop) {
- /* Make a new property in obj so we can watch for the first set. */
- prop = js_DefineProperty(cx, obj, symid, JSVAL_VOID, NULL, NULL, 0);
- }
- } else if (prop->object != obj) {
- /* Clone the prototype property so we can watch the right object. */
- prop = js_DefineProperty(cx, obj, symid,
- prop->object->slots[prop->slot],
- prop->getter, prop->setter, prop->flags);
- }
- if (atom)
- js_DropAtom(cx, atom);
- if (!prop)
- return JS_FALSE;
-
- wp = FindWatchPoint(rt, obj, id);
- if (!wp) {
- wp = JS_malloc(cx, sizeof *wp);
- if (!wp)
- return JS_FALSE;
- if (!js_AddRoot(cx, &wp->closure)) {
- JS_free(cx, wp);
- return JS_FALSE;
- }
- PR_APPEND_LINK(&wp->links, &rt->watchPointList);
- wp->object = obj;
- wp->userid = id;
- wp->prop = js_HoldProperty(cx, prop);
- wp->setter = prop->setter;
- prop->setter = js_watch_set;
- wp->nrefs = 1;
- }
- wp->handler = handler;
- wp->closure = closure;
- return JS_TRUE;
- }
-
- PR_IMPLEMENT(void)
- JS_ClearWatchPoint(JSContext *cx, JSObject *obj, jsval id,
- JSWatchPointHandler *handlerp, void **closurep)
- {
- JSRuntime *rt;
- JSWatchPoint *wp;
-
- rt = cx->runtime;
- for (wp = (JSWatchPoint *)rt->watchPointList.next;
- wp != (JSWatchPoint *)&rt->watchPointList;
- wp = (JSWatchPoint *)wp->links.next) {
- if (wp->object == obj && wp->userid == id) {
- if (handlerp)
- *handlerp = wp->handler;
- if (closurep)
- *closurep = wp->closure;
- DropWatchPoint(cx, wp);
- return;
- }
- }
- if (handlerp)
- *handlerp = NULL;
- if (closurep)
- *closurep = NULL;
- }
-
- PR_IMPLEMENT(void)
- JS_ClearWatchPointsForObject(JSContext *cx, JSObject *obj)
- {
- JSRuntime *rt;
- JSWatchPoint *wp, *next;
-
- rt = cx->runtime;
- for (wp = (JSWatchPoint *)rt->watchPointList.next;
- wp != (JSWatchPoint *)&rt->watchPointList;
- wp = next) {
- next = (JSWatchPoint *)wp->links.next;
- if (wp->object == obj)
- DropWatchPoint(cx, wp);
- }
- }
-
- PR_IMPLEMENT(void)
- JS_ClearAllWatchPoints(JSContext *cx)
- {
- JSRuntime *rt;
- JSWatchPoint *wp, *next;
-
- rt = cx->runtime;
- for (wp = (JSWatchPoint *)rt->watchPointList.next;
- wp != (JSWatchPoint *)&rt->watchPointList;
- wp = next) {
- next = (JSWatchPoint *)wp->links.next;
- DropWatchPoint(cx, wp);
- }
- }
-
- PR_IMPLEMENT(uintN)
- JS_PCToLineNumber(JSContext *cx, JSScript *script, jsbytecode *pc)
- {
- return js_PCToLineNumber(script, pc);
- }
-
- PR_IMPLEMENT(jsbytecode *)
- JS_LineNumberToPC(JSContext *cx, JSScript *script, uintN lineno)
- {
- return js_LineNumberToPC(script, lineno);
- }
-
- PR_IMPLEMENT(JSScript *)
- JS_GetFunctionScript(JSContext *cx, JSFunction *fun)
- {
- return fun->script;
- }
-
- PR_IMPLEMENT(JSPrincipals *)
- JS_GetScriptPrincipals(JSContext *cx, JSScript *script)
- {
- return script->principals;
- }
-
-
- /*
- * Stack Frame Iterator
- */
- PR_IMPLEMENT(JSStackFrame *)
- JS_FrameIterator(JSContext *cx, JSStackFrame **iteratorp)
- {
- *iteratorp = (*iteratorp == NULL) ? cx->fp : (*iteratorp)->down;
- return *iteratorp;
- }
-
- PR_IMPLEMENT(JSScript *)
- JS_GetFrameScript(JSContext *cx, JSStackFrame *fp)
- {
- return fp->script;
- }
-
- PR_IMPLEMENT(jsbytecode *)
- JS_GetFramePC(JSContext *cx, JSStackFrame *fp)
- {
- return fp->pc;
- }
-
- PR_IMPLEMENT(void *)
- JS_GetFrameAnnotation(JSContext *cx, JSStackFrame *fp)
- {
- if (fp->annotation) {
- JSPrincipals *principals = fp->script
- ? fp->script->principals
- : NULL;
-
- if (principals == NULL)
- return NULL;
-
- if (principals->globalPrivilegesEnabled(cx, principals)) {
- /*
- * Only give out an annotation if privileges have not
- * been revoked globally.
- */
- return fp->annotation;
- }
- }
-
- return NULL;
- }
-
- PR_IMPLEMENT(void)
- JS_SetFrameAnnotation(JSContext *cx, JSStackFrame *fp, void *annotation)
- {
- fp->annotation = annotation;
- }
-
- PR_IMPLEMENT(void *)
- JS_GetFramePrincipalArray(JSContext *cx, JSStackFrame *fp)
- {
- JSPrincipals *principals = fp->script
- ? fp->script->principals
- : NULL;
-
- return principals
- ? principals->getPrincipalArray(cx, principals)
- : NULL;
- }
-
- PR_IMPLEMENT(JSBool)
- JS_IsNativeFrame(JSContext *cx, JSStackFrame *fp)
- {
- return fp->fun && fp->fun->call;
- }
-
- PR_IMPLEMENT(JSObject *)
- JS_GetFrameObject(JSContext *cx, JSStackFrame *fp)
- {
- return fp->scopeChain;
- }
-
- PR_IMPLEMENT(JSObject *)
- JS_GetFrameThis(JSContext *cx, JSStackFrame *fp)
- {
- return fp->thisp;
- }
-
- PR_IMPLEMENT(JSFunction *)
- JS_GetFrameFunction(JSContext *cx, JSStackFrame *fp)
- {
- return fp->fun;
- }
-
- /************************************************************************/
-
- PR_IMPLEMENT(const char *)
- JS_GetScriptFilename(JSContext *cx, JSScript *script)
- {
- return script->filename;
- }
-
- PR_IMPLEMENT(uintN)
- JS_GetScriptBaseLineNumber(JSContext *cx, JSScript *script)
- {
- return script->lineno;
- }
-
- PR_IMPLEMENT(uintN)
- JS_GetScriptLineExtent(JSContext *cx, JSScript *script)
- {
- return js_GetScriptLineExtent(script);
- }
-
- /***************************************************************************/
-
- PR_IMPLEMENT(void)
- JS_SetNewScriptHookProc(JSRuntime *rt, JSNewScriptHookProc hookproc,
- void *callerdata)
- {
- rt->newScriptHookProcData = callerdata;
- rt->newScriptHookProc = hookproc;
- }
-
- PR_IMPLEMENT(void)
- JS_SetDestroyScriptHookProc(JSRuntime *rt, JSDestroyScriptHookProc hookproc,
- void *callerdata)
- {
- rt->destroyScriptHookProcData = callerdata;
- rt->destroyScriptHookProc = hookproc;
- }
-
- /***************************************************************************/
-
- PR_IMPLEMENT(JSBool)
- JS_EvaluateInStackFrame(JSContext *cx, JSStackFrame *fp,
- const char *bytes, uintN length,
- const char *filename, uintN lineno,
- jsval *rval)
- {
- JSScript *script;
- JSBool ok;
-
- script = JS_CompileScriptForPrincipals(cx, fp->scopeChain,
- fp->script ? fp->script->principals
- : NULL,
- bytes, length, filename, lineno);
- if (!script)
- return JS_FALSE;
- ok = js_Execute(cx, fp->scopeChain, script, fp, rval);
- JS_DestroyScript(cx, script);
- return ok;
- }
-
- /************************************************************************/
-
- PR_IMPLEMENT(JSProperty *)
- JS_PropertyIterator(JSObject *obj, JSProperty **iteratorp)
- {
- JSProperty *prop;
-
- prop = *iteratorp;
- prop = (prop == NULL) ? obj->map->props : prop->next;
- *iteratorp = prop;
- return prop;
- }
-
- PR_IMPLEMENT(JSBool)
- JS_GetPropertyDesc(JSContext *cx, JSProperty *prop, JSPropertyDesc *pd)
- {
- JSSymbol *sym;
-
- sym = prop->symbols;
- pd->id = sym ? js_IdToValue(sym_id(sym)) : JSVAL_VOID;
- pd->value = prop->object->slots[prop->slot];
- pd->flags = ((prop->flags & JSPROP_ENUMERATE) ? JSPD_ENUMERATE : 0)
- | ((prop->flags & JSPROP_READONLY) ? JSPD_READONLY : 0)
- | ((prop->flags & JSPROP_PERMANENT) ? JSPD_PERMANENT : 0)
- | ((prop->getter == js_GetArgument) ? JSPD_ARGUMENT : 0)
- | ((prop->getter == js_GetLocalVariable) ? JSPD_VARIABLE : 0);
- pd->spare = 0;
- pd->slot = (prop->flags & (JSPD_ARGUMENT | JSPD_VARIABLE))
- ? JSVAL_TO_INT(prop->id)
- : 0;
- if (!sym || !sym->next || (prop->flags & (JSPD_ARGUMENT | JSPD_VARIABLE))) {
- pd->alias = JSVAL_VOID;
- } else {
- pd->alias = js_IdToValue(sym_id(sym->next));
- pd->flags |= JSPD_ALIAS;
- }
- return JS_TRUE;
- }
-
- PR_IMPLEMENT(JSBool)
- JS_GetPropertyDescArray(JSContext *cx, JSObject *obj, JSPropertyDescArray *pda)
- {
- JSScope *scope;
- uint32 i, n;
- JSPropertyDesc *pd;
- JSProperty *prop;
-
- if (!obj->map->clasp->enumerate(cx, obj))
- return JS_FALSE;
- scope = (JSScope *)obj->map;
- if (scope->object != obj) {
- pda->length = 0;
- pda->array = NULL;
- return JS_TRUE;
- }
- n = scope->map.freeslot;
- pd = JS_malloc(cx, (size_t)n * sizeof(JSPropertyDesc));
- if (!pd)
- return JS_FALSE;
- i = 0;
- for (prop = scope->map.props; prop; prop = prop->next) {
- if (!js_AddRoot(cx, &pd[i].id) || !js_AddRoot(cx, &pd[i].value))
- goto bad;
- JS_GetPropertyDesc(cx, prop, &pd[i]);
- if ((pd[i].flags & JSPD_ALIAS) && !js_AddRoot(cx, &pd[i].alias))
- goto bad;
- if (++i == n)
- break;
- }
- pda->length = i;
- pda->array = pd;
- return JS_TRUE;
-
- bad:
- pda->length = i + 1;
- pda->array = pd;
- JS_PutPropertyDescArray(cx, pda);
- return JS_FALSE;
- }
-
- PR_IMPLEMENT(void)
- JS_PutPropertyDescArray(JSContext *cx, JSPropertyDescArray *pda)
- {
- JSPropertyDesc *pd;
- uint32 i;
-
- pd = pda->array;
- for (i = 0; i < pda->length; i++) {
- js_RemoveRoot(cx, &pd[i].id);
- js_RemoveRoot(cx, &pd[i].value);
- if (pd[i].flags & JSPD_ALIAS)
- js_RemoveRoot(cx, &pd[i].alias);
- }
- JS_free(cx, pd);
- }
-