📄 jsdbgapi.c
字号:
/* -*- 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 Communicator client code, released
* March 31, 1998.
*
* 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 of 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 ***** */
/*
* JS debugging API.
*/
#include "jsstddef.h"
#include <string.h>
#include "jstypes.h"
#include "jsutil.h" /* Added by JSIFY */
#include "jsclist.h"
#include "jsapi.h"
#include "jscntxt.h"
#include "jsconfig.h"
#include "jsdbgapi.h"
#include "jsfun.h"
#include "jsgc.h"
#include "jsinterp.h"
#include "jslock.h"
#include "jsobj.h"
#include "jsopcode.h"
#include "jsscope.h"
#include "jsscript.h"
#include "jsstr.h"
typedef struct JSTrap {
JSCList 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;
}
JS_PUBLIC_API(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) {
JS_ASSERT(trap->script == script && trap->pc == pc);
JS_ASSERT(*pc == JSOP_TRAP);
} else {
trap = (JSTrap *) JS_malloc(cx, sizeof *trap);
if (!trap || !js_AddRoot(cx, &trap->closure, "trap->closure")) {
if (trap)
JS_free(cx, trap);
return JS_FALSE;
}
JS_APPEND_LINK(&trap->links, &rt->trapList);
trap->script = script;
trap->pc = pc;
trap->op = (JSOp)*pc;
*pc = JSOP_TRAP;
}
trap->handler = handler;
trap->closure = closure;
return JS_TRUE;
}
JS_PUBLIC_API(JSOp)
JS_GetTrapOpcode(JSContext *cx, JSScript *script, jsbytecode *pc)
{
JSTrap *trap;
trap = FindTrap(cx->runtime, script, pc);
if (!trap) {
JS_ASSERT(0); /* XXX can't happen */
return JSOP_LIMIT;
}
return trap->op;
}
static void
DestroyTrap(JSContext *cx, JSTrap *trap)
{
JS_REMOVE_LINK(&trap->links);
*trap->pc = (jsbytecode)trap->op;
js_RemoveRoot(cx->runtime, &trap->closure);
JS_free(cx, trap);
}
JS_PUBLIC_API(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);
}
JS_PUBLIC_API(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);
}
}
JS_PUBLIC_API(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);
}
}
JS_PUBLIC_API(JSTrapStatus)
JS_HandleTrap(JSContext *cx, JSScript *script, jsbytecode *pc, jsval *rval)
{
JSTrap *trap;
JSTrapStatus status;
jsint op;
trap = FindTrap(cx->runtime, script, pc);
if (!trap) {
JS_ASSERT(0); /* XXX can't happen */
return JSTRAP_ERROR;
}
/*
* It's important that we not use 'trap->' after calling the callback --
* the callback might remove the trap!
*/
op = (jsint)trap->op;
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(op);
}
return status;
}
JS_PUBLIC_API(JSBool)
JS_SetInterrupt(JSRuntime *rt, JSTrapHandler handler, void *closure)
{
rt->interruptHandler = handler;
rt->interruptHandlerData = closure;
return JS_TRUE;
}
JS_PUBLIC_API(JSBool)
JS_ClearInterrupt(JSRuntime *rt, JSTrapHandler *handlerp, void **closurep)
{
if (handlerp)
*handlerp = (JSTrapHandler)rt->interruptHandler;
if (closurep)
*closurep = rt->interruptHandlerData;
rt->interruptHandler = 0;
rt->interruptHandlerData = 0;
return JS_TRUE;
}
/************************************************************************/
typedef struct JSWatchPoint {
JSCList links;
JSObject *object; /* weak link, see js_FinalizeObject */
JSScopeProperty *sprop;
JSPropertyOp setter;
JSWatchPointHandler handler;
void *closure;
jsrefcount nrefs;
} JSWatchPoint;
#define HoldWatchPoint(wp) ((wp)->nrefs++)
static JSBool
DropWatchPoint(JSContext *cx, JSWatchPoint *wp)
{
JSScopeProperty *sprop;
if (--wp->nrefs != 0)
return JS_TRUE;
/*
* Remove wp from the list, then if there are no other watchpoints for
* wp->sprop in any scope, restore wp->sprop->setter from wp.
*/
JS_REMOVE_LINK(&wp->links);
sprop = wp->sprop;
if (!js_GetWatchedSetter(cx->runtime, NULL, sprop)) {
sprop = js_ChangeNativePropertyAttrs(cx, wp->object, sprop,
0, sprop->attrs,
sprop->getter, wp->setter);
if (!sprop)
return JS_FALSE;
}
js_RemoveRoot(cx->runtime, &wp->closure);
JS_free(cx, wp);
return JS_TRUE;
}
void
js_MarkWatchPoints(JSRuntime *rt)
{
JSWatchPoint *wp;
for (wp = (JSWatchPoint *)rt->watchPointList.next;
wp != (JSWatchPoint *)&rt->watchPointList;
wp = (JSWatchPoint *)wp->links.next) {
MARK_SCOPE_PROPERTY(wp->sprop);
}
}
static JSWatchPoint *
FindWatchPoint(JSRuntime *rt, JSScope *scope, jsid id)
{
JSWatchPoint *wp;
for (wp = (JSWatchPoint *)rt->watchPointList.next;
wp != (JSWatchPoint *)&rt->watchPointList;
wp = (JSWatchPoint *)wp->links.next) {
if (wp->object == scope->object && wp->sprop->id == id)
return wp;
}
return NULL;
}
JSScopeProperty *
js_FindWatchPoint(JSRuntime *rt, JSScope *scope, jsid id)
{
JSWatchPoint *wp;
wp = FindWatchPoint(rt, scope, id);
if (!wp)
return NULL;
return wp->sprop;
}
JSPropertyOp
js_GetWatchedSetter(JSRuntime *rt, JSScope *scope,
const JSScopeProperty *sprop)
{
JSWatchPoint *wp;
for (wp = (JSWatchPoint *)rt->watchPointList.next;
wp != (JSWatchPoint *)&rt->watchPointList;
wp = (JSWatchPoint *)wp->links.next) {
if ((!scope || wp->object == scope->object) && wp->sprop == sprop)
return wp->setter;
}
return NULL;
}
JSBool JS_DLL_CALLBACK
js_watch_set(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
{
JSRuntime *rt;
JSWatchPoint *wp;
JSScopeProperty *sprop;
jsval userid;
JSScope *scope;
JSBool ok;
rt = cx->runtime;
for (wp = (JSWatchPoint *)rt->watchPointList.next;
wp != (JSWatchPoint *)&rt->watchPointList;
wp = (JSWatchPoint *)wp->links.next) {
sprop = wp->sprop;
if (wp->object == obj && SPROP_USERID(sprop) == id) {
JS_LOCK_OBJ(cx, obj);
userid = SPROP_USERID(sprop);
scope = OBJ_SCOPE(obj);
JS_UNLOCK_OBJ(cx, obj);
HoldWatchPoint(wp);
ok = wp->handler(cx, obj, userid,
SPROP_HAS_VALID_SLOT(sprop, scope)
? OBJ_GET_SLOT(cx, obj, wp->sprop->slot)
: JSVAL_VOID,
vp, wp->closure);
if (ok) {
/*
* Create pseudo-frame for call to setter so that any
* stack-walking security code in the setter will correctly
* identify the guilty party.
*/
JSObject *funobj = (JSObject *) wp->closure;
JSFunction *fun = (JSFunction *) JS_GetPrivate(cx, funobj);
JSStackFrame frame;
memset(&frame, 0, sizeof(frame));
frame.script = fun->script;
frame.fun = fun;
frame.down = cx->fp;
cx->fp = &frame;
ok = !wp->setter ||
((sprop->attrs & JSPROP_SETTER)
? js_InternalCall(cx, obj, OBJECT_TO_JSVAL(wp->setter),
1, vp, vp)
: wp->setter(cx, OBJ_THIS_OBJECT(cx, obj), userid, vp));
cx->fp = frame.down;
}
return DropWatchPoint(cx, wp);
}
}
JS_ASSERT(0); /* XXX can't happen */
return JS_FALSE;
}
JSBool JS_DLL_CALLBACK
js_watch_set_wrapper(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
jsval *rval)
{
JSObject *funobj;
JSFunction *wrapper;
jsval userid;
funobj = JSVAL_TO_OBJECT(argv[-2]);
wrapper = (JSFunction *) JS_GetPrivate(cx, funobj);
userid = ATOM_KEY(wrapper->atom);
*rval = argv[0];
return js_watch_set(cx, obj, userid, rval);
}
JSPropertyOp
js_WrapWatchedSetter(JSContext *cx, jsid id, uintN attrs, JSPropertyOp setter)
{
JSAtom *atom;
JSFunction *wrapper;
if (!(attrs & JSPROP_SETTER))
return &js_watch_set; /* & to silence schoolmarmish MSVC */
if (!JSVAL_IS_INT(id)) {
atom = (JSAtom *)id;
} else {
atom = js_AtomizeInt(cx, JSVAL_TO_INT(id), 0);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -