зеркало из https://github.com/mozilla/pjs.git
2767 строки
73 KiB
C++
2767 строки
73 KiB
C++
|
/* -*- 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.
|
||
|
*/
|
||
|
|
||
|
/*
|
||
|
* jsd_iiop.cpp
|
||
|
*
|
||
|
* iiop interface to allow access to server backend from client frontend.
|
||
|
*
|
||
|
* John Bandhauer (jband@netscape.com)
|
||
|
* 8/06/97
|
||
|
*
|
||
|
*/
|
||
|
|
||
|
#ifndef SERVER_BUILD
|
||
|
#error jsd_iiop.cpp is for LiveWire only
|
||
|
#endif
|
||
|
|
||
|
#include "jsdebug.h"
|
||
|
#include "jsapi.h"
|
||
|
#include "jsstr.h"
|
||
|
#include "prhash.h"
|
||
|
#include "prmon.h"
|
||
|
#include "prlog.h" /* for PR_ASSERT */
|
||
|
#include "prevent.h"
|
||
|
#include <base/pblock.h>
|
||
|
#include <base/session.h>
|
||
|
#include <frame/req.h>
|
||
|
#include <frame/log.h>
|
||
|
#include <frame/http.h>
|
||
|
|
||
|
#include "corba.h"
|
||
|
#include "NameLib.hpp"
|
||
|
#include "ifaces_s.hh"
|
||
|
|
||
|
// this shouldn't have to be here, but is not defined elsewhere
|
||
|
CosNaming::Name::~Name() {}
|
||
|
|
||
|
// #define JSD_IIOP_TRACE 1
|
||
|
|
||
|
#ifdef JSD_IIOP_TRACE
|
||
|
// quick hack...
|
||
|
|
||
|
static void _write_to_logfile(const char* str)
|
||
|
{
|
||
|
static const char logfilename[] = "c:\\temp\\jsdiiop.log";
|
||
|
static FILE* logfile = NULL;
|
||
|
static PRMonitor* mon = NULL;
|
||
|
|
||
|
if( NULL == mon && NULL == (mon = PR_NewMonitor()) )
|
||
|
return;
|
||
|
|
||
|
PR_EnterMonitor(mon);
|
||
|
|
||
|
if( NULL == logfile && NULL == (logfile = fopen(logfilename, "a+")) )
|
||
|
{
|
||
|
PR_ExitMonitor(mon);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
#ifdef WIN32
|
||
|
int threadid = (int) GetCurrentThreadId();
|
||
|
#else
|
||
|
int threadid = (int) PR_GetCurrentThread();
|
||
|
#endif
|
||
|
|
||
|
char* buf = PR_smprintf("thread: \"%0x\" msg: \"%s\"\n", threadid, str);
|
||
|
fwrite(buf, sizeof(char), strlen(buf), logfile);
|
||
|
fflush(logfile);
|
||
|
free(buf);
|
||
|
PR_ExitMonitor(mon);
|
||
|
}
|
||
|
|
||
|
// #define TRACE(msg) log_error(LOG_VERBOSE, "jsd_iiop", NULL, NULL, "TRACE: %s", msg)
|
||
|
#define TRACE(msg) _write_to_logfile(msg)
|
||
|
#else
|
||
|
#define TRACE(msg)
|
||
|
#endif
|
||
|
|
||
|
/***************************************************************************/
|
||
|
// deal with the fact that char* return values to CORBA implemented methods
|
||
|
// do not get freed!
|
||
|
|
||
|
static void _deferedFree(void* ptr)
|
||
|
{
|
||
|
// it is important that this number be high enough -- no recursive or
|
||
|
// huge thread count usage of this function!
|
||
|
#define DEFERED_FREE_PTR_COUNT 50
|
||
|
static void* cache[DEFERED_FREE_PTR_COUNT];
|
||
|
static current = 0;
|
||
|
static PRMonitor* mon = NULL;
|
||
|
|
||
|
if( NULL == mon )
|
||
|
{
|
||
|
// init the monitor
|
||
|
mon = PR_NewMonitor();
|
||
|
if( NULL == mon )
|
||
|
return;
|
||
|
PR_EnterMonitor(mon);
|
||
|
// belt and suspenders :)
|
||
|
for( int i = 0; i < DEFERED_FREE_PTR_COUNT; i++ )
|
||
|
cache[i] = NULL;
|
||
|
}
|
||
|
else
|
||
|
PR_EnterMonitor(mon);
|
||
|
|
||
|
if( cache[current] )
|
||
|
free( cache[current] );
|
||
|
cache[current] = ptr;
|
||
|
|
||
|
current++ ;
|
||
|
if( current == DEFERED_FREE_PTR_COUNT )
|
||
|
current = 0;
|
||
|
|
||
|
PR_ExitMonitor(mon);
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
/***************************************************************************/
|
||
|
//
|
||
|
// local declarations
|
||
|
//
|
||
|
// prototype these for proper extern 'C'
|
||
|
PR_BEGIN_EXTERN_C
|
||
|
|
||
|
PR_EXTERN(int)
|
||
|
ssjsdebugInit(pblock *pb, Session *sn, Request *rq);
|
||
|
|
||
|
PR_EXTERN(PRUintn)
|
||
|
ssjsdErrorReporter( JSDContext* jsdc,
|
||
|
JSContext* cx,
|
||
|
const char* message,
|
||
|
JSErrorReport* report,
|
||
|
void* callerdata );
|
||
|
|
||
|
PR_EXTERN(void)
|
||
|
ssjsdScriptHookProc( JSDContext* jsdc,
|
||
|
JSDScript* jsdscript,
|
||
|
JSBool creating,
|
||
|
void* callerdata );
|
||
|
|
||
|
PR_EXTERN(PRUintn)
|
||
|
ssjsdExecutionHookProc( JSDContext* jsdc,
|
||
|
JSDThreadState* jsdthreadstate,
|
||
|
PRUintn type,
|
||
|
void* callerdata );
|
||
|
|
||
|
PR_END_EXTERN_C
|
||
|
|
||
|
/**************************/
|
||
|
|
||
|
static PRMonitor* _jsd_iiop_monitor = NULL;
|
||
|
#define LOCK() \
|
||
|
PR_BEGIN_MACRO \
|
||
|
if( ! _jsd_iiop_monitor ) \
|
||
|
_jsd_iiop_monitor = PR_NewMonitor(); \
|
||
|
PR_ASSERT(_jsd_iiop_monitor); \
|
||
|
PR_EnterMonitor(_jsd_iiop_monitor); \
|
||
|
PR_END_MACRO
|
||
|
|
||
|
#define UNLOCK() \
|
||
|
PR_BEGIN_MACRO \
|
||
|
PR_ASSERT(_jsd_iiop_monitor); \
|
||
|
PR_ExitMonitor(_jsd_iiop_monitor); \
|
||
|
PR_END_MACRO
|
||
|
|
||
|
/**************************/
|
||
|
|
||
|
class TestInterface_impl : public _sk_TestInterface
|
||
|
{
|
||
|
public:
|
||
|
TestInterface_impl() : _sk_TestInterface("bogus") {}
|
||
|
virtual char* getFirstAppInList();
|
||
|
virtual void getAppNames(StringReciever_ptr sr);
|
||
|
virtual TestInterface::sequence_of_Thing * getThings();
|
||
|
virtual void callBounce(StringReciever_ptr arg0, CORBA::Long arg1);
|
||
|
};
|
||
|
|
||
|
/**************************/
|
||
|
|
||
|
class SourceTextProvider : public _sk_ISourceTextProvider
|
||
|
{
|
||
|
public:
|
||
|
SourceTextProvider(const char* name);
|
||
|
SourceTextProvider(const char* name, JSDContext* jsdc);
|
||
|
virtual ~SourceTextProvider();
|
||
|
|
||
|
void init(JSDContext* jsdc);
|
||
|
|
||
|
// implement ISourceTextProvider
|
||
|
virtual ISourceTextProvider::sequence_of_string * getAllPages();
|
||
|
virtual void refreshAllPages();
|
||
|
virtual CORBA::Boolean hasPage(const char * arg0);
|
||
|
virtual CORBA::Boolean loadPage(const char * arg0);
|
||
|
virtual void refreshPage(const char * arg0);
|
||
|
virtual char * getPageText(const char * arg0);
|
||
|
virtual CORBA::Long getPageStatus(const char * arg0);
|
||
|
virtual CORBA::Long getPageAlterCount(const char * arg0);
|
||
|
|
||
|
protected:
|
||
|
void _forcePreLoad();
|
||
|
void _lock() {JSD_LockSourceTextSubsystem(_jsdc);}
|
||
|
void _unlock() {JSD_UnlockSourceTextSubsystem(_jsdc);}
|
||
|
|
||
|
private:
|
||
|
JSDContext* _jsdc;
|
||
|
};
|
||
|
|
||
|
/**************************/
|
||
|
|
||
|
class HookKey
|
||
|
{
|
||
|
public:
|
||
|
HookKey(const IJSPC& pc);
|
||
|
IJSPC pc;
|
||
|
PRHashNumber hash_num;
|
||
|
};
|
||
|
|
||
|
/**************************/
|
||
|
|
||
|
// forward declaration...
|
||
|
class StepHandler;
|
||
|
|
||
|
/**************************/
|
||
|
|
||
|
class DebugController : public _sk_IDebugController
|
||
|
{
|
||
|
public:
|
||
|
DebugController(const char* name);
|
||
|
DebugController(const char* name, JSDContext* jsdc);
|
||
|
virtual ~DebugController();
|
||
|
|
||
|
void init(JSDContext* jsdc);
|
||
|
|
||
|
// implement IDebugController
|
||
|
virtual CORBA::Long getMajorVersion() {return JSD_GetMajorVersion();}
|
||
|
virtual CORBA::Long getMinorVersion() {return JSD_GetMinorVersion();}
|
||
|
|
||
|
virtual IJSErrorReporter_ptr setErrorReporter(IJSErrorReporter_ptr arg0);
|
||
|
virtual IJSErrorReporter_ptr getErrorReporter();
|
||
|
|
||
|
virtual IScriptHook_ptr setScriptHook(IScriptHook_ptr arg0);
|
||
|
virtual IScriptHook_ptr getScriptHook();
|
||
|
|
||
|
virtual IJSPC * getClosestPC(const IScript& arg0, CORBA::Long arg1);
|
||
|
|
||
|
virtual IJSSourceLocation * getSourceLocation(const IJSPC& arg0);
|
||
|
|
||
|
virtual IJSExecutionHook_ptr setInterruptHook(IJSExecutionHook_ptr arg0);
|
||
|
virtual IJSExecutionHook_ptr getInterruptHook();
|
||
|
|
||
|
virtual IJSExecutionHook_ptr setDebugBreakHook(IJSExecutionHook_ptr arg0);
|
||
|
virtual IJSExecutionHook_ptr getDebugBreakHook();
|
||
|
|
||
|
virtual IJSExecutionHook_ptr setInstructionHook(IJSExecutionHook_ptr arg0,
|
||
|
const IJSPC& arg1);
|
||
|
virtual IJSExecutionHook_ptr getInstructionHook(const IJSPC& arg0);
|
||
|
|
||
|
virtual CORBA::Boolean isRunningHook(CORBA::Long arg0);
|
||
|
virtual CORBA::Boolean isWaitingForResume(CORBA::Long arg0);
|
||
|
virtual void setThreadContinueState(CORBA::Long arg0, CORBA::Long arg1);
|
||
|
virtual void setThreadReturnValue(CORBA::Long arg0, const char * arg1);
|
||
|
|
||
|
virtual void sendInterrupt();
|
||
|
|
||
|
virtual void sendInterruptStepInto(CORBA::Long arg0);
|
||
|
virtual void sendInterruptStepOver(CORBA::Long arg0);
|
||
|
virtual void sendInterruptStepOut(CORBA::Long arg0);
|
||
|
virtual void reinstateStepper(CORBA::Long arg0);
|
||
|
|
||
|
virtual IExecResult * executeScriptInStackFrame(CORBA::Long arg0,
|
||
|
const IJSStackFrameInfo& arg1,
|
||
|
const char * arg2,
|
||
|
const char * arg3,
|
||
|
CORBA::Long arg4);
|
||
|
virtual void leaveThreadSuspended(CORBA::Long arg0);
|
||
|
virtual void resumeThread(CORBA::Long arg0);
|
||
|
|
||
|
|
||
|
virtual void iterateScripts(IScriptHook_ptr arg0);
|
||
|
|
||
|
public:
|
||
|
// these are called by extern 'C' callbacks (otherwise would be private)
|
||
|
int _callErrorReporter(const char* msg, JSErrorReport* report);
|
||
|
void _callScriptHook(JSDScript* jsdscript, JSBool creating);
|
||
|
int _callExecutionHook(JSDThreadState* jsdthreadstate, PRUintn type);
|
||
|
|
||
|
private:
|
||
|
void _initScript(IScript& s, CORBA::Long jsdscript, PRBool alive);
|
||
|
|
||
|
inline void _initPC(IJSPC& pc, const IScript& script, CORBA::Long offset)
|
||
|
{
|
||
|
pc.script = script;
|
||
|
pc.offset = offset;
|
||
|
}
|
||
|
inline void _initSourceLocation(IJSSourceLocation& sl,
|
||
|
const IJSPC& pc,
|
||
|
CORBA::Long line)
|
||
|
{
|
||
|
sl.pc = pc;
|
||
|
sl.line = line;
|
||
|
}
|
||
|
inline void _initStackFrameInfo(IJSStackFrameInfo& sf, const IJSPC& pc,
|
||
|
CORBA::Long jsdframe)
|
||
|
{
|
||
|
sf.pc = pc;
|
||
|
sf.jsdframe = jsdframe;
|
||
|
}
|
||
|
|
||
|
IJSExecutionHook_ptr _findInstructionHook(const IJSPC& pc);
|
||
|
PRBool _addInstructionHook(const IJSPC& pc,
|
||
|
IJSExecutionHook_ptr hook);
|
||
|
IJSExecutionHook_ptr _removeInstructionHook(const IJSPC& pc);
|
||
|
PRBool _initInstructionHookTable();
|
||
|
void _freeInstructionHookTable();
|
||
|
|
||
|
inline void _lock() {PR_EnterMonitor(_monitor);}
|
||
|
inline void _unlock() {PR_ExitMonitor(_monitor);}
|
||
|
|
||
|
JSDScript* _findScriptAt(const char* filename, int lineno);
|
||
|
void _clearStepHandler(PRBool canReinstate);
|
||
|
void _reinstateStepHandler();
|
||
|
|
||
|
// data...
|
||
|
|
||
|
private:
|
||
|
JSDContext* _jsdc;
|
||
|
IJSErrorReporter_ptr _errorReporter;
|
||
|
IScriptHook_ptr _scriptHook;
|
||
|
IJSExecutionHook_ptr _interruptHook;
|
||
|
IJSExecutionHook_ptr _debugBreakHook;
|
||
|
PRHashTable * _instructionHookTable;
|
||
|
PRMonitor* _monitor;
|
||
|
StepHandler* _stepHandler;
|
||
|
StepHandler* _oldStepHandler;
|
||
|
|
||
|
};
|
||
|
|
||
|
/**************************/
|
||
|
|
||
|
class ThreadState
|
||
|
{
|
||
|
private:
|
||
|
// only our static can construct one of us
|
||
|
ThreadState(); // not implemented
|
||
|
ThreadState(PRThread* currentThread);
|
||
|
~ThreadState();
|
||
|
public:
|
||
|
static ThreadState* getThreadStateForCurrentThread(void);
|
||
|
static ThreadState* findThreadStateForCurrentThread(void);
|
||
|
|
||
|
void callHook(DebugController* controller,
|
||
|
IJSExecutionHook* hook,
|
||
|
sequence_of_IJSStackFrameInfo& stack,
|
||
|
IJSPC* pcTop,
|
||
|
JSDThreadState* jsdthreadstate);
|
||
|
|
||
|
PRBool isRunningHook(void);
|
||
|
PRBool isWaitingForResume(void);
|
||
|
void leaveThreadSuspended(void);
|
||
|
void resumeThread();
|
||
|
|
||
|
inline PREventQueue* getEventQueue(void) {return _mainEventQueue;}
|
||
|
inline IJSThreadState& getJSThreadState(void) {return _jsts;}
|
||
|
|
||
|
// these (especially set) should be called on this thread's ThreadState
|
||
|
PRBool getRunningEval(void) {return _isRunningEval;}
|
||
|
void setRunningEval(PRBool b) {_isRunningEval = b;}
|
||
|
IExecResult& getExecResult(void) {return _execResult;}
|
||
|
|
||
|
|
||
|
public:
|
||
|
// these should be called by the appropriate Events only...
|
||
|
void eventSaysSuicide(void);
|
||
|
void eventSaysResume(void);
|
||
|
void eventSaysCallHook(void);
|
||
|
|
||
|
private:
|
||
|
// private statics...
|
||
|
inline static void _lock()
|
||
|
{
|
||
|
if(! _monitor)
|
||
|
_monitor = PR_NewMonitor();
|
||
|
PR_ASSERT(_monitor);
|
||
|
PR_EnterMonitor(_monitor);
|
||
|
}
|
||
|
inline static void _unlock()
|
||
|
{
|
||
|
PR_ASSERT(_monitor);
|
||
|
PR_ExitMonitor(_monitor);
|
||
|
}
|
||
|
inline static void _wait()
|
||
|
{
|
||
|
PR_ASSERT(_monitor);
|
||
|
PR_Wait(_monitor, PR_INTERVAL_NO_TIMEOUT);
|
||
|
}
|
||
|
inline static void _notify()
|
||
|
{
|
||
|
PR_ASSERT(_monitor);
|
||
|
PR_Notify(_monitor);
|
||
|
}
|
||
|
|
||
|
static void _otherThreadProc(void* arg);
|
||
|
static void _callbackForPRThreadDeath(void *arg);
|
||
|
|
||
|
static PRHashNumber _hash_root(const void *key);
|
||
|
static PRBool _prepThread2IndexTable();
|
||
|
static PRBool _findIndexForThread(PRThread* thread, PRUintn* index);
|
||
|
static PRBool _setIndexForThread(PRThread* thread, PRUintn index);
|
||
|
static PRBool _removeIndexForThread(PRThread* thread);
|
||
|
|
||
|
static ThreadState* _findThreadState(PRThread* currentThread);
|
||
|
static ThreadState* _createThreadState(PRThread* currentThread);
|
||
|
|
||
|
private:
|
||
|
// private instance methods
|
||
|
|
||
|
PRBool _initEventQueues(void);
|
||
|
|
||
|
private:
|
||
|
static PRMonitor* _monitor;
|
||
|
static PRHashTable* _thread2IndexTable;
|
||
|
|
||
|
PRThread* _currentThread;
|
||
|
DebugController* _controller;
|
||
|
IJSExecutionHook* _hook;
|
||
|
IJSThreadState _jsts;
|
||
|
IJSPC* _pcTop;
|
||
|
|
||
|
PRBool _isRunningEval;
|
||
|
IExecResult _execResult;
|
||
|
|
||
|
PRBool _isRunningHook;
|
||
|
PRBool _actualCallToHookIsHappening;
|
||
|
PRBool _leaveSuspended;
|
||
|
|
||
|
PREventQueue* _mainEventQueue;
|
||
|
PREventQueue* _hookCallerEventQueue;
|
||
|
|
||
|
PRBool _hookCallerEventQueueCreateFinished;
|
||
|
PRBool _suicideMessageRecieved;
|
||
|
};
|
||
|
|
||
|
/***************************************************************************/
|
||
|
// These Event classes all MUST cast to PREvent. So....
|
||
|
// 1) no virtual functions
|
||
|
// 2) PREvent must be first in struct OR class must inherit from PREvent
|
||
|
|
||
|
class JSDResumeEvent : public PREvent
|
||
|
{
|
||
|
public:
|
||
|
JSDResumeEvent(ThreadState* ts);
|
||
|
void post(PREventQueue* queue);
|
||
|
|
||
|
static void* static_handle(PREvent* e);
|
||
|
static void static_destroy(PREvent* e);
|
||
|
private:
|
||
|
ThreadState* _ts;
|
||
|
};
|
||
|
|
||
|
/**************************/
|
||
|
|
||
|
class JSDSuicideEvent : public PREvent
|
||
|
{
|
||
|
public:
|
||
|
JSDSuicideEvent(ThreadState* ts);
|
||
|
void post(PREventQueue* queue);
|
||
|
|
||
|
static void* static_handle(PREvent* e);
|
||
|
static void static_destroy(PREvent* e);
|
||
|
private:
|
||
|
ThreadState* _ts;
|
||
|
};
|
||
|
|
||
|
/**************************/
|
||
|
|
||
|
class JSDCallHookEvent : public PREvent
|
||
|
{
|
||
|
public:
|
||
|
JSDCallHookEvent(ThreadState* ts);
|
||
|
void post(PREventQueue* queue);
|
||
|
|
||
|
static void* static_handle(PREvent* e);
|
||
|
static void static_destroy(PREvent* e);
|
||
|
private:
|
||
|
ThreadState* _ts;
|
||
|
};
|
||
|
|
||
|
/**************************/
|
||
|
|
||
|
class JSDExecuteEvent : public PREvent
|
||
|
{
|
||
|
public:
|
||
|
JSDExecuteEvent(JSDContext* jsdc,
|
||
|
ThreadState* ts,
|
||
|
const IJSStackFrameInfo& frame,
|
||
|
const char* text,
|
||
|
const char* filename,
|
||
|
int lineno);
|
||
|
~JSDExecuteEvent();
|
||
|
|
||
|
IExecResult* postSynchronous(PREventQueue* queue);
|
||
|
|
||
|
static void* static_handle(PREvent* e);
|
||
|
static void static_destroy(PREvent* e);
|
||
|
|
||
|
private:
|
||
|
void* _handle();
|
||
|
|
||
|
private:
|
||
|
JSDContext* _jsdc;
|
||
|
ThreadState* _ts;
|
||
|
IJSStackFrameInfo _frame;
|
||
|
char* _text;
|
||
|
char* _filename;
|
||
|
int _lineno;
|
||
|
};
|
||
|
|
||
|
/**************************/
|
||
|
// support for stepping...
|
||
|
|
||
|
class CallChain
|
||
|
{
|
||
|
public:
|
||
|
enum CompareResult
|
||
|
{
|
||
|
EQUAL,
|
||
|
CALLER,
|
||
|
CALLEE,
|
||
|
DISJOINT
|
||
|
};
|
||
|
|
||
|
CallChain(); // not implemented
|
||
|
CallChain(JSDContext* jsdc, JSDThreadState* jsdthreadstate);
|
||
|
~CallChain();
|
||
|
|
||
|
CallChain::CompareResult compare(const CallChain& other);
|
||
|
|
||
|
// data...
|
||
|
private:
|
||
|
JSDContext* _jsdc;
|
||
|
JSDScript** _chain;
|
||
|
int _count;
|
||
|
};
|
||
|
|
||
|
/************************/
|
||
|
|
||
|
class StepHandler
|
||
|
{
|
||
|
public:
|
||
|
|
||
|
enum StepResult
|
||
|
{
|
||
|
STOP,
|
||
|
CONTINUE_SEND_INTERRUPT,
|
||
|
CONTINUE_DONE
|
||
|
};
|
||
|
|
||
|
StepHandler(); // not implemented
|
||
|
StepHandler(JSDContext* jsdc, ThreadState* state, PRBool buildCallChain);
|
||
|
~StepHandler();
|
||
|
|
||
|
virtual StepHandler::StepResult step(JSDThreadState* jsdthreadstate) = 0;
|
||
|
|
||
|
protected:
|
||
|
JSDStackFrameInfo* _topFrame(JSDThreadState* jsdthreadstate)
|
||
|
{
|
||
|
return JSD_GetStackFrame(_jsdc, jsdthreadstate);
|
||
|
}
|
||
|
|
||
|
JSDScript* _topScript(JSDThreadState* jsdthreadstate, JSDStackFrameInfo* jsdframe)
|
||
|
{
|
||
|
return JSD_GetScriptForStackFrame(_jsdc, jsdthreadstate, jsdframe);
|
||
|
}
|
||
|
|
||
|
prword_t _topPC(JSDThreadState* jsdthreadstate, JSDStackFrameInfo* jsdframe)
|
||
|
{
|
||
|
return JSD_GetPCForStackFrame(_jsdc, jsdthreadstate, jsdframe);
|
||
|
}
|
||
|
|
||
|
int _topLine(JSDScript* jsdscript, prword_t pc)
|
||
|
{
|
||
|
return JSD_GetClosestLine(_jsdc, jsdscript, pc);
|
||
|
}
|
||
|
|
||
|
|
||
|
// data...
|
||
|
protected:
|
||
|
JSDContext* _jsdc;
|
||
|
JSDScript* topScriptInitial;
|
||
|
prword_t topPCInitial;
|
||
|
int topLineInitial;
|
||
|
CallChain* _callChain;
|
||
|
};
|
||
|
|
||
|
/************************/
|
||
|
|
||
|
class StepInto : public StepHandler
|
||
|
{
|
||
|
public:
|
||
|
StepInto(JSDContext* jsdc, ThreadState* state);
|
||
|
|
||
|
StepHandler::StepResult step(JSDThreadState* jsdthreadstate);
|
||
|
|
||
|
private:
|
||
|
};
|
||
|
|
||
|
/************************/
|
||
|
|
||
|
class StepOver : public StepHandler
|
||
|
{
|
||
|
public:
|
||
|
StepOver(JSDContext* jsdc, ThreadState* state);
|
||
|
|
||
|
StepHandler::StepResult step(JSDThreadState* jsdthreadstate);
|
||
|
|
||
|
private:
|
||
|
};
|
||
|
|
||
|
/************************/
|
||
|
|
||
|
class StepOut : public StepHandler
|
||
|
{
|
||
|
public:
|
||
|
StepOut(JSDContext* jsdc, ThreadState* state);
|
||
|
|
||
|
StepHandler::StepResult step(JSDThreadState* jsdthreadstate);
|
||
|
|
||
|
private:
|
||
|
};
|
||
|
|
||
|
/***************************************************************************/
|
||
|
/***************************************************************************/
|
||
|
|
||
|
PR_IMPLEMENT(PRUintn)
|
||
|
ssjsdErrorReporter( JSDContext* jsdc,
|
||
|
JSContext* cx,
|
||
|
const char* message,
|
||
|
JSErrorReport* report,
|
||
|
void* callerdata )
|
||
|
{
|
||
|
DebugController* controller = (DebugController*) callerdata;
|
||
|
return (PRUintn) controller->_callErrorReporter(message, report);
|
||
|
}
|
||
|
|
||
|
PR_IMPLEMENT(void)
|
||
|
ssjsdScriptHookProc( JSDContext* jsdc,
|
||
|
JSDScript* jsdscript,
|
||
|
JSBool creating,
|
||
|
void* callerdata )
|
||
|
{
|
||
|
DebugController* controller = (DebugController*) callerdata;
|
||
|
controller->_callScriptHook(jsdscript, creating);
|
||
|
}
|
||
|
|
||
|
PR_IMPLEMENT(PRUintn)
|
||
|
ssjsdExecutionHookProc( JSDContext* jsdc,
|
||
|
JSDThreadState* jsdthreadstate,
|
||
|
PRUintn type,
|
||
|
void* callerdata )
|
||
|
{
|
||
|
DebugController* controller = (DebugController*) callerdata;
|
||
|
return (PRUintn) controller->_callExecutionHook(jsdthreadstate, type);
|
||
|
}
|
||
|
|
||
|
|
||
|
DebugController::DebugController(const char* name, JSDContext* jsdc)
|
||
|
: _sk_IDebugController(name),
|
||
|
_jsdc(NULL),
|
||
|
_errorReporter(NULL),
|
||
|
_scriptHook(NULL),
|
||
|
_interruptHook(NULL),
|
||
|
_debugBreakHook(NULL),
|
||
|
_instructionHookTable(NULL),
|
||
|
_stepHandler(NULL),
|
||
|
_oldStepHandler(NULL)
|
||
|
{
|
||
|
init(jsdc);
|
||
|
}
|
||
|
|
||
|
DebugController::DebugController(const char* name)
|
||
|
: _sk_IDebugController(name),
|
||
|
_jsdc(NULL),
|
||
|
_errorReporter(NULL),
|
||
|
_scriptHook(NULL),
|
||
|
_interruptHook(NULL),
|
||
|
_debugBreakHook(NULL),
|
||
|
_instructionHookTable(NULL),
|
||
|
_stepHandler(NULL),
|
||
|
_oldStepHandler(NULL)
|
||
|
{
|
||
|
// do nothing
|
||
|
}
|
||
|
|
||
|
void
|
||
|
DebugController::init(JSDContext* jsdc)
|
||
|
{
|
||
|
_jsdc = jsdc;
|
||
|
_monitor = PR_NewMonitor();
|
||
|
JSD_SetErrorReporter(_jsdc, ssjsdErrorReporter, this);
|
||
|
JSD_SetScriptHook(_jsdc, ssjsdScriptHookProc, this);
|
||
|
JSD_SetDebugBreakHook(_jsdc, ssjsdExecutionHookProc, this );
|
||
|
_initInstructionHookTable();
|
||
|
}
|
||
|
|
||
|
DebugController::~DebugController()
|
||
|
{
|
||
|
if( _jsdc )
|
||
|
{
|
||
|
JSD_SetErrorReporter(_jsdc, NULL, NULL);
|
||
|
JSD_SetScriptHook(_jsdc, NULL, NULL);
|
||
|
JSD_SetDebugBreakHook(_jsdc, NULL, NULL );
|
||
|
_freeInstructionHookTable();
|
||
|
}
|
||
|
if(_monitor)
|
||
|
PR_DestroyMonitor(_monitor);
|
||
|
}
|
||
|
|
||
|
IJSErrorReporter_ptr
|
||
|
DebugController::setErrorReporter(IJSErrorReporter_ptr arg0)
|
||
|
{
|
||
|
TRACE("DebugController::setErrorReporter called");
|
||
|
IJSErrorReporter_ptr old = _errorReporter;
|
||
|
_errorReporter = arg0;
|
||
|
|
||
|
if( _errorReporter )
|
||
|
_errorReporter->_duplicate(_errorReporter);
|
||
|
|
||
|
// no need to release the old hook, because returning it here
|
||
|
// will cause a release call.
|
||
|
return old;
|
||
|
}
|
||
|
|
||
|
IJSErrorReporter_ptr
|
||
|
DebugController::getErrorReporter()
|
||
|
{
|
||
|
TRACE("DebugController::getErrorReporter called");
|
||
|
if( _errorReporter )
|
||
|
return _errorReporter->_duplicate(_errorReporter);
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
IScriptHook_ptr
|
||
|
DebugController::setScriptHook(IScriptHook_ptr arg0)
|
||
|
{
|
||
|
TRACE("DebugController::setScriptHook called");
|
||
|
IScriptHook_ptr old = _scriptHook;
|
||
|
_scriptHook = arg0;
|
||
|
|
||
|
if( _scriptHook )
|
||
|
_scriptHook->_duplicate(_scriptHook);
|
||
|
|
||
|
// no need to release the old hook, because returning it here
|
||
|
// will cause a release call.
|
||
|
return old;
|
||
|
}
|
||
|
|
||
|
IScriptHook_ptr
|
||
|
DebugController::getScriptHook()
|
||
|
{
|
||
|
TRACE("DebugController::getScriptHook called");
|
||
|
if( _scriptHook )
|
||
|
return _scriptHook->_duplicate(_scriptHook);
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
IJSExecutionHook_ptr
|
||
|
DebugController::setInterruptHook(IJSExecutionHook_ptr arg0)
|
||
|
{
|
||
|
TRACE("DebugController::setInterruptHook called");
|
||
|
IJSExecutionHook_ptr old = _interruptHook;
|
||
|
_interruptHook = arg0;
|
||
|
|
||
|
if( _interruptHook )
|
||
|
_interruptHook->_duplicate(_interruptHook);
|
||
|
|
||
|
// no need to release the old hook, because returning it here
|
||
|
// will cause a release call.
|
||
|
return old;
|
||
|
}
|
||
|
|
||
|
IJSExecutionHook_ptr
|
||
|
DebugController::getInterruptHook()
|
||
|
{
|
||
|
TRACE("DebugController::getInterruptHook called");
|
||
|
if( _interruptHook )
|
||
|
return _interruptHook->_duplicate(_interruptHook);
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
IJSExecutionHook_ptr
|
||
|
DebugController::setDebugBreakHook(IJSExecutionHook_ptr arg0)
|
||
|
{
|
||
|
TRACE("DebugController::setDebugBreakHook called");
|
||
|
IJSExecutionHook_ptr old = _debugBreakHook;
|
||
|
_debugBreakHook = arg0;
|
||
|
|
||
|
if( _debugBreakHook )
|
||
|
_debugBreakHook->_duplicate(_debugBreakHook);
|
||
|
|
||
|
// no need to release the old hook, because returning it here
|
||
|
// will cause a release call.
|
||
|
return old;
|
||
|
}
|
||
|
|
||
|
IJSExecutionHook_ptr
|
||
|
DebugController::getDebugBreakHook()
|
||
|
{
|
||
|
TRACE("DebugController::getDebugBreakHook called");
|
||
|
if( _debugBreakHook )
|
||
|
return _debugBreakHook->_duplicate(_debugBreakHook);
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
|
||
|
IJSExecutionHook_ptr
|
||
|
DebugController::setInstructionHook(IJSExecutionHook_ptr arg0,
|
||
|
const IJSPC& arg1)
|
||
|
{
|
||
|
TRACE("DebugController::setInstructionHook called");
|
||
|
IJSExecutionHook_ptr oldHook = _findInstructionHook(arg1);
|
||
|
if( oldHook )
|
||
|
_removeInstructionHook(arg1);
|
||
|
|
||
|
if(arg0)
|
||
|
{
|
||
|
arg0->_duplicate(arg0);
|
||
|
_addInstructionHook(arg1, arg0);
|
||
|
}
|
||
|
|
||
|
// no need to release the old hook, because returning it here
|
||
|
// will cause a release call.
|
||
|
return oldHook;
|
||
|
}
|
||
|
|
||
|
IJSExecutionHook_ptr
|
||
|
DebugController::getInstructionHook(const IJSPC& arg0)
|
||
|
{
|
||
|
TRACE("DebugController::getInstructionHook() called");
|
||
|
IJSExecutionHook_ptr hook = _findInstructionHook(arg0);
|
||
|
if( hook )
|
||
|
return hook->_duplicate(hook);
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
IJSPC*
|
||
|
DebugController::getClosestPC(const IScript& arg0, CORBA::Long arg1)
|
||
|
{
|
||
|
TRACE("DebugController::getClosestPC() called");
|
||
|
PRUintn offset = JSD_GetClosestPC(_jsdc, (JSDScript*)arg0.jsdscript, arg1);
|
||
|
IJSPC* pc = new IJSPC();
|
||
|
_initPC(*pc, arg0, (CORBA::Long) offset);
|
||
|
return pc;
|
||
|
}
|
||
|
|
||
|
IJSSourceLocation *
|
||
|
DebugController::getSourceLocation(const IJSPC& arg0)
|
||
|
{
|
||
|
TRACE("DebugController::getSourceLocation() called");
|
||
|
JSDScript* jsdscript = (JSDScript*)arg0.script.jsdscript;
|
||
|
int line = JSD_GetClosestLine(_jsdc, jsdscript, (PRUintn) arg0.offset);
|
||
|
int offset = JSD_GetClosestPC(_jsdc, jsdscript, line);
|
||
|
|
||
|
IJSPC pc;
|
||
|
_initPC(pc, arg0.script, (CORBA::Long) offset);
|
||
|
|
||
|
IJSSourceLocation* loc = new IJSSourceLocation();
|
||
|
_initSourceLocation(*loc, pc, (CORBA::Long)line);
|
||
|
return loc;
|
||
|
}
|
||
|
|
||
|
void
|
||
|
DebugController::setThreadContinueState(CORBA::Long arg0, CORBA::Long arg1)
|
||
|
{
|
||
|
TRACE("DebugController::setThreadContinueState() called");
|
||
|
ThreadState* state = (ThreadState*)arg0;
|
||
|
state->getJSThreadState().continueState = arg1;
|
||
|
}
|
||
|
|
||
|
void
|
||
|
DebugController::setThreadReturnValue(CORBA::Long arg0, const char * arg1)
|
||
|
{
|
||
|
TRACE("DebugController::setThreadReturnValue() called");
|
||
|
ThreadState* state = (ThreadState*)arg0;
|
||
|
state->getJSThreadState().returnValue = arg1;
|
||
|
}
|
||
|
|
||
|
void
|
||
|
DebugController::sendInterrupt()
|
||
|
{
|
||
|
TRACE("DebugController::sendInterrupt() called");
|
||
|
_clearStepHandler(PR_FALSE);
|
||
|
JSD_SetInterruptHook(_jsdc, ssjsdExecutionHookProc, this );
|
||
|
}
|
||
|
|
||
|
void DebugController::sendInterruptStepInto(CORBA::Long arg0)
|
||
|
{
|
||
|
TRACE("DebugController::sendInterruptStepInto() called");
|
||
|
_clearStepHandler(PR_FALSE);
|
||
|
_stepHandler = new StepInto(_jsdc, (ThreadState*) arg0);
|
||
|
JSD_SetInterruptHook(_jsdc, ssjsdExecutionHookProc, this );
|
||
|
}
|
||
|
|
||
|
void DebugController::sendInterruptStepOver(CORBA::Long arg0)
|
||
|
{
|
||
|
TRACE("DebugController::sendInterruptStepOver() called");
|
||
|
_clearStepHandler(PR_FALSE);
|
||
|
_stepHandler = new StepOver(_jsdc, (ThreadState*) arg0);
|
||
|
JSD_SetInterruptHook(_jsdc, ssjsdExecutionHookProc, this );
|
||
|
}
|
||
|
|
||
|
void DebugController::sendInterruptStepOut(CORBA::Long arg0)
|
||
|
{
|
||
|
TRACE("DebugController::sendInterruptStepOut() called");
|
||
|
_clearStepHandler(PR_FALSE);
|
||
|
_stepHandler = new StepOut(_jsdc, (ThreadState*) arg0);
|
||
|
JSD_SetInterruptHook(_jsdc, ssjsdExecutionHookProc, this );
|
||
|
}
|
||
|
|
||
|
void DebugController::reinstateStepper(CORBA::Long arg0)
|
||
|
{
|
||
|
TRACE("DebugController::reinstateStepper() called");
|
||
|
_reinstateStepHandler();
|
||
|
}
|
||
|
|
||
|
IExecResult *
|
||
|
DebugController::executeScriptInStackFrame(CORBA::Long arg0,
|
||
|
const IJSStackFrameInfo& arg1,
|
||
|
const char * arg2,
|
||
|
const char * arg3,
|
||
|
CORBA::Long arg4)
|
||
|
{
|
||
|
TRACE("DebugController::executeScriptInStackFrame() called");
|
||
|
ThreadState* state = (ThreadState*) arg0;
|
||
|
|
||
|
if( ! state->isRunningHook() )
|
||
|
{
|
||
|
PR_ASSERT(0);
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
JSDExecuteEvent* e = new JSDExecuteEvent(_jsdc,state,arg1,arg2,arg3,arg4);
|
||
|
return e->postSynchronous(state->getEventQueue());
|
||
|
}
|
||
|
|
||
|
|
||
|
CORBA::Boolean
|
||
|
DebugController::isRunningHook(CORBA::Long arg0)
|
||
|
{
|
||
|
TRACE("DebugController::isRunningHook() called");
|
||
|
ThreadState* state = (ThreadState*) arg0;
|
||
|
return state->isRunningHook();
|
||
|
}
|
||
|
|
||
|
CORBA::Boolean
|
||
|
DebugController::isWaitingForResume(CORBA::Long arg0)
|
||
|
{
|
||
|
TRACE("DebugController::isWaitingForResume() called");
|
||
|
ThreadState* state = (ThreadState*) arg0;
|
||
|
return state->isWaitingForResume();
|
||
|
}
|
||
|
|
||
|
void
|
||
|
DebugController::leaveThreadSuspended(CORBA::Long arg0)
|
||
|
{
|
||
|
TRACE("DebugController::leaveThreadSuspended() called");
|
||
|
ThreadState* state = (ThreadState*) arg0;
|
||
|
state->leaveThreadSuspended();
|
||
|
}
|
||
|
|
||
|
void
|
||
|
DebugController::resumeThread(CORBA::Long arg0)
|
||
|
{
|
||
|
TRACE("DebugController::resumeThread() called");
|
||
|
ThreadState* state = (ThreadState*) arg0;
|
||
|
state->resumeThread();
|
||
|
}
|
||
|
|
||
|
void
|
||
|
DebugController::iterateScripts(IScriptHook_ptr arg0)
|
||
|
{
|
||
|
IScript script;
|
||
|
JSDScript* jsdscript;
|
||
|
JSDScript* iter = NULL;
|
||
|
|
||
|
JSD_LockScriptSubsystem(_jsdc);
|
||
|
while( NULL != (jsdscript = JSD_IterateScripts(_jsdc, &iter)) )
|
||
|
{
|
||
|
_initScript(script, (long) jsdscript, PR_TRUE);
|
||
|
try
|
||
|
{
|
||
|
TRACE("+++HOOK arg0->justLoadedScript() call hook");
|
||
|
arg0->justLoadedScript(script);
|
||
|
TRACE("---HOOK arg0->justLoadedScript() call hook");
|
||
|
}
|
||
|
catch(...)
|
||
|
{
|
||
|
// eat it...
|
||
|
}
|
||
|
}
|
||
|
JSD_UnlockScriptSubsystem(_jsdc);
|
||
|
}
|
||
|
|
||
|
|
||
|
/**************************************/
|
||
|
|
||
|
void
|
||
|
DebugController::_initScript(IScript& s, CORBA::Long jsdscript, PRBool alive)
|
||
|
{
|
||
|
s.url = JSD_GetScriptFilename(_jsdc, (JSDScript*)jsdscript);
|
||
|
s.funname = JSD_GetScriptFunctionName(_jsdc, (JSDScript*)jsdscript);
|
||
|
s.base = JSD_GetScriptBaseLineNumber(_jsdc, (JSDScript*)jsdscript);
|
||
|
s.extent = JSD_GetScriptLineExtent(_jsdc, (JSDScript*)jsdscript);
|
||
|
s.jsdscript = jsdscript;
|
||
|
|
||
|
// init the sections...
|
||
|
|
||
|
LWDBGScript* lwscript = JSDLW_GetLWScript(_jsdc, (JSDScript*)jsdscript);
|
||
|
PRUintn count = 0;
|
||
|
|
||
|
// iterate once to get the count...
|
||
|
// BUT: don't iterate if the script is being destroyed...
|
||
|
if( alive )
|
||
|
{
|
||
|
PRUintn iter = 0;
|
||
|
while( LWDBG_ScriptSectionIterator(lwscript,NULL,NULL,NULL,&iter) )
|
||
|
count++;
|
||
|
}
|
||
|
|
||
|
// if sections exist, then grab those with 'user' code
|
||
|
|
||
|
PRBool addedAtLeastOneSection = PR_FALSE;
|
||
|
if( count )
|
||
|
{
|
||
|
IScriptSection* sections = new IScriptSection[count];
|
||
|
int index = 0;
|
||
|
PRUintn base = s.base;
|
||
|
PRUintn extent;
|
||
|
PRUintn type;
|
||
|
PRUintn iter = 0;
|
||
|
while( LWDBG_ScriptSectionIterator(lwscript,&type,&extent,NULL,&iter) )
|
||
|
{
|
||
|
PRBool hasCode;
|
||
|
switch( type )
|
||
|
{
|
||
|
case LWDBG_SECTIONTYPE_OTHER:
|
||
|
hasCode = PR_FALSE;
|
||
|
break;
|
||
|
case LWDBG_SECTIONTYPE_SERVER:
|
||
|
case LWDBG_SECTIONTYPE_BACKQUOTE:
|
||
|
case LWDBG_SECTIONTYPE_EQBACKQUOTE:
|
||
|
hasCode = PR_TRUE;
|
||
|
break;
|
||
|
case LWDBG_SECTIONTYPE_URL:
|
||
|
default:
|
||
|
hasCode = PR_FALSE;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
if( hasCode )
|
||
|
{
|
||
|
sections[index].base = base;
|
||
|
sections[index].extent = extent+1; // XXX hackage...
|
||
|
index++;
|
||
|
}
|
||
|
base += extent;
|
||
|
}
|
||
|
if( index )
|
||
|
{
|
||
|
s.sections = sequence_of_IScriptSection(count,index,sections,1);
|
||
|
addedAtLeastOneSection = PR_TRUE;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
delete [] sections;
|
||
|
}
|
||
|
}
|
||
|
if( ! addedAtLeastOneSection )
|
||
|
{
|
||
|
PRUintn baseBefore = s.base;
|
||
|
if(alive)
|
||
|
JSDLW_ProcessedToRawLineNumber(_jsdc, (JSDScript*)jsdscript,
|
||
|
s.base, &baseBefore );
|
||
|
// no sections added, add the whole thing as a single section
|
||
|
IScriptSection* sections = new IScriptSection[1];
|
||
|
sections[0].base = baseBefore;
|
||
|
sections[0].extent = s.extent;
|
||
|
s.sections = sequence_of_IScriptSection(1,1,sections,1);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
int
|
||
|
DebugController::_callErrorReporter(const char* msg, JSErrorReport* report)
|
||
|
{
|
||
|
if( ! _errorReporter )
|
||
|
return JSD_ERROR_REPORTER_PASS_ALONG;
|
||
|
|
||
|
const char* filename = NULL;
|
||
|
unsigned int lineno = 0;
|
||
|
const char* linebuf = NULL;
|
||
|
int tokenOffset = 0;
|
||
|
int retval = JSD_ERROR_REPORTER_PASS_ALONG;
|
||
|
|
||
|
if( report )
|
||
|
{
|
||
|
filename = report->filename;
|
||
|
linebuf = report->linebuf;
|
||
|
lineno = report->lineno;
|
||
|
if( report->linebuf && report->tokenptr )
|
||
|
tokenOffset = report->tokenptr - report->linebuf;
|
||
|
|
||
|
// convert linenumber to Raw
|
||
|
JSDScript* jsdscript = _findScriptAt(filename, lineno);
|
||
|
if( jsdscript )
|
||
|
JSDLW_ProcessedToRawLineNumber(_jsdc, jsdscript, lineno, &lineno);
|
||
|
}
|
||
|
|
||
|
|
||
|
// if we are doing an eval on this thread, then gather the info from the
|
||
|
// report and then tell JSD to ignore it.
|
||
|
|
||
|
ThreadState* ts = ThreadState::findThreadStateForCurrentThread();
|
||
|
if( ts && ts->getRunningEval() )
|
||
|
{
|
||
|
IExecResult& result = ts->getExecResult();
|
||
|
|
||
|
result.errorOccured = 1;
|
||
|
result.errorMessage = (const char*) msg;
|
||
|
result.errorFilename = (const char*) filename;
|
||
|
result.errorLineNumber = lineno;
|
||
|
result.errorLineBuffer = (const char*) linebuf;
|
||
|
result.errorTokenOffset = tokenOffset;
|
||
|
|
||
|
return JSD_ERROR_REPORTER_RETURN;
|
||
|
}
|
||
|
// else....
|
||
|
|
||
|
try
|
||
|
{
|
||
|
TRACE("+++HOOK _errorReporter->reportError() call hook");
|
||
|
retval = _errorReporter->reportError(msg,filename,lineno,linebuf,tokenOffset);
|
||
|
TRACE("---HOOK _errorReporter->reportError() call hook");
|
||
|
}
|
||
|
catch(...)
|
||
|
{
|
||
|
// eat it...
|
||
|
}
|
||
|
return retval;
|
||
|
}
|
||
|
|
||
|
void
|
||
|
DebugController::_callScriptHook(JSDScript* jsdscript, JSBool creating)
|
||
|
{
|
||
|
if( ! _scriptHook )
|
||
|
return;
|
||
|
|
||
|
// The debugger does not need to know about scripts created and destroyed
|
||
|
// while doing eval on that thread (which is what must be happening if
|
||
|
// we have the thread stopped, yet have reaced this hook)
|
||
|
//
|
||
|
// This seemed to be causing deadlocks in calling back to the debugger
|
||
|
// under some circumstances
|
||
|
|
||
|
ThreadState* ts = ThreadState::findThreadStateForCurrentThread();
|
||
|
if( ts && ts->isRunningHook() )
|
||
|
return;
|
||
|
|
||
|
IScript script;
|
||
|
_initScript(script, (long) jsdscript, creating);
|
||
|
|
||
|
try
|
||
|
{
|
||
|
if( creating )
|
||
|
{
|
||
|
TRACE("+++HOOK _scriptHook->justLoadedScript() call hook");
|
||
|
_scriptHook->justLoadedScript(script);
|
||
|
TRACE("---HOOK _scriptHook->justLoadedScript() call hook");
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
TRACE("+++HOOK _scriptHook->aboutToUnloadScript() call hook");
|
||
|
_scriptHook->aboutToUnloadScript(script);
|
||
|
TRACE("---HOOK _scriptHook->aboutToUnloadScript() call hook");
|
||
|
}
|
||
|
}
|
||
|
catch(...)
|
||
|
{
|
||
|
// eat it...
|
||
|
}
|
||
|
}
|
||
|
|
||
|
int
|
||
|
DebugController::_callExecutionHook(JSDThreadState* jsdthreadstate, PRUintn type)
|
||
|
{
|
||
|
IJSExecutionHook_ptr hook = NULL;
|
||
|
|
||
|
JSDStackFrameInfo* jsdframeTop = JSD_GetStackFrame(_jsdc, jsdthreadstate);
|
||
|
IScript scriptTop;
|
||
|
_initScript(scriptTop, (CORBA::Long)
|
||
|
JSD_GetScriptForStackFrame(_jsdc, jsdthreadstate,jsdframeTop),
|
||
|
PR_TRUE );
|
||
|
IJSPC pcTop;
|
||
|
_initPC(pcTop, scriptTop, (CORBA::Long)
|
||
|
JSD_GetPCForStackFrame(_jsdc, jsdthreadstate, jsdframeTop));
|
||
|
|
||
|
switch(type)
|
||
|
{
|
||
|
case JSD_HOOK_INTERRUPTED:
|
||
|
hook = _interruptHook;
|
||
|
if( _stepHandler )
|
||
|
{
|
||
|
switch( _stepHandler->step(jsdthreadstate) )
|
||
|
{
|
||
|
case StepHandler::STOP:
|
||
|
_clearStepHandler(PR_FALSE);
|
||
|
break;
|
||
|
case StepHandler::CONTINUE_SEND_INTERRUPT:
|
||
|
hook = NULL;
|
||
|
break;
|
||
|
case StepHandler::CONTINUE_DONE:
|
||
|
_clearStepHandler(PR_FALSE);
|
||
|
hook = NULL;
|
||
|
break;
|
||
|
default:
|
||
|
PR_ASSERT(0);
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
JSD_ClearInterruptHook(_jsdc);
|
||
|
}
|
||
|
break;
|
||
|
case JSD_HOOK_BREAKPOINT:
|
||
|
_clearStepHandler(PR_TRUE);
|
||
|
JSD_ClearInterruptHook(_jsdc);
|
||
|
hook = _findInstructionHook(pcTop);
|
||
|
break;
|
||
|
case JSD_HOOK_DEBUG_REQUESTED:
|
||
|
_clearStepHandler(PR_FALSE);
|
||
|
JSD_ClearInterruptHook(_jsdc);
|
||
|
hook = _debugBreakHook;
|
||
|
break;
|
||
|
default:
|
||
|
// invalid type!
|
||
|
PR_ASSERT(0);
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
if( ! hook )
|
||
|
return JSD_HOOK_RETURN_CONTINUE;
|
||
|
|
||
|
// generate the stack (reverse the order)
|
||
|
int count = JSD_GetCountOfStackFrames(_jsdc, jsdthreadstate);
|
||
|
IJSStackFrameInfo* frames = new IJSStackFrameInfo[count];
|
||
|
|
||
|
JSDStackFrameInfo* jsdframe = JSD_GetStackFrame(_jsdc, jsdthreadstate);
|
||
|
int i = count;
|
||
|
while( jsdframe )
|
||
|
{
|
||
|
IScript script;
|
||
|
_initScript(script, (CORBA::Long)
|
||
|
JSD_GetScriptForStackFrame(_jsdc, jsdthreadstate,jsdframe),
|
||
|
PR_TRUE);
|
||
|
IJSPC pc;
|
||
|
_initPC(pc, script, (CORBA::Long)
|
||
|
JSD_GetPCForStackFrame(_jsdc, jsdthreadstate, jsdframe));
|
||
|
|
||
|
_initStackFrameInfo(frames[--i], pc, (CORBA::Long) jsdframe);
|
||
|
|
||
|
jsdframe = JSD_GetCallingStackFrame(_jsdc, jsdthreadstate, jsdframe);
|
||
|
}
|
||
|
PR_ASSERT(0==i);
|
||
|
sequence_of_IJSStackFrameInfo stack(count,count,frames,1);
|
||
|
|
||
|
ThreadState* state = ThreadState::getThreadStateForCurrentThread();
|
||
|
if( ! state )
|
||
|
{
|
||
|
PR_ASSERT(0);
|
||
|
return JSD_HOOK_RETURN_CONTINUE;
|
||
|
}
|
||
|
|
||
|
if( state->isRunningHook() )
|
||
|
{
|
||
|
TRACE("hit a hook when this thread was already stopped. continuing..." );
|
||
|
return JSD_HOOK_RETURN_CONTINUE;
|
||
|
}
|
||
|
|
||
|
state->callHook(this, hook, stack, &pcTop, jsdthreadstate);
|
||
|
|
||
|
return state->getJSThreadState().continueState;
|
||
|
}
|
||
|
|
||
|
JSDScript*
|
||
|
DebugController::_findScriptAt(const char* filename, int lineno)
|
||
|
{
|
||
|
JSDScript* script;
|
||
|
JSDScript* best_script = NULL;
|
||
|
JSDScript* iter = NULL;
|
||
|
|
||
|
while( NULL != (script = JSD_IterateScripts(_jsdc, &iter)) )
|
||
|
{
|
||
|
if( 0 == strcmp( filename, JSD_GetScriptFilename(_jsdc,script) ) )
|
||
|
{
|
||
|
int first = JSD_GetScriptBaseLineNumber(_jsdc, script);
|
||
|
int last = first + JSD_GetScriptLineExtent(_jsdc, script) - 1;
|
||
|
|
||
|
if( lineno >= first && lineno <= last )
|
||
|
{
|
||
|
// save this as best
|
||
|
best_script = script;
|
||
|
// if this is a function, then we're done.
|
||
|
if( NULL != JSD_GetScriptFunctionName(_jsdc, script) )
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
return best_script;
|
||
|
}
|
||
|
|
||
|
/**************************/
|
||
|
|
||
|
PR_STATIC_CALLBACK(PRHashNumber)
|
||
|
_hash_root(const void *key)
|
||
|
{
|
||
|
return ((HookKey*)key)->hash_num;
|
||
|
}
|
||
|
PR_STATIC_CALLBACK(PRIntn)
|
||
|
_hash_key_comparer(const void *v1, const void *v2)
|
||
|
{
|
||
|
if( ( ((HookKey*)v1)->pc.script.jsdscript ==
|
||
|
((HookKey*)v2)->pc.script.jsdscript ) &&
|
||
|
( ((HookKey*)v1)->pc.offset ==
|
||
|
((HookKey*)v2)->pc.offset ) )
|
||
|
return 1;
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
PR_STATIC_CALLBACK(PRIntn)
|
||
|
_hash_entry_zapper(PRHashEntry *he, PRIntn i, void *arg)
|
||
|
{
|
||
|
// XXX clear the JSD_HOOK ???
|
||
|
delete ((HookKey*)he->key);
|
||
|
((IJSExecutionHook_ptr)he->value)->_release();
|
||
|
he->value = NULL;
|
||
|
he->key = NULL;
|
||
|
return HT_ENUMERATE_NEXT;
|
||
|
}
|
||
|
|
||
|
IJSExecutionHook_ptr
|
||
|
DebugController::_findInstructionHook(const IJSPC& pc)
|
||
|
{
|
||
|
IJSExecutionHook_ptr hook = NULL;
|
||
|
if( _instructionHookTable )
|
||
|
{
|
||
|
_lock();
|
||
|
HookKey key(pc);
|
||
|
hook = (IJSExecutionHook_ptr)
|
||
|
PR_HashTableLookup(_instructionHookTable, &key);
|
||
|
_unlock();
|
||
|
}
|
||
|
return hook;
|
||
|
}
|
||
|
|
||
|
PRBool
|
||
|
DebugController::_addInstructionHook(const IJSPC& pc,
|
||
|
IJSExecutionHook_ptr hook)
|
||
|
{
|
||
|
PRBool retval = PR_FALSE;
|
||
|
if( _instructionHookTable )
|
||
|
{
|
||
|
HookKey* key = new HookKey(pc);
|
||
|
_lock();
|
||
|
retval = PR_HashTableAdd(_instructionHookTable,key,hook)?PR_TRUE:PR_FALSE;
|
||
|
_unlock();
|
||
|
JSD_SetExecutionHook(_jsdc, (JSDScript*)pc.script.jsdscript, pc.offset,
|
||
|
ssjsdExecutionHookProc, this);
|
||
|
}
|
||
|
return retval;
|
||
|
}
|
||
|
|
||
|
IJSExecutionHook_ptr
|
||
|
DebugController::_removeInstructionHook(const IJSPC& pc)
|
||
|
{
|
||
|
JSD_ClearExecutionHook(_jsdc, (JSDScript*)pc.script.jsdscript, pc.offset );
|
||
|
|
||
|
IJSExecutionHook_ptr hook = NULL;
|
||
|
if( _instructionHookTable )
|
||
|
{
|
||
|
HookKey key(pc);
|
||
|
_lock();
|
||
|
PRHashEntry** entry = PR_HashTableRawLookup(_instructionHookTable,
|
||
|
key.hash_num, &key );
|
||
|
if( entry && *entry )
|
||
|
{
|
||
|
IJSExecutionHook_ptr hook = (IJSExecutionHook_ptr)(*entry)->value;
|
||
|
delete ((HookKey*)(*entry)->key);
|
||
|
PR_HashTableRemove(_instructionHookTable, &key );
|
||
|
}
|
||
|
_unlock();
|
||
|
}
|
||
|
return hook;
|
||
|
}
|
||
|
|
||
|
PRBool
|
||
|
DebugController::_initInstructionHookTable()
|
||
|
{
|
||
|
_instructionHookTable = PR_NewHashTable( 256, _hash_root,
|
||
|
_hash_key_comparer,
|
||
|
PR_CompareValues,
|
||
|
NULL, NULL);
|
||
|
return _instructionHookTable ? PR_TRUE : PR_FALSE;
|
||
|
}
|
||
|
|
||
|
void
|
||
|
DebugController::_freeInstructionHookTable()
|
||
|
{
|
||
|
if( _instructionHookTable )
|
||
|
{
|
||
|
PR_HashTableEnumerateEntries(_instructionHookTable, _hash_entry_zapper, NULL);
|
||
|
PR_HashTableDestroy(_instructionHookTable);
|
||
|
}
|
||
|
_instructionHookTable = NULL;
|
||
|
}
|
||
|
|
||
|
void
|
||
|
DebugController::_clearStepHandler(PRBool canReinstate)
|
||
|
{
|
||
|
if( canReinstate )
|
||
|
{
|
||
|
if( _stepHandler )
|
||
|
{
|
||
|
if( _oldStepHandler )
|
||
|
delete _oldStepHandler;
|
||
|
_oldStepHandler = _stepHandler;
|
||
|
_stepHandler = NULL;
|
||
|
JSD_ClearInterruptHook(_jsdc);
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
if( _stepHandler )
|
||
|
{
|
||
|
delete _stepHandler;
|
||
|
_stepHandler = NULL;
|
||
|
JSD_ClearInterruptHook(_jsdc);
|
||
|
}
|
||
|
if( _oldStepHandler )
|
||
|
{
|
||
|
delete _oldStepHandler;
|
||
|
_oldStepHandler = NULL;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void
|
||
|
DebugController::_reinstateStepHandler()
|
||
|
{
|
||
|
if( _oldStepHandler && ! _stepHandler )
|
||
|
{
|
||
|
_stepHandler = _oldStepHandler;
|
||
|
_oldStepHandler = NULL;
|
||
|
JSD_SetInterruptHook(_jsdc, ssjsdExecutionHookProc, this );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**************************/
|
||
|
|
||
|
HookKey::HookKey(const IJSPC& pc)
|
||
|
{
|
||
|
this->pc = pc;
|
||
|
this->hash_num = ((long)pc.script.jsdscript) + (pc.offset*7);
|
||
|
}
|
||
|
|
||
|
/***************************************************************************/
|
||
|
/***************************************************************************/
|
||
|
|
||
|
PRMonitor* ThreadState::_monitor = NULL;
|
||
|
PRHashTable* ThreadState::_thread2IndexTable = NULL;
|
||
|
|
||
|
ThreadState::ThreadState(PRThread* currentThread)
|
||
|
: _currentThread(currentThread),
|
||
|
_controller(NULL),
|
||
|
_hook(NULL),
|
||
|
_pcTop(NULL),
|
||
|
_isRunningEval(PR_FALSE),
|
||
|
_isRunningHook(PR_FALSE),
|
||
|
_actualCallToHookIsHappening(PR_FALSE),
|
||
|
_leaveSuspended(PR_FALSE),
|
||
|
_mainEventQueue(NULL),
|
||
|
_hookCallerEventQueue(NULL),
|
||
|
_hookCallerEventQueueCreateFinished(PR_FALSE),
|
||
|
_suicideMessageRecieved(PR_FALSE),
|
||
|
_execResult()
|
||
|
{
|
||
|
// do nothing...
|
||
|
}
|
||
|
|
||
|
ThreadState::~ThreadState()
|
||
|
{
|
||
|
if( _mainEventQueue )
|
||
|
PR_DestroyEventQueue(_mainEventQueue);
|
||
|
_removeIndexForThread(_currentThread);
|
||
|
}
|
||
|
|
||
|
PRHashNumber
|
||
|
ThreadState::_hash_root(const void *key)
|
||
|
{
|
||
|
PRHashNumber num = (PRHashNumber) key; /* help lame MSVC1.5 on Win16 */
|
||
|
return num >> 2;
|
||
|
}
|
||
|
|
||
|
PRBool
|
||
|
ThreadState::_prepThread2IndexTable()
|
||
|
{
|
||
|
if(_thread2IndexTable)
|
||
|
return PR_TRUE;
|
||
|
|
||
|
_lock();
|
||
|
// re check to avoid nasty race...
|
||
|
if(! _thread2IndexTable)
|
||
|
_thread2IndexTable = PR_NewHashTable( 256, _hash_root,
|
||
|
PR_CompareValues,
|
||
|
PR_CompareValues,
|
||
|
NULL, NULL);
|
||
|
_unlock();
|
||
|
|
||
|
PR_ASSERT(_thread2IndexTable);
|
||
|
return _thread2IndexTable ? PR_TRUE : PR_FALSE;
|
||
|
}
|
||
|
|
||
|
PRBool
|
||
|
ThreadState::_findIndexForThread(PRThread* thread, PRUintn* index)
|
||
|
{
|
||
|
PRUintn indexPlusOne;
|
||
|
|
||
|
PR_ASSERT(thread);
|
||
|
PR_ASSERT(index);
|
||
|
|
||
|
if( ! _prepThread2IndexTable() )
|
||
|
return PR_FALSE;
|
||
|
|
||
|
_lock();
|
||
|
indexPlusOne = (PRUintn) PR_HashTableLookup(_thread2IndexTable, thread);
|
||
|
_unlock();
|
||
|
|
||
|
if(indexPlusOne)
|
||
|
{
|
||
|
*index = indexPlusOne - 1;
|
||
|
return PR_TRUE;
|
||
|
}
|
||
|
return PR_FALSE;
|
||
|
}
|
||
|
|
||
|
PRBool
|
||
|
ThreadState::_setIndexForThread(PRThread* thread, PRUintn index)
|
||
|
{
|
||
|
PRBool retval;
|
||
|
|
||
|
PR_ASSERT(thread);
|
||
|
|
||
|
if( ! _prepThread2IndexTable() )
|
||
|
return PR_FALSE;
|
||
|
|
||
|
_lock();
|
||
|
retval = PR_HashTableAdd(_thread2IndexTable,thread,(void*)(index+1)) ?
|
||
|
PR_TRUE : PR_FALSE;
|
||
|
_unlock();
|
||
|
|
||
|
return retval;
|
||
|
}
|
||
|
|
||
|
PRBool
|
||
|
ThreadState::_removeIndexForThread(PRThread* thread)
|
||
|
{
|
||
|
PRBool retval;
|
||
|
|
||
|
PR_ASSERT(thread);
|
||
|
|
||
|
if( ! _prepThread2IndexTable() )
|
||
|
return PR_FALSE;
|
||
|
|
||
|
_lock();
|
||
|
retval = PR_HashTableRemove(_thread2IndexTable,thread)?PR_TRUE:PR_FALSE;
|
||
|
_unlock();
|
||
|
|
||
|
return retval;
|
||
|
}
|
||
|
|
||
|
/**************************/
|
||
|
|
||
|
ThreadState*
|
||
|
ThreadState::findThreadStateForCurrentThread(void)
|
||
|
{
|
||
|
return _findThreadState(PR_GetCurrentThread());
|
||
|
}
|
||
|
|
||
|
ThreadState*
|
||
|
ThreadState::getThreadStateForCurrentThread(void)
|
||
|
{
|
||
|
PRThread* currentThread = PR_GetCurrentThread();
|
||
|
ThreadState* ts = _findThreadState(currentThread);
|
||
|
if(! ts)
|
||
|
ts = _createThreadState(currentThread);
|
||
|
return ts;
|
||
|
}
|
||
|
|
||
|
ThreadState*
|
||
|
ThreadState::_findThreadState(PRThread* currentThread)
|
||
|
{
|
||
|
PRUintn index;
|
||
|
|
||
|
PR_ASSERT(PR_GetCurrentThread() == currentThread);
|
||
|
|
||
|
if( _findIndexForThread(currentThread, &index) )
|
||
|
return (ThreadState*) PR_GetThreadPrivate(index);
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
ThreadState*
|
||
|
ThreadState::_createThreadState(PRThread* currentThread)
|
||
|
{
|
||
|
PRUintn index;
|
||
|
|
||
|
if(PR_FAILURE == PR_NewThreadPrivateIndex(&index,_callbackForPRThreadDeath))
|
||
|
return NULL;
|
||
|
|
||
|
if( ! _setIndexForThread(currentThread, index) )
|
||
|
return NULL;
|
||
|
|
||
|
ThreadState* ts = new ThreadState(currentThread);
|
||
|
|
||
|
if( ! ts->_initEventQueues() )
|
||
|
return NULL;
|
||
|
|
||
|
if( PR_FAILURE == PR_SetThreadPrivate(index, ts) )
|
||
|
return NULL;
|
||
|
|
||
|
return ts;
|
||
|
}
|
||
|
|
||
|
/**************************/
|
||
|
|
||
|
PRBool
|
||
|
ThreadState::_initEventQueues(void)
|
||
|
{
|
||
|
char name[64];
|
||
|
|
||
|
PR_ASSERT(NULL == _mainEventQueue);
|
||
|
PR_ASSERT(NULL == _hookCallerEventQueue);
|
||
|
PR_ASSERT(! _hookCallerEventQueueCreateFinished);
|
||
|
PR_ASSERT(! _suicideMessageRecieved);
|
||
|
|
||
|
sprintf(name, "ThreadState::_mainEventQueue_%d", (int) this);
|
||
|
|
||
|
_mainEventQueue = PR_CreateEventQueue(name, _currentThread);
|
||
|
if( ! _mainEventQueue )
|
||
|
return PR_FALSE;
|
||
|
|
||
|
// the _hookCallerEventQueue queue gets created on the 'other' thread.
|
||
|
|
||
|
_lock();
|
||
|
if( PR_CreateThread(PR_USER_THREAD,
|
||
|
_otherThreadProc,
|
||
|
this,
|
||
|
PR_PRIORITY_NORMAL,
|
||
|
PR_GLOBAL_THREAD,
|
||
|
PR_UNJOINABLE_THREAD,
|
||
|
0) )
|
||
|
{
|
||
|
while( ! _hookCallerEventQueueCreateFinished )
|
||
|
{
|
||
|
_wait();
|
||
|
}
|
||
|
}
|
||
|
_unlock();
|
||
|
|
||
|
return NULL == _hookCallerEventQueue ? PR_FALSE : PR_TRUE;
|
||
|
}
|
||
|
|
||
|
void
|
||
|
ThreadState::_otherThreadProc(void* arg)
|
||
|
{
|
||
|
char name[64];
|
||
|
ThreadState* self = (ThreadState*)arg;
|
||
|
|
||
|
sprintf(name, "ThreadState::_hookCallerEventQueue_%d", (int) self);
|
||
|
|
||
|
_lock();
|
||
|
self->_hookCallerEventQueue = PR_CreateEventQueue(name, PR_GetCurrentThread());
|
||
|
self->_hookCallerEventQueueCreateFinished = PR_TRUE;
|
||
|
_notify();
|
||
|
_unlock();
|
||
|
|
||
|
// run in the queue until we process the suicide message
|
||
|
|
||
|
if(self->_hookCallerEventQueue)
|
||
|
{
|
||
|
// run the queue
|
||
|
|
||
|
while( ! self->_suicideMessageRecieved )
|
||
|
{
|
||
|
PREvent* e = PR_WaitForEvent(self->_hookCallerEventQueue);
|
||
|
if(e)
|
||
|
{
|
||
|
PR_HandleEvent(e);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// delete our queue
|
||
|
PR_DestroyEventQueue(self->_hookCallerEventQueue);
|
||
|
self->_hookCallerEventQueue = NULL;
|
||
|
// delete our self!
|
||
|
delete self;
|
||
|
}
|
||
|
// return here ends this 'other' thread
|
||
|
}
|
||
|
|
||
|
void
|
||
|
ThreadState::_callbackForPRThreadDeath(void *arg)
|
||
|
{
|
||
|
ThreadState* self = (ThreadState*)arg;
|
||
|
if(self->_hookCallerEventQueue)
|
||
|
{
|
||
|
// post suicide message
|
||
|
JSDSuicideEvent* e = new JSDSuicideEvent(self);
|
||
|
e->post(self->_hookCallerEventQueue);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// otherwise, just delete it.
|
||
|
delete self;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**************************/
|
||
|
|
||
|
void
|
||
|
ThreadState::callHook(DebugController* controller,
|
||
|
IJSExecutionHook* hook,
|
||
|
sequence_of_IJSStackFrameInfo& stack,
|
||
|
IJSPC* pcTop,
|
||
|
JSDThreadState* jsdthreadstate)
|
||
|
{
|
||
|
PR_ASSERT(controller);
|
||
|
PR_ASSERT(hook);
|
||
|
PR_ASSERT(pcTop);
|
||
|
PR_ASSERT(jsdthreadstate);
|
||
|
PR_ASSERT(_mainEventQueue);
|
||
|
PR_ASSERT(_hookCallerEventQueue);
|
||
|
PR_ASSERT(! _isRunningHook);
|
||
|
PR_ASSERT(! _actualCallToHookIsHappening);
|
||
|
|
||
|
if( ! _mainEventQueue || ! _hookCallerEventQueue )
|
||
|
return;
|
||
|
|
||
|
// setup IJSThreadState
|
||
|
_jsts.stack = stack;
|
||
|
_jsts.continueState = (CORBA::Long) JSD_HOOK_RETURN_CONTINUE;
|
||
|
_jsts.returnValue = (const char*) NULL; // currently ignored
|
||
|
_jsts.status = 0; // currently ignored
|
||
|
_jsts.jsdthreadstate = (CORBA::Long) jsdthreadstate;
|
||
|
_jsts.id = (CORBA::Long) this;
|
||
|
|
||
|
_controller = controller;
|
||
|
_hook = hook;
|
||
|
_pcTop = pcTop;
|
||
|
|
||
|
_leaveSuspended = PR_FALSE;
|
||
|
_actualCallToHookIsHappening = PR_TRUE;
|
||
|
|
||
|
_isRunningHook = PR_TRUE;
|
||
|
|
||
|
// post hook caller event
|
||
|
JSDCallHookEvent* e = new JSDCallHookEvent(this);
|
||
|
e->post(_hookCallerEventQueue);
|
||
|
|
||
|
// proccess events until a JSDResumeEvent clears _isRunningHook
|
||
|
while( _isRunningHook )
|
||
|
{
|
||
|
PREvent* e = PR_WaitForEvent(_mainEventQueue);
|
||
|
if(e)
|
||
|
{
|
||
|
PR_HandleEvent(e);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
PRBool
|
||
|
ThreadState::isRunningHook(void)
|
||
|
{
|
||
|
return _isRunningHook;
|
||
|
}
|
||
|
|
||
|
PRBool ThreadState::isWaitingForResume(void)
|
||
|
{
|
||
|
if(_isRunningHook && _leaveSuspended && ! _actualCallToHookIsHappening)
|
||
|
return PR_TRUE;
|
||
|
return PR_FALSE;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// the critical flags we _lock() to protect are:
|
||
|
// _leaveSuspended
|
||
|
// _actualCallToHookIsHappening
|
||
|
//
|
||
|
|
||
|
void
|
||
|
ThreadState::leaveThreadSuspended(void)
|
||
|
{
|
||
|
_lock();
|
||
|
_leaveSuspended = PR_TRUE;
|
||
|
_unlock();
|
||
|
}
|
||
|
|
||
|
void
|
||
|
ThreadState::resumeThread()
|
||
|
{
|
||
|
_lock();
|
||
|
PR_ASSERT(_isRunningHook);
|
||
|
_leaveSuspended = PR_FALSE;
|
||
|
if(! _actualCallToHookIsHappening)
|
||
|
{
|
||
|
PR_ASSERT(_mainEventQueue);
|
||
|
JSDResumeEvent* e = new JSDResumeEvent(this);
|
||
|
e->post(_mainEventQueue);
|
||
|
}
|
||
|
_unlock();
|
||
|
}
|
||
|
|
||
|
void
|
||
|
ThreadState::eventSaysSuicide(void)
|
||
|
{
|
||
|
_suicideMessageRecieved = PR_TRUE;
|
||
|
}
|
||
|
void
|
||
|
ThreadState::eventSaysResume(void)
|
||
|
{
|
||
|
PR_ASSERT(_isRunningHook);
|
||
|
_isRunningHook = PR_FALSE;
|
||
|
}
|
||
|
|
||
|
// called on 'other' thread...
|
||
|
void
|
||
|
ThreadState::eventSaysCallHook(void)
|
||
|
{
|
||
|
PR_ASSERT(_isRunningHook);
|
||
|
PR_ASSERT(_hook);
|
||
|
|
||
|
try
|
||
|
{
|
||
|
TRACE("+++HOOK _hook->aboutToExecute() call hook");
|
||
|
_hook->aboutToExecute(_jsts, *_pcTop);
|
||
|
TRACE("---HOOK _hook->aboutToExecute() call hook");
|
||
|
}
|
||
|
catch(...)
|
||
|
{
|
||
|
// eat it...
|
||
|
_jsts.continueState = JSD_HOOK_RETURN_CONTINUE;
|
||
|
}
|
||
|
|
||
|
_lock();
|
||
|
_actualCallToHookIsHappening = PR_FALSE;
|
||
|
if(! _leaveSuspended)
|
||
|
{
|
||
|
JSDResumeEvent* e = new JSDResumeEvent(this);
|
||
|
e->post(_mainEventQueue);
|
||
|
}
|
||
|
_unlock();
|
||
|
}
|
||
|
|
||
|
/***************************************************************************/
|
||
|
/***************************************************************************/
|
||
|
|
||
|
|
||
|
JSDResumeEvent::JSDResumeEvent(ThreadState* ts)
|
||
|
: _ts(ts)
|
||
|
{
|
||
|
PR_ASSERT(_ts);
|
||
|
PR_InitEvent(this, NULL, static_handle, static_destroy);
|
||
|
}
|
||
|
|
||
|
void
|
||
|
JSDResumeEvent::post(PREventQueue* queue)
|
||
|
{
|
||
|
PR_ASSERT(queue);
|
||
|
// PR_ENTER_EVENT_QUEUE_MONITOR(queue);
|
||
|
PR_PostEvent(queue, this);
|
||
|
// PR_EXIT_EVENT_QUEUE_MONITOR(queue);
|
||
|
}
|
||
|
|
||
|
void*
|
||
|
JSDResumeEvent::static_handle(PREvent* e)
|
||
|
{
|
||
|
JSDResumeEvent* self = (JSDResumeEvent*) e;
|
||
|
PR_ASSERT(self);
|
||
|
PR_ASSERT(self->_ts);
|
||
|
self->_ts->eventSaysResume();
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
void
|
||
|
JSDResumeEvent::static_destroy(PREvent* e)
|
||
|
{
|
||
|
JSDResumeEvent* self = (JSDResumeEvent*) e;
|
||
|
PR_ASSERT(self);
|
||
|
delete self;
|
||
|
}
|
||
|
|
||
|
/***************************************************************************/
|
||
|
|
||
|
JSDSuicideEvent::JSDSuicideEvent(ThreadState* ts)
|
||
|
: _ts(ts)
|
||
|
|
||
|
{
|
||
|
PR_ASSERT(_ts);
|
||
|
PR_InitEvent(this, NULL, static_handle, static_destroy);
|
||
|
}
|
||
|
|
||
|
void
|
||
|
JSDSuicideEvent::post(PREventQueue* queue)
|
||
|
{
|
||
|
PR_ASSERT(queue);
|
||
|
// PR_ENTER_EVENT_QUEUE_MONITOR(queue);
|
||
|
PR_PostEvent(queue, this);
|
||
|
// PR_EXIT_EVENT_QUEUE_MONITOR(queue);
|
||
|
}
|
||
|
|
||
|
void*
|
||
|
JSDSuicideEvent::static_handle(PREvent* e)
|
||
|
{
|
||
|
JSDSuicideEvent* self = (JSDSuicideEvent*) e;
|
||
|
PR_ASSERT(self);
|
||
|
PR_ASSERT(self->_ts);
|
||
|
self->_ts->eventSaysSuicide();
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
void
|
||
|
JSDSuicideEvent::static_destroy(PREvent* e)
|
||
|
{
|
||
|
JSDSuicideEvent* self = (JSDSuicideEvent*) e;
|
||
|
PR_ASSERT(self);
|
||
|
delete self;
|
||
|
}
|
||
|
|
||
|
/***************************************************************************/
|
||
|
|
||
|
JSDCallHookEvent::JSDCallHookEvent(ThreadState* ts)
|
||
|
: _ts(ts)
|
||
|
{
|
||
|
PR_ASSERT(_ts);
|
||
|
PR_InitEvent(this, NULL, static_handle, static_destroy);
|
||
|
}
|
||
|
|
||
|
void
|
||
|
JSDCallHookEvent::post(PREventQueue* queue)
|
||
|
{
|
||
|
PR_ASSERT(queue);
|
||
|
// PR_ENTER_EVENT_QUEUE_MONITOR(queue);
|
||
|
PR_PostEvent(queue, this);
|
||
|
// PR_EXIT_EVENT_QUEUE_MONITOR(queue);
|
||
|
}
|
||
|
|
||
|
void*
|
||
|
JSDCallHookEvent::static_handle(PREvent* e)
|
||
|
{
|
||
|
JSDCallHookEvent* self = (JSDCallHookEvent*) e;
|
||
|
PR_ASSERT(self);
|
||
|
PR_ASSERT(self->_ts);
|
||
|
self->_ts->eventSaysCallHook();
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
void
|
||
|
JSDCallHookEvent::static_destroy(PREvent* e)
|
||
|
{
|
||
|
JSDCallHookEvent* self = (JSDCallHookEvent*) e;
|
||
|
PR_ASSERT(self);
|
||
|
delete self;
|
||
|
}
|
||
|
|
||
|
/***************************************************************************/
|
||
|
|
||
|
JSDExecuteEvent::JSDExecuteEvent(JSDContext* jsdc,
|
||
|
ThreadState* ts,
|
||
|
const IJSStackFrameInfo& frame,
|
||
|
const char* text,
|
||
|
const char* filename,
|
||
|
int lineno)
|
||
|
: _jsdc(jsdc),
|
||
|
_ts(ts),
|
||
|
_frame(frame),
|
||
|
_text(strdup(text)),
|
||
|
_filename(strdup(filename)), // set below
|
||
|
_lineno(lineno)
|
||
|
{
|
||
|
PR_ASSERT(_jsdc);
|
||
|
PR_ASSERT(_ts);
|
||
|
PR_ASSERT(_text);
|
||
|
PR_ASSERT(_filename);
|
||
|
PR_InitEvent(this, NULL, static_handle, static_destroy);
|
||
|
}
|
||
|
|
||
|
JSDExecuteEvent::~JSDExecuteEvent()
|
||
|
{
|
||
|
if(_text)
|
||
|
free(_text);
|
||
|
if(_filename)
|
||
|
free(_filename);
|
||
|
}
|
||
|
|
||
|
IExecResult*
|
||
|
JSDExecuteEvent::postSynchronous(PREventQueue* queue)
|
||
|
{
|
||
|
PR_ASSERT(queue);
|
||
|
IExecResult* result = (IExecResult*) PR_PostSynchronousEvent(queue, this);
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
void*
|
||
|
JSDExecuteEvent::static_handle(PREvent* e)
|
||
|
{
|
||
|
JSDExecuteEvent* self = (JSDExecuteEvent*) e;
|
||
|
PR_ASSERT(self);
|
||
|
PR_ASSERT(self->_ts);
|
||
|
return self->_handle();
|
||
|
}
|
||
|
|
||
|
void
|
||
|
JSDExecuteEvent::static_destroy(PREvent* e)
|
||
|
{
|
||
|
JSDExecuteEvent* self = (JSDExecuteEvent*) e;
|
||
|
PR_ASSERT(self);
|
||
|
delete self;
|
||
|
}
|
||
|
|
||
|
void*
|
||
|
JSDExecuteEvent::_handle()
|
||
|
{
|
||
|
const IJSThreadState& jsts = _ts->getJSThreadState();
|
||
|
JSDThreadState* jsdthreadstate = (JSDThreadState*)(jsts.jsdthreadstate);
|
||
|
JSDStackFrameInfo* jsdframe = (JSDStackFrameInfo*) _frame.jsdframe;
|
||
|
JSString* jsstr;
|
||
|
jsval rval;
|
||
|
JSBool success;
|
||
|
char* str = NULL;
|
||
|
IExecResult* result = NULL;
|
||
|
|
||
|
// clear error result
|
||
|
_ts->getExecResult() = (const IExecResult&) IExecResult();
|
||
|
|
||
|
_ts->setRunningEval(PR_TRUE);
|
||
|
success = JSD_EvaluateScriptInStackFrame(_jsdc, jsdthreadstate, jsdframe,
|
||
|
_text, strlen(_text),
|
||
|
_filename, _lineno,
|
||
|
&rval);
|
||
|
_ts->setRunningEval(PR_FALSE);
|
||
|
|
||
|
result = new IExecResult((const IExecResult&) _ts->getExecResult());
|
||
|
|
||
|
if( success && ! JSVAL_IS_NULL(rval) && ! JSVAL_IS_VOID(rval) )
|
||
|
{
|
||
|
jsstr = JSD_ValToStringInStackFrame(_jsdc,jsdthreadstate,jsdframe,rval);
|
||
|
if( jsstr )
|
||
|
{
|
||
|
str = (char*) malloc((jsstr->length+1)*sizeof(char));
|
||
|
if(str)
|
||
|
{
|
||
|
memcpy( str, jsstr->bytes, jsstr->length*sizeof(char));
|
||
|
str[jsstr->length] = 0;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
result->result = str;
|
||
|
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
|
||
|
/***************************************************************************/
|
||
|
|
||
|
CallChain::CallChain(JSDContext* jsdc, JSDThreadState* jsdthreadstate)
|
||
|
: _jsdc(jsdc),
|
||
|
_chain(NULL),
|
||
|
_count(0)
|
||
|
{
|
||
|
PR_ASSERT(_jsdc);
|
||
|
PR_ASSERT(jsdthreadstate);
|
||
|
|
||
|
_count = (int) JSD_GetCountOfStackFrames(jsdc, jsdthreadstate);
|
||
|
|
||
|
PR_ASSERT(_count);
|
||
|
|
||
|
_chain = (JSDScript**) new char[_count*sizeof(JSDScript*)];
|
||
|
PR_ASSERT(_chain);
|
||
|
|
||
|
JSDStackFrameInfo* jsdframe = JSD_GetStackFrame(jsdc, jsdthreadstate);
|
||
|
|
||
|
for(int i = _count-1; i >= 0; i--)
|
||
|
{
|
||
|
PR_ASSERT(jsdframe);
|
||
|
_chain[i] = JSD_GetScriptForStackFrame(_jsdc, jsdthreadstate, jsdframe);
|
||
|
PR_ASSERT(_chain[i]);
|
||
|
jsdframe = JSD_GetCallingStackFrame(jsdc, jsdthreadstate, jsdframe);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
CallChain::~CallChain()
|
||
|
{
|
||
|
if( _chain )
|
||
|
delete (char*) _chain;
|
||
|
}
|
||
|
|
||
|
CallChain::CompareResult
|
||
|
CallChain::compare(const CallChain& other)
|
||
|
{
|
||
|
if( this == &other )
|
||
|
return EQUAL;
|
||
|
|
||
|
if( NULL == _chain || NULL == other._chain || 0 == _count || 0 == other._count )
|
||
|
return DISJOINT;
|
||
|
|
||
|
int lesser_count = min( _count, other._count );
|
||
|
for( int i = 0; i < lesser_count; i++ )
|
||
|
{
|
||
|
if( _chain[i] != other._chain[i] )
|
||
|
return DISJOINT;
|
||
|
}
|
||
|
|
||
|
if( _count > other._count )
|
||
|
return CALLER;
|
||
|
if( _count < other._count )
|
||
|
return CALLEE;
|
||
|
return EQUAL;
|
||
|
}
|
||
|
|
||
|
/***************************************************************************/
|
||
|
|
||
|
StepHandler::StepHandler(JSDContext* jsdc, ThreadState* state, PRBool buildCallChain)
|
||
|
: _jsdc(jsdc),
|
||
|
topScriptInitial(NULL),
|
||
|
topPCInitial(0),
|
||
|
topLineInitial(0),
|
||
|
_callChain(NULL)
|
||
|
{
|
||
|
PR_ASSERT(_jsdc);
|
||
|
PR_ASSERT(state);
|
||
|
PR_ASSERT(state->isRunningHook());
|
||
|
|
||
|
JSDThreadState* jsdthreadstate = (JSDThreadState*)
|
||
|
state->getJSThreadState().jsdthreadstate;
|
||
|
PR_ASSERT(jsdthreadstate);
|
||
|
|
||
|
JSDStackFrameInfo* jsdframe = _topFrame(jsdthreadstate);
|
||
|
PR_ASSERT(jsdframe);
|
||
|
|
||
|
topScriptInitial = _topScript(jsdthreadstate, jsdframe);
|
||
|
PR_ASSERT(topScriptInitial);
|
||
|
|
||
|
topPCInitial = _topPC(jsdthreadstate, jsdframe);
|
||
|
topLineInitial = _topLine(topScriptInitial, topPCInitial);
|
||
|
|
||
|
if( buildCallChain )
|
||
|
{
|
||
|
_callChain = new CallChain( _jsdc, jsdthreadstate );
|
||
|
PR_ASSERT(_callChain);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
StepHandler::~StepHandler()
|
||
|
{
|
||
|
if( _callChain )
|
||
|
delete _callChain;
|
||
|
}
|
||
|
|
||
|
/***************************************************************************/
|
||
|
|
||
|
StepInto::StepInto(JSDContext* jsdc, ThreadState* state)
|
||
|
: StepHandler(jsdc, state, PR_FALSE)
|
||
|
{
|
||
|
// do nothing
|
||
|
}
|
||
|
|
||
|
StepHandler::StepResult
|
||
|
StepInto::step(JSDThreadState* jsdthreadstate)
|
||
|
{
|
||
|
JSDStackFrameInfo* topFrame = _topFrame(jsdthreadstate);
|
||
|
JSDScript* topScript = _topScript(jsdthreadstate, topFrame);
|
||
|
|
||
|
// different script, gotta stop
|
||
|
if( topScriptInitial != topScript )
|
||
|
return STOP;
|
||
|
|
||
|
//
|
||
|
// NOTE: This little dance is necessary because line numbers for PCs
|
||
|
// are not always acending. e,g, in:
|
||
|
//
|
||
|
// for(i=0;i<count;i++)
|
||
|
// doSomething();
|
||
|
//
|
||
|
// The "i++" comes after the "doSomething" call as far as PCs go.
|
||
|
// But, in JS the line number associated with the "i++" instuction
|
||
|
// is that of the "for(...". So, the lines for the PC can look like
|
||
|
// 1,1,2,1,2. Thus we are careful in deciding how to step.
|
||
|
//
|
||
|
|
||
|
prword_t topPC = _topPC(jsdthreadstate, topFrame);
|
||
|
int topLine = _topLine(topScript, topPC);
|
||
|
|
||
|
// definately jumping back
|
||
|
if( topPC < topPCInitial && topLine != topLineInitial )
|
||
|
return STOP;
|
||
|
|
||
|
// definately jumping forward
|
||
|
if( topLine > topLineInitial )
|
||
|
return STOP;
|
||
|
|
||
|
return CONTINUE_SEND_INTERRUPT;
|
||
|
}
|
||
|
|
||
|
/***************************************************************************/
|
||
|
|
||
|
StepOver::StepOver(JSDContext* jsdc, ThreadState* state)
|
||
|
: StepHandler(jsdc, state, PR_TRUE)
|
||
|
{
|
||
|
// do nothing
|
||
|
}
|
||
|
|
||
|
StepHandler::StepResult
|
||
|
StepOver::step(JSDThreadState* jsdthreadstate)
|
||
|
{
|
||
|
switch( _callChain->compare( CallChain(_jsdc, jsdthreadstate) ) )
|
||
|
{
|
||
|
case CallChain::EQUAL:
|
||
|
break;
|
||
|
case CallChain::CALLER:
|
||
|
return STOP;
|
||
|
case CallChain::CALLEE:
|
||
|
return CONTINUE_SEND_INTERRUPT;
|
||
|
case CallChain::DISJOINT:
|
||
|
return STOP;
|
||
|
default:
|
||
|
PR_ASSERT(0); // illegal value!
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
JSDStackFrameInfo* topFrame = _topFrame(jsdthreadstate);
|
||
|
JSDScript* topScript = _topScript(jsdthreadstate, topFrame);
|
||
|
prword_t topPC = _topPC(jsdthreadstate, topFrame);
|
||
|
int topLine = _topLine(topScript, topPC);
|
||
|
|
||
|
// definately jumping back
|
||
|
if( topPC < topPCInitial && topLine != topLineInitial )
|
||
|
return STOP;
|
||
|
|
||
|
// definately jumping forward
|
||
|
if( topLine > topLineInitial )
|
||
|
return STOP;
|
||
|
|
||
|
return CONTINUE_SEND_INTERRUPT;
|
||
|
}
|
||
|
|
||
|
/***************************************************************************/
|
||
|
|
||
|
StepOut::StepOut(JSDContext* jsdc, ThreadState* state)
|
||
|
: StepHandler(jsdc, state, PR_TRUE)
|
||
|
{
|
||
|
// do nothing
|
||
|
}
|
||
|
|
||
|
StepHandler::StepResult
|
||
|
StepOut::step(JSDThreadState* jsdthreadstate)
|
||
|
{
|
||
|
switch( _callChain->compare( CallChain(_jsdc, jsdthreadstate) ) )
|
||
|
{
|
||
|
case CallChain::EQUAL:
|
||
|
return CONTINUE_SEND_INTERRUPT;
|
||
|
case CallChain::CALLER:
|
||
|
return STOP;
|
||
|
case CallChain::CALLEE:
|
||
|
return CONTINUE_SEND_INTERRUPT;
|
||
|
case CallChain::DISJOINT:
|
||
|
return STOP;
|
||
|
default:
|
||
|
PR_ASSERT(0); // illegal value!
|
||
|
return STOP;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/***************************************************************************/
|
||
|
/***************************************************************************/
|
||
|
|
||
|
SourceTextProvider::SourceTextProvider(const char* name)
|
||
|
: _sk_ISourceTextProvider(name),
|
||
|
_jsdc(NULL)
|
||
|
|
||
|
{
|
||
|
// do nothing
|
||
|
}
|
||
|
|
||
|
SourceTextProvider::SourceTextProvider(const char* name, JSDContext* jsdc)
|
||
|
: _sk_ISourceTextProvider(name),
|
||
|
_jsdc(NULL)
|
||
|
{
|
||
|
init(jsdc);
|
||
|
}
|
||
|
|
||
|
void
|
||
|
SourceTextProvider::init(JSDContext* jsdc)
|
||
|
{
|
||
|
_jsdc = jsdc;
|
||
|
_forcePreLoad();
|
||
|
}
|
||
|
|
||
|
SourceTextProvider::~SourceTextProvider()
|
||
|
{
|
||
|
}
|
||
|
|
||
|
ISourceTextProvider::sequence_of_string *
|
||
|
SourceTextProvider::getAllPages()
|
||
|
{
|
||
|
TRACE("SourceTextProvider::getAllPages() called");
|
||
|
JSDSourceText* jsdsrc;
|
||
|
JSDSourceText* iter;
|
||
|
ISourceTextProvider::sequence_of_string * seq = NULL;
|
||
|
|
||
|
_lock();
|
||
|
|
||
|
// iterate once to get count;
|
||
|
int count = 0;
|
||
|
iter = NULL;
|
||
|
while( NULL != JSD_IterateSources(_jsdc, &iter) )
|
||
|
count++;
|
||
|
|
||
|
char** pp = (char**) malloc(count * sizeof(void*));
|
||
|
if(pp)
|
||
|
{
|
||
|
int i = 0;
|
||
|
iter = NULL;
|
||
|
while( NULL != (jsdsrc = JSD_IterateSources(_jsdc, &iter)) )
|
||
|
{
|
||
|
const char* url;
|
||
|
if( NULL == (url = JSD_GetSourceURL(_jsdc, jsdsrc)) )
|
||
|
continue;
|
||
|
|
||
|
pp[i++] = strdup(url);
|
||
|
}
|
||
|
seq = new ISourceTextProvider::sequence_of_string(i, i, pp, 1);
|
||
|
}
|
||
|
|
||
|
_unlock();
|
||
|
|
||
|
return seq;
|
||
|
}
|
||
|
|
||
|
void
|
||
|
SourceTextProvider::refreshAllPages()
|
||
|
{
|
||
|
TRACE("SourceTextProvider::refreshAllPages() called");
|
||
|
_lock();
|
||
|
// what we really want is a way to detect if existing pages
|
||
|
// have changed - date, size, etc... -- and force reload of them
|
||
|
// JSD_DestroyAllSources(_jsdc);
|
||
|
_forcePreLoad();
|
||
|
_unlock();
|
||
|
}
|
||
|
|
||
|
CORBA::Boolean
|
||
|
SourceTextProvider::hasPage(const char * arg0)
|
||
|
{
|
||
|
TRACE("SourceTextProvider::hasPage() called");
|
||
|
return JSD_FindSourceForURL(_jsdc, arg0) ? 1 : 0;
|
||
|
|
||
|
}
|
||
|
|
||
|
CORBA::Boolean
|
||
|
SourceTextProvider::loadPage(const char * arg0)
|
||
|
{
|
||
|
TRACE("SourceTextProvider::loadPage() called");
|
||
|
// not implemented...
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
void
|
||
|
SourceTextProvider::refreshPage(const char * arg0)
|
||
|
{
|
||
|
TRACE("SourceTextProvider::refreshPage() called");
|
||
|
// not implemented...
|
||
|
}
|
||
|
|
||
|
char *
|
||
|
SourceTextProvider::getPageText(const char * arg0)
|
||
|
{
|
||
|
TRACE("SourceTextProvider::getPageText() called");
|
||
|
JSDSourceText* jsdsrc;
|
||
|
const char * const_text;
|
||
|
int len;
|
||
|
char* text = NULL;
|
||
|
|
||
|
_lock();
|
||
|
|
||
|
jsdsrc = JSD_FindSourceForURL(_jsdc, arg0);
|
||
|
if( jsdsrc )
|
||
|
{
|
||
|
if( JSD_SOURCE_COMPLETED != JSD_GetSourceStatus(_jsdc, jsdsrc) )
|
||
|
jsdsrc = JSDLW_ForceLoadSource(_jsdc, jsdsrc);
|
||
|
}
|
||
|
|
||
|
if( jsdsrc )
|
||
|
{
|
||
|
if( JSD_GetSourceText(_jsdc, jsdsrc, &const_text, &len) &&
|
||
|
const_text && len )
|
||
|
{
|
||
|
text = (char*) malloc((len+1)*sizeof(char));
|
||
|
if(text)
|
||
|
{
|
||
|
memcpy(text,const_text,len*sizeof(char));
|
||
|
text[len] = 0;
|
||
|
_deferedFree(text);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
_unlock();
|
||
|
|
||
|
return text;
|
||
|
}
|
||
|
|
||
|
CORBA::Long
|
||
|
SourceTextProvider::getPageStatus(const char * arg0)
|
||
|
{
|
||
|
TRACE("SourceTextProvider::getPageStatus() called");
|
||
|
JSDSourceText* jsdsrc;
|
||
|
int status = JSD_SOURCE_INITED;
|
||
|
|
||
|
_lock();
|
||
|
jsdsrc = JSD_FindSourceForURL(_jsdc, arg0);
|
||
|
if(jsdsrc)
|
||
|
status = JSD_GetSourceStatus(_jsdc, jsdsrc);
|
||
|
_unlock();
|
||
|
return (CORBA::Long) status;
|
||
|
}
|
||
|
|
||
|
CORBA::Long
|
||
|
SourceTextProvider::getPageAlterCount(const char * arg0)
|
||
|
{
|
||
|
TRACE("SourceTextProvider::getPageAlterCount() called");
|
||
|
JSDSourceText* jsdsrc;
|
||
|
int altercount = JSD_SOURCE_INITED;
|
||
|
|
||
|
_lock();
|
||
|
jsdsrc = JSD_FindSourceForURL(_jsdc, arg0);
|
||
|
if(jsdsrc)
|
||
|
altercount = JSD_GetSourceAlterCount(_jsdc, jsdsrc);
|
||
|
_unlock();
|
||
|
return (CORBA::Long) altercount;
|
||
|
}
|
||
|
|
||
|
void
|
||
|
SourceTextProvider::_forcePreLoad()
|
||
|
{
|
||
|
LWDBGApp* app;
|
||
|
LWDBGApp* app_iter;
|
||
|
|
||
|
_lock();
|
||
|
|
||
|
// iterate through all pages of all apps to force preload
|
||
|
app_iter = NULL;
|
||
|
while(NULL != (app = LWDBG_EnumerateApps(&app_iter)))
|
||
|
{
|
||
|
const char* pagename;
|
||
|
void* page_iter = NULL;
|
||
|
while(NULL != (pagename = LWDBG_EnumeratePageNames(app,&page_iter)))
|
||
|
{
|
||
|
JSDLW_PreLoadSource(_jsdc, app, pagename, JS_FALSE );
|
||
|
}
|
||
|
}
|
||
|
_unlock();
|
||
|
}
|
||
|
|
||
|
/***************************************************************************/
|
||
|
|
||
|
char *
|
||
|
TestInterface_impl::getFirstAppInList()
|
||
|
{
|
||
|
static char buf[1024];
|
||
|
|
||
|
LWDBGApp* iter = NULL;
|
||
|
LWDBGApp* app;
|
||
|
|
||
|
app = LWDBG_EnumerateApps(&iter);
|
||
|
if( app )
|
||
|
return ((char*) LWDBG_GetURI(app)) + 1;
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
void
|
||
|
TestInterface_impl::getAppNames(StringReciever_ptr sr)
|
||
|
{
|
||
|
LWDBGApp* iter = NULL;
|
||
|
LWDBGApp* app;
|
||
|
|
||
|
while( NULL != (app = LWDBG_EnumerateApps(&iter)) )
|
||
|
{
|
||
|
char* name = ((char*) LWDBG_GetURI(app)) + 1;
|
||
|
|
||
|
try
|
||
|
{
|
||
|
sr->recieveString(name);
|
||
|
}
|
||
|
catch(const CORBA::Exception&)
|
||
|
{
|
||
|
// eat it and bail...
|
||
|
return;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static void
|
||
|
InitThing(Thing& t, const char* s, int i)
|
||
|
{
|
||
|
t.s = s;
|
||
|
t.i = i;
|
||
|
}
|
||
|
|
||
|
TestInterface::sequence_of_Thing *
|
||
|
TestInterface_impl::getThings()
|
||
|
{
|
||
|
Thing* p = new Thing[3];
|
||
|
InitThing(p[0],"zero",0);
|
||
|
InitThing(p[1],"one" ,1);
|
||
|
InitThing(p[2],"two" ,2);
|
||
|
return new TestInterface::sequence_of_Thing(3, 3, p, 1);
|
||
|
}
|
||
|
|
||
|
void
|
||
|
TestInterface_impl::callBounce(StringReciever_ptr arg0, CORBA::Long arg1)
|
||
|
{
|
||
|
try
|
||
|
{
|
||
|
arg0->bounce(arg1);
|
||
|
}
|
||
|
catch(const CORBA::Exception&)
|
||
|
{
|
||
|
// eat it and bail...
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
/***************************************************************************/
|
||
|
/***************************************************************************/
|
||
|
|
||
|
static JSDContext* _jsdc = NULL;
|
||
|
static TestInterface_impl _test;
|
||
|
static SourceTextProvider _stp("JSSourceTextProvider");
|
||
|
static DebugController _controller("JSDebugController");
|
||
|
static char* _hostname;
|
||
|
|
||
|
static const char*
|
||
|
_WAIReturnType2String( WAIReturnType_t t )
|
||
|
{
|
||
|
switch(t)
|
||
|
{
|
||
|
case WAISPISuccess: return "WAISPISuccess";
|
||
|
case WAISPIFailure: return "WAISPIFailure";
|
||
|
case WAISPIBadparam: return "WAISPIBadparam";
|
||
|
case WAISPINonameservice: return "WAISPINonameservice";
|
||
|
default: return "unknown error code";
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// PR_STATIC_CALLBACK(void)
|
||
|
// ssjsdTestThreadProc(void* arg)
|
||
|
// {
|
||
|
// CORBA::ORB_var orb;
|
||
|
// CORBA::BOA_var boa;
|
||
|
//
|
||
|
// int zero = 0;
|
||
|
// orb = CORBA::ORB_init(zero,NULL);
|
||
|
// boa = orb->BOA_init(zero,NULL);
|
||
|
//
|
||
|
// boa->obj_is_ready(&_test);
|
||
|
// WAIReturnType_t reg = registerObject(_hostname, "test", &_test );
|
||
|
//
|
||
|
// log_error(LOG_VERBOSE, "ssjsdTestThreadProc", NULL, NULL,
|
||
|
// "registration of _test %s",
|
||
|
// _WAIReturnType2String(reg) );
|
||
|
//
|
||
|
// boa->impl_is_ready();
|
||
|
// }
|
||
|
//
|
||
|
// PR_STATIC_CALLBACK(void)
|
||
|
// ssjsdDebugControllerThreadProc(void* arg)
|
||
|
// {
|
||
|
// CORBA::ORB_var orb;
|
||
|
// CORBA::BOA_var boa;
|
||
|
//
|
||
|
// int zero = 0;
|
||
|
// orb = CORBA::ORB_init(zero,NULL);
|
||
|
// boa = orb->BOA_init(zero,NULL);
|
||
|
//
|
||
|
// _controller.init(_jsdc);
|
||
|
// boa->obj_is_ready(&_controller);
|
||
|
// WAIReturnType_t reg = registerObject(_hostname, "JSDebugController", &_controller );
|
||
|
//
|
||
|
// log_error(LOG_VERBOSE, "ssjsdDebugControllerThreadProc", NULL, NULL,
|
||
|
// "registration of DebugController %s",
|
||
|
// _WAIReturnType2String(reg) );
|
||
|
//
|
||
|
// boa->impl_is_ready();
|
||
|
// }
|
||
|
//
|
||
|
// PR_STATIC_CALLBACK(void)
|
||
|
// ssjsdSourceTextProviderThreadProc(void* arg)
|
||
|
// {
|
||
|
// CORBA::ORB_var orb;
|
||
|
// CORBA::BOA_var boa;
|
||
|
//
|
||
|
// int zero = 0;
|
||
|
// orb = CORBA::ORB_init(zero,NULL);
|
||
|
// boa = orb->BOA_init(zero,NULL);
|
||
|
//
|
||
|
// _stp.init(_jsdc);
|
||
|
// boa->obj_is_ready(&_stp);
|
||
|
// WAIReturnType_t reg = registerObject(_hostname, "JSSourceTextProvider", &_stp );
|
||
|
//
|
||
|
// log_error(LOG_VERBOSE, "ssjsdSourceTextProviderThreadProc", NULL, NULL,
|
||
|
// "registration of SourceTextProvider %s",
|
||
|
// _WAIReturnType2String(reg) );
|
||
|
// boa->impl_is_ready();
|
||
|
// }
|
||
|
//
|
||
|
// static PRThread* _TestThread;
|
||
|
// static PRThread* _DebugControllerThread;
|
||
|
// static PRThread* _SourceTextProviderThread;
|
||
|
//
|
||
|
// static void
|
||
|
// _exportRemotedObjectsOnSeperateThreads()
|
||
|
// {
|
||
|
// ///////////////////////////
|
||
|
//
|
||
|
// _TestThread =
|
||
|
// PR_CreateThread( PR_USER_THREAD,
|
||
|
// ssjsdTestThreadProc,
|
||
|
// NULL,
|
||
|
// PR_PRIORITY_NORMAL,
|
||
|
// PR_GLOBAL_THREAD,
|
||
|
// PR_UNJOINABLE_THREAD,
|
||
|
// 0 );
|
||
|
//
|
||
|
// log_error(LOG_VERBOSE, "ssjsdebugInit", sn, rq,
|
||
|
// "create test thread %s",
|
||
|
// _TestThread == NULL ? "failed" : "succeeded" );
|
||
|
//
|
||
|
// ///////////////////////////
|
||
|
//
|
||
|
// _DebugControllerThread =
|
||
|
// PR_CreateThread( PR_USER_THREAD,
|
||
|
// ssjsdDebugControllerThreadProc,
|
||
|
// NULL,
|
||
|
// PR_PRIORITY_NORMAL,
|
||
|
// PR_GLOBAL_THREAD,
|
||
|
// PR_UNJOINABLE_THREAD,
|
||
|
// 0 );
|
||
|
//
|
||
|
// log_error(LOG_VERBOSE, "ssjsdebugInit", sn, rq,
|
||
|
// "create debug controller thread %s",
|
||
|
// _DebugControllerThread == NULL ? "failed" : "succeeded" );
|
||
|
//
|
||
|
//
|
||
|
// ///////////////////////////
|
||
|
//
|
||
|
// _SourceTextProviderThread =
|
||
|
// PR_CreateThread( PR_USER_THREAD,
|
||
|
// ssjsdSourceTextProviderThreadProc,
|
||
|
// NULL,
|
||
|
// PR_PRIORITY_NORMAL,
|
||
|
// PR_GLOBAL_THREAD,
|
||
|
// PR_UNJOINABLE_THREAD,
|
||
|
// 0 );
|
||
|
//
|
||
|
// log_error(LOG_VERBOSE, "ssjsdebugInit", sn, rq,
|
||
|
// "create text provider thread %s",
|
||
|
// _SourceTextProviderThread == NULL ? "failed" : "succeeded" );
|
||
|
// }
|
||
|
|
||
|
|
||
|
static PRBool
|
||
|
_registerOb(const char *name, CORBA::Object_ptr obj)
|
||
|
{
|
||
|
WAIReturnType_t reg = registerObject(_hostname, name, obj);
|
||
|
|
||
|
log_error(LOG_VERBOSE, "ssjsdebugInit", NULL, NULL,
|
||
|
"registration of %s returned %s",
|
||
|
name, _WAIReturnType2String(reg));
|
||
|
|
||
|
return WAISPISuccess == reg ? PR_TRUE : PR_FALSE;
|
||
|
}
|
||
|
|
||
|
static void
|
||
|
_exportRemotedObjectsOnThisThread()
|
||
|
{
|
||
|
CORBA::ORB_var orb;
|
||
|
CORBA::BOA_var boa;
|
||
|
|
||
|
int zero = 0;
|
||
|
orb = CORBA::ORB_init(zero,NULL);
|
||
|
boa = orb->BOA_init(zero,NULL);
|
||
|
|
||
|
///////////////////////////
|
||
|
|
||
|
// boa->obj_is_ready(&_test);
|
||
|
// _registerOb("test", &_test, force );
|
||
|
|
||
|
///////////////////////////
|
||
|
|
||
|
_controller.init(_jsdc);
|
||
|
boa->obj_is_ready(&_controller);
|
||
|
_registerOb("JSDebugController", &_controller);
|
||
|
|
||
|
///////////////////////////
|
||
|
|
||
|
_stp.init(_jsdc);
|
||
|
boa->obj_is_ready(&_stp);
|
||
|
_registerOb("JSSourceTextProvider", &_stp);
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
/* NSAPI initialization function */
|
||
|
|
||
|
PR_IMPLEMENT(int)
|
||
|
ssjsdebugInit(pblock *pb, Session *sn, Request *rq)
|
||
|
{
|
||
|
JSD_SetUserCallbacks(LWDBG_GetTaskState(), NULL, NULL);
|
||
|
_jsdc = JSD_DebuggerOn();
|
||
|
|
||
|
log_error(LOG_VERBOSE, "ssjsdebugInit", NULL, NULL,
|
||
|
"starting with iiop, JSD_DebuggerOn() %s", _jsdc == NULL ? "failed" : "succeeded" );
|
||
|
|
||
|
if(_jsdc)
|
||
|
{
|
||
|
_hostname = http_uri2url("","");
|
||
|
|
||
|
log_error(LOG_VERBOSE, "ssjsdebugInit", NULL, NULL,
|
||
|
"hostname = %s", _hostname );
|
||
|
|
||
|
_exportRemotedObjectsOnThisThread();
|
||
|
}
|
||
|
return REQ_PROCEED;
|
||
|
}
|