gecko/js/jsd/jsd_stak.cpp
Ryan VanderMeulen c60e2ccac4 Backed out 12 changesets (bug 927782) for SM rootanalysis orange.
Backed out changeset f86d2d4cfadf (bug 927782)
Backed out changeset 51d6617835d1 (bug 927782)
Backed out changeset eed9795fa80e (bug 927782)
Backed out changeset b971de7edfff (bug 927782)
Backed out changeset 5f086f95b305 (bug 927782)
Backed out changeset 8c74b1f68590 (bug 927782)
Backed out changeset f1237f11edcd (bug 927782)
Backed out changeset d6946bd743b3 (bug 927782)
Backed out changeset cbdd50c96b85 (bug 927782)
Backed out changeset fc7a979712fc (bug 927782)
Backed out changeset c8304ccf88e9 (bug 927782)
Backed out changeset 9d99e9ca7b32 (bug 927782)
2013-12-06 15:03:19 -05:00

575 lines
16 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 - Call stack support
*/
#include "jsd.h"
#include "jsfriendapi.h"
#include "nsCxPusher.h"
using mozilla::AutoPushJSContext;
#ifdef DEBUG
void JSD_ASSERT_VALID_THREAD_STATE(JSDThreadState* jsdthreadstate)
{
JS_ASSERT(jsdthreadstate);
JS_ASSERT(jsdthreadstate->stackDepth > 0);
}
void JSD_ASSERT_VALID_STACK_FRAME(JSDStackFrameInfo* jsdframe)
{
JS_ASSERT(jsdframe);
JS_ASSERT(jsdframe->jsdthreadstate);
}
#endif
static JSDStackFrameInfo*
_addNewFrame(JSDContext* jsdc,
JSDThreadState* jsdthreadstate,
JSScript* script,
uintptr_t pc,
bool isConstructing,
JSAbstractFramePtr frame)
{
JSDStackFrameInfo* jsdframe;
JSDScript* jsdscript = nullptr;
JSD_LOCK_SCRIPTS(jsdc);
jsdscript = jsd_FindJSDScript(jsdc, script);
JSD_UNLOCK_SCRIPTS(jsdc);
if (!jsdscript || (jsdc->flags & JSD_HIDE_DISABLED_FRAMES &&
!JSD_IS_DEBUG_ENABLED(jsdc, jsdscript)))
{
return nullptr;
}
if (!JSD_IS_DEBUG_ENABLED(jsdc, jsdscript))
jsdthreadstate->flags |= TS_HAS_DISABLED_FRAME;
jsdframe = (JSDStackFrameInfo*) calloc(1, sizeof(JSDStackFrameInfo));
if( ! jsdframe )
return nullptr;
jsdframe->jsdthreadstate = jsdthreadstate;
jsdframe->jsdscript = jsdscript;
jsdframe->isConstructing = isConstructing;
jsdframe->pc = pc;
jsdframe->frame = frame;
JS_APPEND_LINK(&jsdframe->links, &jsdthreadstate->stack);
jsdthreadstate->stackDepth++;
return jsdframe;
}
static void
_destroyFrame(JSDStackFrameInfo* jsdframe)
{
/* kill any alloc'd objects in frame here... */
if( jsdframe )
free(jsdframe);
}
JSDThreadState*
jsd_NewThreadState(JSDContext* jsdc, JSContext *cx )
{
JSDThreadState* jsdthreadstate;
jsdthreadstate = (JSDThreadState*)calloc(1, sizeof(JSDThreadState));
if( ! jsdthreadstate )
return nullptr;
jsdthreadstate->context = cx;
jsdthreadstate->thread = JSD_CURRENT_THREAD();
JS_INIT_CLIST(&jsdthreadstate->stack);
jsdthreadstate->stackDepth = 0;
JS_BeginRequest(jsdthreadstate->context);
JSBrokenFrameIterator iter(cx);
while(!iter.done())
{
JSAbstractFramePtr frame = iter.abstractFramePtr();
JS::RootedScript script(cx, frame.script());
uintptr_t pc = (uintptr_t)iter.pc();
JS::RootedValue dummyThis(cx);
/*
* don't construct a JSDStackFrame for dummy frames (those without a
* |this| object, or native frames, if JSD_INCLUDE_NATIVE_FRAMES
* isn't set.
*/
if (frame.getThisValue(cx, &dummyThis))
{
bool isConstructing = iter.isConstructing();
JSDStackFrameInfo *frameInfo = _addNewFrame( jsdc, jsdthreadstate, script, pc, isConstructing, frame );
if ((jsdthreadstate->stackDepth == 0 && !frameInfo) ||
(jsdthreadstate->stackDepth == 1 && frameInfo &&
frameInfo->jsdscript && !JSD_IS_DEBUG_ENABLED(jsdc, frameInfo->jsdscript)))
{
/*
* if we failed to create the first frame, or the top frame
* is not enabled for debugging, fail the entire thread state.
*/
JS_INIT_CLIST(&jsdthreadstate->links);
JS_EndRequest(jsdthreadstate->context);
jsd_DestroyThreadState(jsdc, jsdthreadstate);
return nullptr;
}
}
++iter;
}
JS_EndRequest(jsdthreadstate->context);
if (jsdthreadstate->stackDepth == 0)
{
free(jsdthreadstate);
return nullptr;
}
JSD_LOCK_THREADSTATES(jsdc);
JS_APPEND_LINK(&jsdthreadstate->links, &jsdc->threadsStates);
JSD_UNLOCK_THREADSTATES(jsdc);
return jsdthreadstate;
}
void
jsd_DestroyThreadState(JSDContext* jsdc, JSDThreadState* jsdthreadstate)
{
JSDStackFrameInfo* jsdframe;
JSCList* list;
JS_ASSERT(jsdthreadstate);
JS_ASSERT(JSD_CURRENT_THREAD() == jsdthreadstate->thread);
JSD_LOCK_THREADSTATES(jsdc);
JS_REMOVE_LINK(&jsdthreadstate->links);
JSD_UNLOCK_THREADSTATES(jsdc);
list = &jsdthreadstate->stack;
while( (JSDStackFrameInfo*)list != (jsdframe = (JSDStackFrameInfo*)list->next) )
{
JS_REMOVE_LINK(&jsdframe->links);
_destroyFrame(jsdframe);
}
free(jsdthreadstate);
}
unsigned
jsd_GetCountOfStackFrames(JSDContext* jsdc, JSDThreadState* jsdthreadstate)
{
unsigned count = 0;
JSD_LOCK_THREADSTATES(jsdc);
if( jsd_IsValidThreadState(jsdc, jsdthreadstate) )
count = jsdthreadstate->stackDepth;
JSD_UNLOCK_THREADSTATES(jsdc);
return count;
}
JSDStackFrameInfo*
jsd_GetStackFrame(JSDContext* jsdc, JSDThreadState* jsdthreadstate)
{
JSDStackFrameInfo* jsdframe = nullptr;
JSD_LOCK_THREADSTATES(jsdc);
if( jsd_IsValidThreadState(jsdc, jsdthreadstate) )
jsdframe = (JSDStackFrameInfo*) JS_LIST_HEAD(&jsdthreadstate->stack);
JSD_UNLOCK_THREADSTATES(jsdc);
return jsdframe;
}
JSContext *
jsd_GetJSContext (JSDContext* jsdc, JSDThreadState* jsdthreadstate)
{
JSContext* cx = nullptr;
JSD_LOCK_THREADSTATES(jsdc);
if( jsd_IsValidThreadState(jsdc, jsdthreadstate) )
cx = jsdthreadstate->context;
JSD_UNLOCK_THREADSTATES(jsdc);
return cx;
}
JSDStackFrameInfo*
jsd_GetCallingStackFrame(JSDContext* jsdc,
JSDThreadState* jsdthreadstate,
JSDStackFrameInfo* jsdframe)
{
JSDStackFrameInfo* nextjsdframe = nullptr;
JSD_LOCK_THREADSTATES(jsdc);
if( jsd_IsValidFrameInThreadState(jsdc, jsdthreadstate, jsdframe) )
if( JS_LIST_HEAD(&jsdframe->links) != &jsdframe->jsdthreadstate->stack )
nextjsdframe = (JSDStackFrameInfo*) JS_LIST_HEAD(&jsdframe->links);
JSD_UNLOCK_THREADSTATES(jsdc);
return nextjsdframe;
}
JSDScript*
jsd_GetScriptForStackFrame(JSDContext* jsdc,
JSDThreadState* jsdthreadstate,
JSDStackFrameInfo* jsdframe)
{
JSDScript* jsdscript = nullptr;
JSD_LOCK_THREADSTATES(jsdc);
if( jsd_IsValidFrameInThreadState(jsdc, jsdthreadstate, jsdframe) )
jsdscript = jsdframe->jsdscript;
JSD_UNLOCK_THREADSTATES(jsdc);
return jsdscript;
}
uintptr_t
jsd_GetPCForStackFrame(JSDContext* jsdc,
JSDThreadState* jsdthreadstate,
JSDStackFrameInfo* jsdframe)
{
uintptr_t pc = 0;
JSD_LOCK_THREADSTATES(jsdc);
if( jsd_IsValidFrameInThreadState(jsdc, jsdthreadstate, jsdframe) )
pc = jsdframe->pc;
JSD_UNLOCK_THREADSTATES(jsdc);
return pc;
}
JSDValue*
jsd_GetCallObjectForStackFrame(JSDContext* jsdc,
JSDThreadState* jsdthreadstate,
JSDStackFrameInfo* jsdframe)
{
JSObject* obj;
JSDValue* jsdval = nullptr;
JSD_LOCK_THREADSTATES(jsdc);
if( jsd_IsValidFrameInThreadState(jsdc, jsdthreadstate, jsdframe) )
{
obj = jsdframe->frame.callObject(jsdthreadstate->context);
if(obj)
jsdval = JSD_NewValue(jsdc, OBJECT_TO_JSVAL(obj));
}
JSD_UNLOCK_THREADSTATES(jsdc);
return jsdval;
}
JSDValue*
jsd_GetScopeChainForStackFrame(JSDContext* jsdc,
JSDThreadState* jsdthreadstate,
JSDStackFrameInfo* jsdframe)
{
JS::RootedObject obj(jsdthreadstate->context);
JSDValue* jsdval = nullptr;
JSD_LOCK_THREADSTATES(jsdc);
if( jsd_IsValidFrameInThreadState(jsdc, jsdthreadstate, jsdframe) )
{
JS_BeginRequest(jsdthreadstate->context);
obj = jsdframe->frame.scopeChain(jsdthreadstate->context);
JS_EndRequest(jsdthreadstate->context);
if(obj)
jsdval = JSD_NewValue(jsdc, OBJECT_TO_JSVAL(obj));
}
JSD_UNLOCK_THREADSTATES(jsdc);
return jsdval;
}
JSDValue*
jsd_GetThisForStackFrame(JSDContext* jsdc,
JSDThreadState* jsdthreadstate,
JSDStackFrameInfo* jsdframe)
{
JSDValue* jsdval = nullptr;
JSD_LOCK_THREADSTATES(jsdc);
if( jsd_IsValidFrameInThreadState(jsdc, jsdthreadstate, jsdframe) )
{
bool ok;
JS::RootedValue thisval(jsdthreadstate->context);
JS_BeginRequest(jsdthreadstate->context);
ok = jsdframe->frame.getThisValue(jsdthreadstate->context, &thisval);
JS_EndRequest(jsdthreadstate->context);
if(ok)
jsdval = JSD_NewValue(jsdc, thisval);
}
JSD_UNLOCK_THREADSTATES(jsdc);
return jsdval;
}
JSString*
jsd_GetIdForStackFrame(JSDContext* jsdc,
JSDThreadState* jsdthreadstate,
JSDStackFrameInfo* jsdframe)
{
JSString *rv = nullptr;
JSD_LOCK_THREADSTATES(jsdc);
if( jsd_IsValidFrameInThreadState(jsdc, jsdthreadstate, jsdframe) )
{
JSFunction *fun = jsdframe->frame.maybeFun();
if( fun )
{
rv = JS_GetFunctionId (fun);
/*
* For compatibility we return "anonymous", not an empty string
* here.
*/
if( !rv )
rv = JS_GetAnonymousString(jsdc->jsrt);
}
}
JSD_UNLOCK_THREADSTATES(jsdc);
return rv;
}
bool
jsd_IsStackFrameDebugger(JSDContext* jsdc,
JSDThreadState* jsdthreadstate,
JSDStackFrameInfo* jsdframe)
{
bool rv = true;
JSD_LOCK_THREADSTATES(jsdc);
if( jsd_IsValidFrameInThreadState(jsdc, jsdthreadstate, jsdframe) )
{
rv = jsdframe->frame.isDebuggerFrame();
}
JSD_UNLOCK_THREADSTATES(jsdc);
return rv;
}
bool
jsd_IsStackFrameConstructing(JSDContext* jsdc,
JSDThreadState* jsdthreadstate,
JSDStackFrameInfo* jsdframe)
{
bool rv = true;
JSD_LOCK_THREADSTATES(jsdc);
if( jsd_IsValidFrameInThreadState(jsdc, jsdthreadstate, jsdframe) )
{
rv = jsdframe->isConstructing;
}
JSD_UNLOCK_THREADSTATES(jsdc);
return rv;
}
bool
jsd_EvaluateUCScriptInStackFrame(JSDContext* jsdc,
JSDThreadState* jsdthreadstate,
JSDStackFrameInfo* jsdframe,
const jschar *bytes, unsigned length,
const char *filename, unsigned lineno,
bool eatExceptions, JS::MutableHandleValue rval)
{
bool retval;
bool valid;
JSExceptionState* exceptionState = nullptr;
JS_ASSERT(JSD_CURRENT_THREAD() == jsdthreadstate->thread);
JSD_LOCK_THREADSTATES(jsdc);
valid = jsd_IsValidFrameInThreadState(jsdc, jsdthreadstate, jsdframe);
JSD_UNLOCK_THREADSTATES(jsdc);
if( ! valid )
return false;
AutoPushJSContext cx(jsdthreadstate->context);
JS_ASSERT(cx);
if (eatExceptions)
exceptionState = JS_SaveExceptionState(cx);
JS_ClearPendingException(cx);
jsd_StartingEvalUsingFilename(jsdc, filename);
retval = jsdframe->frame.evaluateUCInStackFrame(cx, bytes, length, filename, lineno,
rval);
jsd_FinishedEvalUsingFilename(jsdc, filename);
if (eatExceptions)
JS_RestoreExceptionState(cx, exceptionState);
return retval;
}
bool
jsd_EvaluateScriptInStackFrame(JSDContext* jsdc,
JSDThreadState* jsdthreadstate,
JSDStackFrameInfo* jsdframe,
const char *bytes, unsigned length,
const char *filename, unsigned lineno,
bool eatExceptions, JS::MutableHandleValue rval)
{
bool retval;
bool valid;
JSExceptionState* exceptionState = nullptr;
JS_ASSERT(JSD_CURRENT_THREAD() == jsdthreadstate->thread);
JSD_LOCK_THREADSTATES(jsdc);
valid = jsd_IsValidFrameInThreadState(jsdc, jsdthreadstate, jsdframe);
JSD_UNLOCK_THREADSTATES(jsdc);
if (!valid)
return false;
AutoPushJSContext cx(jsdthreadstate->context);
JS_ASSERT(cx);
if (eatExceptions)
exceptionState = JS_SaveExceptionState(cx);
JS_ClearPendingException(cx);
jsd_StartingEvalUsingFilename(jsdc, filename);
retval = jsdframe->frame.evaluateInStackFrame(cx, bytes, length, filename, lineno,
rval);
jsd_FinishedEvalUsingFilename(jsdc, filename);
if (eatExceptions)
JS_RestoreExceptionState(cx, exceptionState);
return retval;
}
JSString*
jsd_ValToStringInStackFrame(JSDContext* jsdc,
JSDThreadState* jsdthreadstate,
JSDStackFrameInfo* jsdframe,
jsval val)
{
bool valid;
JSString* retval;
JSExceptionState* exceptionState;
JSContext* cx;
JSD_LOCK_THREADSTATES(jsdc);
valid = jsd_IsValidFrameInThreadState(jsdc, jsdthreadstate, jsdframe);
JSD_UNLOCK_THREADSTATES(jsdc);
if( ! valid )
return nullptr;
cx = jsdthreadstate->context;
JS_ASSERT(cx);
exceptionState = JS_SaveExceptionState(cx);
JS::RootedValue v(cx, val);
retval = JS::ToString(cx, v);
JS_RestoreExceptionState(cx, exceptionState);
return retval;
}
bool
jsd_IsValidThreadState(JSDContext* jsdc,
JSDThreadState* jsdthreadstate)
{
JSDThreadState *cur;
JS_ASSERT( JSD_THREADSTATES_LOCKED(jsdc) );
for( cur = (JSDThreadState*)jsdc->threadsStates.next;
cur != (JSDThreadState*)&jsdc->threadsStates;
cur = (JSDThreadState*)cur->links.next )
{
if( cur == jsdthreadstate )
return true;
}
return false;
}
bool
jsd_IsValidFrameInThreadState(JSDContext* jsdc,
JSDThreadState* jsdthreadstate,
JSDStackFrameInfo* jsdframe)
{
JS_ASSERT(JSD_THREADSTATES_LOCKED(jsdc));
if( ! jsd_IsValidThreadState(jsdc, jsdthreadstate) )
return false;
if( jsdframe->jsdthreadstate != jsdthreadstate )
return false;
JSD_ASSERT_VALID_THREAD_STATE(jsdthreadstate);
JSD_ASSERT_VALID_STACK_FRAME(jsdframe);
return true;
}
static JSContext*
_getContextForThreadState(JSDContext* jsdc, JSDThreadState* jsdthreadstate)
{
bool valid;
JSD_LOCK_THREADSTATES(jsdc);
valid = jsd_IsValidThreadState(jsdc, jsdthreadstate);
JSD_UNLOCK_THREADSTATES(jsdc);
if( valid )
return jsdthreadstate->context;
return nullptr;
}
JSDValue*
jsd_GetException(JSDContext* jsdc, JSDThreadState* jsdthreadstate)
{
JSContext* cx;
if(!(cx = _getContextForThreadState(jsdc, jsdthreadstate)))
return nullptr;
JS::RootedValue val(cx);
if(JS_GetPendingException(cx, &val))
return jsd_NewValue(jsdc, val);
return nullptr;
}
bool
jsd_SetException(JSDContext* jsdc, JSDThreadState* jsdthreadstate,
JSDValue* jsdval)
{
JSContext* cx;
if(!(cx = _getContextForThreadState(jsdc, jsdthreadstate)))
return false;
if(jsdval) {
JS::RootedValue exn(cx, JSD_GetValueWrappedJSVal(jsdc, jsdval));
JS_SetPendingException(cx, exn);
} else {
JS_ClearPendingException(cx);
}
return true;
}