зеркало из https://github.com/mozilla/gecko-dev.git
1961 строка
54 KiB
C++
1961 строка
54 KiB
C++
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
|
* vim: set ts=8 sw=4 et tw=78:
|
|
*
|
|
* ***** 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 "jsversion.h"
|
|
#include "jsdbgapi.h"
|
|
#include "jsemit.h"
|
|
#include "jsfun.h"
|
|
#include "jsgc.h"
|
|
#include "jsinterp.h"
|
|
#include "jslock.h"
|
|
#include "jsobj.h"
|
|
#include "jsopcode.h"
|
|
#include "jsparse.h"
|
|
#include "jsscope.h"
|
|
#include "jsscript.h"
|
|
#include "jsstr.h"
|
|
|
|
#include "jsautooplen.h"
|
|
|
|
typedef struct JSTrap {
|
|
JSCList links;
|
|
JSScript *script;
|
|
jsbytecode *pc;
|
|
JSOp op;
|
|
JSTrapHandler handler;
|
|
void *closure;
|
|
} JSTrap;
|
|
|
|
#define DBG_LOCK(rt) JS_ACQUIRE_LOCK((rt)->debuggerLock)
|
|
#define DBG_UNLOCK(rt) JS_RELEASE_LOCK((rt)->debuggerLock)
|
|
#define DBG_LOCK_EVAL(rt,expr) (DBG_LOCK(rt), (expr), DBG_UNLOCK(rt))
|
|
|
|
/*
|
|
* NB: FindTrap must be called with rt->debuggerLock acquired.
|
|
*/
|
|
static JSTrap *
|
|
FindTrap(JSRuntime *rt, JSScript *script, jsbytecode *pc)
|
|
{
|
|
JSTrap *trap;
|
|
|
|
for (trap = (JSTrap *)rt->trapList.next;
|
|
&trap->links != &rt->trapList;
|
|
trap = (JSTrap *)trap->links.next) {
|
|
if (trap->script == script && trap->pc == pc)
|
|
return trap;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
jsbytecode *
|
|
js_UntrapScriptCode(JSContext *cx, JSScript *script)
|
|
{
|
|
jsbytecode *code;
|
|
JSRuntime *rt;
|
|
JSTrap *trap;
|
|
|
|
code = script->code;
|
|
rt = cx->runtime;
|
|
DBG_LOCK(rt);
|
|
for (trap = (JSTrap *)rt->trapList.next;
|
|
&trap->links !=
|
|
&rt->trapList;
|
|
trap = (JSTrap *)trap->links.next) {
|
|
if (trap->script == script &&
|
|
(size_t)(trap->pc - script->code) < script->length) {
|
|
if (code == script->code) {
|
|
jssrcnote *sn, *notes;
|
|
size_t nbytes;
|
|
|
|
nbytes = script->length * sizeof(jsbytecode);
|
|
notes = SCRIPT_NOTES(script);
|
|
for (sn = notes; !SN_IS_TERMINATOR(sn); sn = SN_NEXT(sn))
|
|
continue;
|
|
nbytes += (sn - notes + 1) * sizeof *sn;
|
|
|
|
code = (jsbytecode *) JS_malloc(cx, nbytes);
|
|
if (!code)
|
|
break;
|
|
memcpy(code, script->code, nbytes);
|
|
JS_CLEAR_GSN_CACHE(cx);
|
|
}
|
|
code[trap->pc - script->code] = trap->op;
|
|
}
|
|
}
|
|
DBG_UNLOCK(rt);
|
|
return code;
|
|
}
|
|
|
|
JS_PUBLIC_API(JSBool)
|
|
JS_SetTrap(JSContext *cx, JSScript *script, jsbytecode *pc,
|
|
JSTrapHandler handler, void *closure)
|
|
{
|
|
JSTrap *junk, *trap, *twin;
|
|
JSRuntime *rt;
|
|
uint32 sample;
|
|
|
|
JS_ASSERT((JSOp) *pc != JSOP_TRAP);
|
|
junk = NULL;
|
|
rt = cx->runtime;
|
|
DBG_LOCK(rt);
|
|
trap = FindTrap(rt, script, pc);
|
|
if (trap) {
|
|
JS_ASSERT(trap->script == script && trap->pc == pc);
|
|
JS_ASSERT(*pc == JSOP_TRAP);
|
|
} else {
|
|
sample = rt->debuggerMutations;
|
|
DBG_UNLOCK(rt);
|
|
trap = (JSTrap *) JS_malloc(cx, sizeof *trap);
|
|
if (!trap)
|
|
return JS_FALSE;
|
|
trap->closure = NULL;
|
|
if(!js_AddRoot(cx, &trap->closure, "trap->closure")) {
|
|
JS_free(cx, trap);
|
|
return JS_FALSE;
|
|
}
|
|
DBG_LOCK(rt);
|
|
twin = (rt->debuggerMutations != sample)
|
|
? FindTrap(rt, script, pc)
|
|
: NULL;
|
|
if (twin) {
|
|
junk = trap;
|
|
trap = twin;
|
|
} else {
|
|
JS_APPEND_LINK(&trap->links, &rt->trapList);
|
|
++rt->debuggerMutations;
|
|
trap->script = script;
|
|
trap->pc = pc;
|
|
trap->op = (JSOp)*pc;
|
|
*pc = JSOP_TRAP;
|
|
}
|
|
}
|
|
trap->handler = handler;
|
|
trap->closure = closure;
|
|
DBG_UNLOCK(rt);
|
|
if (junk) {
|
|
js_RemoveRoot(rt, &junk->closure);
|
|
JS_free(cx, junk);
|
|
}
|
|
return JS_TRUE;
|
|
}
|
|
|
|
JS_PUBLIC_API(JSOp)
|
|
JS_GetTrapOpcode(JSContext *cx, JSScript *script, jsbytecode *pc)
|
|
{
|
|
JSRuntime *rt;
|
|
JSTrap *trap;
|
|
JSOp op;
|
|
|
|
rt = cx->runtime;
|
|
DBG_LOCK(rt);
|
|
trap = FindTrap(rt, script, pc);
|
|
op = trap ? trap->op : (JSOp) *pc;
|
|
DBG_UNLOCK(rt);
|
|
return op;
|
|
}
|
|
|
|
static void
|
|
DestroyTrapAndUnlock(JSContext *cx, JSTrap *trap)
|
|
{
|
|
++cx->runtime->debuggerMutations;
|
|
JS_REMOVE_LINK(&trap->links);
|
|
*trap->pc = (jsbytecode)trap->op;
|
|
DBG_UNLOCK(cx->runtime);
|
|
|
|
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;
|
|
|
|
DBG_LOCK(cx->runtime);
|
|
trap = FindTrap(cx->runtime, script, pc);
|
|
if (handlerp)
|
|
*handlerp = trap ? trap->handler : NULL;
|
|
if (closurep)
|
|
*closurep = trap ? trap->closure : NULL;
|
|
if (trap)
|
|
DestroyTrapAndUnlock(cx, trap);
|
|
else
|
|
DBG_UNLOCK(cx->runtime);
|
|
}
|
|
|
|
JS_PUBLIC_API(void)
|
|
JS_ClearScriptTraps(JSContext *cx, JSScript *script)
|
|
{
|
|
JSRuntime *rt;
|
|
JSTrap *trap, *next;
|
|
uint32 sample;
|
|
|
|
rt = cx->runtime;
|
|
DBG_LOCK(rt);
|
|
for (trap = (JSTrap *)rt->trapList.next;
|
|
&trap->links != &rt->trapList;
|
|
trap = next) {
|
|
next = (JSTrap *)trap->links.next;
|
|
if (trap->script == script) {
|
|
sample = rt->debuggerMutations;
|
|
DestroyTrapAndUnlock(cx, trap);
|
|
DBG_LOCK(rt);
|
|
if (rt->debuggerMutations != sample + 1)
|
|
next = (JSTrap *)rt->trapList.next;
|
|
}
|
|
}
|
|
DBG_UNLOCK(rt);
|
|
}
|
|
|
|
JS_PUBLIC_API(void)
|
|
JS_ClearAllTraps(JSContext *cx)
|
|
{
|
|
JSRuntime *rt;
|
|
JSTrap *trap, *next;
|
|
uint32 sample;
|
|
|
|
rt = cx->runtime;
|
|
DBG_LOCK(rt);
|
|
for (trap = (JSTrap *)rt->trapList.next;
|
|
&trap->links != &rt->trapList;
|
|
trap = next) {
|
|
next = (JSTrap *)trap->links.next;
|
|
sample = rt->debuggerMutations;
|
|
DestroyTrapAndUnlock(cx, trap);
|
|
DBG_LOCK(rt);
|
|
if (rt->debuggerMutations != sample + 1)
|
|
next = (JSTrap *)rt->trapList.next;
|
|
}
|
|
DBG_UNLOCK(rt);
|
|
}
|
|
|
|
JS_PUBLIC_API(JSTrapStatus)
|
|
JS_HandleTrap(JSContext *cx, JSScript *script, jsbytecode *pc, jsval *rval)
|
|
{
|
|
JSTrap *trap;
|
|
jsint op;
|
|
JSTrapStatus status;
|
|
|
|
DBG_LOCK(cx->runtime);
|
|
trap = FindTrap(cx->runtime, script, pc);
|
|
JS_ASSERT(!trap || trap->handler);
|
|
if (!trap) {
|
|
op = (JSOp) *pc;
|
|
DBG_UNLOCK(cx->runtime);
|
|
|
|
/* Defend against "pc for wrong script" API usage error. */
|
|
JS_ASSERT(op != JSOP_TRAP);
|
|
|
|
#ifdef JS_THREADSAFE
|
|
/* If the API was abused, we must fail for want of the real op. */
|
|
if (op == JSOP_TRAP)
|
|
return JSTRAP_ERROR;
|
|
|
|
/* Assume a race with a debugger thread and try to carry on. */
|
|
*rval = INT_TO_JSVAL(op);
|
|
return JSTRAP_CONTINUE;
|
|
#else
|
|
/* Always fail if single-threaded (must be an API usage error). */
|
|
return JSTRAP_ERROR;
|
|
#endif
|
|
}
|
|
DBG_UNLOCK(cx->runtime);
|
|
|
|
/*
|
|
* 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->globalDebugHooks.interruptHandler = handler;
|
|
rt->globalDebugHooks.interruptHandlerData = closure;
|
|
return JS_TRUE;
|
|
}
|
|
|
|
JS_PUBLIC_API(JSBool)
|
|
JS_ClearInterrupt(JSRuntime *rt, JSTrapHandler *handlerp, void **closurep)
|
|
{
|
|
if (handlerp)
|
|
*handlerp = (JSTrapHandler)rt->globalDebugHooks.interruptHandler;
|
|
if (closurep)
|
|
*closurep = rt->globalDebugHooks.interruptHandlerData;
|
|
rt->globalDebugHooks.interruptHandler = 0;
|
|
rt->globalDebugHooks.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;
|
|
uintN flags;
|
|
} JSWatchPoint;
|
|
|
|
#define JSWP_LIVE 0x1 /* live because set and not cleared */
|
|
#define JSWP_HELD 0x2 /* held while running handler/setter */
|
|
|
|
/*
|
|
* NB: DropWatchPointAndUnlock releases cx->runtime->debuggerLock in all cases.
|
|
*/
|
|
static JSBool
|
|
DropWatchPointAndUnlock(JSContext *cx, JSWatchPoint *wp, uintN flag)
|
|
{
|
|
JSBool ok, found;
|
|
JSScopeProperty *sprop;
|
|
JSScope *scope;
|
|
JSPropertyOp setter;
|
|
|
|
ok = JS_TRUE;
|
|
wp->flags &= ~flag;
|
|
if (wp->flags != 0) {
|
|
DBG_UNLOCK(cx->runtime);
|
|
return ok;
|
|
}
|
|
|
|
/*
|
|
* Remove wp from the list, then if there are no other watchpoints for
|
|
* wp->sprop in any scope, restore wp->sprop->setter from wp.
|
|
*/
|
|
++cx->runtime->debuggerMutations;
|
|
JS_REMOVE_LINK(&wp->links);
|
|
sprop = wp->sprop;
|
|
|
|
/*
|
|
* Passing null for the scope parameter tells js_GetWatchedSetter to find
|
|
* any watch point for sprop, and not to lock or unlock rt->debuggerLock.
|
|
* If js_ChangeNativePropertyAttrs fails, propagate failure after removing
|
|
* wp->closure's root and freeing wp.
|
|
*/
|
|
setter = js_GetWatchedSetter(cx->runtime, NULL, sprop);
|
|
DBG_UNLOCK(cx->runtime);
|
|
if (!setter) {
|
|
JS_LOCK_OBJ(cx, wp->object);
|
|
scope = OBJ_SCOPE(wp->object);
|
|
found = (scope->object == wp->object &&
|
|
SCOPE_GET_PROPERTY(scope, sprop->id));
|
|
JS_UNLOCK_SCOPE(cx, scope);
|
|
|
|
/*
|
|
* If the property wasn't found on wp->object or didn't exist, then
|
|
* someone else has dealt with this sprop, and we don't need to change
|
|
* the property attributes.
|
|
*/
|
|
if (found) {
|
|
sprop = js_ChangeScopePropertyAttrs(cx, scope, sprop,
|
|
0, sprop->attrs,
|
|
sprop->getter,
|
|
wp->setter);
|
|
if (!sprop)
|
|
ok = JS_FALSE;
|
|
}
|
|
}
|
|
|
|
JS_free(cx, wp);
|
|
return ok;
|
|
}
|
|
|
|
/*
|
|
* NB: js_TraceWatchPoints does not acquire cx->runtime->debuggerLock, since
|
|
* the debugger should never be racing with the GC (i.e., the debugger must
|
|
* respect the request model).
|
|
*/
|
|
void
|
|
js_TraceWatchPoints(JSTracer *trc, JSObject *obj)
|
|
{
|
|
JSRuntime *rt;
|
|
JSWatchPoint *wp;
|
|
|
|
rt = trc->context->runtime;
|
|
|
|
for (wp = (JSWatchPoint *)rt->watchPointList.next;
|
|
&wp->links != &rt->watchPointList;
|
|
wp = (JSWatchPoint *)wp->links.next) {
|
|
if (wp->object == obj) {
|
|
TRACE_SCOPE_PROPERTY(trc, wp->sprop);
|
|
if ((wp->sprop->attrs & JSPROP_SETTER) && wp->setter) {
|
|
JS_CALL_OBJECT_TRACER(trc, (JSObject *)wp->setter,
|
|
"wp->setter");
|
|
}
|
|
JS_SET_TRACING_NAME(trc, "wp->closure");
|
|
js_CallValueTracerIfGCThing(trc, (jsval) wp->closure);
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
js_SweepWatchPoints(JSContext *cx)
|
|
{
|
|
JSRuntime *rt;
|
|
JSWatchPoint *wp, *next;
|
|
uint32 sample;
|
|
|
|
rt = cx->runtime;
|
|
DBG_LOCK(rt);
|
|
for (wp = (JSWatchPoint *)rt->watchPointList.next;
|
|
&wp->links != &rt->watchPointList;
|
|
wp = next) {
|
|
next = (JSWatchPoint *)wp->links.next;
|
|
if (js_IsAboutToBeFinalized(cx, wp->object)) {
|
|
sample = rt->debuggerMutations;
|
|
|
|
/* Ignore failures. */
|
|
DropWatchPointAndUnlock(cx, wp, JSWP_LIVE);
|
|
DBG_LOCK(rt);
|
|
if (rt->debuggerMutations != sample + 1)
|
|
next = (JSWatchPoint *)rt->watchPointList.next;
|
|
}
|
|
}
|
|
DBG_UNLOCK(rt);
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
* NB: FindWatchPoint must be called with rt->debuggerLock acquired.
|
|
*/
|
|
static JSWatchPoint *
|
|
FindWatchPoint(JSRuntime *rt, JSScope *scope, jsid id)
|
|
{
|
|
JSWatchPoint *wp;
|
|
|
|
for (wp = (JSWatchPoint *)rt->watchPointList.next;
|
|
&wp->links != &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;
|
|
JSScopeProperty *sprop;
|
|
|
|
DBG_LOCK(rt);
|
|
wp = FindWatchPoint(rt, scope, id);
|
|
sprop = wp ? wp->sprop : NULL;
|
|
DBG_UNLOCK(rt);
|
|
return sprop;
|
|
}
|
|
|
|
/*
|
|
* Secret handshake with DropWatchPointAndUnlock: if (!scope), we know our
|
|
* caller has acquired rt->debuggerLock, so we don't have to.
|
|
*/
|
|
JSPropertyOp
|
|
js_GetWatchedSetter(JSRuntime *rt, JSScope *scope,
|
|
const JSScopeProperty *sprop)
|
|
{
|
|
JSPropertyOp setter;
|
|
JSWatchPoint *wp;
|
|
|
|
setter = NULL;
|
|
if (scope)
|
|
DBG_LOCK(rt);
|
|
for (wp = (JSWatchPoint *)rt->watchPointList.next;
|
|
&wp->links != &rt->watchPointList;
|
|
wp = (JSWatchPoint *)wp->links.next) {
|
|
if ((!scope || wp->object == scope->object) && wp->sprop == sprop) {
|
|
setter = wp->setter;
|
|
break;
|
|
}
|
|
}
|
|
if (scope)
|
|
DBG_UNLOCK(rt);
|
|
return setter;
|
|
}
|
|
|
|
JSBool
|
|
js_watch_set(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
|
|
{
|
|
JSRuntime *rt;
|
|
JSWatchPoint *wp;
|
|
JSScopeProperty *sprop;
|
|
jsval propid, userid;
|
|
JSScope *scope;
|
|
JSBool ok;
|
|
|
|
rt = cx->runtime;
|
|
DBG_LOCK(rt);
|
|
for (wp = (JSWatchPoint *)rt->watchPointList.next;
|
|
&wp->links != &rt->watchPointList;
|
|
wp = (JSWatchPoint *)wp->links.next) {
|
|
sprop = wp->sprop;
|
|
if (wp->object == obj && SPROP_USERID(sprop) == id &&
|
|
!(wp->flags & JSWP_HELD)) {
|
|
wp->flags |= JSWP_HELD;
|
|
DBG_UNLOCK(rt);
|
|
|
|
JS_LOCK_OBJ(cx, obj);
|
|
propid = ID_TO_VALUE(sprop->id);
|
|
userid = (sprop->flags & SPROP_HAS_SHORTID)
|
|
? INT_TO_JSVAL(sprop->shortid)
|
|
: propid;
|
|
scope = OBJ_SCOPE(obj);
|
|
JS_UNLOCK_OBJ(cx, obj);
|
|
|
|
/* NB: wp is held, so we can safely dereference it still. */
|
|
ok = wp->handler(cx, obj, propid,
|
|
SPROP_HAS_VALID_SLOT(sprop, scope)
|
|
? OBJ_GET_SLOT(cx, obj, sprop->slot)
|
|
: JSVAL_VOID,
|
|
vp, wp->closure);
|
|
if (ok) {
|
|
/*
|
|
* Create a pseudo-frame for the setter invocation so that any
|
|
* stack-walking security code under the setter will correctly
|
|
* identify the guilty party. So that the watcher appears to
|
|
* be active to obj_eval and other such code, point frame.pc
|
|
* at the JSOP_STOP at the end of the script.
|
|
*
|
|
* The pseudo-frame is not created for fast natives as they
|
|
* are treated as interpreter frame extensions and always
|
|
* trusted.
|
|
*/
|
|
JSObject *closure;
|
|
JSClass *clasp;
|
|
JSFunction *fun;
|
|
JSScript *script;
|
|
JSBool injectFrame;
|
|
uintN nslots;
|
|
jsval smallv[5];
|
|
jsval *argv;
|
|
JSStackFrame frame;
|
|
JSFrameRegs regs;
|
|
|
|
closure = (JSObject *) wp->closure;
|
|
clasp = OBJ_GET_CLASS(cx, closure);
|
|
if (clasp == &js_FunctionClass) {
|
|
fun = GET_FUNCTION_PRIVATE(cx, closure);
|
|
script = FUN_SCRIPT(fun);
|
|
} else if (clasp == &js_ScriptClass) {
|
|
fun = NULL;
|
|
script = (JSScript *) JS_GetPrivate(cx, closure);
|
|
} else {
|
|
fun = NULL;
|
|
script = NULL;
|
|
}
|
|
|
|
nslots = 2;
|
|
injectFrame = JS_TRUE;
|
|
if (fun) {
|
|
nslots += FUN_MINARGS(fun);
|
|
if (!FUN_INTERPRETED(fun)) {
|
|
nslots += fun->u.n.extra;
|
|
injectFrame = !(fun->flags & JSFUN_FAST_NATIVE);
|
|
}
|
|
}
|
|
|
|
if (injectFrame) {
|
|
if (nslots <= JS_ARRAY_LENGTH(smallv)) {
|
|
argv = smallv;
|
|
} else {
|
|
argv = (jsval *) JS_malloc(cx, nslots * sizeof(jsval));
|
|
if (!argv) {
|
|
DBG_LOCK(rt);
|
|
DropWatchPointAndUnlock(cx, wp, JSWP_HELD);
|
|
return JS_FALSE;
|
|
}
|
|
}
|
|
|
|
argv[0] = OBJECT_TO_JSVAL(closure);
|
|
argv[1] = JSVAL_NULL;
|
|
memset(argv + 2, 0, (nslots - 2) * sizeof(jsval));
|
|
|
|
memset(&frame, 0, sizeof(frame));
|
|
frame.script = script;
|
|
frame.regs = NULL;
|
|
if (script) {
|
|
JS_ASSERT(script->length >= JSOP_STOP_LENGTH);
|
|
regs.pc = script->code + script->length
|
|
- JSOP_STOP_LENGTH;
|
|
regs.sp = NULL;
|
|
frame.regs = ®s;
|
|
}
|
|
frame.callee = closure;
|
|
frame.fun = fun;
|
|
frame.argv = argv + 2;
|
|
frame.down = js_GetTopStackFrame(cx);
|
|
frame.scopeChain = OBJ_GET_PARENT(cx, closure);
|
|
|
|
cx->fp = &frame;
|
|
}
|
|
#ifdef __GNUC__
|
|
else
|
|
argv = NULL; /* suppress bogus gcc warnings */
|
|
#endif
|
|
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));
|
|
if (injectFrame) {
|
|
/* Evil code can cause us to have an arguments object. */
|
|
if (frame.callobj)
|
|
ok &= js_PutCallObject(cx, &frame);
|
|
if (frame.argsobj)
|
|
ok &= js_PutArgsObject(cx, &frame);
|
|
|
|
cx->fp = frame.down;
|
|
if (argv != smallv)
|
|
JS_free(cx, argv);
|
|
}
|
|
}
|
|
DBG_LOCK(rt);
|
|
return DropWatchPointAndUnlock(cx, wp, JSWP_HELD) && ok;
|
|
}
|
|
}
|
|
DBG_UNLOCK(rt);
|
|
return JS_TRUE;
|
|
}
|
|
|
|
JSBool
|
|
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]);
|
|
JS_ASSERT(OBJ_GET_CLASS(cx, funobj) == &js_FunctionClass);
|
|
wrapper = GET_FUNCTION_PRIVATE(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 (JSID_IS_ATOM(id)) {
|
|
atom = JSID_TO_ATOM(id);
|
|
} else if (JSID_IS_INT(id)) {
|
|
if (!js_ValueToStringId(cx, INT_JSID_TO_JSVAL(id), &id))
|
|
return NULL;
|
|
atom = JSID_TO_ATOM(id);
|
|
} else {
|
|
atom = NULL;
|
|
}
|
|
wrapper = js_NewFunction(cx, NULL, js_watch_set_wrapper, 1, 0,
|
|
OBJ_GET_PARENT(cx, (JSObject *)setter),
|
|
atom);
|
|
if (!wrapper)
|
|
return NULL;
|
|
return (JSPropertyOp) FUN_OBJECT(wrapper);
|
|
}
|
|
|
|
JS_PUBLIC_API(JSBool)
|
|
JS_SetWatchPoint(JSContext *cx, JSObject *obj, jsval idval,
|
|
JSWatchPointHandler handler, void *closure)
|
|
{
|
|
jsid propid;
|
|
JSObject *pobj;
|
|
JSProperty *prop;
|
|
JSScopeProperty *sprop;
|
|
JSRuntime *rt;
|
|
JSBool ok;
|
|
JSWatchPoint *wp;
|
|
JSPropertyOp watcher;
|
|
|
|
if (!OBJ_IS_NATIVE(obj)) {
|
|
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_CANT_WATCH,
|
|
OBJ_GET_CLASS(cx, obj)->name);
|
|
return JS_FALSE;
|
|
}
|
|
|
|
if (JSVAL_IS_INT(idval))
|
|
propid = INT_JSVAL_TO_JSID(idval);
|
|
else if (!js_ValueToStringId(cx, idval, &propid))
|
|
return JS_FALSE;
|
|
|
|
if (!js_LookupProperty(cx, obj, propid, &pobj, &prop))
|
|
return JS_FALSE;
|
|
sprop = (JSScopeProperty *) prop;
|
|
rt = cx->runtime;
|
|
if (!sprop) {
|
|
/* Check for a deleted symbol watchpoint, which holds its property. */
|
|
sprop = js_FindWatchPoint(rt, OBJ_SCOPE(obj), propid);
|
|
if (!sprop) {
|
|
/* Make a new property in obj so we can watch for the first set. */
|
|
if (!js_DefineProperty(cx, obj, propid, JSVAL_VOID,
|
|
NULL, NULL, JSPROP_ENUMERATE,
|
|
&prop)) {
|
|
return JS_FALSE;
|
|
}
|
|
sprop = (JSScopeProperty *) prop;
|
|
}
|
|
} else if (pobj != obj) {
|
|
/* Clone the prototype property so we can watch the right object. */
|
|
jsval value;
|
|
JSPropertyOp getter, setter;
|
|
uintN attrs, flags;
|
|
intN shortid;
|
|
|
|
if (OBJ_IS_NATIVE(pobj)) {
|
|
value = SPROP_HAS_VALID_SLOT(sprop, OBJ_SCOPE(pobj))
|
|
? LOCKED_OBJ_GET_SLOT(pobj, sprop->slot)
|
|
: JSVAL_VOID;
|
|
getter = sprop->getter;
|
|
setter = sprop->setter;
|
|
attrs = sprop->attrs;
|
|
flags = sprop->flags;
|
|
shortid = sprop->shortid;
|
|
} else {
|
|
if (!OBJ_GET_PROPERTY(cx, pobj, propid, &value) ||
|
|
!OBJ_GET_ATTRIBUTES(cx, pobj, propid, prop, &attrs)) {
|
|
OBJ_DROP_PROPERTY(cx, pobj, prop);
|
|
return JS_FALSE;
|
|
}
|
|
getter = setter = NULL;
|
|
flags = 0;
|
|
shortid = 0;
|
|
}
|
|
OBJ_DROP_PROPERTY(cx, pobj, prop);
|
|
|
|
/* Recall that obj is native, whether or not pobj is native. */
|
|
if (!js_DefineNativeProperty(cx, obj, propid, value, getter, setter,
|
|
attrs, flags, shortid, &prop)) {
|
|
return JS_FALSE;
|
|
}
|
|
sprop = (JSScopeProperty *) prop;
|
|
}
|
|
|
|
/*
|
|
* At this point, prop/sprop exists in obj, obj is locked, and we must
|
|
* OBJ_DROP_PROPERTY(cx, obj, prop) before returning.
|
|
*/
|
|
ok = JS_TRUE;
|
|
DBG_LOCK(rt);
|
|
wp = FindWatchPoint(rt, OBJ_SCOPE(obj), propid);
|
|
if (!wp) {
|
|
DBG_UNLOCK(rt);
|
|
watcher = js_WrapWatchedSetter(cx, propid, sprop->attrs, sprop->setter);
|
|
if (!watcher) {
|
|
ok = JS_FALSE;
|
|
goto out;
|
|
}
|
|
|
|
wp = (JSWatchPoint *) JS_malloc(cx, sizeof *wp);
|
|
if (!wp) {
|
|
ok = JS_FALSE;
|
|
goto out;
|
|
}
|
|
wp->handler = NULL;
|
|
wp->closure = NULL;
|
|
wp->object = obj;
|
|
JS_ASSERT(sprop->setter != js_watch_set || pobj != obj);
|
|
wp->setter = sprop->setter;
|
|
wp->flags = JSWP_LIVE;
|
|
|
|
/* XXXbe nest in obj lock here */
|
|
sprop = js_ChangeNativePropertyAttrs(cx, obj, sprop, 0, sprop->attrs,
|
|
sprop->getter, watcher);
|
|
if (!sprop) {
|
|
/* Self-link so DropWatchPointAndUnlock can JS_REMOVE_LINK it. */
|
|
JS_INIT_CLIST(&wp->links);
|
|
DBG_LOCK(rt);
|
|
DropWatchPointAndUnlock(cx, wp, JSWP_LIVE);
|
|
ok = JS_FALSE;
|
|
goto out;
|
|
}
|
|
wp->sprop = sprop;
|
|
|
|
/*
|
|
* Now that wp is fully initialized, append it to rt's wp list.
|
|
* Because obj is locked we know that no other thread could have added
|
|
* a watchpoint for (obj, propid).
|
|
*/
|
|
DBG_LOCK(rt);
|
|
JS_ASSERT(!FindWatchPoint(rt, OBJ_SCOPE(obj), propid));
|
|
JS_APPEND_LINK(&wp->links, &rt->watchPointList);
|
|
++rt->debuggerMutations;
|
|
}
|
|
wp->handler = handler;
|
|
wp->closure = closure;
|
|
DBG_UNLOCK(rt);
|
|
|
|
out:
|
|
OBJ_DROP_PROPERTY(cx, obj, prop);
|
|
return ok;
|
|
}
|
|
|
|
JS_PUBLIC_API(JSBool)
|
|
JS_ClearWatchPoint(JSContext *cx, JSObject *obj, jsval id,
|
|
JSWatchPointHandler *handlerp, void **closurep)
|
|
{
|
|
JSRuntime *rt;
|
|
JSWatchPoint *wp;
|
|
|
|
rt = cx->runtime;
|
|
DBG_LOCK(rt);
|
|
for (wp = (JSWatchPoint *)rt->watchPointList.next;
|
|
&wp->links != &rt->watchPointList;
|
|
wp = (JSWatchPoint *)wp->links.next) {
|
|
if (wp->object == obj && SPROP_USERID(wp->sprop) == id) {
|
|
if (handlerp)
|
|
*handlerp = wp->handler;
|
|
if (closurep)
|
|
*closurep = wp->closure;
|
|
return DropWatchPointAndUnlock(cx, wp, JSWP_LIVE);
|
|
}
|
|
}
|
|
DBG_UNLOCK(rt);
|
|
if (handlerp)
|
|
*handlerp = NULL;
|
|
if (closurep)
|
|
*closurep = NULL;
|
|
return JS_TRUE;
|
|
}
|
|
|
|
JS_PUBLIC_API(JSBool)
|
|
JS_ClearWatchPointsForObject(JSContext *cx, JSObject *obj)
|
|
{
|
|
JSRuntime *rt;
|
|
JSWatchPoint *wp, *next;
|
|
uint32 sample;
|
|
|
|
rt = cx->runtime;
|
|
DBG_LOCK(rt);
|
|
for (wp = (JSWatchPoint *)rt->watchPointList.next;
|
|
&wp->links != &rt->watchPointList;
|
|
wp = next) {
|
|
next = (JSWatchPoint *)wp->links.next;
|
|
if (wp->object == obj) {
|
|
sample = rt->debuggerMutations;
|
|
if (!DropWatchPointAndUnlock(cx, wp, JSWP_LIVE))
|
|
return JS_FALSE;
|
|
DBG_LOCK(rt);
|
|
if (rt->debuggerMutations != sample + 1)
|
|
next = (JSWatchPoint *)rt->watchPointList.next;
|
|
}
|
|
}
|
|
DBG_UNLOCK(rt);
|
|
return JS_TRUE;
|
|
}
|
|
|
|
JS_PUBLIC_API(JSBool)
|
|
JS_ClearAllWatchPoints(JSContext *cx)
|
|
{
|
|
JSRuntime *rt;
|
|
JSWatchPoint *wp, *next;
|
|
uint32 sample;
|
|
|
|
rt = cx->runtime;
|
|
DBG_LOCK(rt);
|
|
for (wp = (JSWatchPoint *)rt->watchPointList.next;
|
|
&wp->links != &rt->watchPointList;
|
|
wp = next) {
|
|
next = (JSWatchPoint *)wp->links.next;
|
|
sample = rt->debuggerMutations;
|
|
if (!DropWatchPointAndUnlock(cx, wp, JSWP_LIVE))
|
|
return JS_FALSE;
|
|
DBG_LOCK(rt);
|
|
if (rt->debuggerMutations != sample + 1)
|
|
next = (JSWatchPoint *)rt->watchPointList.next;
|
|
}
|
|
DBG_UNLOCK(rt);
|
|
return JS_TRUE;
|
|
}
|
|
|
|
/************************************************************************/
|
|
|
|
JS_PUBLIC_API(uintN)
|
|
JS_PCToLineNumber(JSContext *cx, JSScript *script, jsbytecode *pc)
|
|
{
|
|
return js_PCToLineNumber(cx, script, pc);
|
|
}
|
|
|
|
JS_PUBLIC_API(jsbytecode *)
|
|
JS_LineNumberToPC(JSContext *cx, JSScript *script, uintN lineno)
|
|
{
|
|
return js_LineNumberToPC(script, lineno);
|
|
}
|
|
|
|
JS_PUBLIC_API(JSScript *)
|
|
JS_GetFunctionScript(JSContext *cx, JSFunction *fun)
|
|
{
|
|
return FUN_SCRIPT(fun);
|
|
}
|
|
|
|
JS_PUBLIC_API(JSNative)
|
|
JS_GetFunctionNative(JSContext *cx, JSFunction *fun)
|
|
{
|
|
return FUN_NATIVE(fun);
|
|
}
|
|
|
|
JS_PUBLIC_API(JSFastNative)
|
|
JS_GetFunctionFastNative(JSContext *cx, JSFunction *fun)
|
|
{
|
|
return FUN_FAST_NATIVE(fun);
|
|
}
|
|
|
|
JS_PUBLIC_API(JSPrincipals *)
|
|
JS_GetScriptPrincipals(JSContext *cx, JSScript *script)
|
|
{
|
|
return script->principals;
|
|
}
|
|
|
|
/************************************************************************/
|
|
|
|
/*
|
|
* Stack Frame Iterator
|
|
*/
|
|
JS_PUBLIC_API(JSStackFrame *)
|
|
JS_FrameIterator(JSContext *cx, JSStackFrame **iteratorp)
|
|
{
|
|
*iteratorp = (*iteratorp == NULL) ? js_GetTopStackFrame(cx) : (*iteratorp)->down;
|
|
return *iteratorp;
|
|
}
|
|
|
|
JS_PUBLIC_API(JSScript *)
|
|
JS_GetFrameScript(JSContext *cx, JSStackFrame *fp)
|
|
{
|
|
return fp->script;
|
|
}
|
|
|
|
JS_PUBLIC_API(jsbytecode *)
|
|
JS_GetFramePC(JSContext *cx, JSStackFrame *fp)
|
|
{
|
|
return fp->regs ? fp->regs->pc : NULL;
|
|
}
|
|
|
|
JS_PUBLIC_API(JSStackFrame *)
|
|
JS_GetScriptedCaller(JSContext *cx, JSStackFrame *fp)
|
|
{
|
|
return js_GetScriptedCaller(cx, fp);
|
|
}
|
|
|
|
JS_PUBLIC_API(JSPrincipals *)
|
|
JS_StackFramePrincipals(JSContext *cx, JSStackFrame *fp)
|
|
{
|
|
JSSecurityCallbacks *callbacks;
|
|
|
|
if (fp->fun) {
|
|
callbacks = JS_GetSecurityCallbacks(cx);
|
|
if (callbacks && callbacks->findObjectPrincipals) {
|
|
if (FUN_OBJECT(fp->fun) != fp->callee)
|
|
return callbacks->findObjectPrincipals(cx, fp->callee);
|
|
/* FALL THROUGH */
|
|
}
|
|
}
|
|
if (fp->script)
|
|
return fp->script->principals;
|
|
return NULL;
|
|
}
|
|
|
|
JS_PUBLIC_API(JSPrincipals *)
|
|
JS_EvalFramePrincipals(JSContext *cx, JSStackFrame *fp, JSStackFrame *caller)
|
|
{
|
|
JSPrincipals *principals, *callerPrincipals;
|
|
JSSecurityCallbacks *callbacks;
|
|
|
|
callbacks = JS_GetSecurityCallbacks(cx);
|
|
if (callbacks && callbacks->findObjectPrincipals) {
|
|
principals = callbacks->findObjectPrincipals(cx, fp->callee);
|
|
} else {
|
|
principals = NULL;
|
|
}
|
|
if (!caller)
|
|
return principals;
|
|
callerPrincipals = JS_StackFramePrincipals(cx, caller);
|
|
return (callerPrincipals && principals &&
|
|
callerPrincipals->subsume(callerPrincipals, principals))
|
|
? principals
|
|
: callerPrincipals;
|
|
}
|
|
|
|
JS_PUBLIC_API(void *)
|
|
JS_GetFrameAnnotation(JSContext *cx, JSStackFrame *fp)
|
|
{
|
|
if (fp->annotation && fp->script) {
|
|
JSPrincipals *principals = JS_StackFramePrincipals(cx, fp);
|
|
|
|
if (principals && principals->globalPrivilegesEnabled(cx, principals)) {
|
|
/*
|
|
* Give out an annotation only if privileges have not been revoked
|
|
* or disabled globally.
|
|
*/
|
|
return fp->annotation;
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
JS_PUBLIC_API(void)
|
|
JS_SetFrameAnnotation(JSContext *cx, JSStackFrame *fp, void *annotation)
|
|
{
|
|
fp->annotation = annotation;
|
|
}
|
|
|
|
JS_PUBLIC_API(void *)
|
|
JS_GetFramePrincipalArray(JSContext *cx, JSStackFrame *fp)
|
|
{
|
|
JSPrincipals *principals;
|
|
|
|
principals = JS_StackFramePrincipals(cx, fp);
|
|
if (!principals)
|
|
return NULL;
|
|
return principals->getPrincipalArray(cx, principals);
|
|
}
|
|
|
|
JS_PUBLIC_API(JSBool)
|
|
JS_IsNativeFrame(JSContext *cx, JSStackFrame *fp)
|
|
{
|
|
return !fp->script;
|
|
}
|
|
|
|
/* this is deprecated, use JS_GetFrameScopeChain instead */
|
|
JS_PUBLIC_API(JSObject *)
|
|
JS_GetFrameObject(JSContext *cx, JSStackFrame *fp)
|
|
{
|
|
return fp->scopeChain;
|
|
}
|
|
|
|
JS_PUBLIC_API(JSObject *)
|
|
JS_GetFrameScopeChain(JSContext *cx, JSStackFrame *fp)
|
|
{
|
|
/* Force creation of argument and call objects if not yet created */
|
|
(void) JS_GetFrameCallObject(cx, fp);
|
|
return js_GetScopeChain(cx, fp);
|
|
}
|
|
|
|
JS_PUBLIC_API(JSObject *)
|
|
JS_GetFrameCallObject(JSContext *cx, JSStackFrame *fp)
|
|
{
|
|
if (! fp->fun)
|
|
return NULL;
|
|
|
|
/* Force creation of argument object if not yet created */
|
|
(void) js_GetArgsObject(cx, fp);
|
|
|
|
/*
|
|
* XXX ill-defined: null return here means error was reported, unlike a
|
|
* null returned above or in the #else
|
|
*/
|
|
return js_GetCallObject(cx, fp, NULL);
|
|
}
|
|
|
|
JS_PUBLIC_API(JSObject *)
|
|
JS_GetFrameThis(JSContext *cx, JSStackFrame *fp)
|
|
{
|
|
JSStackFrame *afp;
|
|
|
|
if (fp->flags & JSFRAME_COMPUTED_THIS)
|
|
return fp->thisp;
|
|
|
|
/* js_ComputeThis gets confused if fp != cx->fp, so set it aside. */
|
|
if (js_GetTopStackFrame(cx) != fp) {
|
|
afp = cx->fp;
|
|
if (afp) {
|
|
afp->dormantNext = cx->dormantFrameChain;
|
|
cx->dormantFrameChain = afp;
|
|
cx->fp = fp;
|
|
}
|
|
} else {
|
|
afp = NULL;
|
|
}
|
|
|
|
if (!fp->thisp && fp->argv)
|
|
fp->thisp = js_ComputeThis(cx, JS_TRUE, fp->argv);
|
|
|
|
if (afp) {
|
|
cx->fp = afp;
|
|
cx->dormantFrameChain = afp->dormantNext;
|
|
afp->dormantNext = NULL;
|
|
}
|
|
|
|
return fp->thisp;
|
|
}
|
|
|
|
JS_PUBLIC_API(JSFunction *)
|
|
JS_GetFrameFunction(JSContext *cx, JSStackFrame *fp)
|
|
{
|
|
return fp->fun;
|
|
}
|
|
|
|
JS_PUBLIC_API(JSObject *)
|
|
JS_GetFrameFunctionObject(JSContext *cx, JSStackFrame *fp)
|
|
{
|
|
if (!fp->fun)
|
|
return NULL;
|
|
|
|
JS_ASSERT(OBJ_GET_CLASS(cx, fp->callee) == &js_FunctionClass);
|
|
JS_ASSERT(OBJ_GET_PRIVATE(cx, fp->callee) == fp->fun);
|
|
return fp->callee;
|
|
}
|
|
|
|
JS_PUBLIC_API(JSBool)
|
|
JS_IsConstructorFrame(JSContext *cx, JSStackFrame *fp)
|
|
{
|
|
return (fp->flags & JSFRAME_CONSTRUCTING) != 0;
|
|
}
|
|
|
|
JS_PUBLIC_API(JSObject *)
|
|
JS_GetFrameCalleeObject(JSContext *cx, JSStackFrame *fp)
|
|
{
|
|
return fp->callee;
|
|
}
|
|
|
|
JS_PUBLIC_API(JSBool)
|
|
JS_IsDebuggerFrame(JSContext *cx, JSStackFrame *fp)
|
|
{
|
|
return (fp->flags & JSFRAME_DEBUGGER) != 0;
|
|
}
|
|
|
|
JS_PUBLIC_API(jsval)
|
|
JS_GetFrameReturnValue(JSContext *cx, JSStackFrame *fp)
|
|
{
|
|
return fp->rval;
|
|
}
|
|
|
|
JS_PUBLIC_API(void)
|
|
JS_SetFrameReturnValue(JSContext *cx, JSStackFrame *fp, jsval rval)
|
|
{
|
|
fp->rval = rval;
|
|
}
|
|
|
|
/************************************************************************/
|
|
|
|
JS_PUBLIC_API(const char *)
|
|
JS_GetScriptFilename(JSContext *cx, JSScript *script)
|
|
{
|
|
return script->filename;
|
|
}
|
|
|
|
JS_PUBLIC_API(uintN)
|
|
JS_GetScriptBaseLineNumber(JSContext *cx, JSScript *script)
|
|
{
|
|
return script->lineno;
|
|
}
|
|
|
|
JS_PUBLIC_API(uintN)
|
|
JS_GetScriptLineExtent(JSContext *cx, JSScript *script)
|
|
{
|
|
return js_GetScriptLineExtent(script);
|
|
}
|
|
|
|
JS_PUBLIC_API(JSVersion)
|
|
JS_GetScriptVersion(JSContext *cx, JSScript *script)
|
|
{
|
|
return (JSVersion) (script->version & JSVERSION_MASK);
|
|
}
|
|
|
|
/***************************************************************************/
|
|
|
|
JS_PUBLIC_API(void)
|
|
JS_SetNewScriptHook(JSRuntime *rt, JSNewScriptHook hook, void *callerdata)
|
|
{
|
|
rt->globalDebugHooks.newScriptHook = hook;
|
|
rt->globalDebugHooks.newScriptHookData = callerdata;
|
|
}
|
|
|
|
JS_PUBLIC_API(void)
|
|
JS_SetDestroyScriptHook(JSRuntime *rt, JSDestroyScriptHook hook,
|
|
void *callerdata)
|
|
{
|
|
rt->globalDebugHooks.destroyScriptHook = hook;
|
|
rt->globalDebugHooks.destroyScriptHookData = callerdata;
|
|
}
|
|
|
|
/***************************************************************************/
|
|
|
|
JS_PUBLIC_API(JSBool)
|
|
JS_EvaluateUCInStackFrame(JSContext *cx, JSStackFrame *fp,
|
|
const jschar *chars, uintN length,
|
|
const char *filename, uintN lineno,
|
|
jsval *rval)
|
|
{
|
|
JSObject *scobj;
|
|
JSScript *script, *oldscript;
|
|
JSStackFrame **disp, *displaySave;
|
|
JSBool ok;
|
|
|
|
scobj = JS_GetFrameScopeChain(cx, fp);
|
|
if (!scobj)
|
|
return JS_FALSE;
|
|
|
|
oldscript = fp->script;
|
|
script = js_CompileScript(cx, scobj, fp, JS_StackFramePrincipals(cx, fp),
|
|
TCF_COMPILE_N_GO |
|
|
TCF_PUT_STATIC_DEPTH(oldscript->staticDepth + 1),
|
|
chars, length, NULL,
|
|
filename, lineno);
|
|
if (!script)
|
|
return JS_FALSE;
|
|
|
|
/* Ensure that the display is up to date for this particular stack frame. */
|
|
if (oldscript->staticDepth < JS_DISPLAY_SIZE) {
|
|
disp = &cx->display[oldscript->staticDepth];
|
|
displaySave = *disp;
|
|
*disp = fp;
|
|
} else {
|
|
disp = NULL;
|
|
displaySave = NULL;
|
|
}
|
|
ok = js_Execute(cx, scobj, script, fp, JSFRAME_DEBUGGER | JSFRAME_EVAL,
|
|
rval);
|
|
if (disp)
|
|
*disp = displaySave;
|
|
js_DestroyScript(cx, script);
|
|
return ok;
|
|
}
|
|
|
|
JS_PUBLIC_API(JSBool)
|
|
JS_EvaluateInStackFrame(JSContext *cx, JSStackFrame *fp,
|
|
const char *bytes, uintN length,
|
|
const char *filename, uintN lineno,
|
|
jsval *rval)
|
|
{
|
|
jschar *chars;
|
|
JSBool ok;
|
|
size_t len = length;
|
|
|
|
chars = js_InflateString(cx, bytes, &len);
|
|
if (!chars)
|
|
return JS_FALSE;
|
|
length = (uintN) len;
|
|
ok = JS_EvaluateUCInStackFrame(cx, fp, chars, length, filename, lineno,
|
|
rval);
|
|
JS_free(cx, chars);
|
|
|
|
return ok;
|
|
}
|
|
|
|
/************************************************************************/
|
|
|
|
/* XXXbe this all needs to be reworked to avoid requiring JSScope types. */
|
|
|
|
JS_PUBLIC_API(JSScopeProperty *)
|
|
JS_PropertyIterator(JSObject *obj, JSScopeProperty **iteratorp)
|
|
{
|
|
JSScopeProperty *sprop;
|
|
JSScope *scope;
|
|
|
|
sprop = *iteratorp;
|
|
scope = OBJ_SCOPE(obj);
|
|
|
|
/* XXXbe minor(?) incompatibility: iterate in reverse definition order */
|
|
if (!sprop) {
|
|
sprop = SCOPE_LAST_PROP(scope);
|
|
} else {
|
|
while ((sprop = sprop->parent) != NULL) {
|
|
if (!SCOPE_HAD_MIDDLE_DELETE(scope))
|
|
break;
|
|
if (SCOPE_HAS_PROPERTY(scope, sprop))
|
|
break;
|
|
}
|
|
}
|
|
*iteratorp = sprop;
|
|
return sprop;
|
|
}
|
|
|
|
JS_PUBLIC_API(JSBool)
|
|
JS_GetPropertyDesc(JSContext *cx, JSObject *obj, JSScopeProperty *sprop,
|
|
JSPropertyDesc *pd)
|
|
{
|
|
JSScope *scope;
|
|
JSScopeProperty *aprop;
|
|
jsval lastException;
|
|
JSBool wasThrowing;
|
|
|
|
pd->id = ID_TO_VALUE(sprop->id);
|
|
|
|
wasThrowing = cx->throwing;
|
|
if (wasThrowing) {
|
|
lastException = cx->exception;
|
|
if (JSVAL_IS_GCTHING(lastException) &&
|
|
!js_AddRoot(cx, &lastException, "lastException")) {
|
|
return JS_FALSE;
|
|
}
|
|
cx->throwing = JS_FALSE;
|
|
}
|
|
|
|
if (!js_GetProperty(cx, obj, sprop->id, &pd->value)) {
|
|
if (!cx->throwing) {
|
|
pd->flags = JSPD_ERROR;
|
|
pd->value = JSVAL_VOID;
|
|
} else {
|
|
pd->flags = JSPD_EXCEPTION;
|
|
pd->value = cx->exception;
|
|
}
|
|
} else {
|
|
pd->flags = 0;
|
|
}
|
|
|
|
cx->throwing = wasThrowing;
|
|
if (wasThrowing) {
|
|
cx->exception = lastException;
|
|
if (JSVAL_IS_GCTHING(lastException))
|
|
js_RemoveRoot(cx->runtime, &lastException);
|
|
}
|
|
|
|
pd->flags |= ((sprop->attrs & JSPROP_ENUMERATE) ? JSPD_ENUMERATE : 0)
|
|
| ((sprop->attrs & JSPROP_READONLY) ? JSPD_READONLY : 0)
|
|
| ((sprop->attrs & JSPROP_PERMANENT) ? JSPD_PERMANENT : 0);
|
|
pd->spare = 0;
|
|
if (sprop->getter == js_GetCallArg) {
|
|
pd->slot = sprop->shortid;
|
|
pd->flags |= JSPD_ARGUMENT;
|
|
} else if (sprop->getter == js_GetCallVar) {
|
|
pd->slot = sprop->shortid;
|
|
pd->flags |= JSPD_VARIABLE;
|
|
} else {
|
|
pd->slot = 0;
|
|
}
|
|
pd->alias = JSVAL_VOID;
|
|
scope = OBJ_SCOPE(obj);
|
|
if (SPROP_HAS_VALID_SLOT(sprop, scope)) {
|
|
for (aprop = SCOPE_LAST_PROP(scope); aprop; aprop = aprop->parent) {
|
|
if (aprop != sprop && aprop->slot == sprop->slot) {
|
|
pd->alias = ID_TO_VALUE(aprop->id);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
return JS_TRUE;
|
|
}
|
|
|
|
JS_PUBLIC_API(JSBool)
|
|
JS_GetPropertyDescArray(JSContext *cx, JSObject *obj, JSPropertyDescArray *pda)
|
|
{
|
|
JSClass *clasp;
|
|
JSScope *scope;
|
|
uint32 i, n;
|
|
JSPropertyDesc *pd;
|
|
JSScopeProperty *sprop;
|
|
|
|
clasp = OBJ_GET_CLASS(cx, obj);
|
|
if (!OBJ_IS_NATIVE(obj) || (clasp->flags & JSCLASS_NEW_ENUMERATE)) {
|
|
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
|
|
JSMSG_CANT_DESCRIBE_PROPS, clasp->name);
|
|
return JS_FALSE;
|
|
}
|
|
if (!clasp->enumerate(cx, obj))
|
|
return JS_FALSE;
|
|
|
|
/* have no props, or object's scope has not mutated from that of proto */
|
|
scope = OBJ_SCOPE(obj);
|
|
if (scope->object != obj || scope->entryCount == 0) {
|
|
pda->length = 0;
|
|
pda->array = NULL;
|
|
return JS_TRUE;
|
|
}
|
|
|
|
n = STOBJ_NSLOTS(obj);
|
|
if (n > scope->entryCount)
|
|
n = scope->entryCount;
|
|
pd = (JSPropertyDesc *) JS_malloc(cx, (size_t)n * sizeof(JSPropertyDesc));
|
|
if (!pd)
|
|
return JS_FALSE;
|
|
i = 0;
|
|
for (sprop = SCOPE_LAST_PROP(scope); sprop; sprop = sprop->parent) {
|
|
if (SCOPE_HAD_MIDDLE_DELETE(scope) && !SCOPE_HAS_PROPERTY(scope, sprop))
|
|
continue;
|
|
if (!js_AddRoot(cx, &pd[i].id, NULL))
|
|
goto bad;
|
|
if (!js_AddRoot(cx, &pd[i].value, NULL))
|
|
goto bad;
|
|
if (!JS_GetPropertyDesc(cx, obj, sprop, &pd[i]))
|
|
goto bad;
|
|
if ((pd[i].flags & JSPD_ALIAS) && !js_AddRoot(cx, &pd[i].alias, NULL))
|
|
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;
|
|
}
|
|
|
|
JS_PUBLIC_API(void)
|
|
JS_PutPropertyDescArray(JSContext *cx, JSPropertyDescArray *pda)
|
|
{
|
|
JSPropertyDesc *pd;
|
|
uint32 i;
|
|
|
|
pd = pda->array;
|
|
for (i = 0; i < pda->length; i++) {
|
|
js_RemoveRoot(cx->runtime, &pd[i].id);
|
|
js_RemoveRoot(cx->runtime, &pd[i].value);
|
|
if (pd[i].flags & JSPD_ALIAS)
|
|
js_RemoveRoot(cx->runtime, &pd[i].alias);
|
|
}
|
|
JS_free(cx, pd);
|
|
}
|
|
|
|
/************************************************************************/
|
|
|
|
JS_PUBLIC_API(JSBool)
|
|
JS_SetDebuggerHandler(JSRuntime *rt, JSTrapHandler handler, void *closure)
|
|
{
|
|
rt->globalDebugHooks.debuggerHandler = handler;
|
|
rt->globalDebugHooks.debuggerHandlerData = closure;
|
|
return JS_TRUE;
|
|
}
|
|
|
|
JS_PUBLIC_API(JSBool)
|
|
JS_SetSourceHandler(JSRuntime *rt, JSSourceHandler handler, void *closure)
|
|
{
|
|
rt->globalDebugHooks.sourceHandler = handler;
|
|
rt->globalDebugHooks.sourceHandlerData = closure;
|
|
return JS_TRUE;
|
|
}
|
|
|
|
JS_PUBLIC_API(JSBool)
|
|
JS_SetExecuteHook(JSRuntime *rt, JSInterpreterHook hook, void *closure)
|
|
{
|
|
rt->globalDebugHooks.executeHook = hook;
|
|
rt->globalDebugHooks.executeHookData = closure;
|
|
return JS_TRUE;
|
|
}
|
|
|
|
JS_PUBLIC_API(JSBool)
|
|
JS_SetCallHook(JSRuntime *rt, JSInterpreterHook hook, void *closure)
|
|
{
|
|
rt->globalDebugHooks.callHook = hook;
|
|
rt->globalDebugHooks.callHookData = closure;
|
|
return JS_TRUE;
|
|
}
|
|
|
|
JS_PUBLIC_API(JSBool)
|
|
JS_SetObjectHook(JSRuntime *rt, JSObjectHook hook, void *closure)
|
|
{
|
|
rt->globalDebugHooks.objectHook = hook;
|
|
rt->globalDebugHooks.objectHookData = closure;
|
|
return JS_TRUE;
|
|
}
|
|
|
|
JS_PUBLIC_API(JSBool)
|
|
JS_SetThrowHook(JSRuntime *rt, JSTrapHandler hook, void *closure)
|
|
{
|
|
rt->globalDebugHooks.throwHook = hook;
|
|
rt->globalDebugHooks.throwHookData = closure;
|
|
return JS_TRUE;
|
|
}
|
|
|
|
JS_PUBLIC_API(JSBool)
|
|
JS_SetDebugErrorHook(JSRuntime *rt, JSDebugErrorHook hook, void *closure)
|
|
{
|
|
rt->globalDebugHooks.debugErrorHook = hook;
|
|
rt->globalDebugHooks.debugErrorHookData = closure;
|
|
return JS_TRUE;
|
|
}
|
|
|
|
/************************************************************************/
|
|
|
|
JS_PUBLIC_API(size_t)
|
|
JS_GetObjectTotalSize(JSContext *cx, JSObject *obj)
|
|
{
|
|
size_t nbytes;
|
|
JSScope *scope;
|
|
|
|
nbytes = sizeof *obj;
|
|
if (obj->dslots) {
|
|
nbytes += ((uint32)obj->dslots[-1] - JS_INITIAL_NSLOTS + 1)
|
|
* sizeof obj->dslots[0];
|
|
}
|
|
if (OBJ_IS_NATIVE(obj)) {
|
|
scope = OBJ_SCOPE(obj);
|
|
if (scope->object == obj) {
|
|
nbytes += sizeof *scope;
|
|
nbytes += SCOPE_CAPACITY(scope) * sizeof(JSScopeProperty *);
|
|
}
|
|
}
|
|
return nbytes;
|
|
}
|
|
|
|
static size_t
|
|
GetAtomTotalSize(JSContext *cx, JSAtom *atom)
|
|
{
|
|
size_t nbytes;
|
|
|
|
nbytes = sizeof(JSAtom *) + sizeof(JSDHashEntryStub);
|
|
if (ATOM_IS_STRING(atom)) {
|
|
nbytes += sizeof(JSString);
|
|
nbytes += (JSFLATSTR_LENGTH(ATOM_TO_STRING(atom)) + 1) * sizeof(jschar);
|
|
} else if (ATOM_IS_DOUBLE(atom)) {
|
|
nbytes += sizeof(jsdouble);
|
|
}
|
|
return nbytes;
|
|
}
|
|
|
|
JS_PUBLIC_API(size_t)
|
|
JS_GetFunctionTotalSize(JSContext *cx, JSFunction *fun)
|
|
{
|
|
size_t nbytes;
|
|
|
|
nbytes = sizeof *fun;
|
|
nbytes += JS_GetObjectTotalSize(cx, FUN_OBJECT(fun));
|
|
if (FUN_INTERPRETED(fun))
|
|
nbytes += JS_GetScriptTotalSize(cx, fun->u.i.script);
|
|
if (fun->atom)
|
|
nbytes += GetAtomTotalSize(cx, fun->atom);
|
|
return nbytes;
|
|
}
|
|
|
|
#include "jsemit.h"
|
|
|
|
JS_PUBLIC_API(size_t)
|
|
JS_GetScriptTotalSize(JSContext *cx, JSScript *script)
|
|
{
|
|
size_t nbytes, pbytes;
|
|
jsatomid i;
|
|
jssrcnote *sn, *notes;
|
|
JSObjectArray *objarray;
|
|
JSPrincipals *principals;
|
|
|
|
nbytes = sizeof *script;
|
|
if (script->u.object)
|
|
nbytes += JS_GetObjectTotalSize(cx, script->u.object);
|
|
|
|
nbytes += script->length * sizeof script->code[0];
|
|
nbytes += script->atomMap.length * sizeof script->atomMap.vector[0];
|
|
for (i = 0; i < script->atomMap.length; i++)
|
|
nbytes += GetAtomTotalSize(cx, script->atomMap.vector[i]);
|
|
|
|
if (script->filename)
|
|
nbytes += strlen(script->filename) + 1;
|
|
|
|
notes = SCRIPT_NOTES(script);
|
|
for (sn = notes; !SN_IS_TERMINATOR(sn); sn = SN_NEXT(sn))
|
|
continue;
|
|
nbytes += (sn - notes + 1) * sizeof *sn;
|
|
|
|
if (script->objectsOffset != 0) {
|
|
objarray = JS_SCRIPT_OBJECTS(script);
|
|
i = objarray->length;
|
|
nbytes += sizeof *objarray + i * sizeof objarray->vector[0];
|
|
do {
|
|
nbytes += JS_GetObjectTotalSize(cx, objarray->vector[--i]);
|
|
} while (i != 0);
|
|
}
|
|
|
|
if (script->regexpsOffset != 0) {
|
|
objarray = JS_SCRIPT_REGEXPS(script);
|
|
i = objarray->length;
|
|
nbytes += sizeof *objarray + i * sizeof objarray->vector[0];
|
|
do {
|
|
nbytes += JS_GetObjectTotalSize(cx, objarray->vector[--i]);
|
|
} while (i != 0);
|
|
}
|
|
|
|
if (script->trynotesOffset != 0) {
|
|
nbytes += sizeof(JSTryNoteArray) +
|
|
JS_SCRIPT_TRYNOTES(script)->length * sizeof(JSTryNote);
|
|
}
|
|
|
|
principals = script->principals;
|
|
if (principals) {
|
|
JS_ASSERT(principals->refcount);
|
|
pbytes = sizeof *principals;
|
|
if (principals->refcount > 1)
|
|
pbytes = JS_HOWMANY(pbytes, principals->refcount);
|
|
nbytes += pbytes;
|
|
}
|
|
|
|
return nbytes;
|
|
}
|
|
|
|
JS_PUBLIC_API(uint32)
|
|
JS_GetTopScriptFilenameFlags(JSContext *cx, JSStackFrame *fp)
|
|
{
|
|
if (!fp)
|
|
fp = js_GetTopStackFrame(cx);
|
|
while (fp) {
|
|
if (fp->script)
|
|
return JS_GetScriptFilenameFlags(fp->script);
|
|
fp = fp->down;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
JS_PUBLIC_API(uint32)
|
|
JS_GetScriptFilenameFlags(JSScript *script)
|
|
{
|
|
JS_ASSERT(script);
|
|
if (!script->filename)
|
|
return JSFILENAME_NULL;
|
|
return js_GetScriptFilenameFlags(script->filename);
|
|
}
|
|
|
|
JS_PUBLIC_API(JSBool)
|
|
JS_FlagScriptFilenamePrefix(JSRuntime *rt, const char *prefix, uint32 flags)
|
|
{
|
|
if (!js_SaveScriptFilenameRT(rt, prefix, flags))
|
|
return JS_FALSE;
|
|
return JS_TRUE;
|
|
}
|
|
|
|
JS_PUBLIC_API(JSBool)
|
|
JS_IsSystemObject(JSContext *cx, JSObject *obj)
|
|
{
|
|
return STOBJ_IS_SYSTEM(obj);
|
|
}
|
|
|
|
JS_PUBLIC_API(JSObject *)
|
|
JS_NewSystemObject(JSContext *cx, JSClass *clasp, JSObject *proto,
|
|
JSObject *parent, JSBool system)
|
|
{
|
|
JSObject *obj;
|
|
|
|
obj = js_NewObject(cx, clasp, proto, parent, 0);
|
|
if (obj && system)
|
|
STOBJ_SET_SYSTEM(obj);
|
|
return obj;
|
|
}
|
|
|
|
/************************************************************************/
|
|
|
|
JS_PUBLIC_API(JSDebugHooks *)
|
|
JS_GetGlobalDebugHooks(JSRuntime *rt)
|
|
{
|
|
return &rt->globalDebugHooks;
|
|
}
|
|
|
|
JS_PUBLIC_API(JSDebugHooks *)
|
|
JS_SetContextDebugHooks(JSContext *cx, JSDebugHooks *hooks)
|
|
{
|
|
JSDebugHooks *old;
|
|
|
|
JS_ASSERT(hooks);
|
|
old = cx->debugHooks;
|
|
cx->debugHooks = hooks;
|
|
return old;
|
|
}
|
|
|
|
#ifdef MOZ_SHARK
|
|
|
|
#include <CHUD/CHUD.h>
|
|
|
|
JS_PUBLIC_API(JSBool)
|
|
JS_StartChudRemote()
|
|
{
|
|
if (chudIsRemoteAccessAcquired() &&
|
|
(chudStartRemotePerfMonitor("Mozilla") == chudSuccess)) {
|
|
return JS_TRUE;
|
|
}
|
|
|
|
return JS_FALSE;
|
|
}
|
|
|
|
JS_PUBLIC_API(JSBool)
|
|
JS_StopChudRemote()
|
|
{
|
|
if (chudIsRemoteAccessAcquired() &&
|
|
(chudStopRemotePerfMonitor() == chudSuccess)) {
|
|
return JS_TRUE;
|
|
}
|
|
|
|
return JS_FALSE;
|
|
}
|
|
|
|
JS_PUBLIC_API(JSBool)
|
|
JS_ConnectShark()
|
|
{
|
|
if (!chudIsInitialized() && (chudInitialize() != chudSuccess))
|
|
return JS_FALSE;
|
|
|
|
if (chudAcquireRemoteAccess() != chudSuccess)
|
|
return JS_FALSE;
|
|
|
|
return JS_TRUE;
|
|
}
|
|
|
|
JS_PUBLIC_API(JSBool)
|
|
JS_DisconnectShark()
|
|
{
|
|
if (chudIsRemoteAccessAcquired() && (chudReleaseRemoteAccess() != chudSuccess))
|
|
return JS_FALSE;
|
|
|
|
return JS_TRUE;
|
|
}
|
|
|
|
JS_FRIEND_API(JSBool)
|
|
js_StartShark(JSContext *cx, JSObject *obj,
|
|
uintN argc, jsval *argv, jsval *rval)
|
|
{
|
|
if (!JS_StartChudRemote()) {
|
|
JS_ReportError(cx, "Error starting CHUD.");
|
|
}
|
|
|
|
return JS_TRUE;
|
|
}
|
|
|
|
JS_FRIEND_API(JSBool)
|
|
js_StopShark(JSContext *cx, JSObject *obj,
|
|
uintN argc, jsval *argv, jsval *rval)
|
|
{
|
|
if (!JS_StopChudRemote()) {
|
|
JS_ReportError(cx, "Error stopping CHUD.");
|
|
}
|
|
|
|
return JS_TRUE;
|
|
}
|
|
|
|
JS_FRIEND_API(JSBool)
|
|
js_ConnectShark(JSContext *cx, JSObject *obj,
|
|
uintN argc, jsval *argv, jsval *rval)
|
|
{
|
|
if (!JS_ConnectShark()) {
|
|
JS_ReportError(cx, "Error connecting to Shark.");
|
|
}
|
|
|
|
return JS_TRUE;
|
|
}
|
|
|
|
JS_FRIEND_API(JSBool)
|
|
js_DisconnectShark(JSContext *cx, JSObject *obj,
|
|
uintN argc, jsval *argv, jsval *rval)
|
|
{
|
|
if (!JS_DisconnectShark()) {
|
|
JS_ReportError(cx, "Error disconnecting from Shark.");
|
|
}
|
|
|
|
return JS_TRUE;
|
|
}
|
|
|
|
#endif /* MOZ_SHARK */
|
|
|
|
#ifdef MOZ_CALLGRIND
|
|
|
|
#include <valgrind/callgrind.h>
|
|
|
|
JS_FRIEND_API(JSBool)
|
|
js_StartCallgrind(JSContext *cx, JSObject *obj,
|
|
uintN argc, jsval *argv, jsval *rval)
|
|
{
|
|
CALLGRIND_START_INSTRUMENTATION;
|
|
CALLGRIND_ZERO_STATS;
|
|
return JS_TRUE;
|
|
}
|
|
|
|
JS_FRIEND_API(JSBool)
|
|
js_StopCallgrind(JSContext *cx, JSObject *obj,
|
|
uintN argc, jsval *argv, jsval *rval)
|
|
{
|
|
CALLGRIND_STOP_INSTRUMENTATION;
|
|
return JS_TRUE;
|
|
}
|
|
|
|
JS_FRIEND_API(JSBool)
|
|
js_DumpCallgrind(JSContext *cx, JSObject *obj,
|
|
uintN argc, jsval *argv, jsval *rval)
|
|
{
|
|
JSString *str;
|
|
char *cstr;
|
|
|
|
if (argc > 0 && JSVAL_IS_STRING(argv[0])) {
|
|
str = JSVAL_TO_STRING(argv[0]);
|
|
cstr = js_DeflateString(cx, JSSTRING_CHARS(str), JSSTRING_LENGTH(str));
|
|
if (cstr) {
|
|
CALLGRIND_DUMP_STATS_AT(cstr);
|
|
JS_free(cx, cstr);
|
|
return JS_TRUE;
|
|
}
|
|
}
|
|
CALLGRIND_DUMP_STATS;
|
|
|
|
return JS_TRUE;
|
|
}
|
|
|
|
#endif /* MOZ_CALLGRIND */
|
|
|
|
#ifdef MOZ_VTUNE
|
|
#include <VTuneApi.h>
|
|
|
|
static const char *vtuneErrorMessages[] = {
|
|
"unknown, error #0",
|
|
"invalid 'max samples' field",
|
|
"invalid 'samples per buffer' field",
|
|
"invalid 'sample interval' field",
|
|
"invalid path",
|
|
"sample file in use",
|
|
"invalid 'number of events' field",
|
|
"unknown, error #7",
|
|
"internal error",
|
|
"bad event name",
|
|
"VTStopSampling called without calling VTStartSampling",
|
|
"no events selected for event-based sampling",
|
|
"events selected cannot be run together",
|
|
"no sampling parameters",
|
|
"sample database already exists",
|
|
"sampling already started",
|
|
"time-based sampling not supported",
|
|
"invalid 'sampling parameters size' field",
|
|
"invalid 'event size' field",
|
|
"sampling file already bound",
|
|
"invalid event path",
|
|
"invalid license",
|
|
"invalid 'global options' field",
|
|
|
|
};
|
|
|
|
JS_FRIEND_API(JSBool)
|
|
js_StartVtune(JSContext *cx, JSObject *obj,
|
|
uintN argc, jsval *argv, jsval *rval)
|
|
{
|
|
VTUNE_EVENT events[] = {
|
|
{ 1000000, 0, 0, 0, "CPU_CLK_UNHALTED.CORE" },
|
|
{ 1000000, 0, 0, 0, "INST_RETIRED.ANY" },
|
|
};
|
|
|
|
U32 n_events = sizeof(events) / sizeof(VTUNE_EVENT);
|
|
char *default_filename = "mozilla-vtune.tb5";
|
|
JSString *str;
|
|
U32 status;
|
|
|
|
VTUNE_SAMPLING_PARAMS params = {
|
|
sizeof(VTUNE_SAMPLING_PARAMS),
|
|
sizeof(VTUNE_EVENT),
|
|
0, 0, /* Reserved fields */
|
|
1, /* Initialize in "paused" state */
|
|
0, /* Max samples, or 0 for "continuous" */
|
|
4096, /* Samples per buffer */
|
|
0.1, /* Sampling interval in ms */
|
|
1, /* 1 for event-based sampling, 0 for time-based */
|
|
|
|
n_events,
|
|
events,
|
|
default_filename,
|
|
};
|
|
|
|
if (argc > 0 && JSVAL_IS_STRING(argv[0])) {
|
|
str = JSVAL_TO_STRING(argv[0]);
|
|
params.tb5Filename = js_DeflateString(cx,
|
|
JSSTRING_CHARS(str),
|
|
JSSTRING_LENGTH(str));
|
|
}
|
|
|
|
status = VTStartSampling(¶ms);
|
|
|
|
if (params.tb5Filename != default_filename)
|
|
JS_free(cx, params.tb5Filename);
|
|
|
|
if (status != 0) {
|
|
if (status == VTAPI_MULTIPLE_RUNS)
|
|
VTStopSampling(0);
|
|
if (status < sizeof(vtuneErrorMessages))
|
|
JS_ReportError(cx, "Vtune setup error: %s",
|
|
vtuneErrorMessages[status]);
|
|
else
|
|
JS_ReportError(cx, "Vtune setup error: %d",
|
|
status);
|
|
return JS_FALSE;
|
|
}
|
|
return JS_TRUE;
|
|
}
|
|
|
|
JS_FRIEND_API(JSBool)
|
|
js_StopVtune(JSContext *cx, JSObject *obj,
|
|
uintN argc, jsval *argv, jsval *rval)
|
|
{
|
|
U32 status = VTStopSampling(1);
|
|
if (status) {
|
|
if (status < sizeof(vtuneErrorMessages))
|
|
JS_ReportError(cx, "Vtune shutdown error: %s",
|
|
vtuneErrorMessages[status]);
|
|
else
|
|
JS_ReportError(cx, "Vtune shutdown error: %d",
|
|
status);
|
|
return JS_FALSE;
|
|
}
|
|
return JS_TRUE;
|
|
}
|
|
|
|
JS_FRIEND_API(JSBool)
|
|
js_PauseVtune(JSContext *cx, JSObject *obj,
|
|
uintN argc, jsval *argv, jsval *rval)
|
|
{
|
|
VTPause();
|
|
return JS_TRUE;
|
|
}
|
|
|
|
JS_FRIEND_API(JSBool)
|
|
js_ResumeVtune(JSContext *cx, JSObject *obj,
|
|
uintN argc, jsval *argv, jsval *rval)
|
|
{
|
|
VTResume();
|
|
return JS_TRUE;
|
|
}
|
|
|
|
#endif /* MOZ_VTUNE */
|