diff options
Diffstat (limited to 'js/jsd')
42 files changed, 15896 insertions, 0 deletions
diff --git a/js/jsd/Makefile.in b/js/jsd/Makefile.in new file mode 100644 index 0000000..10e7461 --- /dev/null +++ b/js/jsd/Makefile.in @@ -0,0 +1,31 @@ +#!gmake +# +# 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/. + + + +DEPTH = @DEPTH@ +topsrcdir = @top_srcdir@ +VPATH = @srcdir@ +srcdir = @srcdir@ +relativesrcdir = @relativesrcdir@ + +include $(DEPTH)/config/autoconf.mk + +IS_COMPONENT = 1 +LIBXUL_LIBRARY = 1 + +MODULE_NAME = JavaScript_Debugger +EXPORT_LIBRARY = 1 + +# REQUIRES = java js + +ifdef JS_THREADSAFE +DEFINES += -DJS_THREADSAFE +endif + +include $(topsrcdir)/config/rules.mk + +DEFINES += -DEXPORT_JSD_API diff --git a/js/jsd/README b/js/jsd/README new file mode 100644 index 0000000..25295a8 --- /dev/null +++ b/js/jsd/README @@ -0,0 +1,6 @@ +js/jsd contains code for debugging support for the C-based JavaScript engine +in js/src. jsd_xpc.cpp provides an XPCOM binding for the library. + +js/jsd/jsdb is a console debugger using only native code (see README in that +directory.) This debugger is no longer being actively developed, though it +should work. diff --git a/js/jsd/idl/jsdIDebuggerService.idl b/js/jsd/idl/jsdIDebuggerService.idl new file mode 100644 index 0000000..618f342 --- /dev/null +++ b/js/jsd/idl/jsdIDebuggerService.idl @@ -0,0 +1,1219 @@ +/* -*- Mode: IDL; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* 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/. */ + +#include "nsISupports.idl" + +%{ C++ +#include "jsdebug.h" +#include "nsAString.h" +%} + +[ptr] native JSDContext(JSDContext); +[ptr] native JSDObject(JSDObject); +[ptr] native JSDProperty(JSDProperty); +[ptr] native JSDScript(JSDScript); +[ptr] native JSDStackFrameInfo(JSDStackFrameInfo); +[ptr] native JSDThreadState(JSDThreadState); +[ptr] native JSDValue(JSDValue); +[ptr] native JSRuntime(JSRuntime); +[ptr] native JSContext(JSContext); +[ptr] native JSCompartment(JSCompartment); + +/* interfaces we declare in this file */ +interface jsdIDebuggerService; +interface jsdIFilter; +interface jsdINestCallback; +interface jsdIFilterEnumerator; +interface jsdIContextEnumerator; +interface jsdIScriptEnumerator; +interface jsdIScriptHook; +interface jsdIErrorHook; +interface jsdIExecutionHook; +interface jsdICallHook; +interface jsdIEphemeral; +interface jsdIContext; +interface jsdIStackFrame; +interface jsdIScript; +interface jsdIValue; +interface jsdIObject; +interface jsdIProperty; +interface jsdIActivationCallback; + +/** + * Debugger service. It is not a good idea to have more than one active client + * of the debugger service. + */ +[scriptable, uuid(029b8f0a-aa84-47eb-a60f-1a4752b7ad06)] +interface jsdIDebuggerService : nsISupports +{ + /** Internal use only. */ + [noscript] readonly attribute JSDContext JSDContext; + + /** + * Called when an error or warning occurs. + */ + attribute jsdIErrorHook errorHook; + /** + * Called when a jsdIScript is created or destroyed. + */ + attribute jsdIScriptHook scriptHook; + /** + * Called when the engine encounters a breakpoint. + */ + attribute jsdIExecutionHook breakpointHook; + /** + * Called when the engine encounters the debugger keyword. + */ + attribute jsdIExecutionHook debuggerHook; + /** + * Called when the errorHook returns false. + */ + attribute jsdIExecutionHook debugHook; + /** + * Called before the next PC is executed. + */ + attribute jsdIExecutionHook interruptHook; + /** + * Called when an exception is thrown (even if it will be caught.) + */ + attribute jsdIExecutionHook throwHook; + /** + * Called before and after a toplevel script is evaluated. + */ + attribute jsdICallHook topLevelHook; + /** + * Called before and after a function is called. + */ + attribute jsdICallHook functionHook; + + /** + * Link native frames in call stacks. + */ + const unsigned long ENABLE_NATIVE_FRAMES = 0x01; + /** + * Normally, if a script has a 0 in JSD_SCRIPT_PROFILE_BIT it is included in + * profile data, otherwise it is not profiled. Setting the + * PROFILE_WHEN_SET flag reverses this convention. + */ + const unsigned long PROFILE_WHEN_SET = 0x02; + /** + * Normally, when the script in the top frame of a thread state has a 1 in + * JSD_SCRIPT_DEBUG_BIT, the execution hook is ignored. Setting the + * DEBUG_WHEN_SET flag reverses this convention. + */ + const unsigned long DEBUG_WHEN_SET = 0x04; + /** + * When this flag is set the internal call hook will collect profile data. + */ + const unsigned long COLLECT_PROFILE_DATA = 0x08; + /** + * When this flag is set, stack frames that are disabled for debugging + * will not appear in the call stack chain. + */ + const unsigned long HIDE_DISABLED_FRAMES = 0x10; + /** + * When this flag is set, the debugger will only check the + * JSD_SCRIPT_DEBUG_BIT on the top (most recent) stack frame. This + * makes it possible to stop in an enabled frame which was called from + * a stack that contains a disabled frame. + * + * When this flag is *not* set, any stack that contains a disabled frame + * will not be debugged (the execution hook will not be invoked.) + * + * This only applies when the reason for calling the hook would have + * been TYPE_INTERRUPTED or TYPE_THROW. TYPE_BREAKPOINT, + * TYPE_DEBUG_REQUESTED, and TYPE_DEBUGGER_KEYWORD always stop, regardless + * of this setting, as long as the top frame is not disabled. + * + * If HIDE_DISABLED_FRAMES is set, this is effectively set as well. + */ + const unsigned long MASK_TOP_FRAME_ONLY = 0x20; + /** + * This flag has been retired, do not re-use. It previously provided a hook + * for object allocation. + */ + const unsigned long DISABLE_OBJECT_TRACE_RETIRED = 0x40; + + /** + * Debugger service flags. + */ + attribute unsigned long flags; + + /** + * Major version number of implementation. + */ + readonly attribute unsigned long implementationMajor; + /** + * Minor version number of implementation. + */ + readonly attribute unsigned long implementationMinor; + /** + * Free form AUTF8String identifier for implementation. + */ + readonly attribute AUTF8String implementationString; + + /** + * |true| if the debugger service has been turned on. This does not + * necessarily mean another app is actively using the service, as the + * autostart pref may have turned the service on. + */ + readonly attribute boolean isOn; + + + /** + * Synchronous activation of the debugger is no longer supported, + * and will throw an exception. + */ + void on(); + + /** + * Turn on the debugger. This function should only be called from + * JavaScript code. The debugger will be enabled on the runtime the call is + * made on, as determined by nsIXPCNativeCallContext. + * + * The debugger will be activated asynchronously, because there can be no + * JS on the stack when code is to be re-compiled for debug mode. + */ + void asyncOn(in jsdIActivationCallback callback); + + /** + * Called by nsIXPConnect after it's had a chance to recompile for + * debug mode. + */ + [noscript] void activateDebugger(in JSRuntime rt); + + /** + * Called by nsIXPConnect to deactivate debugger on setup failure. + */ + [noscript] void deactivateDebugger(); + + /** + * Recompile all active scripts in the runtime for debugMode. + */ + [noscript] void recompileForDebugMode(in JSContext cx, in JSCompartment comp, in boolean mode); + + /** + * Turn the debugger off. This will invalidate all of your jsdIEphemeral + * derived objects, and clear all of your breakpoints. + */ + void off (); + + /** + * Peek at the current pause depth of the debugger. + * + * @return depth Number of pause() calls still waiting to be unPause()d. + */ + readonly attribute unsigned long pauseDepth; + /** + * Temporarily disable the debugger. Hooks will not be called while the + * debugger is paused. Multiple calls to pause will increase the "pause + * depth", and equal number of unPause calles must be made to resume + * normal debugging. + * + * @return depth Number of times pause has been called since the debugger + * has been unpaused. + */ + unsigned long pause(); + /** + * Undo a pause. Once this is called, the debugger won't start + * getting execution callbacks until the stack is fully unwound so + * that no JS scripts are live. There is no way to query whether + * there are such scripts left to unwind at a given point in time. + * + * @return depth The number of remaining pending pause calls. + */ + unsigned long unPause(); + + /** + * Force the engine to perform garbage collection. + */ + void GC(); + + /** + * Clear profile data for all scripts. + */ + void clearProfileData(); + + /** + * Adds an execution hook filter. These filters are consulted each time one + * of the jsdIExecutionHooks is about to be called. Filters are matched in + * a first in, first compared fashion. The first filter to match determines + * whether or not the hook is called. Use swapFilter to reorder existing + * filters, and removeFilter to remove them. + * + * If |filter| is already present this method throws NS_ERROR_INVALID_ARG. + * + * @param filter Object representing the filter to add. + * @param after Insert |filter| after this one. Pass null to insert at + * the beginning. + */ + void insertFilter(in jsdIFilter filter, in jsdIFilter after); + /** + * Same as insertFilter, except always add to the end of the list. + */ + void appendFilter(in jsdIFilter filter); + /** + * Remove a filter. + * + * If |filter| is not present this method throws NS_ERROR_INVALID_ARG. + * + * @param filter Object representing the filter to remove. Must be the exact + * object passed to addFilter, not just a new object with the same + * properties. + */ + void removeFilter(in jsdIFilter filter); + /** + * Swap position of two filters. + * + * If |filter_a| is not present, this method throws NS_ERROR_INVALID_ARG. + * If |filter_b| is not present, filter_a is replaced by filter_b. + * If |filter_a| == |filter_b|, then filter is refreshed. + */ + void swapFilters(in jsdIFilter filter_a, in jsdIFilter filter_b); + /** + * Enumerate registered filters. This routine refreshes each filter before + * passing them on to the enumeration function. Calling this with a null + * |enumerator| is equivalent to jsdIService::refreshFilters. + * + * @param enumerator jsdIFilterEnumerator instance to be called back for the + * enumeration. + */ + void enumerateFilters(in jsdIFilterEnumerator enumerator); + /** + * Force the debugger to resync its internal filter cache with the + * actual values in the jsdIFilter objects. To refresh a single filter + * use jsdIService::swapFilters. This method is equivalent to + * jsdIService::enumerateFilters with a null enumerator. + */ + void refreshFilters(); + /** + * Clear the list of filters. + */ + void clearFilters(); + + /** + * Enumerate all known contexts. + */ + void enumerateContexts(in jsdIContextEnumerator enumerator); + + /** + * Enumerate all scripts the debugger knows about. Any scripts created + * before you turned the debugger on, or after turning the debugger off + * will not be available unless the autostart perf is set. + * + * @param enumerator jsdIScriptEnumerator instance to be called back for + * the enumeration. + */ + void enumerateScripts(in jsdIScriptEnumerator enumerator); + /** + * Clear all breakpoints in all scripts. + */ + void clearAllBreakpoints(); + + /** + * When called from JavaScript, this method returns the jsdIValue wrapper + * for the given value. If a wrapper does not exist one will be created. + * When called from another language this method returns an xpconnect + * defined error code. + */ + jsdIValue wrapValue(in jsval value); + + /* XXX these two routines are candidates for refactoring. The only problem + * is that it is not clear where and how they should land. + */ + + /** + * Push a new network queue, and enter a new UI event loop. + * @param callback jsdINestCallback instance to be called back after the + * network queue has been pushed, but before the + * UI loop starts. + * @return depth returns the current number of times the event loop has been + * nested. your code can use it for sanity checks. + */ + unsigned long enterNestedEventLoop(in jsdINestCallback callback); + /** + * Exit the current nested event loop after the current iteration completes, + * and pop the network event queue. + * + * @return depth returns the current number of times the event loop has been + * nested. your code can use it for sanity checks. + */ + unsigned long exitNestedEventLoop(); + + /** + * Output dump of JS heap. + * + * @param fileName Filename to dump the heap into. + */ + void dumpHeap(in AUTF8String fileName); +}; + +/* callback interfaces */ + +/** + * Object representing a pattern of global object and/or url the debugger should + * ignore. The debugger service itself will not modify properties of these + * objects. + */ +[scriptable, uuid(9ae587cd-b78c-47f0-a612-4b3a211a6a71)] +interface jsdIFilter : nsISupports +{ + /** + * These two bytes of the flags attribute are reserved for interpretation + * by the jsdService implementation. You can do what you like with the + * remaining flags. + */ + const unsigned long FLAG_RESERVED_MASK = 0xFF; + /** + * Filters without this flag set are ignored. + */ + const unsigned long FLAG_ENABLED = 0x01; + /** + * Filters with this flag set are "pass" filters, they allow matching hooks + * to continue. Filters without this flag block matching hooks. + */ + const unsigned long FLAG_PASS = 0x02; + + /** + * FLAG_* values from above, OR'd together. + */ + attribute unsigned long flags; + + /** + * String representing the url pattern to be filtered. Supports limited + * glob matching, at the beginning and end of the pattern only. For example, + * "chrome://venkman*" filters all urls that start with chrome/venkman, + * "*.cgi" filters all cgi's, and "http://myserver/utils.js" filters only + * the utils.js file on "myserver". A null urlPattern matches all urls. + * + * The jsdIService caches this value internally, to if it changes you must + * swap the filter with itself using jsdIService::swapFilters. + */ + attribute AUTF8String urlPattern; + + /** + * Line number for the start of this filter. Line numbers are one based. + * Assigning a 0 to this attribute will tell the debugger to ignore the + * entire file. + */ + attribute unsigned long startLine; + + /** + * Line number for the end of this filter. Line numbers are one based. + * Assigning a 0 to this attribute will tell the debugger to ignore from + * |startLine| to the end of the file. + */ + attribute unsigned long endLine; +}; + +/** + * Notify client code that debugMode has been activated. + */ +[scriptable, function, uuid(6da7f5fb-3a84-4abe-9e23-8b2045960732)] +interface jsdIActivationCallback : nsISupports +{ + void onDebuggerActivated(); +}; + +/** + * Pass an instance of one of these to jsdIDebuggerService::enterNestedEventLoop. + */ +[scriptable, function, uuid(88bea60f-9b5d-4b39-b08b-1c3a278782c6)] +interface jsdINestCallback : nsISupports +{ + /** + * This method will be called after pre-nesting work has completed, such + * as pushing the js context and network event queue, but before the new + * event loop starts. + */ + void onNest(); +}; + +/** + * Pass an instance of one of these to jsdIDebuggerService::enumerateFilters. + */ +[scriptable, function, uuid(e391ba85-9379-4762-b387-558e38db730f)] +interface jsdIFilterEnumerator : nsISupports +{ + /** + * The enumerateFilter method will be called once for every filter the + * debugger knows about. + */ + void enumerateFilter(in jsdIFilter filter); +}; + +/** + * Pass an instance of one of these to jsdIDebuggerService::enumerateScripts. + */ +[scriptable, function, uuid(4eef60c2-9bbc-48fa-b196-646a832c6c81)] +interface jsdIScriptEnumerator : nsISupports +{ + /** + * The enumerateScript method will be called once for every script the + * debugger knows about. + */ + void enumerateScript(in jsdIScript script); +}; + +/** + * Pass an instance of one of these to jsdIDebuggerService::enumerateContexts. + */ +[scriptable, function, uuid(57d18286-550c-4ca9-ac33-56f12ebba91e)] +interface jsdIContextEnumerator : nsISupports +{ + /** + * The enumerateContext method will be called once for every context + * currently in use. + */ + void enumerateContext(in jsdIContext executionContext); +}; + +/** + * Set jsdIDebuggerService::scriptHook to an instance of one of these. + */ +[scriptable, uuid(d030d1a2-a58a-4f19-b9e3-96da4e2cdd09)] +interface jsdIScriptHook : nsISupports +{ + /** + * Called when scripts are created. + */ + void onScriptCreated(in jsdIScript script); + /** + * Called when the JavaScript engine destroys a script. The jsdIScript + * object passed in will already be invalidated. + */ + void onScriptDestroyed(in jsdIScript script); +}; + +/** + * Hook instances of this interface up to the + * jsdIDebuggerService::functionHook and toplevelHook properties. + */ +[scriptable, function, uuid(3eff1314-7ae3-4cf8-833b-c33c24a55633)] +interface jsdICallHook : nsISupports +{ + /** + * TYPE_* values must be kept in sync with the JSD_HOOK_* #defines + * in jsdebug.h. + */ + + /** + * Toplevel script is starting. + */ + const unsigned long TYPE_TOPLEVEL_START = 0; + /** + * Toplevel script has completed. + */ + const unsigned long TYPE_TOPLEVEL_END = 1; + /** + * Function is being called. + */ + const unsigned long TYPE_FUNCTION_CALL = 2; + /** + * Function is returning. + */ + const unsigned long TYPE_FUNCTION_RETURN = 3; + + /** + * Called before the JavaScript engine executes a top level script or calls + * a function. + */ + void onCall(in jsdIStackFrame frame, in unsigned long type); +}; + +[scriptable, function, uuid(e6b45eee-d974-4d85-9d9e-f5a67218deb4)] +interface jsdIErrorHook : nsISupports +{ + /** + * REPORT_* values must be kept in sync with JSREPORT_* #defines in + * jsapi.h + */ + + /** + * Report is an error. + */ + const unsigned long REPORT_ERROR = 0x00; + /** + * Report is only a warning. + */ + const unsigned long REPORT_WARNING = 0x01; + /** + * Report represents an uncaught exception. + */ + const unsigned long REPORT_EXCEPTION = 0x02; + /** + * Report is due to strict mode. + */ + const unsigned long REPORT_STRICT = 0x04; + + /** + * Called when the JavaScript engine encounters an error. Return |true| + * to pass the error along, |false| to invoke the debugHook. + */ + boolean onError(in AUTF8String message, in AUTF8String fileName, + in unsigned long line, in unsigned long pos, + in unsigned long flags, in unsigned long errnum, + in jsdIValue exc); +}; + +/** + * Hook instances of this interface up to the + * jsdIDebuggerService::breakpointHook, debuggerHook, errorHook, interruptHook, + * and throwHook properties. + */ +[scriptable, function, uuid(3a722496-9d78-4f0a-a797-293d9e8cb8d2)] +interface jsdIExecutionHook : nsISupports +{ + /** + * TYPE_* values must be kept in sync with JSD_HOOK_* #defines in jsdebug.h. + */ + + /** + * Execution stopped because we're in single step mode. + */ + const unsigned long TYPE_INTERRUPTED = 0; + /** + * Execution stopped by a trap instruction (i.e. breakoint.) + */ + const unsigned long TYPE_BREAKPOINT = 1; + /** + * Error handler returned an "invoke debugger" value. + */ + const unsigned long TYPE_DEBUG_REQUESTED = 2; + /** + * Debugger keyword encountered. + */ + const unsigned long TYPE_DEBUGGER_KEYWORD = 3; + /** + * Exception was thrown. + */ + const unsigned long TYPE_THROW = 4; + + /** + * RETURN_* values must be kept in sync with JSD_HOOK_RETURN_* #defines in + * jsdebug.h. + */ + + /** + * Indicates unrecoverable error processing the hook. This will cause + * the script being executed to be aborted without raising a JavaScript + * exception. + */ + const unsigned long RETURN_HOOK_ERROR = 0; + /** + * Continue processing normally. This is the "do nothing special" return + * value for all hook types *except* TYPE_THROW. Returning RETURN_CONTINUE + * from TYPE_THROW cause the exception to be ignored. Return + * RETURN_CONTINUE_THROW to continue exception processing from TYPE_THROW + * hooks. + */ + const unsigned long RETURN_CONTINUE = 1; + /** + * Same effect as RETURN_HOOK_ERROR. + */ + const unsigned long RETURN_ABORT = 2; + /** + * Return the value of the |val| parameter. + */ + const unsigned long RETURN_RET_WITH_VAL = 3; + /** + * Throw the value of the |val| parameter. + */ + const unsigned long RETURN_THROW_WITH_VAL = 4; + /** + * Continue the current throw. + */ + const unsigned long RETURN_CONTINUE_THROW = 5; + + /** + * @param frame A jsdIStackFrame object representing the bottom stack frame. + * @param type One of the jsdIExecutionHook::TYPE_ constants. + * @param val in - Current exception (if any) when this method is called. + * out - If you return RETURN_THROW_WITH_VAL, value to be + * thrown. + * If you return RETURN_RET_WITH_VAL, value to return. + * All other return values, not significant. + * @retval One of the jsdIExecutionHook::RETURN_* constants. + */ + unsigned long onExecute(in jsdIStackFrame frame, + in unsigned long type, inout jsdIValue val); +}; + +/** + * Objects which inherit this interface may go away, with (jsdIScript) or + * without (all others) notification. These objects are generally wrappers + * around JSD structures that go away when you call jsdService::Off(). + */ +[scriptable, uuid(46f1e23e-1dd2-11b2-9ceb-8285f2e95e69)] +interface jsdIEphemeral : nsISupports +{ + /** + * |true| if this object is still valid. If not, many or all of the methods + * and/or properties of the inheritor may no longer be callable. + */ + readonly attribute boolean isValid; + /** + * Mark this instance as invalid. + */ + [noscript] void invalidate(); +}; + +/* handle objects */ + +/** + * Context object. Only context's which are also nsISupports objects can be + * reflected by this interface. + */ +[scriptable, uuid(3e5c934d-6863-4d81-96f5-76a3b962fc2b)] +interface jsdIContext : jsdIEphemeral +{ + /* Internal use only. */ + [noscript] readonly attribute JSContext JSContext; + + /** + * OPT_* values must be kept in sync with JSOPTION_* #defines in jsapi.h. + */ + + /** + * Strict mode is on. + */ + const long OPT_STRICT = 0x01; + /** + * Warnings reported as errors. + */ + const long OPT_WERR = 0x02; + /** + * Makes eval() use the last object on its 'obj' param's scope chain as the + * ECMA 'variables object'. + */ + const long OPT_VAROBJFIX = 0x04; + /** + * Private data for this object is an nsISupports object. Attempting to + * alter this bit will result in an NS_ERROR_ILLEGAL_VALUE. + */ + const long OPT_ISUPPORTS = 0x08; + /** + * OPT_* values above, OR'd together. + */ + attribute unsigned long options; + + /** + * Unique tag among all valid jsdIContext objects, useful as a hash key. + */ + readonly attribute unsigned long tag; + + /** + * Private data for this context, if it is an nsISupports, |null| otherwise. + */ + readonly attribute nsISupports privateData; + + /** + * Retrieve the underlying context wrapped by this jsdIContext. + */ + readonly attribute nsISupports wrappedContext; + + /** + * Top of the scope chain for this context. + */ + readonly attribute jsdIValue globalObject; + + /** + * |true| if this context should be allowed to run scripts, |false| + * otherwise. This attribute is only valid for contexts which implement + * nsIScriptContext. Setting or getting this attribute on any other + * context will throw a NS_ERROR_NO_INTERFACE exception. + */ + attribute boolean scriptsEnabled; +}; + +/** + * Stack frame objects. These are only valid inside the jsdIExecutionHook which + * gave it to you. After you return from that handler the bottom frame, and any + * frame you found attached through it, are invalidated via the jsdIEphemeral + * interface. Once a jsdIStackFrame has been invalidated all method and + * property accesses will throw a NS_ERROR_NOT_AVAILABLE exception. + */ +[scriptable, uuid(7c95422c-7579-4a6f-8ef7-e5b391552ee5)] +interface jsdIStackFrame : jsdIEphemeral +{ + /** Internal use only. */ + [noscript] readonly attribute JSDContext JSDContext; + /** Internal use only. */ + [noscript] readonly attribute JSDThreadState JSDThreadState; + /** Internal use only. */ + [noscript] readonly attribute JSDStackFrameInfo JSDStackFrameInfo; + + /** + * True if stack frame represents a frame created as a result of a debugger + * evaluation. + */ + readonly attribute boolean isDebugger; + /** + * True if stack frame is constructing a new object. + */ + readonly attribute boolean isConstructing; + + /** + * Link to the caller's stack frame. + */ + readonly attribute jsdIStackFrame callingFrame; + /** + * Executon context. + */ + readonly attribute jsdIContext executionContext; + /** + * Function name executing in this stack frame. + */ + readonly attribute AUTF8String functionName; + /** + * Script running in this stack frame, null for native frames. + */ + readonly attribute jsdIScript script; + /** + * Current program counter in this stack frame. + */ + readonly attribute unsigned long pc; + /** + * Current line number (using the script's pc to line map.) + */ + readonly attribute unsigned long line; + /** + * Function object running in this stack frame. + */ + readonly attribute jsdIValue callee; + /** + * Top object in the scope chain. + */ + readonly attribute jsdIValue scope; + /** + * |this| object for this stack frame. + */ + readonly attribute jsdIValue thisValue; + /** + * Evaluate arbitrary JavaScript in this stack frame. + * @param bytes Script to be evaluated. + * @param fileName Filename to compile this script under. This is the + * filename you'll see in error messages, etc. + * @param line Starting line number for this script. One based. + * @retval Result of evaluating the script. + */ + boolean eval(in AString bytes, in AUTF8String fileName, + in unsigned long line, out jsdIValue result); + +}; + +/** + * Script object. In JavaScript engine terms, there's a single script for each + * function, and one for the top level script. + */ +[scriptable, uuid(8ce9b2a2-cc33-48a8-9f47-8696186ed9a5)] +interface jsdIScript : jsdIEphemeral +{ + /** Internal use only. */ + [noscript] readonly attribute JSDContext JSDContext; + /** Internal use only. */ + [noscript] readonly attribute JSDScript JSDScript; + + /** + * Last version set on this context. + * Scripts typically select this with the "language" attribute. + * See the VERSION_* consts on jsdIDebuggerService. + */ + readonly attribute long version; + + /** + * Tag value guaranteed unique among jsdIScript objects. Useful as a + * hash key in script. + */ + readonly attribute unsigned long tag; + + /** + * FLAG_* values need to be kept in sync with JSD_SCRIPT_* #defines in + * jsdebug.h. + */ + + /** + * Determines whether or not to collect profile information for this + * script. The context flag FLAG_PROFILE_WHEN_SET decides the logic. + */ + const unsigned long FLAG_PROFILE = 0x01; + /** + * Determines whether or not to ignore breakpoints, etc. in this script. + * The context flag JSD_DEBUG_WHEN_SET decides the logic. + */ + const unsigned long FLAG_DEBUG = 0x02; + /** + * Determines whether to invoke the onScriptDestroy callback for this + * script. The default is for this to be true if the onScriptCreated + * callback was invoked for this script. + */ + const unsigned long FLAG_CALL_DESTROY_HOOK = 0x04; + + /** + * FLAG_* attributes from above, OR'd together. + */ + attribute unsigned long flags; + + /** + * Filename given for this script when it was compiled. + * This data is copied from the underlying structure when the jsdIScript + * instance is created and is therefore available even after the script is + * invalidated. + */ + readonly attribute AUTF8String fileName; + /** + * Function name for this script. "anonymous" for unnamed functions (or + * a function actually named anonymous), empty for top level scripts. + * This data is copied from the underlying structure when the jsdIScript + * instance is created and is therefore available even after the script is + * invalidated. + */ + readonly attribute AUTF8String functionName; + /** + * The names of the arguments for this function; empty if this is + * not a function. + */ + void getParameterNames([optional] out unsigned long count, + [array, size_is(count), retval] out wstring paramNames); + /** + * Fetch the function object as a jsdIValue. + */ + readonly attribute jsdIValue functionObject; + /** + * Source code for this script, without function declaration. + */ + readonly attribute AString functionSource; + /** + * Line number in source file containing the first line of this script. + * This data is copied from the underlying structure when the jsdIScript + * instance is created and is therefore available even after the script is + * invalidated. + */ + readonly attribute unsigned long baseLineNumber; + /** + * Total number of lines in this script. + * This data is copied from the underlying structure when the jsdIScript + * instance is created and is therefore available even after the script is + * invalidated. + */ + readonly attribute unsigned long lineExtent; + + /** + * Number of times this script has been called. + */ + readonly attribute unsigned long callCount; + /** + * Number of times this script called itself, directly or indirectly. + */ + readonly attribute unsigned long maxRecurseDepth; + /** + * Shortest execution time recorded, in milliseconds. + */ + readonly attribute double minExecutionTime; + /** + * Longest execution time recorded, in milliseconds. + */ + readonly attribute double maxExecutionTime; + /** + * Total time spent in this function, in milliseconds. + */ + readonly attribute double totalExecutionTime; + /** + * Shortest execution time recorded, in milliseconds, excluding time spent + * in other called code. + */ + readonly attribute double minOwnExecutionTime; + /** + * Longest execution time recorded, in milliseconds, excluding time spent + * in other called code. + */ + readonly attribute double maxOwnExecutionTime; + /** + * Total time spent in this function, in milliseconds, excluding time spent + * in other called code. + */ + readonly attribute double totalOwnExecutionTime; + + /** + * Clear profile data for this script. + */ + void clearProfileData(); + + const unsigned long PCMAP_SOURCETEXT = 1; /* map to actual source text */ + const unsigned long PCMAP_PRETTYPRINT = 2; /* map to pretty printed source */ + + /** + * Get the closest line number to a given PC. + * The |pcmap| argument specifies which pc to source line map to use. + */ + unsigned long pcToLine(in unsigned long pc, in unsigned long pcmap); + /** + * Get the first PC associated with a line. + * The |pcmap| argument specifies which pc to source line map to use. + */ + unsigned long lineToPc(in unsigned long line, in unsigned long pcmap); + /** + * Determine is a particular line is executable, like checking that + * lineToPc == pcToLine, except in one call. + * The |pcmap| argument specifies which pc to source line map to use. + */ + boolean isLineExecutable(in unsigned long line, in unsigned long pcmap); + + /** + * Return a list of all executable lines in a script. + * |pcmap| specifies which pc to source line map to use. + * |startLine| and |maxLines| may be used to retrieve a chunk at a time. + */ + void getExecutableLines(in unsigned long pcmap, + in unsigned long startLine, in unsigned long maxLines, + [optional] out unsigned long count, + [array, size_is(count), retval] out unsigned long executableLines); + + /** + * Set a breakpoint at a PC in this script. + */ + void setBreakpoint(in unsigned long pc); + /** + * Clear a breakpoint at a PC in this script. + */ + void clearBreakpoint(in unsigned long pc); + /** + * Clear all breakpoints set in this script. + */ + void clearAllBreakpoints(); + /** + * Call interrupt hook at least once per source line + */ + void enableSingleStepInterrupts(in boolean mode); +}; + +/** + * Value objects. Represents typeless JavaScript values (jsval in SpiderMonkey + * terminology.) These are valid until the debugger is turned off. Holding a + * jsdIValue adds a root for the underlying JavaScript value, so don't keep it + * if you don't need to. + */ +[scriptable, uuid(1cd3535b-4ddb-4202-9053-e0ec88f5c82b)] +interface jsdIValue : jsdIEphemeral +{ + /** Internal use only. */ + [noscript] readonly attribute JSDContext JSDContext; + /** Internal use only. */ + [noscript] readonly attribute JSDValue JSDValue; + + /** + * |false| unless the value is a function declared in script. + */ + readonly attribute boolean isNative; + /** + * |true| if the value represents a number, either double or integer. + * |false| for all other values, including numbers assigned as strings + * (eg. x = "1";) + */ + readonly attribute boolean isNumber; + /** + * |true| if the value represents a JavaScript primitive number or AUTF8String + */ + readonly attribute boolean isPrimitive; + + /** Value is either |true| or |false|. */ + const unsigned long TYPE_BOOLEAN = 0; + /** Value is a primitive number that is too large to fit in an integer. */ + const unsigned long TYPE_DOUBLE = 1; + /** Value is a primitive number that fits into an integer. */ + const unsigned long TYPE_INT = 2; + /** Value is a function. */ + const unsigned long TYPE_FUNCTION = 3; + /** Value is |null|. */ + const unsigned long TYPE_NULL = 4; + /** Value is an object. */ + const unsigned long TYPE_OBJECT = 5; + /** Value is a primitive AUTF8String. */ + const unsigned long TYPE_STRING = 6; + /** Value is void. */ + const unsigned long TYPE_VOID = 7; + + /** + * One of the TYPE_* values above. + */ + readonly attribute unsigned long jsType; + /** + * Prototype value if this value represents an object, null if the value is + * not an object or the object has no prototype. + */ + readonly attribute jsdIValue jsPrototype; + /** + * Parent value if this value represents an object, null if the value is not + * an object or the object has no parent. + */ + readonly attribute jsdIValue jsParent; + /** + * Class name if this value represents an object. Empty AUTF8String if the value + * is not an object. + */ + readonly attribute AUTF8String jsClassName; + /** + * Constructor name if this value represents an object. Empty AUTF8String if the + * value is not an object. + */ + readonly attribute jsdIValue jsConstructor; + /** + * Function name if this value represents a function. Empty AUTF8String if the + * value is not a function. + */ + readonly attribute AUTF8String jsFunctionName; + + /** + * Value if interpreted as a boolean. Converts if necessary. + */ + readonly attribute boolean booleanValue; + /** + * Value if interpreted as a double. Converts if necessary. + */ + readonly attribute double doubleValue; + /** + * Value if interpreted as an integer. Converts if necessary. + */ + readonly attribute long intValue; + /** + * Value if interpreted as an object. + */ + readonly attribute jsdIObject objectValue; + /** + * Value if interpreted as a AUTF8String. Converts if necessary. + */ + readonly attribute AUTF8String stringValue; + + /** + * Number of properties. 0 if the value is not an object, or the value is + * an object but has no properties. + */ + readonly attribute long propertyCount; + + /** + * Retrieves all properties if this value represents an object. If this + * value is not an object a 0 element array is returned. + * @param propArray Array of jsdIProperty values for this value. + * @param length Size of array. + */ + void getProperties([array, size_is(length)] out jsdIProperty propArray, + out unsigned long length); + /** + * Retrieves a single property from the value. Only valid if the value + * represents an object. + * @param name Name of the property to retrieve. + * @retval jsdIProperty for the requested property name or null if no + * property exists for the requested name. + */ + jsdIProperty getProperty(in AUTF8String name); + + /** + * jsdIValues are wrappers around JavaScript engine structures. Much of the + * data is copied instead of shared. The refresh method is used to resync + * the jsdIValue with the underlying structure. + */ + void refresh(); + + /** + * When called from JavaScript, this method returns the JavaScript value + * wrapped by this jsdIValue. The calling script is free to use the result + * as it would any other JavaScript value. + * When called from another language this method returns an xpconnect + * defined error code. + */ + [implicit_jscontext] jsval getWrappedValue(); + + /** + * If this is a function value, return its associated jsdIScript. + * Otherwise, return null. + */ + readonly attribute jsdIScript script; +}; + +/** + * Properties specific to values which are also objects. + * XXX We don't add roots for these yet, so make sure you hold on to the + * jsdIValue from whence your jsdIObject instance came for at least as long as + * you hold the jsdIObject. + * XXX Maybe the jsClassName, jsConstructorName, and property related attribute/ + * functions from jsdIValue should move to this interface. We could inherit from + * jsdIValue or use interface flattening or something. + */ +[scriptable, uuid(87d86308-7a27-4255-b23c-ce2394f02473)] +interface jsdIObject : nsISupports +{ + /** Internal use only. */ + [noscript] readonly attribute JSDContext JSDContext; + /** Internal use only. */ + [noscript] readonly attribute JSDObject JSDObject; + + /** + * The URL (filename) that contains the script which caused this object + * to be created. + */ + readonly attribute AUTF8String creatorURL; + /** + * Line number in the creatorURL where this object was created. + */ + readonly attribute unsigned long creatorLine; + /** + * The URL (filename) that contains the script which defined the constructor + * used to create this object. + */ + readonly attribute AUTF8String constructorURL; + /** + * Line number in the creatorURL where this object was created. + */ + readonly attribute unsigned long constructorLine; + /** + * jsdIValue for this object. + */ + readonly attribute jsdIValue value; +}; + +/** + * Representation of a property of an object. When an instance is invalid, all + * method and property access will result in a NS_UNAVAILABLE error. + */ +[scriptable, uuid(acf1329e-aaf6-4d6a-a1eb-f75858566f09)] +interface jsdIProperty : jsdIEphemeral +{ + /** Internal use only. */ + [noscript] readonly attribute JSDContext JSDContext; + /** Internal use only. */ + [noscript] readonly attribute JSDProperty JSDProperty; + + /** + * FLAG_* values must be kept in sync with JSDPD_* #defines in jsdebug.h. + */ + + /** visible to for/in loop */ + const unsigned long FLAG_ENUMERATE = 0x01; + /** assignment is error */ + const unsigned long FLAG_READONLY = 0x02; + /** property cannot be deleted */ + const unsigned long FLAG_PERMANENT = 0x04; + /** property has an alias id */ + const unsigned long FLAG_ALIAS = 0x08; + /** argument to function */ + const unsigned long FLAG_ARGUMENT = 0x10; + /** local variable in function */ + const unsigned long FLAG_VARIABLE = 0x20; + /** exception occurred looking up property, value is exception */ + const unsigned long FLAG_EXCEPTION = 0x40; + /** native getter returned JS_FALSE without throwing an exception */ + const unsigned long FLAG_ERROR = 0x80; + /** found via explicit lookup (property defined elsewhere.) */ + const unsigned long FLAG_HINTED = 0x800; + + /** FLAG_* values OR'd together, representing the flags for this property. */ + readonly attribute unsigned long flags; + /** jsdIValue representing the alias for this property. */ + readonly attribute jsdIValue alias; + /** name for this property. */ + readonly attribute jsdIValue name; + /** value of this property. */ + readonly attribute jsdIValue value; +}; diff --git a/js/jsd/idl/moz.build b/js/jsd/idl/moz.build new file mode 100644 index 0000000..4300fd3 --- /dev/null +++ b/js/jsd/idl/moz.build @@ -0,0 +1,14 @@ +# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*- +# vim: set filetype=python: +# 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/. + +XPIDL_SOURCES += [ + 'jsdIDebuggerService.idl', +] + +XPIDL_MODULE = 'jsdservice' + +MODULE = 'jsdebug' + diff --git a/js/jsd/jsd.h b/js/jsd/jsd.h new file mode 100644 index 0000000..91ebf5e --- /dev/null +++ b/js/jsd/jsd.h @@ -0,0 +1,1109 @@ +/* -*- 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/. */ + +/* + * Header for JavaScript Debugging support - Internal ONLY declarations + */ + +#ifndef jsd_h___ +#define jsd_h___ + +/* +* NOTE: This is a *private* header file and should only be included by +* the sources in js/jsd. Defining EXPORT_JSD_API in an outside module +* using jsd would be bad. +*/ +#define EXPORT_JSD_API 1 /* if used, must be set before include of jsdebug.h */ + +/* +* These can be controled by the makefile, but this allows a place to set +* the values always used in the mozilla client, but perhaps done differently +* in other embeddings. +*/ +#ifdef MOZILLA_CLIENT +#define JSD_THREADSAFE 1 +/* define JSD_HAS_DANGEROUS_THREAD 1 */ +#define JSD_USE_NSPR_LOCKS 1 +#endif /* MOZILLA_CLIENT */ + +#include "jstypes.h" +#include "jsprf.h" +#include "jsutil.h" /* Added by JSIFY */ +#include "jshash.h" /* Added by JSIFY */ +#include "jsclist.h" +#include "jsdebug.h" +#include "jsapi.h" +#include "jsdbgapi.h" +#include "jsd_lock.h" + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#ifdef LIVEWIRE +#include <base/pblock.h> +#include <base/session.h> +#include <frame/log.h> +#include <frame/req.h> +#endif /* LIVEWIRE */ + +#define JSD_MAJOR_VERSION 1 +#define JSD_MINOR_VERSION 1 + +/***************************************************************************/ +/* handy macros */ +#undef CHECK_BIT_FLAG +#define CHECK_BIT_FLAG(f,b) ((f)&(b)) +#undef SET_BIT_FLAG +#define SET_BIT_FLAG(f,b) ((f)|=(b)) +#undef CLEAR_BIT_FLAG +#define CLEAR_BIT_FLAG(f,b) ((f)&=(~(b))) + +#define JSD_IS_DEBUG_ENABLED(jsdc,jsdscript) \ + (!(((jsdc->flags & JSD_DEBUG_WHEN_SET) ? 1 : 0) ^ \ + ((jsdscript->flags & JSD_SCRIPT_DEBUG_BIT) ? 1 : 0))) +#define JSD_IS_PROFILE_ENABLED(jsdc,jsdscript) \ + ((jsdc->flags & JSD_COLLECT_PROFILE_DATA) && \ + (!(((jsdc->flags & JSD_PROFILE_WHEN_SET) ? 1 : 0) ^ \ + ((jsdscript->flags & JSD_SCRIPT_PROFILE_BIT) ? 1 : 0)))) + + +/***************************************************************************/ +/* These are not exposed in jsdebug.h - typedef here for consistency */ + +typedef struct JSDExecHook JSDExecHook; +typedef struct JSDAtom JSDAtom; +typedef struct JSDProfileData JSDProfileData; +/***************************************************************************/ +/* Our structures */ + +/* +* XXX What I'm calling a JSDContext is really more of a JSDTaskState. +*/ + +struct JSDContext +{ + JSCList links; /* we are part of a JSCList */ + JSBool inited; + void* data; + uint32_t flags; + JSD_ScriptHookProc scriptHook; + void* scriptHookData; + JSD_ExecutionHookProc interruptHook; + void* interruptHookData; + JSRuntime* jsrt; + JSD_ErrorReporter errorReporter; + void* errorReporterData; + JSCList threadsStates; + JSD_ExecutionHookProc debugBreakHook; + void* debugBreakHookData; + JSD_ExecutionHookProc debuggerHook; + void* debuggerHookData; + JSD_ExecutionHookProc throwHook; + void* throwHookData; + JSD_CallHookProc functionHook; + void* functionHookData; + JSD_CallHookProc toplevelHook; + void* toplevelHookData; + JSObject* glob; + JSD_UserCallbacks userCallbacks; + void* user; + JSCList scripts; + JSHashTable* scriptsTable; + JSCList sources; + JSCList removedSources; + unsigned sourceAlterCount; + JSHashTable* atoms; + JSCList objectsList; + JSHashTable* objectsTable; + JSDProfileData* callingFunctionPData; + int64_t lastReturnTime; +#ifdef JSD_THREADSAFE + JSDStaticLock* scriptsLock; + JSDStaticLock* sourceTextLock; + JSDStaticLock* objectsLock; + JSDStaticLock* atomsLock; + JSDStaticLock* threadStatesLock; +#endif /* JSD_THREADSAFE */ +#ifdef JSD_HAS_DANGEROUS_THREAD + void* dangerousThread; +#endif /* JSD_HAS_DANGEROUS_THREAD */ + +}; + +struct JSDScript +{ + JSCList links; /* we are part of a JSCList */ + JSDContext* jsdc; /* JSDContext for this jsdscript */ + JSScript* script; /* script we are wrapping */ + unsigned lineBase; /* we cache this */ + unsigned lineExtent; /* we cache this */ + JSCList hooks; /* JSCList of JSDExecHooks for this script */ + char* url; + uint32_t flags; + void* data; + + JSDProfileData *profileData; + +#ifdef LIVEWIRE + LWDBGApp* app; + LWDBGScript* lwscript; +#endif +}; + +struct JSDProfileData +{ + JSDProfileData* caller; + int64_t lastCallStart; + int64_t runningTime; + unsigned callCount; + unsigned recurseDepth; + unsigned maxRecurseDepth; + double minExecutionTime; + double maxExecutionTime; + double totalExecutionTime; + double minOwnExecutionTime; + double maxOwnExecutionTime; + double totalOwnExecutionTime; +}; + +struct JSDSourceText +{ + JSCList links; /* we are part of a JSCList */ + char* url; + char* text; + unsigned textLength; + unsigned textSpace; + JSBool dirty; + JSDSourceStatus status; + unsigned alterCount; + JSBool doingEval; +}; + +struct JSDExecHook +{ + JSCList links; /* we are part of a JSCList */ + JSDScript* jsdscript; + uintptr_t pc; + JSD_ExecutionHookProc hook; + void* callerdata; +}; + +#define TS_HAS_DISABLED_FRAME 0x01 + +struct JSDThreadState +{ + JSCList links; /* we are part of a JSCList */ + JSContext* context; + void* thread; + JSCList stack; + unsigned stackDepth; + unsigned flags; +}; + +struct JSDStackFrameInfo +{ + JSCList links; /* we are part of a JSCList */ + JSDThreadState* jsdthreadstate; + JSDScript* jsdscript; + uintptr_t pc; + bool isConstructing; + JSAbstractFramePtr frame; +}; + +#define GOT_PROTO ((short) (1 << 0)) +#define GOT_PROPS ((short) (1 << 1)) +#define GOT_PARENT ((short) (1 << 2)) +#define GOT_CTOR ((short) (1 << 3)) + +struct JSDValue +{ + jsval val; + int nref; + JSCList props; + JSString* string; + JSString* funName; + const char* className; + JSDValue* proto; + JSDValue* parent; + JSDValue* ctor; + unsigned flags; +}; + +struct JSDProperty +{ + JSCList links; /* we are part of a JSCList */ + int nref; + JSDValue* val; + JSDValue* name; + JSDValue* alias; + unsigned flags; +}; + +struct JSDAtom +{ + char* str; /* must be first element in struct for compare */ + int refcount; +}; + +struct JSDObject +{ + JSCList links; /* we are part of a JSCList */ + JSObject* obj; + JSDAtom* newURL; + unsigned newLineno; + JSDAtom* ctorURL; + unsigned ctorLineno; + JSDAtom* ctorName; +}; + +/***************************************************************************/ +/* Code validation support */ + +#ifdef DEBUG +extern void JSD_ASSERT_VALID_CONTEXT(JSDContext* jsdc); +extern void JSD_ASSERT_VALID_SCRIPT(JSDScript* jsdscript); +extern void JSD_ASSERT_VALID_SOURCE_TEXT(JSDSourceText* jsdsrc); +extern void JSD_ASSERT_VALID_THREAD_STATE(JSDThreadState* jsdthreadstate); +extern void JSD_ASSERT_VALID_STACK_FRAME(JSDStackFrameInfo* jsdframe); +extern void JSD_ASSERT_VALID_EXEC_HOOK(JSDExecHook* jsdhook); +extern void JSD_ASSERT_VALID_VALUE(JSDValue* jsdval); +extern void JSD_ASSERT_VALID_PROPERTY(JSDProperty* jsdprop); +extern void JSD_ASSERT_VALID_OBJECT(JSDObject* jsdobj); +#else +#define JSD_ASSERT_VALID_CONTEXT(x) ((void)0) +#define JSD_ASSERT_VALID_SCRIPT(x) ((void)0) +#define JSD_ASSERT_VALID_SOURCE_TEXT(x) ((void)0) +#define JSD_ASSERT_VALID_THREAD_STATE(x)((void)0) +#define JSD_ASSERT_VALID_STACK_FRAME(x) ((void)0) +#define JSD_ASSERT_VALID_EXEC_HOOK(x) ((void)0) +#define JSD_ASSERT_VALID_VALUE(x) ((void)0) +#define JSD_ASSERT_VALID_PROPERTY(x) ((void)0) +#define JSD_ASSERT_VALID_OBJECT(x) ((void)0) +#endif + +/***************************************************************************/ +/* higher level functions */ + +extern JSDContext* +jsd_DebuggerOnForUser(JSRuntime* jsrt, + JSD_UserCallbacks* callbacks, + void* user, + JSObject* scopeobj); + +extern JSDContext* +jsd_DebuggerOn(void); + +extern void +jsd_DebuggerOff(JSDContext* jsdc); + +extern void +jsd_DebuggerPause(JSDContext* jsdc, JSBool forceAllHooksOff); + +extern void +jsd_DebuggerUnpause(JSDContext* jsdc); + +extern void +jsd_SetUserCallbacks(JSRuntime* jsrt, JSD_UserCallbacks* callbacks, void* user); + +extern JSDContext* +jsd_JSDContextForJSContext(JSContext* context); + +extern void* +jsd_SetContextPrivate(JSDContext* jsdc, void *data); + +extern void* +jsd_GetContextPrivate(JSDContext* jsdc); + +extern void +jsd_ClearAllProfileData(JSDContext* jsdc); + +extern JSBool +jsd_SetErrorReporter(JSDContext* jsdc, + JSD_ErrorReporter reporter, + void* callerdata); + +extern JSBool +jsd_GetErrorReporter(JSDContext* jsdc, + JSD_ErrorReporter* reporter, + void** callerdata); + +/***************************************************************************/ +/* Script functions */ + +extern JSBool +jsd_InitScriptManager(JSDContext *jsdc); + +extern void +jsd_DestroyScriptManager(JSDContext* jsdc); + +extern JSDScript* +jsd_FindJSDScript(JSDContext* jsdc, + JSScript *script); + +extern JSDScript* +jsd_FindOrCreateJSDScript(JSDContext *jsdc, + JSContext *cx, + JSScript *script, + JSAbstractFramePtr frame); + +extern JSDProfileData* +jsd_GetScriptProfileData(JSDContext* jsdc, JSDScript *script); + +extern uint32_t +jsd_GetScriptFlags(JSDContext *jsdc, JSDScript *script); + +extern void +jsd_SetScriptFlags(JSDContext *jsdc, JSDScript *script, uint32_t flags); + +extern unsigned +jsd_GetScriptCallCount(JSDContext* jsdc, JSDScript *script); + +extern unsigned +jsd_GetScriptMaxRecurseDepth(JSDContext* jsdc, JSDScript *script); + +extern double +jsd_GetScriptMinExecutionTime(JSDContext* jsdc, JSDScript *script); + +extern double +jsd_GetScriptMaxExecutionTime(JSDContext* jsdc, JSDScript *script); + +extern double +jsd_GetScriptTotalExecutionTime(JSDContext* jsdc, JSDScript *script); + +extern double +jsd_GetScriptMinOwnExecutionTime(JSDContext* jsdc, JSDScript *script); + +extern double +jsd_GetScriptMaxOwnExecutionTime(JSDContext* jsdc, JSDScript *script); + +extern double +jsd_GetScriptTotalOwnExecutionTime(JSDContext* jsdc, JSDScript *script); + +extern void +jsd_ClearScriptProfileData(JSDContext* jsdc, JSDScript *script); + +extern JSScript * +jsd_GetJSScript (JSDContext *jsdc, JSDScript *script); + +extern JSFunction * +jsd_GetJSFunction (JSDContext *jsdc, JSDScript *script); + +extern JSDScript* +jsd_IterateScripts(JSDContext* jsdc, JSDScript **iterp); + +extern void * +jsd_SetScriptPrivate (JSDScript *jsdscript, void *data); + +extern void * +jsd_GetScriptPrivate (JSDScript *jsdscript); + +extern JSBool +jsd_IsActiveScript(JSDContext* jsdc, JSDScript *jsdscript); + +extern const char* +jsd_GetScriptFilename(JSDContext* jsdc, JSDScript *jsdscript); + +extern JSString* +jsd_GetScriptFunctionId(JSDContext* jsdc, JSDScript *jsdscript); + +extern unsigned +jsd_GetScriptBaseLineNumber(JSDContext* jsdc, JSDScript *jsdscript); + +extern unsigned +jsd_GetScriptLineExtent(JSDContext* jsdc, JSDScript *jsdscript); + +extern JSBool +jsd_SetScriptHook(JSDContext* jsdc, JSD_ScriptHookProc hook, void* callerdata); + +extern JSBool +jsd_GetScriptHook(JSDContext* jsdc, JSD_ScriptHookProc* hook, void** callerdata); + +extern uintptr_t +jsd_GetClosestPC(JSDContext* jsdc, JSDScript* jsdscript, unsigned line); + +extern unsigned +jsd_GetClosestLine(JSDContext* jsdc, JSDScript* jsdscript, uintptr_t pc); + +extern JSBool +jsd_GetLinePCs(JSDContext* jsdc, JSDScript* jsdscript, + unsigned startLine, unsigned maxLines, + unsigned* count, unsigned** lines, uintptr_t** pcs); + +extern void +jsd_NewScriptHookProc( + JSContext *cx, + const char *filename, /* URL this script loads from */ + unsigned lineno, /* line where this script starts */ + JSScript *script, + JSFunction *fun, + void* callerdata); + +extern void +jsd_DestroyScriptHookProc( + JSFreeOp *fop, + JSScript *script, + void* callerdata); + +/* Script execution hook functions */ + +extern JSBool +jsd_SetExecutionHook(JSDContext* jsdc, + JSDScript* jsdscript, + uintptr_t pc, + JSD_ExecutionHookProc hook, + void* callerdata); + +extern JSBool +jsd_ClearExecutionHook(JSDContext* jsdc, + JSDScript* jsdscript, + uintptr_t pc); + +extern JSBool +jsd_ClearAllExecutionHooksForScript(JSDContext* jsdc, JSDScript* jsdscript); + +extern JSBool +jsd_ClearAllExecutionHooks(JSDContext* jsdc); + +extern void +jsd_ScriptCreated(JSDContext* jsdc, + JSContext *cx, + const char *filename, /* URL this script loads from */ + unsigned lineno, /* line where this script starts */ + JSScript *script, + JSFunction *fun); + +extern void +jsd_ScriptDestroyed(JSDContext* jsdc, + JSFreeOp *fop, + JSScript *script); + +/***************************************************************************/ +/* Source Text functions */ + +extern JSDSourceText* +jsd_IterateSources(JSDContext* jsdc, JSDSourceText **iterp); + +extern JSDSourceText* +jsd_FindSourceForURL(JSDContext* jsdc, const char* url); + +extern const char* +jsd_GetSourceURL(JSDContext* jsdc, JSDSourceText* jsdsrc); + +extern JSBool +jsd_GetSourceText(JSDContext* jsdc, JSDSourceText* jsdsrc, + const char** ppBuf, int* pLen); + +extern void +jsd_ClearSourceText(JSDContext* jsdc, JSDSourceText* jsdsrc); + +extern JSDSourceStatus +jsd_GetSourceStatus(JSDContext* jsdc, JSDSourceText* jsdsrc); + +extern JSBool +jsd_IsSourceDirty(JSDContext* jsdc, JSDSourceText* jsdsrc); + +extern void +jsd_SetSourceDirty(JSDContext* jsdc, JSDSourceText* jsdsrc, JSBool dirty); + +extern unsigned +jsd_GetSourceAlterCount(JSDContext* jsdc, JSDSourceText* jsdsrc); + +extern unsigned +jsd_IncrementSourceAlterCount(JSDContext* jsdc, JSDSourceText* jsdsrc); + +extern JSDSourceText* +jsd_NewSourceText(JSDContext* jsdc, const char* url); + +extern JSDSourceText* +jsd_AppendSourceText(JSDContext* jsdc, + JSDSourceText* jsdsrc, + const char* text, /* *not* zero terminated */ + size_t length, + JSDSourceStatus status); + +extern JSDSourceText* +jsd_AppendUCSourceText(JSDContext* jsdc, + JSDSourceText* jsdsrc, + const jschar* text, /* *not* zero terminated */ + size_t length, + JSDSourceStatus status); + +/* convienence function for adding complete source of url in one call */ +extern JSBool +jsd_AddFullSourceText(JSDContext* jsdc, + const char* text, /* *not* zero terminated */ + size_t length, + const char* url); + +extern void +jsd_DestroyAllSources(JSDContext* jsdc); + +extern char* +jsd_BuildNormalizedURL(const char* url_string); + +extern void +jsd_StartingEvalUsingFilename(JSDContext* jsdc, const char* url); + +extern void +jsd_FinishedEvalUsingFilename(JSDContext* jsdc, const char* url); + +/***************************************************************************/ +/* Interrupt Hook functions */ + +extern JSBool +jsd_SetInterruptHook(JSDContext* jsdc, + JSD_ExecutionHookProc hook, + void* callerdata); + +extern JSBool +jsd_ClearInterruptHook(JSDContext* jsdc); + +extern JSBool +jsd_EnableSingleStepInterrupts(JSDContext* jsdc, + JSDScript* jsdscript, + JSBool enable); + +extern JSBool +jsd_SetDebugBreakHook(JSDContext* jsdc, + JSD_ExecutionHookProc hook, + void* callerdata); + +extern JSBool +jsd_ClearDebugBreakHook(JSDContext* jsdc); + +extern JSBool +jsd_SetDebuggerHook(JSDContext* jsdc, + JSD_ExecutionHookProc hook, + void* callerdata); + +extern JSBool +jsd_ClearDebuggerHook(JSDContext* jsdc); + +extern JSTrapStatus +jsd_CallExecutionHook(JSDContext* jsdc, + JSContext* cx, + unsigned type, + JSD_ExecutionHookProc hook, + void* hookData, + jsval* rval); + +extern JSBool +jsd_CallCallHook (JSDContext* jsdc, + JSContext* cx, + unsigned type, + JSD_CallHookProc hook, + void* hookData); + +extern JSBool +jsd_SetThrowHook(JSDContext* jsdc, + JSD_ExecutionHookProc hook, + void* callerdata); +extern JSBool +jsd_ClearThrowHook(JSDContext* jsdc); + +extern JSTrapStatus +jsd_DebuggerHandler(JSContext *cx, JSScript *script, jsbytecode *pc, + jsval *rval, void *closure); + +extern JSTrapStatus +jsd_ThrowHandler(JSContext *cx, JSScript *script, jsbytecode *pc, + jsval *rval, void *closure); + +extern JSBool +jsd_SetFunctionHook(JSDContext* jsdc, + JSD_CallHookProc hook, + void* callerdata); + +extern JSBool +jsd_ClearFunctionHook(JSDContext* jsdc); + +extern JSBool +jsd_SetTopLevelHook(JSDContext* jsdc, + JSD_CallHookProc hook, + void* callerdata); + +extern JSBool +jsd_ClearTopLevelHook(JSDContext* jsdc); + +/***************************************************************************/ +/* Stack Frame functions */ + +extern unsigned +jsd_GetCountOfStackFrames(JSDContext* jsdc, JSDThreadState* jsdthreadstate); + +extern JSDStackFrameInfo* +jsd_GetStackFrame(JSDContext* jsdc, JSDThreadState* jsdthreadstate); + +extern JSContext* +jsd_GetJSContext(JSDContext* jsdc, JSDThreadState* jsdthreadstate); + +extern JSDStackFrameInfo* +jsd_GetCallingStackFrame(JSDContext* jsdc, + JSDThreadState* jsdthreadstate, + JSDStackFrameInfo* jsdframe); + +extern JSDScript* +jsd_GetScriptForStackFrame(JSDContext* jsdc, + JSDThreadState* jsdthreadstate, + JSDStackFrameInfo* jsdframe); + +extern uintptr_t +jsd_GetPCForStackFrame(JSDContext* jsdc, + JSDThreadState* jsdthreadstate, + JSDStackFrameInfo* jsdframe); + +extern JSDValue* +jsd_GetCallObjectForStackFrame(JSDContext* jsdc, + JSDThreadState* jsdthreadstate, + JSDStackFrameInfo* jsdframe); + +extern JSDValue* +jsd_GetScopeChainForStackFrame(JSDContext* jsdc, + JSDThreadState* jsdthreadstate, + JSDStackFrameInfo* jsdframe); + +extern JSBool +jsd_IsStackFrameDebugger(JSDContext* jsdc, + JSDThreadState* jsdthreadstate, + JSDStackFrameInfo* jsdframe); + +extern JSBool +jsd_IsStackFrameConstructing(JSDContext* jsdc, + JSDThreadState* jsdthreadstate, + JSDStackFrameInfo* jsdframe); + +extern JSDValue* +jsd_GetThisForStackFrame(JSDContext* jsdc, + JSDThreadState* jsdthreadstate, + JSDStackFrameInfo* jsdframe); + +extern JSString* +jsd_GetIdForStackFrame(JSDContext* jsdc, + JSDThreadState* jsdthreadstate, + JSDStackFrameInfo* jsdframe); + +extern JSDThreadState* +jsd_NewThreadState(JSDContext* jsdc, JSContext *cx); + +extern void +jsd_DestroyThreadState(JSDContext* jsdc, JSDThreadState* jsdthreadstate); + +extern JSBool +jsd_EvaluateUCScriptInStackFrame(JSDContext* jsdc, + JSDThreadState* jsdthreadstate, + JSDStackFrameInfo* jsdframe, + const jschar *bytes, unsigned length, + const char *filename, unsigned lineno, + JSBool eatExceptions, JS::MutableHandleValue rval); + +extern JSBool +jsd_EvaluateScriptInStackFrame(JSDContext* jsdc, + JSDThreadState* jsdthreadstate, + JSDStackFrameInfo* jsdframe, + const char *bytes, unsigned length, + const char *filename, unsigned lineno, + JSBool eatExceptions, JS::MutableHandleValue rval); + +extern JSString* +jsd_ValToStringInStackFrame(JSDContext* jsdc, + JSDThreadState* jsdthreadstate, + JSDStackFrameInfo* jsdframe, + jsval val); + +extern JSBool +jsd_IsValidThreadState(JSDContext* jsdc, + JSDThreadState* jsdthreadstate); + +extern JSBool +jsd_IsValidFrameInThreadState(JSDContext* jsdc, + JSDThreadState* jsdthreadstate, + JSDStackFrameInfo* jsdframe); + +extern JSDValue* +jsd_GetException(JSDContext* jsdc, JSDThreadState* jsdthreadstate); + +extern JSBool +jsd_SetException(JSDContext* jsdc, JSDThreadState* jsdthreadstate, + JSDValue* jsdval); + +/***************************************************************************/ +/* Locking support */ + +/* protos are in js_lock.h for: + * jsd_CreateLock + * jsd_Lock + * jsd_Unlock + * jsd_IsLocked + * jsd_CurrentThread + */ + +#ifdef JSD_THREADSAFE + +/* the system-wide lock */ +extern JSDStaticLock* _jsd_global_lock; +#define JSD_LOCK() \ + JS_BEGIN_MACRO \ + if(!_jsd_global_lock) \ + _jsd_global_lock = jsd_CreateLock(); \ + JS_ASSERT(_jsd_global_lock); \ + jsd_Lock(_jsd_global_lock); \ + JS_END_MACRO + +#define JSD_UNLOCK() \ + JS_BEGIN_MACRO \ + JS_ASSERT(_jsd_global_lock); \ + jsd_Unlock(_jsd_global_lock); \ + JS_END_MACRO + +/* locks for the subsystems of a given context */ +#define JSD_INIT_LOCKS(jsdc) \ + ( (NULL != (jsdc->scriptsLock = jsd_CreateLock())) && \ + (NULL != (jsdc->sourceTextLock = jsd_CreateLock())) && \ + (NULL != (jsdc->atomsLock = jsd_CreateLock())) && \ + (NULL != (jsdc->objectsLock = jsd_CreateLock())) && \ + (NULL != (jsdc->threadStatesLock = jsd_CreateLock())) ) + +#define JSD_LOCK_SCRIPTS(jsdc) jsd_Lock(jsdc->scriptsLock) +#define JSD_UNLOCK_SCRIPTS(jsdc) jsd_Unlock(jsdc->scriptsLock) + +#define JSD_LOCK_SOURCE_TEXT(jsdc) jsd_Lock(jsdc->sourceTextLock) +#define JSD_UNLOCK_SOURCE_TEXT(jsdc) jsd_Unlock(jsdc->sourceTextLock) + +#define JSD_LOCK_ATOMS(jsdc) jsd_Lock(jsdc->atomsLock) +#define JSD_UNLOCK_ATOMS(jsdc) jsd_Unlock(jsdc->atomsLock) + +#define JSD_LOCK_OBJECTS(jsdc) jsd_Lock(jsdc->objectsLock) +#define JSD_UNLOCK_OBJECTS(jsdc) jsd_Unlock(jsdc->objectsLock) + +#define JSD_LOCK_THREADSTATES(jsdc) jsd_Lock(jsdc->threadStatesLock) +#define JSD_UNLOCK_THREADSTATES(jsdc) jsd_Unlock(jsdc->threadStatesLock) + +#else /* !JSD_THREADSAFE */ + +#define JSD_LOCK() ((void)0) +#define JSD_UNLOCK() ((void)0) + +#define JSD_INIT_LOCKS(jsdc) 1 + +#define JSD_LOCK_SCRIPTS(jsdc) ((void)0) +#define JSD_UNLOCK_SCRIPTS(jsdc) ((void)0) + +#define JSD_LOCK_SOURCE_TEXT(jsdc) ((void)0) +#define JSD_UNLOCK_SOURCE_TEXT(jsdc) ((void)0) + +#define JSD_LOCK_ATOMS(jsdc) ((void)0) +#define JSD_UNLOCK_ATOMS(jsdc) ((void)0) + +#define JSD_LOCK_OBJECTS(jsdc) ((void)0) +#define JSD_UNLOCK_OBJECTS(jsdc) ((void)0) + +#define JSD_LOCK_THREADSTATES(jsdc) ((void)0) +#define JSD_UNLOCK_THREADSTATES(jsdc) ((void)0) + +#endif /* JSD_THREADSAFE */ + +/* NOTE: These are intended for ASSERTs. Thus we supply checks for both + * LOCKED and UNLOCKED (rather that just LOCKED and !LOCKED) so that in + * the DEBUG non-Threadsafe case we can have an ASSERT that always succeeds + * without having to special case things in the code. + */ +#if defined(JSD_THREADSAFE) && defined(DEBUG) +#define JSD_SCRIPTS_LOCKED(jsdc) (jsd_IsLocked(jsdc->scriptsLock)) +#define JSD_SOURCE_TEXT_LOCKED(jsdc) (jsd_IsLocked(jsdc->sourceTextLock)) +#define JSD_ATOMS_LOCKED(jsdc) (jsd_IsLocked(jsdc->atomsLock)) +#define JSD_OBJECTS_LOCKED(jsdc) (jsd_IsLocked(jsdc->objectsLock)) +#define JSD_THREADSTATES_LOCKED(jsdc) (jsd_IsLocked(jsdc->threadStatesLock)) +#define JSD_SCRIPTS_UNLOCKED(jsdc) (!jsd_IsLocked(jsdc->scriptsLock)) +#define JSD_SOURCE_TEXT_UNLOCKED(jsdc) (!jsd_IsLocked(jsdc->sourceTextLock)) +#define JSD_ATOMS_UNLOCKED(jsdc) (!jsd_IsLocked(jsdc->atomsLock)) +#define JSD_OBJECTS_UNLOCKED(jsdc) (!jsd_IsLocked(jsdc->objectsLock)) +#define JSD_THREADSTATES_UNLOCKED(jsdc) (!jsd_IsLocked(jsdc->threadStatesLock)) +#else +#define JSD_SCRIPTS_LOCKED(jsdc) 1 +#define JSD_SOURCE_TEXT_LOCKED(jsdc) 1 +#define JSD_ATOMS_LOCKED(jsdc) 1 +#define JSD_OBJECTS_LOCKED(jsdc) 1 +#define JSD_THREADSTATES_LOCKED(jsdc) 1 +#define JSD_SCRIPTS_UNLOCKED(jsdc) 1 +#define JSD_SOURCE_TEXT_UNLOCKED(jsdc) 1 +#define JSD_ATOMS_UNLOCKED(jsdc) 1 +#define JSD_OBJECTS_UNLOCKED(jsdc) 1 +#define JSD_THREADSTATES_UNLOCKED(jsdc) 1 +#endif /* defined(JSD_THREADSAFE) && defined(DEBUG) */ + +/***************************************************************************/ +/* Threading support */ + +#ifdef JSD_THREADSAFE + +#define JSD_CURRENT_THREAD() jsd_CurrentThread() + +#else /* !JSD_THREADSAFE */ + +#define JSD_CURRENT_THREAD() ((void*)0) + +#endif /* JSD_THREADSAFE */ + +/***************************************************************************/ +/* Dangerous thread support */ + +#ifdef JSD_HAS_DANGEROUS_THREAD + +#define JSD_IS_DANGEROUS_THREAD(jsdc) \ + (JSD_CURRENT_THREAD() == jsdc->dangerousThread) + +#else /* !JSD_HAS_DANGEROUS_THREAD */ + +#define JSD_IS_DANGEROUS_THREAD(jsdc) 0 + +#endif /* JSD_HAS_DANGEROUS_THREAD */ + +/***************************************************************************/ +/* Value and Property Functions */ + +extern JSDValue* +jsd_NewValue(JSDContext* jsdc, jsval val); + +extern void +jsd_DropValue(JSDContext* jsdc, JSDValue* jsdval); + +extern jsval +jsd_GetValueWrappedJSVal(JSDContext* jsdc, JSDValue* jsdval); + +extern void +jsd_RefreshValue(JSDContext* jsdc, JSDValue* jsdval); + +/**************************************************/ + +extern JSBool +jsd_IsValueObject(JSDContext* jsdc, JSDValue* jsdval); + +extern JSBool +jsd_IsValueNumber(JSDContext* jsdc, JSDValue* jsdval); + +extern JSBool +jsd_IsValueInt(JSDContext* jsdc, JSDValue* jsdval); + +extern JSBool +jsd_IsValueDouble(JSDContext* jsdc, JSDValue* jsdval); + +extern JSBool +jsd_IsValueString(JSDContext* jsdc, JSDValue* jsdval); + +extern JSBool +jsd_IsValueBoolean(JSDContext* jsdc, JSDValue* jsdval); + +extern JSBool +jsd_IsValueNull(JSDContext* jsdc, JSDValue* jsdval); + +extern JSBool +jsd_IsValueVoid(JSDContext* jsdc, JSDValue* jsdval); + +extern JSBool +jsd_IsValuePrimitive(JSDContext* jsdc, JSDValue* jsdval); + +extern JSBool +jsd_IsValueFunction(JSDContext* jsdc, JSDValue* jsdval); + +extern JSBool +jsd_IsValueNative(JSDContext* jsdc, JSDValue* jsdval); + +/**************************************************/ + +extern JSBool +jsd_GetValueBoolean(JSDContext* jsdc, JSDValue* jsdval); + +extern int32_t +jsd_GetValueInt(JSDContext* jsdc, JSDValue* jsdval); + +extern double +jsd_GetValueDouble(JSDContext* jsdc, JSDValue* jsdval); + +extern JSString* +jsd_GetValueString(JSDContext* jsdc, JSDValue* jsdval); + +extern JSString* +jsd_GetValueFunctionId(JSDContext* jsdc, JSDValue* jsdval); + +extern JSFunction* +jsd_GetValueFunction(JSDContext* jsdc, JSDValue* jsdval); + +/**************************************************/ + +extern unsigned +jsd_GetCountOfProperties(JSDContext* jsdc, JSDValue* jsdval); + +extern JSDProperty* +jsd_IterateProperties(JSDContext* jsdc, JSDValue* jsdval, JSDProperty **iterp); + +extern JSDProperty* +jsd_GetValueProperty(JSDContext* jsdc, JSDValue* jsdval, JSString* name); + +extern JSDValue* +jsd_GetValuePrototype(JSDContext* jsdc, JSDValue* jsdval); + +extern JSDValue* +jsd_GetValueParent(JSDContext* jsdc, JSDValue* jsdval); + +extern JSDValue* +jsd_GetValueConstructor(JSDContext* jsdc, JSDValue* jsdval); + +extern const char* +jsd_GetValueClassName(JSDContext* jsdc, JSDValue* jsdval); + +extern JSDScript* +jsd_GetScriptForValue(JSDContext* jsdc, JSDValue* jsdval); + +/**************************************************/ + +extern void +jsd_DropProperty(JSDContext* jsdc, JSDProperty* jsdprop); + +extern JSDValue* +jsd_GetPropertyName(JSDContext* jsdc, JSDProperty* jsdprop); + +extern JSDValue* +jsd_GetPropertyValue(JSDContext* jsdc, JSDProperty* jsdprop); + +extern JSDValue* +jsd_GetPropertyAlias(JSDContext* jsdc, JSDProperty* jsdprop); + +extern unsigned +jsd_GetPropertyFlags(JSDContext* jsdc, JSDProperty* jsdprop); + +/**************************************************/ +/* Stepping Functions */ + +extern void * +jsd_FunctionCallHook(JSContext *cx, JSAbstractFramePtr frame, bool isConstructing, + JSBool before, JSBool *ok, void *closure); + +extern void * +jsd_TopLevelCallHook(JSContext *cx, JSAbstractFramePtr frame, bool isConstructing, + JSBool before, JSBool *ok, void *closure); + +/**************************************************/ +/* Object Functions */ + +extern JSBool +jsd_InitObjectManager(JSDContext* jsdc); + +extern void +jsd_DestroyObjectManager(JSDContext* jsdc); + +extern void +jsd_DestroyObjects(JSDContext* jsdc); + +extern void +jsd_Constructing(JSDContext* jsdc, JSContext *cx, JSObject *obj, + JSAbstractFramePtr frame); + +extern JSDObject* +jsd_IterateObjects(JSDContext* jsdc, JSDObject** iterp); + +extern JSObject* +jsd_GetWrappedObject(JSDContext* jsdc, JSDObject* jsdobj); + +extern const char* +jsd_GetObjectNewURL(JSDContext* jsdc, JSDObject* jsdobj); + +extern unsigned +jsd_GetObjectNewLineNumber(JSDContext* jsdc, JSDObject* jsdobj); + +extern const char* +jsd_GetObjectConstructorURL(JSDContext* jsdc, JSDObject* jsdobj); + +extern unsigned +jsd_GetObjectConstructorLineNumber(JSDContext* jsdc, JSDObject* jsdobj); + +extern const char* +jsd_GetObjectConstructorName(JSDContext* jsdc, JSDObject* jsdobj); + +extern JSDObject* +jsd_GetJSDObjectForJSObject(JSDContext* jsdc, JSObject* jsobj); + +extern JSDObject* +jsd_GetObjectForValue(JSDContext* jsdc, JSDValue* jsdval); + +/* +* returns new refcounted JSDValue +*/ +extern JSDValue* +jsd_GetValueForObject(JSDContext* jsdc, JSDObject* jsdobj); + +/**************************************************/ +/* Atom Functions */ + +extern JSBool +jsd_CreateAtomTable(JSDContext* jsdc); + +extern void +jsd_DestroyAtomTable(JSDContext* jsdc); + +extern JSDAtom* +jsd_AddAtom(JSDContext* jsdc, const char* str); + +extern JSDAtom* +jsd_CloneAtom(JSDContext* jsdc, JSDAtom* atom); + +extern void +jsd_DropAtom(JSDContext* jsdc, JSDAtom* atom); + +#define JSD_ATOM_TO_STRING(a) ((const char*)((a)->str)) + +struct AutoSaveExceptionState { + AutoSaveExceptionState(JSContext *cx) : mCx(cx) { + mState = JS_SaveExceptionState(cx); + } + ~AutoSaveExceptionState() { + JS_RestoreExceptionState(mCx, mState); + } + JSContext *mCx; + JSExceptionState *mState; +}; + +/***************************************************************************/ +/* Livewire specific API */ +#ifdef LIVEWIRE + +extern LWDBGScript* +jsdlw_GetLWScript(JSDContext* jsdc, JSDScript* jsdscript); + +extern char* +jsdlw_BuildAppRelativeFilename(LWDBGApp* app, const char* filename); + +extern JSDSourceText* +jsdlw_PreLoadSource(JSDContext* jsdc, LWDBGApp* app, + const char* filename, JSBool clear); + +extern JSDSourceText* +jsdlw_ForceLoadSource(JSDContext* jsdc, JSDSourceText* jsdsrc); + +extern JSBool +jsdlw_UserCodeAtPC(JSDContext* jsdc, JSDScript* jsdscript, uintptr_t pc); + +extern JSBool +jsdlw_RawToProcessedLineNumber(JSDContext* jsdc, JSDScript* jsdscript, + unsigned lineIn, unsigned* lineOut); + +extern JSBool +jsdlw_ProcessedToRawLineNumber(JSDContext* jsdc, JSDScript* jsdscript, + unsigned lineIn, unsigned* lineOut); + +#if 0 +/* our hook proc for LiveWire app start/stop */ +extern void +jsdlw_AppHookProc(LWDBGApp* app, + JSBool created, + void *callerdata); +#endif + + +#endif +/***************************************************************************/ + +#endif /* jsd_h___ */ diff --git a/js/jsd/jsd1640.def b/js/jsd/jsd1640.def new file mode 100644 index 0000000..48af380 --- /dev/null +++ b/js/jsd/jsd1640.def @@ -0,0 +1,77 @@ +; -*- Mode: Fundamental; tab-width: 4; indent-tabs-mode: nil -*- +; +; 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/. + + +LIBRARY JSD1640.DLL +EXETYPE WINDOWS +PROTMODE + +DESCRIPTION 'Netscape 16-bit JavaScript Debugger Library' + +CODE LOADONCALL MOVEABLE DISCARDABLE +DATA PRELOAD MOVEABLE SINGLE + +HEAPSIZE 8192 + +EXPORTS + WEP @1 RESIDENTNAME NONAME + _JSD_AppendSourceText + _JSD_ClearAllExecutionHooks + _JSD_ClearAllExecutionHooksForScript + _JSD_ClearDebugBreakHook + _JSD_ClearExecutionHook + _JSD_ClearInterruptHook + _JSD_ClearSourceText + _JSD_DebuggerOff + _JSD_DebuggerOn + _JSD_EvaluateScriptInStackFrame + _JSD_FindSourceForURL + _JSD_GetCallingStackFrame + _JSD_GetClosestLine + _JSD_GetClosestPC + _JSD_GetCountOfStackFrames + _JSD_GetMajorVersion + _JSD_GetMinorVersion + _JSD_GetPCForStackFrame + _JSD_GetScriptBaseLineNumber + _JSD_GetScriptFilename + _JSD_GetScriptForStackFrame + _JSD_GetScriptFunctionId + _JSD_GetScriptHook + _JSD_GetScriptLineExtent + _JSD_GetSourceAlterCount + _JSD_GetSourceStatus + _JSD_GetSourceText + _JSD_GetSourceURL + _JSD_GetStackFrame + _JSD_IncrementSourceAlterCount + _JSD_IsSourceDirty + _JSD_IterateScripts + _JSD_IterateSources + _JSD_LockScriptSubsystem + _JSD_LockSourceTextSubsystem + _JSD_NewSourceText + _JSD_SetDebugBreakHook + _JSD_SetErrorReporter + _JSD_SetExecutionHook + _JSD_SetInterruptHook + _JSD_SetScriptHook + _JSD_SetSourceDirty + _JSD_SetUserCallbacks + _JSD_UnlockScriptSubsystem + _JSD_UnlockSourceTextSubsystem + _Java_netscape_jsdebug_DebugController__0005fsetController_stub + _Java_netscape_jsdebug_DebugController_executeScriptInStackFrame_stub + _Java_netscape_jsdebug_DebugController_sendInterrupt_stub + _Java_netscape_jsdebug_DebugController_setInstructionHook0_stub + _Java_netscape_jsdebug_JSPC_getSourceLocation_stub + _Java_netscape_jsdebug_JSSourceTextProvider_loadSourceTextItem_stub + _Java_netscape_jsdebug_JSSourceTextProvider_refreshSourceTextVector_stub + _Java_netscape_jsdebug_JSStackFrameInfo_getCaller0_stub + _Java_netscape_jsdebug_JSStackFrameInfo_getPC_stub + _Java_netscape_jsdebug_JSThreadState_countStackFrames_stub + _Java_netscape_jsdebug_JSThreadState_getCurrentFrame_stub + _Java_netscape_jsdebug_Script_getClosestPC_stub diff --git a/js/jsd/jsd1640.rc b/js/jsd/jsd1640.rc new file mode 100644 index 0000000..7c3cf55 --- /dev/null +++ b/js/jsd/jsd1640.rc @@ -0,0 +1,67 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* 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/. */ + + + +///////////////////////////////////////////////////////////////////////////// + +// Version stamp for this .DLL + +#include <windows.h> + +#include <ver.h> + + + +VS_VERSION_INFO VERSIONINFO + + FILEVERSION 4 // major, minor, release (alpha 1), build # + + PRODUCTVERSION 4 + + FILEFLAGSMASK 0 + + FILEFLAGS 0 // final version + + FILEOS VOS_DOS_WINDOWS16 + + FILETYPE VFT_DLL + + FILESUBTYPE 0 // not used + +BEGIN + + BLOCK "StringFileInfo" + + BEGIN + + BLOCK "040904E4" // Lang=US English, CharSet=Windows Multilingual + + BEGIN + + VALUE "CompanyName", "Netscape Communications Corporation\0" + + VALUE "FileDescription", "Netscape 16-bit JavaScript Debugger Module\0" + + VALUE "FileVersion", "4.0\0" + + VALUE "InternalName", "JSD1640\0" + + VALUE "LegalCopyright", "Copyright Netscape Communications. 1994-96\0" + + VALUE "LegalTrademarks", "Netscape, Mozilla\0" + + VALUE "OriginalFilename","JSD1640.DLL\0" + + VALUE "ProductName", "NETSCAPE\0" + + VALUE "ProductVersion", "4.0\0" + + END + + END + +END + diff --git a/js/jsd/jsd3240.rc b/js/jsd/jsd3240.rc new file mode 100644 index 0000000..5b2bc34 --- /dev/null +++ b/js/jsd/jsd3240.rc @@ -0,0 +1,86 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* 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/. */ + + + +//Microsoft Developer Studio generated resource script. +// +#include "resource.h" + +#define APSTUDIO_READONLY_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 2 resource. +// +#include "winver.h" + +///////////////////////////////////////////////////////////////////////////// +#undef APSTUDIO_READONLY_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +// +// Version +// + +VS_VERSION_INFO VERSIONINFO + FILEVERSION 4,0,0,0 + PRODUCTVERSION 4,0,0,0 + FILEFLAGSMASK 0x3fL +#ifdef _DEBUG + FILEFLAGS 0x1L +#else + FILEFLAGS 0x0L +#endif + FILEOS 0x10004L + FILETYPE 0x2L + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904e4" + BEGIN + VALUE "CompanyName", "Netscape Communications Corporation\0" + VALUE "FileDescription", "Netscape 32-bit JavaScript Debugger Module\0" + VALUE "FileVersion", "4.0\0" + VALUE "InternalName", "JSD3240\0" + VALUE "LegalCopyright", "Copyright Netscape Communications. 1994-96\0" + VALUE "LegalTrademarks", "Netscape, Mozilla\0" + VALUE "OriginalFilename", "jsd3240.dll\0" + VALUE "ProductName", "NETSCAPE\0" + VALUE "ProductVersion", "4.0\0" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1252 + END +END + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +1 TEXTINCLUDE DISCARDABLE +BEGIN + "resource.h\0" +END + +2 TEXTINCLUDE DISCARDABLE +BEGIN + "#include ""winver.h""\r\n" + "\0" +END + +3 TEXTINCLUDE DISCARDABLE +BEGIN + "\r\n" + "\0" +END + +#endif // APSTUDIO_INVOKED + +/////////////////////////////////////////////////////////////////////////////
\ No newline at end of file diff --git a/js/jsd/jsd_atom.cpp b/js/jsd/jsd_atom.cpp new file mode 100644 index 0000000..dec6b39 --- /dev/null +++ b/js/jsd/jsd_atom.cpp @@ -0,0 +1,147 @@ +/* -*- 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 - Atom support + */ + +#include "jsd.h" + +/* #define TEST_ATOMS 1 */ + +#ifdef TEST_ATOMS +static void +_testAtoms(JSDContext*jsdc) +{ + JSDAtom* atom0 = jsd_AddAtom(jsdc, "foo"); + JSDAtom* atom1 = jsd_AddAtom(jsdc, "foo"); + JSDAtom* atom2 = jsd_AddAtom(jsdc, "bar"); + JSDAtom* atom3 = jsd_CloneAtom(jsdc, atom1); + JSDAtom* atom4 = jsd_CloneAtom(jsdc, atom2); + + const char* c0 = JSD_ATOM_TO_STRING(atom0); + const char* c1 = JSD_ATOM_TO_STRING(atom1); + const char* c2 = JSD_ATOM_TO_STRING(atom2); + const char* c3 = JSD_ATOM_TO_STRING(atom3); + const char* c4 = JSD_ATOM_TO_STRING(atom4); + + jsd_DropAtom(jsdc, atom0); + jsd_DropAtom(jsdc, atom1); + jsd_DropAtom(jsdc, atom2); + jsd_DropAtom(jsdc, atom3); + jsd_DropAtom(jsdc, atom4); +} +#endif + +static int +_atom_smasher(JSHashEntry *he, int i, void *arg) +{ + JS_ASSERT(he); + JS_ASSERT(he->value); + JS_ASSERT(((JSDAtom*)(he->value))->str); + + free(((JSDAtom*)(he->value))->str); + free(he->value); + he->value = NULL; + he->key = NULL; + return HT_ENUMERATE_NEXT; +} + +static int +_compareAtomKeys(const void *v1, const void *v2) +{ + return 0 == strcmp((const char*)v1, (const char*)v2); +} + +static int +_compareAtoms(const void *v1, const void *v2) +{ + return 0 == strcmp(((JSDAtom*)v1)->str, ((JSDAtom*)v2)->str); +} + + +JSBool +jsd_CreateAtomTable(JSDContext* jsdc) +{ + jsdc->atoms = JS_NewHashTable(256, JS_HashString, + _compareAtomKeys, _compareAtoms, + NULL, NULL); +#ifdef TEST_ATOMS + _testAtoms(jsdc); +#endif + return !!jsdc->atoms; +} + +void +jsd_DestroyAtomTable(JSDContext* jsdc) +{ + if( jsdc->atoms ) + { + JS_HashTableEnumerateEntries(jsdc->atoms, _atom_smasher, NULL); + JS_HashTableDestroy(jsdc->atoms); + jsdc->atoms = NULL; + } +} + +JSDAtom* +jsd_AddAtom(JSDContext* jsdc, const char* str) +{ + JSDAtom* atom; + + if(!str) + { + JS_ASSERT(0); + return NULL; + } + + JSD_LOCK_ATOMS(jsdc); + + atom = (JSDAtom*) JS_HashTableLookup(jsdc->atoms, str); + + if( atom ) + atom->refcount++; + else + { + atom = (JSDAtom*) malloc(sizeof(JSDAtom)); + if( atom ) + { + atom->str = strdup(str); + atom->refcount = 1; + if(!JS_HashTableAdd(jsdc->atoms, atom->str, atom)) + { + free(atom->str); + free(atom); + atom = NULL; + } + } + } + + JSD_UNLOCK_ATOMS(jsdc); + return atom; +} + +JSDAtom* +jsd_CloneAtom(JSDContext* jsdc, JSDAtom* atom) +{ + JSD_LOCK_ATOMS(jsdc); + atom->refcount++; + JSD_UNLOCK_ATOMS(jsdc); + return atom; +} + +void +jsd_DropAtom(JSDContext* jsdc, JSDAtom* atom) +{ + JSD_LOCK_ATOMS(jsdc); + if(! --atom->refcount) + { + JS_HashTableRemove(jsdc->atoms, atom->str); + free(atom->str); + free(atom); + } + JSD_UNLOCK_ATOMS(jsdc); +} + diff --git a/js/jsd/jsd_high.cpp b/js/jsd/jsd_high.cpp new file mode 100644 index 0000000..ed3512a --- /dev/null +++ b/js/jsd/jsd_high.cpp @@ -0,0 +1,419 @@ +/* -*- 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 - 'High Level' functions + */ + +#include "jsd.h" +#include "nsCxPusher.h" +#include "nsContentUtils.h" + +using mozilla::AutoSafeJSContext; + +/***************************************************************************/ + +/* XXX not 'static' because of old Mac CodeWarrior bug */ +JSCList _jsd_context_list = JS_INIT_STATIC_CLIST(&_jsd_context_list); + +/* these are used to connect JSD_SetUserCallbacks() with JSD_DebuggerOn() */ +static JSD_UserCallbacks _callbacks; +static void* _user = NULL; +static JSRuntime* _jsrt = NULL; + +#ifdef JSD_HAS_DANGEROUS_THREAD +static void* _dangerousThread = NULL; +#endif + +#ifdef JSD_THREADSAFE +JSDStaticLock* _jsd_global_lock = NULL; +#endif + +#ifdef DEBUG +void JSD_ASSERT_VALID_CONTEXT(JSDContext* jsdc) +{ + JS_ASSERT(jsdc->inited); + JS_ASSERT(jsdc->jsrt); + JS_ASSERT(jsdc->glob); +} +#endif + +/***************************************************************************/ +/* xpconnect related utility functions implemented in jsd_xpc.cpp */ + +extern void +global_finalize(JSFreeOp* fop, JSObject* obj); + +extern JSObject* +CreateJSDGlobal(JSContext *cx, JSClass *clasp); + +/***************************************************************************/ + + +static JSClass global_class = { + "JSDGlobal", JSCLASS_GLOBAL_FLAGS | + JSCLASS_HAS_PRIVATE | JSCLASS_PRIVATE_IS_NSISUPPORTS, + JS_PropertyStub, JS_DeletePropertyStub, JS_PropertyStub, JS_StrictPropertyStub, + JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, global_finalize +}; + +static JSBool +_validateUserCallbacks(JSD_UserCallbacks* callbacks) +{ + return !callbacks || + (callbacks->size && callbacks->size <= sizeof(JSD_UserCallbacks)); +} + +static JSDContext* +_newJSDContext(JSRuntime* jsrt, + JSD_UserCallbacks* callbacks, + void* user, + JSObject* scopeobj) +{ + JSDContext* jsdc = NULL; + bool ok = true; + AutoSafeJSContext cx; + + if( ! jsrt ) + return NULL; + + if( ! _validateUserCallbacks(callbacks) ) + return NULL; + + jsdc = (JSDContext*) calloc(1, sizeof(JSDContext)); + if( ! jsdc ) + goto label_newJSDContext_failure; + + if( ! JSD_INIT_LOCKS(jsdc) ) + goto label_newJSDContext_failure; + + JS_INIT_CLIST(&jsdc->links); + + jsdc->jsrt = jsrt; + + if( callbacks ) + memcpy(&jsdc->userCallbacks, callbacks, callbacks->size); + + jsdc->user = user; + +#ifdef JSD_HAS_DANGEROUS_THREAD + jsdc->dangerousThread = _dangerousThread; +#endif + + JS_INIT_CLIST(&jsdc->threadsStates); + JS_INIT_CLIST(&jsdc->sources); + JS_INIT_CLIST(&jsdc->removedSources); + + jsdc->sourceAlterCount = 1; + + if( ! jsd_CreateAtomTable(jsdc) ) + goto label_newJSDContext_failure; + + if( ! jsd_InitObjectManager(jsdc) ) + goto label_newJSDContext_failure; + + if( ! jsd_InitScriptManager(jsdc) ) + goto label_newJSDContext_failure; + + + jsdc->glob = CreateJSDGlobal(cx, &global_class); + + if( ! jsdc->glob ) + goto label_newJSDContext_failure; + + { + JSAutoCompartment ac(cx, jsdc->glob); + ok = JS_AddNamedObjectRoot(cx, &jsdc->glob, "JSD context global") && + JS_InitStandardClasses(cx, jsdc->glob); + } + if( ! ok ) + goto label_newJSDContext_failure; + + jsdc->data = NULL; + jsdc->inited = JS_TRUE; + + JSD_LOCK(); + JS_INSERT_LINK(&jsdc->links, &_jsd_context_list); + JSD_UNLOCK(); + + return jsdc; + +label_newJSDContext_failure: + if( jsdc ) { + if ( jsdc->glob ) + JS_RemoveObjectRootRT(JS_GetRuntime(cx), &jsdc->glob); + jsd_DestroyObjectManager(jsdc); + jsd_DestroyAtomTable(jsdc); + free(jsdc); + } + return NULL; +} + +static void +_destroyJSDContext(JSDContext* jsdc) +{ + JSD_ASSERT_VALID_CONTEXT(jsdc); + + JSD_LOCK(); + JS_REMOVE_LINK(&jsdc->links); + JSD_UNLOCK(); + + if ( jsdc->glob ) { + JS_RemoveObjectRootRT(jsdc->jsrt, &jsdc->glob); + } + jsd_DestroyObjectManager(jsdc); + jsd_DestroyAtomTable(jsdc); + + jsdc->inited = JS_FALSE; + + /* + * We should free jsdc here, but we let it leak in case there are any + * asynchronous hooks calling into the system using it as a handle + * + * XXX we also leak the locks + */ +} + +/***************************************************************************/ + +JSDContext* +jsd_DebuggerOnForUser(JSRuntime* jsrt, + JSD_UserCallbacks* callbacks, + void* user, + JSObject* scopeobj) +{ + JSDContext* jsdc; + + jsdc = _newJSDContext(jsrt, callbacks, user, scopeobj); + if( ! jsdc ) + return NULL; + + /* + * Set hooks here. The new/destroy script hooks are on even when + * the debugger is paused. The destroy hook so we'll clean up + * internal data structures when scripts are destroyed, and the + * newscript hook for backwards compatibility for now. We'd like + * to stop doing that. + */ + JS_SetNewScriptHookProc(jsdc->jsrt, jsd_NewScriptHookProc, jsdc); + JS_SetDestroyScriptHookProc(jsdc->jsrt, jsd_DestroyScriptHookProc, jsdc); + jsd_DebuggerUnpause(jsdc); +#ifdef LIVEWIRE + LWDBG_SetNewScriptHookProc(jsd_NewScriptHookProc, jsdc); +#endif + if( jsdc->userCallbacks.setContext ) + jsdc->userCallbacks.setContext(jsdc, jsdc->user); + return jsdc; +} + +JSDContext* +jsd_DebuggerOn(void) +{ + JS_ASSERT(_jsrt); + JS_ASSERT(_validateUserCallbacks(&_callbacks)); + return jsd_DebuggerOnForUser(_jsrt, &_callbacks, _user, NULL); +} + +void +jsd_DebuggerOff(JSDContext* jsdc) +{ + jsd_DebuggerPause(jsdc, JS_TRUE); + /* clear hooks here */ + JS_SetNewScriptHookProc(jsdc->jsrt, NULL, NULL); + JS_SetDestroyScriptHookProc(jsdc->jsrt, NULL, NULL); +#ifdef LIVEWIRE + LWDBG_SetNewScriptHookProc(NULL,NULL); +#endif + + /* clean up */ + JSD_LockScriptSubsystem(jsdc); + jsd_DestroyScriptManager(jsdc); + JSD_UnlockScriptSubsystem(jsdc); + jsd_DestroyAllSources(jsdc); + + _destroyJSDContext(jsdc); + + if( jsdc->userCallbacks.setContext ) + jsdc->userCallbacks.setContext(NULL, jsdc->user); +} + +void +jsd_DebuggerPause(JSDContext* jsdc, JSBool forceAllHooksOff) +{ + JS_SetDebuggerHandler(jsdc->jsrt, NULL, NULL); + if (forceAllHooksOff || !(jsdc->flags & JSD_COLLECT_PROFILE_DATA)) { + JS_SetExecuteHook(jsdc->jsrt, NULL, NULL); + JS_SetCallHook(jsdc->jsrt, NULL, NULL); + } + JS_SetThrowHook(jsdc->jsrt, NULL, NULL); + JS_SetDebugErrorHook(jsdc->jsrt, NULL, NULL); +} + +static JSBool +jsd_DebugErrorHook(JSContext *cx, const char *message, + JSErrorReport *report, void *closure); + +void +jsd_DebuggerUnpause(JSDContext* jsdc) +{ + JS_SetDebuggerHandler(jsdc->jsrt, jsd_DebuggerHandler, jsdc); + JS_SetExecuteHook(jsdc->jsrt, jsd_TopLevelCallHook, jsdc); + JS_SetCallHook(jsdc->jsrt, jsd_FunctionCallHook, jsdc); + JS_SetThrowHook(jsdc->jsrt, jsd_ThrowHandler, jsdc); + JS_SetDebugErrorHook(jsdc->jsrt, jsd_DebugErrorHook, jsdc); +} + +void +jsd_SetUserCallbacks(JSRuntime* jsrt, JSD_UserCallbacks* callbacks, void* user) +{ + _jsrt = jsrt; + _user = user; + +#ifdef JSD_HAS_DANGEROUS_THREAD + _dangerousThread = JSD_CURRENT_THREAD(); +#endif + + if( callbacks ) + memcpy(&_callbacks, callbacks, sizeof(JSD_UserCallbacks)); + else + memset(&_callbacks, 0 , sizeof(JSD_UserCallbacks)); +} + +void* +jsd_SetContextPrivate(JSDContext* jsdc, void *data) +{ + jsdc->data = data; + return data; +} + +void* +jsd_GetContextPrivate(JSDContext* jsdc) +{ + return jsdc->data; +} + +void +jsd_ClearAllProfileData(JSDContext* jsdc) +{ + JSDScript *current; + + JSD_LOCK_SCRIPTS(jsdc); + current = (JSDScript *)jsdc->scripts.next; + while (current != (JSDScript *)&jsdc->scripts) + { + jsd_ClearScriptProfileData(jsdc, current); + current = (JSDScript *)current->links.next; + } + + JSD_UNLOCK_SCRIPTS(jsdc); +} + +JSDContext* +jsd_JSDContextForJSContext(JSContext* context) +{ + JSDContext* iter; + JSDContext* jsdc = NULL; + JSRuntime* runtime = JS_GetRuntime(context); + + JSD_LOCK(); + for( iter = (JSDContext*)_jsd_context_list.next; + iter != (JSDContext*)&_jsd_context_list; + iter = (JSDContext*)iter->links.next ) + { + if( runtime == iter->jsrt ) + { + jsdc = iter; + break; + } + } + JSD_UNLOCK(); + return jsdc; +} + +static JSBool +jsd_DebugErrorHook(JSContext *cx, const char *message, + JSErrorReport *report, void *closure) +{ + JSDContext* jsdc = (JSDContext*) closure; + JSD_ErrorReporter errorReporter; + void* errorReporterData; + + if( ! jsdc ) + { + JS_ASSERT(0); + return JS_TRUE; + } + if( JSD_IS_DANGEROUS_THREAD(jsdc) ) + return JS_TRUE; + + /* local in case hook gets cleared on another thread */ + JSD_LOCK(); + errorReporter = jsdc->errorReporter; + errorReporterData = jsdc->errorReporterData; + JSD_UNLOCK(); + + if(!errorReporter) + return JS_TRUE; + + switch(errorReporter(jsdc, cx, message, report, errorReporterData)) + { + case JSD_ERROR_REPORTER_PASS_ALONG: + return JS_TRUE; + case JSD_ERROR_REPORTER_RETURN: + return JS_FALSE; + case JSD_ERROR_REPORTER_DEBUG: + { + jsval rval; + JSD_ExecutionHookProc hook; + void* hookData; + + /* local in case hook gets cleared on another thread */ + JSD_LOCK(); + hook = jsdc->debugBreakHook; + hookData = jsdc->debugBreakHookData; + JSD_UNLOCK(); + + jsd_CallExecutionHook(jsdc, cx, JSD_HOOK_DEBUG_REQUESTED, + hook, hookData, &rval); + /* XXX Should make this dependent on ExecutionHook retval */ + return JS_TRUE; + } + case JSD_ERROR_REPORTER_CLEAR_RETURN: + if(report && JSREPORT_IS_EXCEPTION(report->flags)) + JS_ClearPendingException(cx); + return JS_FALSE; + default: + JS_ASSERT(0); + break; + } + return JS_TRUE; +} + +JSBool +jsd_SetErrorReporter(JSDContext* jsdc, + JSD_ErrorReporter reporter, + void* callerdata) +{ + JSD_LOCK(); + jsdc->errorReporter = reporter; + jsdc->errorReporterData = callerdata; + JSD_UNLOCK(); + return JS_TRUE; +} + +JSBool +jsd_GetErrorReporter(JSDContext* jsdc, + JSD_ErrorReporter* reporter, + void** callerdata) +{ + JSD_LOCK(); + if( reporter ) + *reporter = jsdc->errorReporter; + if( callerdata ) + *callerdata = jsdc->errorReporterData; + JSD_UNLOCK(); + return JS_TRUE; +} diff --git a/js/jsd/jsd_hook.cpp b/js/jsd/jsd_hook.cpp new file mode 100644 index 0000000..95e6318 --- /dev/null +++ b/js/jsd/jsd_hook.cpp @@ -0,0 +1,332 @@ +/* -*- 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 - Hook support + */ + +#include "jsd.h" + +JSTrapStatus +jsd_InterruptHandler(JSContext *cx, JSScript *script, jsbytecode *pc, jsval *rval, + void *closure) +{ + JSDScript* jsdscript; + JSDContext* jsdc = (JSDContext*) closure; + JSD_ExecutionHookProc hook; + void* hookData; + + if( ! jsdc || ! jsdc->inited ) + return JSTRAP_CONTINUE; + + if( JSD_IS_DANGEROUS_THREAD(jsdc) ) + return JSTRAP_CONTINUE; + + /* local in case jsdc->interruptHook gets cleared on another thread */ + JSD_LOCK(); + hook = jsdc->interruptHook; + hookData = jsdc->interruptHookData; + JSD_UNLOCK(); + + if (!hook) + return JSTRAP_CONTINUE; + + JSD_LOCK_SCRIPTS(jsdc); + jsdscript = jsd_FindOrCreateJSDScript(jsdc, cx, script, JSNullFramePtr()); + JSD_UNLOCK_SCRIPTS(jsdc); + if( ! jsdscript ) + return JSTRAP_CONTINUE; + +#ifdef LIVEWIRE + if( ! jsdlw_UserCodeAtPC(jsdc, jsdscript, (uintptr_t)pc) ) + return JSTRAP_CONTINUE; +#endif + + return jsd_CallExecutionHook(jsdc, cx, JSD_HOOK_INTERRUPTED, + hook, hookData, rval); +} + +JSTrapStatus +jsd_DebuggerHandler(JSContext *cx, JSScript *script, jsbytecode *pc, + jsval *rval, void *closure) +{ + JSDScript* jsdscript; + JSDContext* jsdc = (JSDContext*) closure; + JSD_ExecutionHookProc hook; + void* hookData; + + if( ! jsdc || ! jsdc->inited ) + return JSTRAP_CONTINUE; + + if( JSD_IS_DANGEROUS_THREAD(jsdc) ) + return JSTRAP_CONTINUE; + + /* local in case jsdc->debuggerHook gets cleared on another thread */ + JSD_LOCK(); + hook = jsdc->debuggerHook; + hookData = jsdc->debuggerHookData; + JSD_UNLOCK(); + if(!hook) + return JSTRAP_CONTINUE; + + JSD_LOCK_SCRIPTS(jsdc); + jsdscript = jsd_FindOrCreateJSDScript(jsdc, cx, script, JSNullFramePtr()); + JSD_UNLOCK_SCRIPTS(jsdc); + if( ! jsdscript ) + return JSTRAP_CONTINUE; + + return jsd_CallExecutionHook(jsdc, cx, JSD_HOOK_DEBUGGER_KEYWORD, + hook, hookData, rval); +} + + +JSTrapStatus +jsd_ThrowHandler(JSContext *cx, JSScript *script, jsbytecode *pc, + jsval *rval, void *closure) +{ + JSDScript* jsdscript; + JSDContext* jsdc = (JSDContext*) closure; + JSD_ExecutionHookProc hook; + void* hookData; + + if( ! jsdc || ! jsdc->inited ) + return JSTRAP_CONTINUE; + + if( JSD_IS_DANGEROUS_THREAD(jsdc) ) + return JSTRAP_CONTINUE; + + /* local in case jsdc->throwHook gets cleared on another thread */ + JSD_LOCK(); + hook = jsdc->throwHook; + hookData = jsdc->throwHookData; + JSD_UNLOCK(); + if (!hook) + return JSTRAP_CONTINUE; + + JSD_LOCK_SCRIPTS(jsdc); + jsdscript = jsd_FindOrCreateJSDScript(jsdc, cx, script, JSNullFramePtr()); + JSD_UNLOCK_SCRIPTS(jsdc); + if( ! jsdscript ) + return JSTRAP_CONTINUE; + + JS_GetPendingException(cx, rval); + + return jsd_CallExecutionHook(jsdc, cx, JSD_HOOK_THROW, + hook, hookData, rval); +} + +JSTrapStatus +jsd_CallExecutionHook(JSDContext* jsdc, + JSContext *cx, + unsigned type, + JSD_ExecutionHookProc hook, + void* hookData, + jsval* rval) +{ + unsigned hookanswer = JSD_HOOK_THROW == type ? + JSD_HOOK_RETURN_CONTINUE_THROW : + JSD_HOOK_RETURN_CONTINUE; + JSDThreadState* jsdthreadstate; + + if(hook && NULL != (jsdthreadstate = jsd_NewThreadState(jsdc,cx))) + { + if ((type != JSD_HOOK_THROW && type != JSD_HOOK_INTERRUPTED) || + jsdc->flags & JSD_MASK_TOP_FRAME_ONLY || + !(jsdthreadstate->flags & TS_HAS_DISABLED_FRAME)) + { + /* + * if it's not a throw and it's not an interrupt, + * or we're only masking the top frame, + * or there are no disabled frames in this stack, + * then call out. + */ + hookanswer = hook(jsdc, jsdthreadstate, type, hookData, rval); + jsd_DestroyThreadState(jsdc, jsdthreadstate); + } + } + + switch(hookanswer) + { + case JSD_HOOK_RETURN_ABORT: + case JSD_HOOK_RETURN_HOOK_ERROR: + return JSTRAP_ERROR; + case JSD_HOOK_RETURN_RET_WITH_VAL: + return JSTRAP_RETURN; + case JSD_HOOK_RETURN_THROW_WITH_VAL: + return JSTRAP_THROW; + case JSD_HOOK_RETURN_CONTINUE: + break; + case JSD_HOOK_RETURN_CONTINUE_THROW: + /* only makes sense for jsd_ThrowHandler (which init'd rval) */ + JS_ASSERT(JSD_HOOK_THROW == type); + return JSTRAP_THROW; + default: + JS_ASSERT(0); + break; + } + return JSTRAP_CONTINUE; +} + +JSBool +jsd_CallCallHook (JSDContext* jsdc, + JSContext *cx, + unsigned type, + JSD_CallHookProc hook, + void* hookData) +{ + JSBool hookanswer; + JSDThreadState* jsdthreadstate; + + hookanswer = JS_FALSE; + if(hook && NULL != (jsdthreadstate = jsd_NewThreadState(jsdc, cx))) + { + hookanswer = hook(jsdc, jsdthreadstate, type, hookData); + jsd_DestroyThreadState(jsdc, jsdthreadstate); + } + + return hookanswer; +} + +JSBool +jsd_SetInterruptHook(JSDContext* jsdc, + JSD_ExecutionHookProc hook, + void* callerdata) +{ + JSD_LOCK(); + jsdc->interruptHookData = callerdata; + jsdc->interruptHook = hook; + JS_SetInterrupt(jsdc->jsrt, jsd_InterruptHandler, (void*) jsdc); + JSD_UNLOCK(); + + return JS_TRUE; +} + +JSBool +jsd_ClearInterruptHook(JSDContext* jsdc) +{ + JSD_LOCK(); + JS_ClearInterrupt(jsdc->jsrt, NULL, NULL ); + jsdc->interruptHook = NULL; + JSD_UNLOCK(); + + return JS_TRUE; +} + +JSBool +jsd_SetDebugBreakHook(JSDContext* jsdc, + JSD_ExecutionHookProc hook, + void* callerdata) +{ + JSD_LOCK(); + jsdc->debugBreakHookData = callerdata; + jsdc->debugBreakHook = hook; + JSD_UNLOCK(); + + return JS_TRUE; +} + +JSBool +jsd_ClearDebugBreakHook(JSDContext* jsdc) +{ + JSD_LOCK(); + jsdc->debugBreakHook = NULL; + JSD_UNLOCK(); + + return JS_TRUE; +} + +JSBool +jsd_SetDebuggerHook(JSDContext* jsdc, + JSD_ExecutionHookProc hook, + void* callerdata) +{ + JSD_LOCK(); + jsdc->debuggerHookData = callerdata; + jsdc->debuggerHook = hook; + JSD_UNLOCK(); + + return JS_TRUE; +} + +JSBool +jsd_ClearDebuggerHook(JSDContext* jsdc) +{ + JSD_LOCK(); + jsdc->debuggerHook = NULL; + JSD_UNLOCK(); + + return JS_TRUE; +} + +JSBool +jsd_SetThrowHook(JSDContext* jsdc, + JSD_ExecutionHookProc hook, + void* callerdata) +{ + JSD_LOCK(); + jsdc->throwHookData = callerdata; + jsdc->throwHook = hook; + JSD_UNLOCK(); + + return JS_TRUE; +} + +JSBool +jsd_ClearThrowHook(JSDContext* jsdc) +{ + JSD_LOCK(); + jsdc->throwHook = NULL; + JSD_UNLOCK(); + + return JS_TRUE; +} + +JSBool +jsd_SetFunctionHook(JSDContext* jsdc, + JSD_CallHookProc hook, + void* callerdata) +{ + JSD_LOCK(); + jsdc->functionHookData = callerdata; + jsdc->functionHook = hook; + JSD_UNLOCK(); + + return JS_TRUE; +} + +JSBool +jsd_ClearFunctionHook(JSDContext* jsdc) +{ + JSD_LOCK(); + jsdc->functionHook = NULL; + JSD_UNLOCK(); + + return JS_TRUE; +} + +JSBool +jsd_SetTopLevelHook(JSDContext* jsdc, + JSD_CallHookProc hook, + void* callerdata) +{ + JSD_LOCK(); + jsdc->toplevelHookData = callerdata; + jsdc->toplevelHook = hook; + JSD_UNLOCK(); + + return JS_TRUE; +} + +JSBool +jsd_ClearTopLevelHook(JSDContext* jsdc) +{ + JSD_LOCK(); + jsdc->toplevelHook = NULL; + JSD_UNLOCK(); + + return JS_TRUE; +} + diff --git a/js/jsd/jsd_java.cpp b/js/jsd/jsd_java.cpp new file mode 100644 index 0000000..c5c0aec --- /dev/null +++ b/js/jsd/jsd_java.cpp @@ -0,0 +1,779 @@ +/* -*- 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/. */ + +/* this is all going away... replaced by code in js/jsd/java */ + +#if 0 + +#include "native.h" +#include "jsdebug.h" + +#include "_gen/netscape_jsdebug_DebugController.h" +#include "_gen/netscape_jsdebug_Script.h" +#include "_gen/netscape_jsdebug_JSThreadState.h" +#include "_gen/netscape_jsdebug_JSStackFrameInfo.h" +#include "_gen/netscape_jsdebug_JSPC.h" +#include "_gen/netscape_jsdebug_JSSourceTextProvider.h" +#include "_gen/netscape_jsdebug_JSErrorReporter.h" + +static JSDContext* context = 0; +static struct Hnetscape_jsdebug_DebugController* controller = 0; + +/***************************************************************************/ +/* helpers */ + +static JHandle* +_constructInteger(ExecEnv *ee, long i) +{ + return (JHandle*) + execute_java_constructor(ee, "java/lang/Integer", 0, "(I)", i); +} + +static JHandle* +_putHash(ExecEnv *ee, JHandle* tbl, JHandle* key, JHandle* ob) +{ + return (JHandle*) + execute_java_dynamic_method( + ee, tbl, "put", + "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;", + key, ob ); +} + +static JHandle* +_getHash(ExecEnv *ee, JHandle* tbl, JHandle* key) +{ + return (JHandle*) + execute_java_dynamic_method( + ee, tbl, "get", + "(Ljava/lang/Object;)Ljava/lang/Object;", + key ); +} + +static JHandle* +_removeHash(ExecEnv *ee, JHandle* tbl, JHandle* key) +{ + return (JHandle*) + execute_java_dynamic_method( + ee, tbl, "remove", + "(Ljava/lang/Object;)Ljava/lang/Object;", + key ); +} + +struct Hnetscape_jsdebug_JSStackFrameInfo* +_constructJSStackFrameInfo( ExecEnv* ee, JSDStackFrameInfo* jsdframe, + struct Hnetscape_jsdebug_JSThreadState* threadState ) +{ + struct Hnetscape_jsdebug_JSStackFrameInfo* frame; + + frame = (struct Hnetscape_jsdebug_JSStackFrameInfo*) + execute_java_constructor( ee, "netscape/jsdebug/JSStackFrameInfo", 0, + "(Lnetscape/jsdebug/JSThreadState;)", + threadState ); + if( ! frame ) + return NULL; + + /* XXX fill in additional fields */ + unhand(frame)->_nativePtr = (long) jsdframe; + + return frame; +} + +struct Hnetscape_jsdebug_JSPC* +_constructJSPC( ExecEnv* ee, struct Hnetscape_jsdebug_Script* script, long pc ) +{ + struct Hnetscape_jsdebug_JSPC * pcOb; + + pcOb = (struct Hnetscape_jsdebug_JSPC *) + execute_java_constructor( ee, "netscape/jsdebug/JSPC", 0, + "(Lnetscape/jsdebug/Script;I)", + script, pc ); + if( ! pcOb ) + return NULL; + + /* XXX fill in additional fields */ + + return pcOb; +} + +static Hnetscape_jsdebug_Script* +_scriptObFromJSDScriptPtr( ExecEnv* ee, JSDScript* jsdscript ) +{ + JHandle* tbl = (JHandle*) unhand(controller)->scriptTable; + JHandle* key = _constructInteger(ee,(long)jsdscript); + return (Hnetscape_jsdebug_Script*) _getHash( ee, tbl, key ); +} + +/***************************************************************************/ + +void +_scriptHook( JSDContext* jsdc, + JSDScript* jsdscript, + JSBool creating, + void* callerdata ) +{ + Hnetscape_jsdebug_Script* script; + ExecEnv* ee = EE(); + + if( ! context || ! controller || ! ee ) + return; + + if( creating ) + { + char* url = (char*)JSD_GetScriptFilename (jsdc, jsdscript); + JSString* function = JSD_GetScriptFunctionId (jsdc, jsdscript); + int base = JSD_GetScriptBaseLineNumber (jsdc, jsdscript); + int extent = JSD_GetScriptLineExtent (jsdc, jsdscript); + + if( ! url ) + { + return; + /* url = ""; */ + } + + /* create Java Object for Script */ + script = (Hnetscape_jsdebug_Script*) + execute_java_constructor(ee, "netscape/jsdebug/Script", 0, "()"); + + if( ! script ) + return; + + /* set the members */ + unhand(script)->_url = makeJavaString(url,strlen(url)); + unhand(script)->_function = function ? makeJavaString(function,strlen(function)) : 0; + unhand(script)->_baseLineNumber = base; + unhand(script)->_lineExtent = extent; + unhand(script)->_nativePtr = (long)jsdscript; + + /* add it to the hash table */ + _putHash( ee, (JHandle*) unhand(controller)->scriptTable, + _constructInteger(ee, (long)jsdscript), (JHandle*)script ); + + /* call the hook */ + if( unhand(controller)->scriptHook ) + { + execute_java_dynamic_method( ee,(JHandle*)unhand(controller)->scriptHook, + "justLoadedScript", + "(Lnetscape/jsdebug/Script;)V", + script ); + } + } + else + { + JHandle* tbl = (JHandle*) unhand(controller)->scriptTable; + JHandle* key = _constructInteger(ee,(long)jsdscript); + + /* find Java Object for Script */ + script = (Hnetscape_jsdebug_Script*) _getHash( ee, tbl, key ); + + if( ! script ) + return; + + /* remove it from the hash table */ + _removeHash( ee, tbl, key ); + + /* call the hook */ + if( unhand(controller)->scriptHook ) + { + execute_java_dynamic_method( ee,(JHandle*)unhand(controller)->scriptHook, + "aboutToUnloadScript", + "(Lnetscape/jsdebug/Script;)V", + script ); + } + /* set the Script as invalid */ + execute_java_dynamic_method( ee,(JHandle*)script, + "_setInvalid", + "()V" ); + } +} + +/***************************************************************************/ +unsigned +_executionHook( JSDContext* jsdc, + JSDThreadState* jsdstate, + unsigned type, + void* callerdata ) +{ + Hnetscape_jsdebug_JSThreadState* threadState; + Hnetscape_jsdebug_Script* script; + JHandle* pcOb; + JSDStackFrameInfo* jsdframe; + JSDScript* jsdscript; + int pc; + JHandle* tblScript; + JHandle* keyScript; + ExecEnv* ee = EE(); + + if( ! context || ! controller || ! ee ) + return JSD_HOOK_RETURN_HOOK_ERROR; + + /* get the JSDStackFrameInfo */ + jsdframe = JSD_GetStackFrame(jsdc, jsdstate); + if( ! jsdframe ) + return JSD_HOOK_RETURN_HOOK_ERROR; + + /* get the JSDScript */ + jsdscript = JSD_GetScriptForStackFrame(jsdc, jsdstate, jsdframe); + if( ! jsdscript ) + return JSD_HOOK_RETURN_HOOK_ERROR; + + /* find Java Object for Script */ + tblScript = (JHandle*) unhand(controller)->scriptTable; + keyScript = _constructInteger(ee, (long)jsdscript); + script = (Hnetscape_jsdebug_Script*) _getHash( ee, tblScript, keyScript ); + if( ! script ) + return JSD_HOOK_RETURN_HOOK_ERROR; + + /* generate a JSPC */ + pc = JSD_GetPCForStackFrame(jsdc, jsdstate, jsdframe); + + pcOb = (JHandle*) + _constructJSPC(ee, script, pc); + if( ! pcOb ) + return JSD_HOOK_RETURN_HOOK_ERROR; + + /* build a JSThreadState */ + threadState = (struct Hnetscape_jsdebug_JSThreadState*) + execute_java_constructor( ee, "netscape/jsdebug/JSThreadState",0,"()"); + if( ! threadState ) + return JSD_HOOK_RETURN_HOOK_ERROR; + + /* populate the ThreadState */ + /* XXX FILL IN THE REST... */ + unhand(threadState)->valid = 1; /* correct value for true? */ + unhand(threadState)->currentFramePtr = (long) jsdframe; + unhand(threadState)->nativeThreadState = (long) jsdstate; + unhand(threadState)->continueState = netscape_jsdebug_JSThreadState_DEBUG_STATE_RUN; + + /* XXX FILL IN THE REST... */ + + + /* find and call the appropriate Hook */ + if( JSD_HOOK_INTERRUPTED == type ) + { + JHandle* hook; + + /* clear the JSD level hook (must reset on next sendInterrupt0()*/ + JSD_ClearInterruptHook(context); + + hook = (JHandle*) unhand(controller)->interruptHook; + if( ! hook ) + return JSD_HOOK_RETURN_HOOK_ERROR; + + /* call the hook */ + execute_java_dynamic_method( + ee, hook, "aboutToExecute", + "(Lnetscape/jsdebug/ThreadStateBase;Lnetscape/jsdebug/PC;)V", + threadState, pcOb ); + } + else if( JSD_HOOK_DEBUG_REQUESTED == type ) + { + JHandle* hook; + + hook = (JHandle*) unhand(controller)->debugBreakHook; + if( ! hook ) + return JSD_HOOK_RETURN_HOOK_ERROR; + + /* call the hook */ + execute_java_dynamic_method( + ee, hook, "aboutToExecute", + "(Lnetscape/jsdebug/ThreadStateBase;Lnetscape/jsdebug/PC;)V", + threadState, pcOb ); + } + else if( JSD_HOOK_BREAKPOINT == type ) + { + JHandle* hook; + + hook = (JHandle*) + execute_java_dynamic_method( + ee,(JHandle*)controller, + "getInstructionHook0", + "(Lnetscape/jsdebug/PC;)Lnetscape/jsdebug/InstructionHook;", + pcOb ); + if( ! hook ) + return JSD_HOOK_RETURN_HOOK_ERROR; + + /* call the hook */ + execute_java_dynamic_method( + ee, hook, "aboutToExecute", + "(Lnetscape/jsdebug/ThreadStateBase;)V", + threadState ); + } + + if( netscape_jsdebug_JSThreadState_DEBUG_STATE_THROW == + unhand(threadState)->continueState ) + return JSD_HOOK_RETURN_ABORT; + + return JSD_HOOK_RETURN_CONTINUE; +} + +unsigned +_errorReporter( JSDContext* jsdc, + JSContext* cx, + const char* message, + JSErrorReport* report, + void* callerdata ) +{ + JHandle* reporter; + JHandle* msg = NULL; + JHandle* filename = NULL; + int lineno = 0; + JHandle* linebuf = NULL; + int tokenOffset = 0; + ExecEnv* ee = EE(); + + if( ! context || ! controller || ! ee ) + return JSD_ERROR_REPORTER_PASS_ALONG; + + reporter = (JHandle*) unhand(controller)->errorReporter; + if( ! reporter ) + return JSD_ERROR_REPORTER_PASS_ALONG; + + if( message ) + msg = (JHandle*) makeJavaString((char*)message, strlen(message)); + if( report && report->filename ) + filename = (JHandle*) makeJavaString((char*)report->filename, strlen(report->filename)); + if( report && report->linebuf ) + linebuf = (JHandle*) makeJavaString((char*)report->linebuf, strlen(report->linebuf)); + if( report ) + lineno = report->lineno; + if( report && report->linebuf && report->tokenptr ) + tokenOffset = report->tokenptr - report->linebuf; + + return (int) + execute_java_dynamic_method( + ee, reporter, "reportError", + "(Ljava/lang/String;Ljava/lang/String;ILjava/lang/String;I)I", + msg, + filename, + lineno, + linebuf, + tokenOffset ); +} + +/***************************************************************************/ +/* from "_gen\netscape_jsdebug_DebugController.h" */ + + +/* XXX HACK */ +JSDContext* _simContext = 0; + +void netscape_jsdebug_DebugController__setController(struct Hnetscape_jsdebug_DebugController * self,/*boolean*/ long on) +{ + if(on) + { + context = JSD_DebuggerOn(); + if( ! context ) + return; + + _simContext = context; /* XXX HACK */ + + unhand(self)->_nativeContext = (long) context; + controller = self; + JSD_SetScriptHook(context, _scriptHook, (void*)1 ); + JSD_SetErrorReporter(context, _errorReporter, (void*)1 ); + JSD_SetDebugBreakHook(context, _executionHook, (void*)1 ); + } + else + { + /* XXX stop somehow... */ + /* kill context */ + JSD_SetDebugBreakHook(context, NULL, NULL ); + JSD_SetErrorReporter(context, NULL, NULL); + JSD_SetScriptHook(context, NULL, NULL); + context = 0; + controller = 0; + } +} + +void netscape_jsdebug_DebugController_setInstructionHook0(struct Hnetscape_jsdebug_DebugController * self,struct Hnetscape_jsdebug_PC * pcOb) +{ + Hnetscape_jsdebug_Script* script; + JSDScript* jsdscript; + unsigned pc; + ExecEnv* ee = EE(); + + if( ! context || ! controller || ! ee ) + return; + + script = (Hnetscape_jsdebug_Script*) + execute_java_dynamic_method(ee, (JHandle*)pcOb, "getScript","()Lnetscape/jsdebug/Script;"); + + if( ! script ) + return; + + jsdscript = (JSDScript*) unhand(script)->_nativePtr; + if( ! jsdscript ) + return; + + pc = (unsigned) + execute_java_dynamic_method(ee, (JHandle*)pcOb, "getPC","()I"); + + JSD_SetExecutionHook(context, jsdscript, pc, _executionHook, 0); +} + +void netscape_jsdebug_DebugController_sendInterrupt0(struct Hnetscape_jsdebug_DebugController * self) +{ + if( ! context || ! controller ) + return; + JSD_SetInterruptHook(context, _executionHook, 0); +} + +struct Hjava_lang_String *netscape_jsdebug_DebugController_executeScriptInStackFrame0(struct Hnetscape_jsdebug_DebugController *self,struct Hnetscape_jsdebug_JSStackFrameInfo *frame,struct Hjava_lang_String *src,struct Hjava_lang_String *filename,long lineno) +{ + struct Hnetscape_jsdebug_JSThreadState* threadStateOb; + JSDThreadState* jsdthreadstate; + JSDStackFrameInfo* jsdframe; + char* filenameC; + char* srcC; + JSString* jsstr; + jsval rval; + JSBool success; + int srclen; + + threadStateOb = (struct Hnetscape_jsdebug_JSThreadState*)unhand(frame)->threadState; + jsdthreadstate = (JSDThreadState*) unhand(threadStateOb)->nativeThreadState; + + jsdframe = (JSDStackFrameInfo*) unhand(frame)->_nativePtr; + + if( ! context || ! controller || ! jsdframe ) + return NULL; + + filenameC = allocCString(filename); + if( ! filenameC ) + return NULL; + srcC = allocCString(src); + if( ! srcC ) + { + free(filenameC); + return NULL; + } + + srclen = strlen(srcC); + + success = JSD_EvaluateScriptInStackFrame(context, jsdthreadstate, jsdframe, + srcC, srclen, + filenameC, lineno, &rval); + + /* XXX crashing on Windows under Symantec (though I can't see why!)*/ + + free(filenameC); + free(srcC); + + + if( ! success ) + return NULL; + + if( JSVAL_IS_NULL(rval) || JSVAL_IS_VOID(rval) ) + return NULL; + + jsstr = JSD_ValToStringInStackFrame(context,jsdthreadstate,jsdframe,rval); + if( ! jsstr ) + return NULL; + + /* XXXbe should use JS_GetStringChars and preserve Unicode. */ + return makeJavaString((char*)JS_GetStringBytes(jsstr), JS_GetStringLength(jsstr)); +} + +long netscape_jsdebug_DebugController_getNativeMajorVersion(struct Hnetscape_jsdebug_DebugController* self) +{ + return (long) JSD_GetMajorVersion(); +} + +long netscape_jsdebug_DebugController_getNativeMinorVersion(struct Hnetscape_jsdebug_DebugController* self) +{ + return (long) JSD_GetMinorVersion(); +} + +/***************************************************************************/ +/* from "_gen\netscape_jsdebug_Script.h" */ + +struct Hnetscape_jsdebug_JSPC *netscape_jsdebug_Script_getClosestPC(struct Hnetscape_jsdebug_Script * self,long line) +{ + unsigned pc; + JSDScript* jsdscript; + + if( ! context || ! controller ) + return 0; + + jsdscript = (JSDScript*) unhand(self)->_nativePtr; + if( ! jsdscript ) + return 0; + + pc = JSD_GetClosestPC(context, jsdscript, line); + + if( -1 == pc ) + return 0; + return _constructJSPC( 0, self, pc); +} + +/***************************************************************************/ +/* from "_gen\netscape_jsdebug_JSThreadState.h" */ + +long netscape_jsdebug_JSThreadState_countStackFrames(struct Hnetscape_jsdebug_JSThreadState * self) +{ + JSDThreadState* jsdstate; + + if( ! context || ! controller ) + return 0; + + jsdstate = (JSDThreadState*) unhand(self)->nativeThreadState; + + if( ! jsdstate ) + return 0; + + return (long) JSD_GetCountOfStackFrames(context, jsdstate); +} + +struct Hnetscape_jsdebug_StackFrameInfo *netscape_jsdebug_JSThreadState_getCurrentFrame(struct Hnetscape_jsdebug_JSThreadState * self) +{ + JSDThreadState* jsdstate; + JSDStackFrameInfo* jsdframe; + + if( ! context || ! controller ) + return NULL; + + jsdstate = (JSDThreadState*) unhand(self)->nativeThreadState; + + if( ! jsdstate ) + return NULL; + + jsdframe = JSD_GetStackFrame(context, jsdstate); + if( ! jsdframe ) + return NULL; + + return (struct Hnetscape_jsdebug_StackFrameInfo*) + _constructJSStackFrameInfo( 0, jsdframe, self ); +} + + +/***************************************************************************/ +/* from "_gen\netscape_jsdebug_JSStackFrameInfo.h" */ + +struct Hnetscape_jsdebug_StackFrameInfo *netscape_jsdebug_JSStackFrameInfo_getCaller0(struct Hnetscape_jsdebug_JSStackFrameInfo * self) +{ + JSDStackFrameInfo* jsdframeCur; + JSDStackFrameInfo* jsdframeCaller; + struct Hnetscape_jsdebug_JSThreadState* threadState; + JSDThreadState* jsdthreadstate; + + if( ! context || ! controller ) + return NULL; + + jsdframeCur = (JSDStackFrameInfo*) unhand(self)->_nativePtr; + if( ! jsdframeCur ) + return NULL; + + threadState = (struct Hnetscape_jsdebug_JSThreadState*) unhand(self)->threadState; + if( ! threadState ) + return NULL; + + jsdthreadstate = (JSDThreadState*) unhand(threadState)->nativeThreadState; + if( ! jsdthreadstate ) + return NULL; + + jsdframeCaller = JSD_GetCallingStackFrame(context, jsdthreadstate, jsdframeCur); + if( ! jsdframeCaller ) + return NULL; + + return (struct Hnetscape_jsdebug_StackFrameInfo*) + _constructJSStackFrameInfo( 0, jsdframeCaller, threadState ); +} +struct Hnetscape_jsdebug_PC *netscape_jsdebug_JSStackFrameInfo_getPC(struct Hnetscape_jsdebug_JSStackFrameInfo * self) +{ + JSDScript* jsdscript; + JSDStackFrameInfo* jsdframe; + struct Hnetscape_jsdebug_Script* script; + struct Hnetscape_jsdebug_JSThreadState* threadState; + JSDThreadState* jsdthreadstate; + int pc; + ExecEnv* ee = EE(); + + if( ! context || ! controller || ! ee ) + return NULL; + + jsdframe = (JSDStackFrameInfo*) unhand(self)->_nativePtr; + if( ! jsdframe ) + return NULL; + + threadState = (struct Hnetscape_jsdebug_JSThreadState*) unhand(self)->threadState; + if( ! threadState ) + return NULL; + + jsdthreadstate = (JSDThreadState*) unhand(threadState)->nativeThreadState; + if( ! jsdthreadstate ) + return NULL; + + jsdscript = JSD_GetScriptForStackFrame(context, jsdthreadstate, jsdframe ); + if( ! jsdscript ) + return NULL; + + script = _scriptObFromJSDScriptPtr(ee, jsdscript); + if( ! script ) + return NULL; + + pc = JSD_GetPCForStackFrame(context, jsdthreadstate, jsdframe); + if( ! pc ) + return NULL; + + return (struct Hnetscape_jsdebug_PC*) _constructJSPC(ee, script, pc); +} + +/***************************************************************************/ +/* from "_gen\netscape_jsdebug_JSPC.h" */ + +struct Hnetscape_jsdebug_SourceLocation *netscape_jsdebug_JSPC_getSourceLocation(struct Hnetscape_jsdebug_JSPC * self) +{ + JSDScript* jsdscript; + struct Hnetscape_jsdebug_Script* script; + struct Hnetscape_jsdebug_JSPC* newPCOb; + int line; + int newpc; + int pc; + ExecEnv* ee = EE(); + + if( ! context || ! controller || ! ee ) + return NULL; + + script = unhand(self)->script; + + if( ! script ) + return NULL; + + jsdscript = (JSDScript*) unhand(script)->_nativePtr; + if( ! jsdscript ) + return NULL; + pc = unhand(self)->pc; + + line = JSD_GetClosestLine(context, jsdscript, pc); + newpc = JSD_GetClosestPC(context, jsdscript, line); + + newPCOb = _constructJSPC(ee, script, newpc ); + if( ! newPCOb ) + return NULL; + + return (struct Hnetscape_jsdebug_SourceLocation *) + execute_java_constructor( ee, "netscape/jsdebug/JSSourceLocation", 0, + "(Lnetscape/jsdebug/JSPC;I)", + newPCOb, line ); +} + +/***************************************************************************/ +/* from "_gen\netscape_jsdebug_JSSourceTextProvider.h" */ + +struct Hnetscape_jsdebug_SourceTextItem *netscape_jsdebug_JSSourceTextProvider_loadSourceTextItem0(struct Hnetscape_jsdebug_JSSourceTextProvider * self,struct Hjava_lang_String * url) +{ + /* this should attempt to load the source for the indicated URL */ + return NULL; +} + +void netscape_jsdebug_JSSourceTextProvider_refreshSourceTextVector(struct Hnetscape_jsdebug_JSSourceTextProvider * self) +{ + + JHandle* vec; + JHandle* itemOb; + JSDSourceText* iterp = 0; + JSDSourceText* item; + const char* url; + struct Hjava_lang_String* urlOb; + ExecEnv* ee = EE(); + + if( ! context || ! controller || ! ee ) + return; + + /* create new vector */ + vec = (JHandle*) execute_java_constructor(ee, "netscape/util/Vector", 0, "()"); + if( ! vec ) + return; + + /* lock the native subsystem */ + JSD_LockSourceTextSubsystem(context); + + /* iterate through the native items */ + while( 0 != (item = JSD_IterateSources(context, &iterp)) ) + { + int urlStrLen; + int status = JSD_GetSourceStatus(context,item); + + /* try to find Java object */ + url = JSD_GetSourceURL(context, item); + if( ! url || 0 == (urlStrLen = strlen(url)) ) /* ignoring those with no url */ + continue; + + urlOb = makeJavaString((char*)url,urlStrLen); + if( ! urlOb ) + continue; + + itemOb = (JHandle*) + execute_java_dynamic_method( ee, (JHandle*)self, "findSourceTextItem0", + "(Ljava/lang/String;)Lnetscape/jsdebug/SourceTextItem;", + urlOb ); + + if( ! itemOb ) + { + /* if not found then generate new item */ + struct Hjava_lang_String* textOb; + const char* str; + int length; + + if( ! JSD_GetSourceText(context, item, &str, &length ) ) + { + str = ""; + length = 0; + } + textOb = makeJavaString((char*)str, length); + + itemOb = (JHandle*) + execute_java_constructor(ee, "netscape/jsdebug/SourceTextItem",0, + "(Ljava/lang/String;Ljava/lang/String;I)", + urlOb, textOb, status ); + } + else if( JSD_IsSourceDirty(context, item) && + JSD_SOURCE_CLEARED != status ) + { + /* if found and dirty then update */ + struct Hjava_lang_String* textOb; + const char* str; + int length; + + if( ! JSD_GetSourceText(context, item, &str, &length ) ) + { + str = ""; + length = 0; + } + textOb = makeJavaString((char*)str, length); + execute_java_dynamic_method(ee, itemOb, "setText", + "(Ljava/lang/String;)V", textOb); + execute_java_dynamic_method(ee, itemOb, "setStatus", + "(I)V", status ); + execute_java_dynamic_method(ee, itemOb, "setDirty", "(Z)V", 1 ); + } + + /* we have our copy; clear the native cached text */ + if( JSD_SOURCE_INITED != status && + JSD_SOURCE_PARTIAL != status && + JSD_SOURCE_CLEARED != status ) + { + JSD_ClearSourceText(context, item); + } + + /* set the item clean */ + JSD_SetSourceDirty(context, item, FALSE ); + + /* add the item to the vector */ + if( itemOb ) + execute_java_dynamic_method(ee, vec, "addElement", + "(Ljava/lang/Object;)V", itemOb ); + } + /* unlock the native subsystem */ + JSD_UnlockSourceTextSubsystem(context); + + /* set main vector to our new vector */ + + unhand(self)->_sourceTextVector = (struct Hnetscape_util_Vector*) vec; +} + + +#endif diff --git a/js/jsd/jsd_lock.cpp b/js/jsd/jsd_lock.cpp new file mode 100644 index 0000000..9583f28 --- /dev/null +++ b/js/jsd/jsd_lock.cpp @@ -0,0 +1,227 @@ +/* -*- 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 - Locking and threading support + */ + +/* +* ifdef JSD_USE_NSPR_LOCKS then you must build and run against NSPR2. +* Otherwise, there are stubs that can be filled in with your own locking +* code. Also, note that these stubs include a jsd_CurrentThread() +* implementation that only works on Win32 - this is needed for the inprocess +* Java-based debugger. +*/ + +#include "jsd.h" + +#include "js/Utility.h" + +#ifdef JSD_THREADSAFE + +#ifdef JSD_USE_NSPR_LOCKS + +#include "prlock.h" +#include "prthread.h" + +#ifdef JSD_ATTACH_THREAD_HACK +#include "pprthred.h" /* need this as long as JS_AttachThread is needed */ +#endif + +struct JSDStaticLock +{ + void* owner; + PRLock* lock; + int count; +#ifdef DEBUG + uint16_t sig; +#endif +}; + +/* + * This exists to wrap non-NSPR theads (e.g. Java threads) in NSPR wrappers. + * XXX We ignore the memory leak issue. + * It is claimed that future versions of NSPR will automatically wrap on + * the call to PR_GetCurrentThread. + * + * XXX We ignore the memory leak issue - i.e. we never call PR_DetachThread. + * + */ +#undef _CURRENT_THREAD +#ifdef JSD_ATTACH_THREAD_HACK +#define _CURRENT_THREAD(out) \ +JS_BEGIN_MACRO \ + out = (void*) PR_GetCurrentThread(); \ + if(!out) \ + out = (void*) JS_AttachThread(PR_USER_THREAD,PR_PRIORITY_NORMAL,NULL);\ + JS_ASSERT(out); \ +JS_END_MACRO +#else +#define _CURRENT_THREAD(out) \ +JS_BEGIN_MACRO \ + out = (void*) PR_GetCurrentThread(); \ + JS_ASSERT(out); \ +JS_END_MACRO +#endif + +#ifdef DEBUG +#define JSD_LOCK_SIG 0x10CC10CC +void ASSERT_VALID_LOCK(JSDStaticLock* lock) +{ + JS_ASSERT(lock); + JS_ASSERT(lock->lock); + JS_ASSERT(lock->count >= 0); + JS_ASSERT(lock->sig == (uint16_t) JSD_LOCK_SIG); +} +#else +#define ASSERT_VALID_LOCK(x) ((void)0) +#endif + +JSDStaticLock* +jsd_CreateLock() +{ + JSDStaticLock* lock; + + if( ! (lock = js_pod_calloc<JSDStaticLock>()) || + ! (lock->lock = PR_NewLock()) ) + { + if(lock) + { + free(lock); + lock = NULL; + } + } +#ifdef DEBUG + if(lock) lock->sig = (uint16_t) JSD_LOCK_SIG; +#endif + return lock; +} + +void +jsd_Lock(JSDStaticLock* lock) +{ + void* me; + ASSERT_VALID_LOCK(lock); + _CURRENT_THREAD(me); + + if(lock->owner == me) + { + lock->count++; + JS_ASSERT(lock->count > 1); + } + else + { + PR_Lock(lock->lock); /* this can block... */ + JS_ASSERT(lock->owner == 0); + JS_ASSERT(lock->count == 0); + lock->count = 1; + lock->owner = me; + } +} + +void +jsd_Unlock(JSDStaticLock* lock) +{ + void* me; + ASSERT_VALID_LOCK(lock); + _CURRENT_THREAD(me); + + /* it's an error to unlock a lock you don't own */ + JS_ASSERT(lock->owner == me); + if(lock->owner != me) + return; + + if(--lock->count == 0) + { + lock->owner = NULL; + PR_Unlock(lock->lock); + } +} + +#ifdef DEBUG +JSBool +jsd_IsLocked(JSDStaticLock* lock) +{ + void* me; + ASSERT_VALID_LOCK(lock); + _CURRENT_THREAD(me); + if (lock->owner != me) + return JS_FALSE; + JS_ASSERT(lock->count > 0); + return JS_TRUE; +} +#endif /* DEBUG */ + +void* +jsd_CurrentThread() +{ + void* me; + _CURRENT_THREAD(me); + return me; +} + + +#else /* ! JSD_USE_NSPR_LOCKS */ + +#ifdef WIN32 +#pragma message("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!") +#pragma message("!! you are compiling the stubbed version of jsd_lock.c !!") +#pragma message("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!") +#endif + +/* + * NOTE: 'Real' versions of these locks must be reentrant in the sense that + * they support nested calls to lock and unlock. + */ + +void* +jsd_CreateLock() +{ + return (void*)1; +} + +void +jsd_Lock(void* lock) +{ +} + +void +jsd_Unlock(void* lock) +{ +} + +#ifdef DEBUG +JSBool +jsd_IsLocked(void* lock) +{ + return JS_TRUE; +} +#endif /* DEBUG */ + +/* + * This Windows only thread id code is here to allow the Java-based + * JSDebugger to work with the single threaded js.c shell (even without + * real locking and threading support). + */ + +#ifdef WIN32 +/* bogus (but good enough) declaration*/ +extern void* __stdcall GetCurrentThreadId(void); +#endif + +void* +jsd_CurrentThread() +{ +#ifdef WIN32 + return GetCurrentThreadId(); +#else + return (void*)1; +#endif +} + +#endif /* JSD_USE_NSPR_LOCKS */ + +#endif /* JSD_THREADSAFE */ diff --git a/js/jsd/jsd_lock.h b/js/jsd/jsd_lock.h new file mode 100644 index 0000000..b1e8c31 --- /dev/null +++ b/js/jsd/jsd_lock.h @@ -0,0 +1,43 @@ +/* -*- 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/. */ + +/* + * Header for JavaScript Debugging support - Locking and threading functions + */ + +#ifndef jsd_lock_h___ +#define jsd_lock_h___ + +/* + * If you want to support threading and locking, define JSD_THREADSAFE and + * implement the functions below. + */ + +/* + * NOTE: These locks must be reentrant in the sense that they support + * nested calls to lock and unlock. + */ + +typedef struct JSDStaticLock JSDStaticLock; + +extern JSDStaticLock* +jsd_CreateLock(); + +extern void +jsd_Lock(JSDStaticLock* lock); + +extern void +jsd_Unlock(JSDStaticLock* lock); + +#ifdef DEBUG +extern JSBool +jsd_IsLocked(JSDStaticLock* lock); +#endif /* DEBUG */ + +extern void* +jsd_CurrentThread(); + +#endif /* jsd_lock_h___ */ diff --git a/js/jsd/jsd_obj.cpp b/js/jsd/jsd_obj.cpp new file mode 100644 index 0000000..a27b533 --- /dev/null +++ b/js/jsd/jsd_obj.cpp @@ -0,0 +1,234 @@ +/* -*- 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 - Object support + */ + +#include "jsd.h" + +/* +* #define JSD_TRACE 1 +*/ + +#ifdef JSD_TRACE +#define TRACEOBJ(jsdc, jsdobj, which) _traceObj(jsdc, jsdobj, which) + +static char * +_describeObj(JSDContext* jsdc, JSDObject *jsdobj) +{ + return + JS_smprintf("%0x new'd in %s at line %d using ctor %s in %s at line %d", + (int)jsdobj, + JSD_GetObjectNewURL(jsdc, jsdobj), + JSD_GetObjectNewLineNumber(jsdc, jsdobj), + JSD_GetObjectConstructorName(jsdc, jsdobj), + JSD_GetObjectConstructorURL(jsdc, jsdobj), + JSD_GetObjectConstructorLineNumber(jsdc, jsdobj)); +} + +static void +_traceObj(JSDContext* jsdc, JSDObject* jsdobj, int which) +{ + char* description; + + if( !jsdobj ) + return; + + description = _describeObj(jsdc, jsdobj); + + printf("%s : %s\n", + which == 0 ? "new " : + which == 1 ? "final" : + "ctor ", + description); + if(description) + free(description); +} +#else +#define TRACEOBJ(jsdc, jsdobj, which) ((void)0) +#endif /* JSD_TRACE */ + +#ifdef DEBUG +void JSD_ASSERT_VALID_OBJECT(JSDObject* jsdobj) +{ + JS_ASSERT(jsdobj); + JS_ASSERT(!JS_CLIST_IS_EMPTY(&jsdobj->links)); + JS_ASSERT(jsdobj->obj); +} +#endif + + +static void +_destroyJSDObject(JSDContext* jsdc, JSDObject* jsdobj) +{ + JS_ASSERT(JSD_OBJECTS_LOCKED(jsdc)); + + JS_REMOVE_LINK(&jsdobj->links); + JS_HashTableRemove(jsdc->objectsTable, jsdobj->obj); + + if(jsdobj->newURL) + jsd_DropAtom(jsdc, jsdobj->newURL); + if(jsdobj->ctorURL) + jsd_DropAtom(jsdc, jsdobj->ctorURL); + if(jsdobj->ctorName) + jsd_DropAtom(jsdc, jsdobj->ctorName); + free(jsdobj); +} + +void +jsd_Constructing(JSDContext* jsdc, JSContext *cx, JSObject *obj, + JSAbstractFramePtr frame) +{ + JSDObject* jsdobj; + JS::RootedScript script(cx); + JSDScript* jsdscript; + const char* ctorURL; + JSString* ctorNameStr; + const char* ctorName; + + JSD_LOCK_OBJECTS(jsdc); + jsdobj = jsd_GetJSDObjectForJSObject(jsdc, obj); + if( jsdobj && !jsdobj->ctorURL ) + { + script = frame.script(); + if( script ) + { + ctorURL = JS_GetScriptFilename(cx, script); + if( ctorURL ) + jsdobj->ctorURL = jsd_AddAtom(jsdc, ctorURL); + + JSD_LOCK_SCRIPTS(jsdc); + jsdscript = jsd_FindOrCreateJSDScript(jsdc, cx, script, frame); + JSD_UNLOCK_SCRIPTS(jsdc); + if( jsdscript && (ctorNameStr = jsd_GetScriptFunctionId(jsdc, jsdscript)) ) { + if( (ctorName = JS_EncodeString(cx, ctorNameStr)) ) { + jsdobj->ctorName = jsd_AddAtom(jsdc, ctorName); + JS_free(cx, (void *) ctorName); + } + } + jsdobj->ctorLineno = JS_GetScriptBaseLineNumber(cx, script); + } + } + TRACEOBJ(jsdc, jsdobj, 3); + JSD_UNLOCK_OBJECTS(jsdc); +} + +static JSHashNumber +_hash_root(const void *key) +{ + return ((JSHashNumber)(ptrdiff_t) key) >> 2; /* help lame MSVC1.5 on Win16 */ +} + +JSBool +jsd_InitObjectManager(JSDContext* jsdc) +{ + JS_INIT_CLIST(&jsdc->objectsList); + jsdc->objectsTable = JS_NewHashTable(256, _hash_root, + JS_CompareValues, JS_CompareValues, + NULL, NULL); + return !!jsdc->objectsTable; +} + +void +jsd_DestroyObjectManager(JSDContext* jsdc) +{ + jsd_DestroyObjects(jsdc); + JSD_LOCK_OBJECTS(jsdc); + JS_HashTableDestroy(jsdc->objectsTable); + JSD_UNLOCK_OBJECTS(jsdc); +} + +void +jsd_DestroyObjects(JSDContext* jsdc) +{ + JSD_LOCK_OBJECTS(jsdc); + while( !JS_CLIST_IS_EMPTY(&jsdc->objectsList) ) + _destroyJSDObject(jsdc, (JSDObject*)JS_NEXT_LINK(&jsdc->objectsList)); + JSD_UNLOCK_OBJECTS(jsdc); +} + +JSDObject* +jsd_IterateObjects(JSDContext* jsdc, JSDObject** iterp) +{ + JSDObject *jsdobj = *iterp; + + JS_ASSERT(JSD_OBJECTS_LOCKED(jsdc)); + + if( !jsdobj ) + jsdobj = (JSDObject *)jsdc->objectsList.next; + if( jsdobj == (JSDObject *)&jsdc->objectsList ) + return NULL; + *iterp = (JSDObject*) jsdobj->links.next; + return jsdobj; +} + +JSObject* +jsd_GetWrappedObject(JSDContext* jsdc, JSDObject* jsdobj) +{ + return jsdobj->obj; +} + +const char* +jsd_GetObjectNewURL(JSDContext* jsdc, JSDObject* jsdobj) +{ + if( jsdobj->newURL ) + return JSD_ATOM_TO_STRING(jsdobj->newURL); + return NULL; +} + +unsigned +jsd_GetObjectNewLineNumber(JSDContext* jsdc, JSDObject* jsdobj) +{ + return jsdobj->newLineno; +} + +const char* +jsd_GetObjectConstructorURL(JSDContext* jsdc, JSDObject* jsdobj) +{ + if( jsdobj->ctorURL ) + return JSD_ATOM_TO_STRING(jsdobj->ctorURL); + return NULL; +} + +unsigned +jsd_GetObjectConstructorLineNumber(JSDContext* jsdc, JSDObject* jsdobj) +{ + return jsdobj->ctorLineno; +} + +const char* +jsd_GetObjectConstructorName(JSDContext* jsdc, JSDObject* jsdobj) +{ + if( jsdobj->ctorName ) + return JSD_ATOM_TO_STRING(jsdobj->ctorName); + return NULL; +} + +JSDObject* +jsd_GetJSDObjectForJSObject(JSDContext* jsdc, JSObject* jsobj) +{ + JSDObject* jsdobj; + + JSD_LOCK_OBJECTS(jsdc); + jsdobj = (JSDObject*) JS_HashTableLookup(jsdc->objectsTable, jsobj); + JSD_UNLOCK_OBJECTS(jsdc); + return jsdobj; +} + +JSDObject* +jsd_GetObjectForValue(JSDContext* jsdc, JSDValue* jsdval) +{ + return jsd_GetJSDObjectForJSObject(jsdc, JSVAL_TO_OBJECT(jsdval->val)); +} + +JSDValue* +jsd_GetValueForObject(JSDContext* jsdc, JSDObject* jsdobj) +{ + return jsd_NewValue(jsdc, OBJECT_TO_JSVAL(jsdobj->obj)); +} + + diff --git a/js/jsd/jsd_scpt.cpp b/js/jsd/jsd_scpt.cpp new file mode 100644 index 0000000..91e9b2f --- /dev/null +++ b/js/jsd/jsd_scpt.cpp @@ -0,0 +1,966 @@ +/* -*- 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 - Script support + */ + +#include "jsd.h" +#include "jsfriendapi.h" +#include "nsCxPusher.h" + +using mozilla::AutoSafeJSContext; + +/* Comment this out to disable (NT specific) dumping as we go */ +/* +** #ifdef DEBUG +** #define JSD_DUMP 1 +** #endif +*/ + +#define NOT_SET_YET -1 + +/***************************************************************************/ + +#ifdef DEBUG +void JSD_ASSERT_VALID_SCRIPT(JSDScript* jsdscript) +{ + JS_ASSERT(jsdscript); + JS_ASSERT(jsdscript->script); +} +void JSD_ASSERT_VALID_EXEC_HOOK(JSDExecHook* jsdhook) +{ + JS_ASSERT(jsdhook); + JS_ASSERT(jsdhook->hook); +} +#endif + +#ifdef LIVEWIRE +static JSBool +HasFileExtention(const char* name, const char* ext) +{ + int i; + int len = strlen(ext); + const char* p = strrchr(name,'.'); + if( !p ) + return JS_FALSE; + p++; + for(i = 0; i < len; i++ ) + { + JS_ASSERT(islower(ext[i])); + if( 0 == p[i] || tolower(p[i]) != ext[i] ) + return JS_FALSE; + } + if( 0 != p[i] ) + return JS_FALSE; + return JS_TRUE; +} +#endif /* LIVEWIRE */ + +static JSDScript* +_newJSDScript(JSDContext* jsdc, + JSContext *cx, + JSScript *script_) +{ + JS::RootedScript script(cx, script_); + if ( JS_GetScriptIsSelfHosted(script) ) + return NULL; + + JSDScript* jsdscript; + unsigned lineno; + const char* raw_filename; + + JS_ASSERT(JSD_SCRIPTS_LOCKED(jsdc)); + + /* these are inlined javascript: urls and we can't handle them now */ + lineno = (unsigned) JS_GetScriptBaseLineNumber(cx, script); + if( lineno == 0 ) + return NULL; + + jsdscript = (JSDScript*) calloc(1, sizeof(JSDScript)); + if( ! jsdscript ) + return NULL; + + raw_filename = JS_GetScriptFilename(cx,script); + + JS_HashTableAdd(jsdc->scriptsTable, (void *)script, (void *)jsdscript); + JS_APPEND_LINK(&jsdscript->links, &jsdc->scripts); + jsdscript->jsdc = jsdc; + jsdscript->script = script; + jsdscript->lineBase = lineno; + jsdscript->lineExtent = (unsigned)NOT_SET_YET; + jsdscript->data = NULL; +#ifndef LIVEWIRE + jsdscript->url = (char*) jsd_BuildNormalizedURL(raw_filename); +#else + jsdscript->app = LWDBG_GetCurrentApp(); + if( jsdscript->app && raw_filename ) + { + jsdscript->url = jsdlw_BuildAppRelativeFilename(jsdscript->app, raw_filename); + if( function ) + { + JSString* funid = JS_GetFunctionId(function); + char* funbytes; + const char* funnanme; + if( fuinid ) + { + funbytes = JS_EncodeString(cx, funid); + funname = funbytes ? funbytes : ""; + } + else + { + funbytes = NULL; + funname = "anonymous"; + } + jsdscript->lwscript = + LWDBG_GetScriptOfFunction(jsdscript->app,funname); + JS_Free(cx, funbytes); + + /* also, make sure this file is added to filelist if is .js file */ + if( HasFileExtention(raw_filename,"js") || + HasFileExtention(raw_filename,"sjs") ) + { + jsdlw_PreLoadSource(jsdc, jsdscript->app, raw_filename, JS_FALSE); + } + } + else + { + jsdscript->lwscript = LWDBG_GetCurrentTopLevelScript(); + } + } +#endif + + JS_INIT_CLIST(&jsdscript->hooks); + + return jsdscript; +} + +static void +_destroyJSDScript(JSDContext* jsdc, + JSDScript* jsdscript) +{ + JS_ASSERT(JSD_SCRIPTS_LOCKED(jsdc)); + + /* destroy all hooks */ + jsd_ClearAllExecutionHooksForScript(jsdc, jsdscript); + + JS_REMOVE_LINK(&jsdscript->links); + if(jsdscript->url) + free(jsdscript->url); + + if (jsdscript->profileData) + free(jsdscript->profileData); + + free(jsdscript); +} + +/***************************************************************************/ + +#ifdef JSD_DUMP +#ifndef XP_WIN +void +OutputDebugString (char *buf) +{ + fprintf (stderr, "%s", buf); +} +#endif + +static void +_dumpJSDScript(JSDContext* jsdc, JSDScript* jsdscript, const char* leadingtext) +{ + const char* name; + JSString* fun; + unsigned base; + unsigned extent; + char Buf[256]; + size_t n; + + name = jsd_GetScriptFilename(jsdc, jsdscript); + fun = jsd_GetScriptFunctionId(jsdc, jsdscript); + base = jsd_GetScriptBaseLineNumber(jsdc, jsdscript); + extent = jsd_GetScriptLineExtent(jsdc, jsdscript); + n = size_t(snprintf(Buf, sizeof(Buf), "%sscript=%08X, %s, ", + leadingtext, (unsigned) jsdscript->script, + name ? name : "no URL")); + if (n + 1 < sizeof(Buf)) { + if (fun) { + n += size_t(snprintf(Buf + n, sizeof(Buf) - n, "%s", "no fun")); + } else { + n += JS_PutEscapedFlatString(Buf + n, sizeof(Buf) - n, + JS_ASSERT_STRING_IS_FLAT(fun), 0); + Buf[sizeof(Buf) - 1] = '\0'; + } + if (n + 1 < sizeof(Buf)) + snprintf(Buf + n, sizeof(Buf) - n, ", %d-%d\n", base, base + extent - 1); + } + OutputDebugString( Buf ); +} + +static void +_dumpJSDScriptList( JSDContext* jsdc ) +{ + JSDScript* iterp = NULL; + JSDScript* jsdscript = NULL; + + OutputDebugString( "*** JSDScriptDump\n" ); + while( NULL != (jsdscript = jsd_IterateScripts(jsdc, &iterp)) ) + _dumpJSDScript( jsdc, jsdscript, " script: " ); +} +#endif /* JSD_DUMP */ + +/***************************************************************************/ +static JSHashNumber +jsd_hash_script(const void *key) +{ + return ((JSHashNumber)(ptrdiff_t) key) >> 2; /* help lame MSVC1.5 on Win16 */ +} + +static void * +jsd_alloc_script_table(void *priv, size_t size) +{ + return malloc(size); +} + +static void +jsd_free_script_table(void *priv, void *item, size_t size) +{ + free(item); +} + +static JSHashEntry * +jsd_alloc_script_entry(void *priv, const void *item) +{ + return (JSHashEntry*) malloc(sizeof(JSHashEntry)); +} + +static void +jsd_free_script_entry(void *priv, JSHashEntry *he, unsigned flag) +{ + if (flag == HT_FREE_ENTRY) + { + _destroyJSDScript((JSDContext*) priv, (JSDScript*) he->value); + free(he); + } +} + +static JSHashAllocOps script_alloc_ops = { + jsd_alloc_script_table, jsd_free_script_table, + jsd_alloc_script_entry, jsd_free_script_entry +}; + +#ifndef JSD_SCRIPT_HASH_SIZE +#define JSD_SCRIPT_HASH_SIZE 1024 +#endif + +JSBool +jsd_InitScriptManager(JSDContext* jsdc) +{ + JS_INIT_CLIST(&jsdc->scripts); + jsdc->scriptsTable = JS_NewHashTable(JSD_SCRIPT_HASH_SIZE, jsd_hash_script, + JS_CompareValues, JS_CompareValues, + &script_alloc_ops, (void*) jsdc); + return !!jsdc->scriptsTable; +} + +void +jsd_DestroyScriptManager(JSDContext* jsdc) +{ + JSD_LOCK_SCRIPTS(jsdc); + if (jsdc->scriptsTable) + JS_HashTableDestroy(jsdc->scriptsTable); + JSD_UNLOCK_SCRIPTS(jsdc); +} + +JSDScript* +jsd_FindJSDScript( JSDContext* jsdc, + JSScript *script ) +{ + JS_ASSERT(JSD_SCRIPTS_LOCKED(jsdc)); + return (JSDScript*) JS_HashTableLookup(jsdc->scriptsTable, (void *)script); +} + +JSDScript * +jsd_FindOrCreateJSDScript(JSDContext *jsdc, + JSContext *cx, + JSScript *script_, + JSAbstractFramePtr frame) +{ + JS::RootedScript script(cx, script_); + JSDScript *jsdscript; + JS_ASSERT(JSD_SCRIPTS_LOCKED(jsdc)); + + jsdscript = jsd_FindJSDScript(jsdc, script); + if (jsdscript) + return jsdscript; + + /* Fallback for unknown scripts: create a new script. */ + if (!frame) { + JSBrokenFrameIterator iter(cx); + if (!iter.done()) + frame = iter.abstractFramePtr(); + } + if (frame) + jsdscript = _newJSDScript(jsdc, cx, script); + + return jsdscript; +} + +JSDProfileData* +jsd_GetScriptProfileData(JSDContext* jsdc, JSDScript *script) +{ + if (!script->profileData) + script->profileData = (JSDProfileData*)calloc(1, sizeof(JSDProfileData)); + + return script->profileData; +} + +uint32_t +jsd_GetScriptFlags(JSDContext *jsdc, JSDScript *script) +{ + return script->flags; +} + +void +jsd_SetScriptFlags(JSDContext *jsdc, JSDScript *script, uint32_t flags) +{ + script->flags = flags; +} + +unsigned +jsd_GetScriptCallCount(JSDContext* jsdc, JSDScript *script) +{ + if (script->profileData) + return script->profileData->callCount; + + return 0; +} + +unsigned +jsd_GetScriptMaxRecurseDepth(JSDContext* jsdc, JSDScript *script) +{ + if (script->profileData) + return script->profileData->maxRecurseDepth; + + return 0; +} + +double +jsd_GetScriptMinExecutionTime(JSDContext* jsdc, JSDScript *script) +{ + if (script->profileData) + return script->profileData->minExecutionTime; + + return 0.0; +} + +double +jsd_GetScriptMaxExecutionTime(JSDContext* jsdc, JSDScript *script) +{ + if (script->profileData) + return script->profileData->maxExecutionTime; + + return 0.0; +} + +double +jsd_GetScriptTotalExecutionTime(JSDContext* jsdc, JSDScript *script) +{ + if (script->profileData) + return script->profileData->totalExecutionTime; + + return 0.0; +} + +double +jsd_GetScriptMinOwnExecutionTime(JSDContext* jsdc, JSDScript *script) +{ + if (script->profileData) + return script->profileData->minOwnExecutionTime; + + return 0.0; +} + +double +jsd_GetScriptMaxOwnExecutionTime(JSDContext* jsdc, JSDScript *script) +{ + if (script->profileData) + return script->profileData->maxOwnExecutionTime; + + return 0.0; +} + +double +jsd_GetScriptTotalOwnExecutionTime(JSDContext* jsdc, JSDScript *script) +{ + if (script->profileData) + return script->profileData->totalOwnExecutionTime; + + return 0.0; +} + +void +jsd_ClearScriptProfileData(JSDContext* jsdc, JSDScript *script) +{ + if (script->profileData) + { + free(script->profileData); + script->profileData = NULL; + } +} + +JSScript * +jsd_GetJSScript (JSDContext *jsdc, JSDScript *script) +{ + return script->script; +} + +JSFunction * +jsd_GetJSFunction (JSDContext *jsdc, JSDScript *script) +{ + AutoSafeJSContext cx; // NB: Actually unused. + return JS_GetScriptFunction(cx, script->script); +} + +JSDScript* +jsd_IterateScripts(JSDContext* jsdc, JSDScript **iterp) +{ + JSDScript *jsdscript = *iterp; + + JS_ASSERT(JSD_SCRIPTS_LOCKED(jsdc)); + + if( !jsdscript ) + jsdscript = (JSDScript *)jsdc->scripts.next; + if( jsdscript == (JSDScript *)&jsdc->scripts ) + return NULL; + *iterp = (JSDScript*) jsdscript->links.next; + return jsdscript; +} + +void * +jsd_SetScriptPrivate(JSDScript *jsdscript, void *data) +{ + void *rval = jsdscript->data; + jsdscript->data = data; + return rval; +} + +void * +jsd_GetScriptPrivate(JSDScript *jsdscript) +{ + return jsdscript->data; +} + +JSBool +jsd_IsActiveScript(JSDContext* jsdc, JSDScript *jsdscript) +{ + JSDScript *current; + + JS_ASSERT(JSD_SCRIPTS_LOCKED(jsdc)); + + for( current = (JSDScript *)jsdc->scripts.next; + current != (JSDScript *)&jsdc->scripts; + current = (JSDScript *)current->links.next ) + { + if(jsdscript == current) + return JS_TRUE; + } + return JS_FALSE; +} + +const char* +jsd_GetScriptFilename(JSDContext* jsdc, JSDScript *jsdscript) +{ + return jsdscript->url; +} + +JSString* +jsd_GetScriptFunctionId(JSDContext* jsdc, JSDScript *jsdscript) +{ + JSString* str; + JSFunction *fun = jsd_GetJSFunction(jsdc, jsdscript); + + if( ! fun ) + return NULL; + str = JS_GetFunctionId(fun); + + /* For compatibility we return "anonymous", not an empty string here. */ + return str ? str : JS_GetAnonymousString(jsdc->jsrt); +} + +unsigned +jsd_GetScriptBaseLineNumber(JSDContext* jsdc, JSDScript *jsdscript) +{ + return jsdscript->lineBase; +} + +unsigned +jsd_GetScriptLineExtent(JSDContext* jsdc, JSDScript *jsdscript) +{ + AutoSafeJSContext cx; + JSAutoCompartment ac(cx, jsdc->glob); // Just in case. + if( NOT_SET_YET == (int)jsdscript->lineExtent ) + jsdscript->lineExtent = JS_GetScriptLineExtent(cx, jsdscript->script); + return jsdscript->lineExtent; +} + +uintptr_t +jsd_GetClosestPC(JSDContext* jsdc, JSDScript* jsdscript, unsigned line) +{ + uintptr_t pc; + + if( !jsdscript ) + return 0; +#ifdef LIVEWIRE + if( jsdscript->lwscript ) + { + unsigned newline; + jsdlw_RawToProcessedLineNumber(jsdc, jsdscript, line, &newline); + if( line != newline ) + line = newline; + } +#endif + + AutoSafeJSContext cx; + JSAutoCompartment ac(cx, jsdscript->script); + pc = (uintptr_t) JS_LineNumberToPC(cx, jsdscript->script, line ); + return pc; +} + +unsigned +jsd_GetClosestLine(JSDContext* jsdc, JSDScript* jsdscript, uintptr_t pc) +{ + unsigned first = jsdscript->lineBase; + unsigned last = first + jsd_GetScriptLineExtent(jsdc, jsdscript) - 1; + unsigned line = 0; + + if (pc) { + AutoSafeJSContext cx; + JSAutoCompartment ac(cx, jsdscript->script); + line = JS_PCToLineNumber(cx, jsdscript->script, (jsbytecode*)pc); + } + + if( line < first ) + return first; + if( line > last ) + return last; + +#ifdef LIVEWIRE + if( jsdscript && jsdscript->lwscript ) + { + unsigned newline; + jsdlw_ProcessedToRawLineNumber(jsdc, jsdscript, line, &newline); + line = newline; + } +#endif + + return line; +} + +JSBool +jsd_GetLinePCs(JSDContext* jsdc, JSDScript* jsdscript, + unsigned startLine, unsigned maxLines, + unsigned* count, unsigned** retLines, uintptr_t** retPCs) +{ + unsigned first = jsdscript->lineBase; + unsigned last = first + jsd_GetScriptLineExtent(jsdc, jsdscript) - 1; + JSBool ok; + jsbytecode **pcs; + unsigned i; + + if (last < startLine) + return JS_TRUE; + + AutoSafeJSContext cx; + JSAutoCompartment ac(cx, jsdscript->script); + + ok = JS_GetLinePCs(cx, jsdscript->script, + startLine, maxLines, + count, retLines, &pcs); + + if (ok) { + if (retPCs) { + for (i = 0; i < *count; ++i) { + (*retPCs)[i] = (*pcs)[i]; + } + } + + JS_free(cx, pcs); + } + + return ok; +} + +JSBool +jsd_SetScriptHook(JSDContext* jsdc, JSD_ScriptHookProc hook, void* callerdata) +{ + JSD_LOCK(); + jsdc->scriptHook = hook; + jsdc->scriptHookData = callerdata; + JSD_UNLOCK(); + return JS_TRUE; +} + +JSBool +jsd_GetScriptHook(JSDContext* jsdc, JSD_ScriptHookProc* hook, void** callerdata) +{ + JSD_LOCK(); + if( hook ) + *hook = jsdc->scriptHook; + if( callerdata ) + *callerdata = jsdc->scriptHookData; + JSD_UNLOCK(); + return JS_TRUE; +} + +JSBool +jsd_EnableSingleStepInterrupts(JSDContext* jsdc, JSDScript* jsdscript, JSBool enable) +{ + JSBool rv; + AutoSafeJSContext cx; + JSAutoCompartment ac(cx, jsdscript->script); + JSD_LOCK(); + rv = JS_SetSingleStepMode(cx, jsdscript->script, enable); + JSD_UNLOCK(); + return rv; +} + + +/***************************************************************************/ + +void +jsd_NewScriptHookProc( + JSContext *cx, + const char *filename, /* URL this script loads from */ + unsigned lineno, /* line where this script starts */ + JSScript *script, + JSFunction *fun, + void* callerdata ) +{ + JSDScript* jsdscript = NULL; + JSDContext* jsdc = (JSDContext*) callerdata; + JSD_ScriptHookProc hook; + void* hookData; + + JSD_ASSERT_VALID_CONTEXT(jsdc); + + if( JSD_IS_DANGEROUS_THREAD(jsdc) ) + return; + + JSD_LOCK_SCRIPTS(jsdc); + jsdscript = _newJSDScript(jsdc, cx, script); + JSD_UNLOCK_SCRIPTS(jsdc); + if( ! jsdscript ) + return; + +#ifdef JSD_DUMP + JSD_LOCK_SCRIPTS(jsdc); + _dumpJSDScript(jsdc, jsdscript, "***NEW Script: "); + _dumpJSDScriptList( jsdc ); + JSD_UNLOCK_SCRIPTS(jsdc); +#endif /* JSD_DUMP */ + + /* local in case jsdc->scriptHook gets cleared on another thread */ + JSD_LOCK(); + hook = jsdc->scriptHook; + if( hook ) + jsdscript->flags = jsdscript->flags | JSD_SCRIPT_CALL_DESTROY_HOOK_BIT; + hookData = jsdc->scriptHookData; + JSD_UNLOCK(); + + if( hook ) + hook(jsdc, jsdscript, JS_TRUE, hookData); +} + +void +jsd_DestroyScriptHookProc( + JSFreeOp *fop, + JSScript *script_, + void* callerdata ) +{ + JSDScript* jsdscript = NULL; + JSDContext* jsdc = (JSDContext*) callerdata; + // NB: We're called during GC, so we can't push a cx. Root directly with + // the runtime. + JS::RootedScript script(jsdc->jsrt, script_); + JSD_ScriptHookProc hook; + void* hookData; + + JSD_ASSERT_VALID_CONTEXT(jsdc); + + if( JSD_IS_DANGEROUS_THREAD(jsdc) ) + return; + + JSD_LOCK_SCRIPTS(jsdc); + jsdscript = jsd_FindJSDScript(jsdc, script); + JSD_UNLOCK_SCRIPTS(jsdc); + + if( ! jsdscript ) + return; + +#ifdef JSD_DUMP + JSD_LOCK_SCRIPTS(jsdc); + _dumpJSDScript(jsdc, jsdscript, "***DESTROY Script: "); + JSD_UNLOCK_SCRIPTS(jsdc); +#endif /* JSD_DUMP */ + + /* local in case hook gets cleared on another thread */ + JSD_LOCK(); + hook = (jsdscript->flags & JSD_SCRIPT_CALL_DESTROY_HOOK_BIT) ? jsdc->scriptHook : NULL; + hookData = jsdc->scriptHookData; + JSD_UNLOCK(); + + if( hook ) + hook(jsdc, jsdscript, JS_FALSE, hookData); + + JSD_LOCK_SCRIPTS(jsdc); + JS_HashTableRemove(jsdc->scriptsTable, (void *)script); + JSD_UNLOCK_SCRIPTS(jsdc); + +#ifdef JSD_DUMP + JSD_LOCK_SCRIPTS(jsdc); + _dumpJSDScriptList(jsdc); + JSD_UNLOCK_SCRIPTS(jsdc); +#endif /* JSD_DUMP */ +} + + +/***************************************************************************/ + +static JSDExecHook* +_findHook(JSDContext* jsdc, JSDScript* jsdscript, uintptr_t pc) +{ + JSDExecHook* jsdhook; + JSCList* list = &jsdscript->hooks; + + for( jsdhook = (JSDExecHook*)list->next; + jsdhook != (JSDExecHook*)list; + jsdhook = (JSDExecHook*)jsdhook->links.next ) + { + if (jsdhook->pc == pc) + return jsdhook; + } + return NULL; +} + +static JSBool +_isActiveHook(JSDContext* jsdc, JSScript *script, JSDExecHook* jsdhook) +{ + JSDExecHook* current; + JSCList* list; + JSDScript* jsdscript; + + JSD_LOCK_SCRIPTS(jsdc); + jsdscript = jsd_FindJSDScript(jsdc, script); + if( ! jsdscript) + { + JSD_UNLOCK_SCRIPTS(jsdc); + return JS_FALSE; + } + + list = &jsdscript->hooks; + + for( current = (JSDExecHook*)list->next; + current != (JSDExecHook*)list; + current = (JSDExecHook*)current->links.next ) + { + if(current == jsdhook) + { + JSD_UNLOCK_SCRIPTS(jsdc); + return JS_TRUE; + } + } + JSD_UNLOCK_SCRIPTS(jsdc); + return JS_FALSE; +} + + +JSTrapStatus +jsd_TrapHandler(JSContext *cx, JSScript *script_, jsbytecode *pc, jsval *rval, + jsval closure) +{ + JS::RootedScript script(cx, script_); + JSDExecHook* jsdhook = (JSDExecHook*) JSVAL_TO_PRIVATE(closure); + JSD_ExecutionHookProc hook; + void* hookData; + JSDContext* jsdc; + JSDScript* jsdscript; + + JSD_LOCK(); + + if( NULL == (jsdc = jsd_JSDContextForJSContext(cx)) || + ! _isActiveHook(jsdc, script, jsdhook) ) + { + JSD_UNLOCK(); + return JSTRAP_CONTINUE; + } + + JSD_ASSERT_VALID_EXEC_HOOK(jsdhook); + JS_ASSERT(!jsdhook->pc || jsdhook->pc == (uintptr_t)pc); + JS_ASSERT(jsdhook->jsdscript->script == script); + JS_ASSERT(jsdhook->jsdscript->jsdc == jsdc); + + hook = jsdhook->hook; + hookData = jsdhook->callerdata; + jsdscript = jsdhook->jsdscript; + + /* do not use jsdhook-> after this point */ + JSD_UNLOCK(); + + if( ! jsdc || ! jsdc->inited ) + return JSTRAP_CONTINUE; + + if( JSD_IS_DANGEROUS_THREAD(jsdc) ) + return JSTRAP_CONTINUE; + +#ifdef LIVEWIRE + if( ! jsdlw_UserCodeAtPC(jsdc, jsdscript, (uintptr_t)pc) ) + return JSTRAP_CONTINUE; +#endif + + return jsd_CallExecutionHook(jsdc, cx, JSD_HOOK_BREAKPOINT, + hook, hookData, rval); +} + + + +JSBool +jsd_SetExecutionHook(JSDContext* jsdc, + JSDScript* jsdscript, + uintptr_t pc, + JSD_ExecutionHookProc hook, + void* callerdata) +{ + JSDExecHook* jsdhook; + JSBool rv; + + JSD_LOCK(); + if( ! hook ) + { + jsd_ClearExecutionHook(jsdc, jsdscript, pc); + JSD_UNLOCK(); + return JS_TRUE; + } + + jsdhook = _findHook(jsdc, jsdscript, pc); + if( jsdhook ) + { + jsdhook->hook = hook; + jsdhook->callerdata = callerdata; + JSD_UNLOCK(); + return JS_TRUE; + } + /* else... */ + + jsdhook = (JSDExecHook*)calloc(1, sizeof(JSDExecHook)); + if( ! jsdhook ) { + JSD_UNLOCK(); + return JS_FALSE; + } + jsdhook->jsdscript = jsdscript; + jsdhook->pc = pc; + jsdhook->hook = hook; + jsdhook->callerdata = callerdata; + + { + AutoSafeJSContext cx; + JSAutoCompartment ac(cx, jsdscript->script); + rv = JS_SetTrap(cx, jsdscript->script, + (jsbytecode*)pc, jsd_TrapHandler, + PRIVATE_TO_JSVAL(jsdhook)); + } + + if ( ! rv ) { + free(jsdhook); + JSD_UNLOCK(); + return JS_FALSE; + } + + JS_APPEND_LINK(&jsdhook->links, &jsdscript->hooks); + JSD_UNLOCK(); + + return JS_TRUE; +} + +JSBool +jsd_ClearExecutionHook(JSDContext* jsdc, + JSDScript* jsdscript, + uintptr_t pc) +{ + JSDExecHook* jsdhook; + + JSD_LOCK(); + + jsdhook = _findHook(jsdc, jsdscript, pc); + if( ! jsdhook ) + { + JSD_UNLOCK(); + return JS_FALSE; + } + + { + AutoSafeJSContext cx; + JSAutoCompartment ac(cx, jsdscript->script); + JS_ClearTrap(cx, jsdscript->script, + (jsbytecode*)pc, NULL, NULL ); + } + + JS_REMOVE_LINK(&jsdhook->links); + free(jsdhook); + + JSD_UNLOCK(); + return JS_TRUE; +} + +JSBool +jsd_ClearAllExecutionHooksForScript(JSDContext* jsdc, JSDScript* jsdscript) +{ + JSDExecHook* jsdhook; + JSCList* list = &jsdscript->hooks; + JSD_LOCK(); + + while( (JSDExecHook*)list != (jsdhook = (JSDExecHook*)list->next) ) + { + JS_REMOVE_LINK(&jsdhook->links); + free(jsdhook); + } + + JS_ClearScriptTraps(jsdc->jsrt, jsdscript->script); + JSD_UNLOCK(); + + return JS_TRUE; +} + +JSBool +jsd_ClearAllExecutionHooks(JSDContext* jsdc) +{ + JSDScript* jsdscript; + JSDScript* iterp = NULL; + + JSD_LOCK(); + while( NULL != (jsdscript = jsd_IterateScripts(jsdc, &iterp)) ) + jsd_ClearAllExecutionHooksForScript(jsdc, jsdscript); + JSD_UNLOCK(); + return JS_TRUE; +} + +void +jsd_ScriptCreated(JSDContext* jsdc, + JSContext *cx, + const char *filename, /* URL this script loads from */ + unsigned lineno, /* line where this script starts */ + JSScript *script, + JSFunction *fun) +{ + jsd_NewScriptHookProc(cx, filename, lineno, script, fun, jsdc); +} + +void +jsd_ScriptDestroyed(JSDContext* jsdc, + JSFreeOp *fop, + JSScript *script) +{ + jsd_DestroyScriptHookProc(fop, script, jsdc); +} diff --git a/js/jsd/jsd_stak.cpp b/js/jsd/jsd_stak.cpp new file mode 100644 index 0000000..8249c35 --- /dev/null +++ b/js/jsd/jsd_stak.cpp @@ -0,0 +1,572 @@ +/* -*- 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 = NULL; + + 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 NULL; + } + + if (!JSD_IS_DEBUG_ENABLED(jsdc, jsdscript)) + jsdthreadstate->flags |= TS_HAS_DISABLED_FRAME; + + jsdframe = (JSDStackFrameInfo*) calloc(1, sizeof(JSDStackFrameInfo)); + if( ! jsdframe ) + return NULL; + + 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 NULL; + + 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 NULL; + } + } + + ++iter; + } + JS_EndRequest(jsdthreadstate->context); + + if (jsdthreadstate->stackDepth == 0) + { + free(jsdthreadstate); + return NULL; + } + + 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 = NULL; + + 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 = NULL; + + 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 = NULL; + + 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 = NULL; + + 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 = NULL; + + 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 = NULL; + + 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 = NULL; + JSD_LOCK_THREADSTATES(jsdc); + + if( jsd_IsValidFrameInThreadState(jsdc, jsdthreadstate, jsdframe) ) + { + JSBool 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 = NULL; + + 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; +} + +JSBool +jsd_IsStackFrameDebugger(JSDContext* jsdc, + JSDThreadState* jsdthreadstate, + JSDStackFrameInfo* jsdframe) +{ + JSBool rv = JS_TRUE; + JSD_LOCK_THREADSTATES(jsdc); + + if( jsd_IsValidFrameInThreadState(jsdc, jsdthreadstate, jsdframe) ) + { + rv = jsdframe->frame.isDebuggerFrame(); + } + + JSD_UNLOCK_THREADSTATES(jsdc); + return rv; +} + +JSBool +jsd_IsStackFrameConstructing(JSDContext* jsdc, + JSDThreadState* jsdthreadstate, + JSDStackFrameInfo* jsdframe) +{ + JSBool rv = JS_TRUE; + JSD_LOCK_THREADSTATES(jsdc); + + if( jsd_IsValidFrameInThreadState(jsdc, jsdthreadstate, jsdframe) ) + { + rv = jsdframe->isConstructing; + } + + JSD_UNLOCK_THREADSTATES(jsdc); + return rv; +} + +JSBool +jsd_EvaluateUCScriptInStackFrame(JSDContext* jsdc, + JSDThreadState* jsdthreadstate, + JSDStackFrameInfo* jsdframe, + const jschar *bytes, unsigned length, + const char *filename, unsigned lineno, + JSBool eatExceptions, JS::MutableHandleValue rval) +{ + JSBool retval; + JSBool valid; + JSExceptionState* exceptionState = NULL; + + JS_ASSERT(JSD_CURRENT_THREAD() == jsdthreadstate->thread); + + JSD_LOCK_THREADSTATES(jsdc); + valid = jsd_IsValidFrameInThreadState(jsdc, jsdthreadstate, jsdframe); + JSD_UNLOCK_THREADSTATES(jsdc); + + if( ! valid ) + return JS_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; +} + +JSBool +jsd_EvaluateScriptInStackFrame(JSDContext* jsdc, + JSDThreadState* jsdthreadstate, + JSDStackFrameInfo* jsdframe, + const char *bytes, unsigned length, + const char *filename, unsigned lineno, + JSBool eatExceptions, JS::MutableHandleValue rval) +{ + JSBool retval; + JSBool valid; + JSExceptionState* exceptionState = NULL; + + JS_ASSERT(JSD_CURRENT_THREAD() == jsdthreadstate->thread); + + JSD_LOCK_THREADSTATES(jsdc); + valid = jsd_IsValidFrameInThreadState(jsdc, jsdthreadstate, jsdframe); + JSD_UNLOCK_THREADSTATES(jsdc); + + if (!valid) + return JS_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) +{ + JSBool valid; + JSString* retval; + JSExceptionState* exceptionState; + JSContext* cx; + + JSD_LOCK_THREADSTATES(jsdc); + valid = jsd_IsValidFrameInThreadState(jsdc, jsdthreadstate, jsdframe); + JSD_UNLOCK_THREADSTATES(jsdc); + + if( ! valid ) + return NULL; + + cx = jsdthreadstate->context; + JS_ASSERT(cx); + + exceptionState = JS_SaveExceptionState(cx); + retval = JS_ValueToString(cx, val); + JS_RestoreExceptionState(cx, exceptionState); + + return retval; +} + +JSBool +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 JS_TRUE; + } + return JS_FALSE; +} + +JSBool +jsd_IsValidFrameInThreadState(JSDContext* jsdc, + JSDThreadState* jsdthreadstate, + JSDStackFrameInfo* jsdframe) +{ + JS_ASSERT(JSD_THREADSTATES_LOCKED(jsdc)); + + if( ! jsd_IsValidThreadState(jsdc, jsdthreadstate) ) + return JS_FALSE; + if( jsdframe->jsdthreadstate != jsdthreadstate ) + return JS_FALSE; + + JSD_ASSERT_VALID_THREAD_STATE(jsdthreadstate); + JSD_ASSERT_VALID_STACK_FRAME(jsdframe); + + return JS_TRUE; +} + +static JSContext* +_getContextForThreadState(JSDContext* jsdc, JSDThreadState* jsdthreadstate) +{ + JSBool valid; + JSD_LOCK_THREADSTATES(jsdc); + valid = jsd_IsValidThreadState(jsdc, jsdthreadstate); + JSD_UNLOCK_THREADSTATES(jsdc); + if( valid ) + return jsdthreadstate->context; + return NULL; +} + +JSDValue* +jsd_GetException(JSDContext* jsdc, JSDThreadState* jsdthreadstate) +{ + JSContext* cx; + jsval val; + + if(!(cx = _getContextForThreadState(jsdc, jsdthreadstate))) + return NULL; + + if(JS_GetPendingException(cx, &val)) + return jsd_NewValue(jsdc, val); + return NULL; +} + +JSBool +jsd_SetException(JSDContext* jsdc, JSDThreadState* jsdthreadstate, + JSDValue* jsdval) +{ + JSContext* cx; + + if(!(cx = _getContextForThreadState(jsdc, jsdthreadstate))) + return JS_FALSE; + + if(jsdval) + JS_SetPendingException(cx, JSD_GetValueWrappedJSVal(jsdc, jsdval)); + else + JS_ClearPendingException(cx); + return JS_TRUE; +} + diff --git a/js/jsd/jsd_step.cpp b/js/jsd/jsd_step.cpp new file mode 100644 index 0000000..da1886a --- /dev/null +++ b/js/jsd/jsd_step.cpp @@ -0,0 +1,286 @@ +/* -*- 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 - Stepping support + */ + +#include "jsd.h" + +/* +* #define JSD_TRACE 1 +*/ + +#ifdef JSD_TRACE + +static char* +_indentSpaces(int i) +{ +#define MAX_INDENT 63 + static char* p = NULL; + if(!p) + { + p = calloc(1, MAX_INDENT+1); + if(!p) return ""; + memset(p, ' ', MAX_INDENT); + } + if(i > MAX_INDENT) return p; + return p + MAX_INDENT-i; +} + +static void +_interpreterTrace(JSDContext* jsdc, JSContext *cx, JSAbstractFramePtr frame, + bool isConstructing, JSBool before) +{ + JSDScript* jsdscript = NULL; + JSScript * script; + static indent = 0; + JSString* funName = NULL; + + script = frame.script(); + if(script) + { + JSD_LOCK_SCRIPTS(jsdc); + jsdscript = jsd_FindOrCreateJSDScript(jsdc, cx, script, frame); + JSD_UNLOCK_SCRIPTS(jsdc); + if(jsdscript) + funName = JSD_GetScriptFunctionId(jsdc, jsdscript); + } + + if(before) + printf("%sentering ", _indentSpaces(indent++)); + else + printf("%sleaving ", _indentSpaces(--indent)); + + if (!funName) + printf("TOP_LEVEL"); + else + JS_FileEscapedString(stdout, funName, 0); + + if(before) + { + jsval thisVal; + + printf("%s this: ", isConstructing ? "constructing":""); + + if (JS_GetFrameThis(cx, frame, &thisVal)) + printf("0x%0llx", (uintptr_t) thisVal); + else + puts("<unavailable>"); + } + printf("\n"); + JS_ASSERT(indent >= 0); +} +#endif + +JSBool +_callHook(JSDContext *jsdc, JSContext *cx, JSAbstractFramePtr frame, bool isConstructing, + JSBool before, unsigned type, JSD_CallHookProc hook, void *hookData) +{ + JSDScript* jsdscript; + JSScript* jsscript; + JSBool hookresult = JS_TRUE; + + if (!jsdc || !jsdc->inited) + return JS_FALSE; + + if (!hook && !(jsdc->flags & JSD_COLLECT_PROFILE_DATA)) + { + /* no hook to call, no profile data needs to be collected, + * so there is nothing to do here. + */ + return hookresult; + } + + if (before && isConstructing) { + JS::RootedValue newObj(cx); + if (!frame.getThisValue(cx, &newObj)) + return JS_FALSE; + jsd_Constructing(jsdc, cx, JSVAL_TO_OBJECT(newObj), frame); + } + + jsscript = frame.script(); + if (jsscript) + { + JSD_LOCK_SCRIPTS(jsdc); + jsdscript = jsd_FindOrCreateJSDScript(jsdc, cx, jsscript, frame); + JSD_UNLOCK_SCRIPTS(jsdc); + + if (jsdscript) + { + if (JSD_IS_PROFILE_ENABLED(jsdc, jsdscript)) + { + JSDProfileData *pdata; + pdata = jsd_GetScriptProfileData (jsdc, jsdscript); + if (pdata) + { + if (before) + { + if (!pdata->lastCallStart) + { + int64_t now; + JSDProfileData *callerpdata; + + /* Get the time just the once, for consistency. */ + now = JS_Now(); + /* This contains a pointer to the profile data for + * the caller of this function. */ + callerpdata = jsdc->callingFunctionPData; + if (callerpdata) + { + int64_t ll_delta; + pdata->caller = callerpdata; + /* We need to 'stop' the timer for the caller. + * Use time since last return if appropriate. */ + ll_delta = jsdc->lastReturnTime + ? now - jsdc->lastReturnTime + : now - callerpdata->lastCallStart; + callerpdata->runningTime += ll_delta; + } + /* We're the new current function, and no return + * has happened yet. */ + jsdc->callingFunctionPData = pdata; + jsdc->lastReturnTime = 0; + /* This function has no running time (just been + * called!), and we'll need the call start time. */ + pdata->runningTime = 0; + pdata->lastCallStart = now; + } else { + if (++pdata->recurseDepth > pdata->maxRecurseDepth) + pdata->maxRecurseDepth = pdata->recurseDepth; + } + /* make sure we're called for the return too. */ + hookresult = JS_TRUE; + } else if (!pdata->recurseDepth && pdata->lastCallStart) { + int64_t now, ll_delta; + double delta; + now = JS_Now(); + ll_delta = now - pdata->lastCallStart; + delta = ll_delta; + delta /= 1000.0; + pdata->totalExecutionTime += delta; + /* minExecutionTime starts as 0, so we need to overwrite + * it on the first call always. */ + if ((0 == pdata->callCount) || + delta < pdata->minExecutionTime) + { + pdata->minExecutionTime = delta; + } + if (delta > pdata->maxExecutionTime) + pdata->maxExecutionTime = delta; + + /* If we last returned from a function (as opposed to + * having last entered this function), we need to inc. + * the running total by the time delta since the last + * return, and use the running total instead of the + * delta calculated above. */ + if (jsdc->lastReturnTime) + { + /* Add last chunk to running time, and use total + * running time as 'delta'. */ + ll_delta = now - jsdc->lastReturnTime; + pdata->runningTime += ll_delta; + delta = pdata->runningTime; + delta /= 1000.0; + } + + pdata->totalOwnExecutionTime += delta; + /* See minExecutionTime comment above. */ + if ((0 == pdata->callCount) || + delta < pdata->minOwnExecutionTime) + { + pdata->minOwnExecutionTime = delta; + } + if (delta > pdata->maxOwnExecutionTime) + pdata->maxOwnExecutionTime = delta; + + /* Current function is now our caller. */ + jsdc->callingFunctionPData = pdata->caller; + /* No hanging pointers, please. */ + pdata->caller = NULL; + /* Mark the time we returned, and indicate this + * function is no longer running. */ + jsdc->lastReturnTime = now; + pdata->lastCallStart = 0; + ++pdata->callCount; + } else if (pdata->recurseDepth) { + --pdata->recurseDepth; + ++pdata->callCount; + } + } + if (hook) + jsd_CallCallHook (jsdc, cx, type, hook, hookData); + } else { + if (hook) + hookresult = + jsd_CallCallHook (jsdc, cx, type, hook, hookData); + else + hookresult = JS_TRUE; + } + } + } + +#ifdef JSD_TRACE + _interpreterTrace(jsdc, cx, frame, isConstructing, before); + return JS_TRUE; +#else + return hookresult; +#endif + +} + +void * +jsd_FunctionCallHook(JSContext *cx, JSAbstractFramePtr frame, bool isConstructing, + JSBool before, JSBool *ok, void *closure) +{ + JSDContext* jsdc; + JSD_CallHookProc hook; + void* hookData; + + jsdc = (JSDContext*) closure; + + /* local in case jsdc->functionHook gets cleared on another thread */ + JSD_LOCK(); + hook = jsdc->functionHook; + hookData = jsdc->functionHookData; + JSD_UNLOCK(); + + if (_callHook (jsdc, cx, frame, isConstructing, before, + (before) ? JSD_HOOK_FUNCTION_CALL : JSD_HOOK_FUNCTION_RETURN, + hook, hookData)) + { + return closure; + } + + return NULL; +} + +void * +jsd_TopLevelCallHook(JSContext *cx, JSAbstractFramePtr frame, bool isConstructing, + JSBool before, JSBool *ok, void *closure) +{ + JSDContext* jsdc; + JSD_CallHookProc hook; + void* hookData; + + jsdc = (JSDContext*) closure; + + /* local in case jsdc->toplevelHook gets cleared on another thread */ + JSD_LOCK(); + hook = jsdc->toplevelHook; + hookData = jsdc->toplevelHookData; + JSD_UNLOCK(); + + if (_callHook (jsdc, cx, frame, isConstructing, before, + (before) ? JSD_HOOK_TOPLEVEL_START : JSD_HOOK_TOPLEVEL_END, + hook, hookData)) + { + return closure; + } + + return NULL; + +} diff --git a/js/jsd/jsd_text.cpp b/js/jsd/jsd_text.cpp new file mode 100644 index 0000000..cd6f58d --- /dev/null +++ b/js/jsd/jsd_text.cpp @@ -0,0 +1,527 @@ +/* -*- 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 - Source Text functions + */ + +#include <ctype.h> +#include "jsd.h" + +#ifdef DEBUG +void JSD_ASSERT_VALID_SOURCE_TEXT(JSDSourceText* jsdsrc) +{ + JS_ASSERT(jsdsrc); + JS_ASSERT(jsdsrc->url); +} +#endif + +/***************************************************************************/ +/* XXX add notification */ + +static void +_clearText(JSDContext* jsdc, JSDSourceText* jsdsrc) +{ + if( jsdsrc->text ) + free(jsdsrc->text); + jsdsrc->text = NULL; + jsdsrc->textLength = 0; + jsdsrc->textSpace = 0; + jsdsrc->status = JSD_SOURCE_CLEARED; + jsdsrc->dirty = JS_TRUE; + jsdsrc->alterCount = jsdc->sourceAlterCount++ ; + jsdsrc->doingEval = JS_FALSE; +} + +static JSBool +_appendText(JSDContext* jsdc, JSDSourceText* jsdsrc, + const char* text, size_t length) +{ +#define MEMBUF_GROW 1000 + + unsigned neededSize = jsdsrc->textLength + length; + + if( neededSize > jsdsrc->textSpace ) + { + char* newBuf; + unsigned iNewSize; + + /* if this is the first alloc, the req might be all that's needed*/ + if( ! jsdsrc->textSpace ) + iNewSize = length; + else + iNewSize = (neededSize * 5 / 4) + MEMBUF_GROW; + + newBuf = (char*) realloc(jsdsrc->text, iNewSize); + if( ! newBuf ) + { + /* try again with the minimal size really asked for */ + iNewSize = neededSize; + newBuf = (char*) realloc(jsdsrc->text, iNewSize); + if( ! newBuf ) + { + /* out of memory */ + _clearText( jsdc, jsdsrc ); + jsdsrc->status = JSD_SOURCE_FAILED; + return JS_FALSE; + } + } + + jsdsrc->text = newBuf; + jsdsrc->textSpace = iNewSize; + } + + memcpy(jsdsrc->text + jsdsrc->textLength, text, length); + jsdsrc->textLength += length; + return JS_TRUE; +} + +static JSDSourceText* +_newSource(JSDContext* jsdc, char* url) +{ + JSDSourceText* jsdsrc = (JSDSourceText*)calloc(1,sizeof(JSDSourceText)); + if( ! jsdsrc ) + return NULL; + + jsdsrc->url = url; + jsdsrc->status = JSD_SOURCE_INITED; + jsdsrc->dirty = JS_TRUE; + jsdsrc->alterCount = jsdc->sourceAlterCount++ ; + + return jsdsrc; +} + +static void +_destroySource(JSDContext* jsdc, JSDSourceText* jsdsrc) +{ + JS_ASSERT(NULL == jsdsrc->text); /* must _clearText() first */ + free(jsdsrc->url); + free(jsdsrc); +} + +static void +_removeSource(JSDContext* jsdc, JSDSourceText* jsdsrc) +{ + JS_REMOVE_LINK(&jsdsrc->links); + _clearText(jsdc, jsdsrc); + _destroySource(jsdc, jsdsrc); +} + +static JSDSourceText* +_addSource(JSDContext* jsdc, char* url) +{ + JSDSourceText* jsdsrc = _newSource(jsdc, url); + if( ! jsdsrc ) + return NULL; + JS_INSERT_LINK(&jsdsrc->links, &jsdc->sources); + return jsdsrc; +} + +static void +_moveSourceToRemovedList(JSDContext* jsdc, JSDSourceText* jsdsrc) +{ + _clearText(jsdc, jsdsrc); + JS_REMOVE_LINK(&jsdsrc->links); + JS_INSERT_LINK(&jsdsrc->links, &jsdc->removedSources); +} + +static void +_removeSourceFromRemovedList( JSDContext* jsdc, JSDSourceText* jsdsrc ) +{ + JS_REMOVE_LINK(&jsdsrc->links); + _destroySource( jsdc, jsdsrc ); +} + +static JSBool +_isSourceInSourceList(JSDContext* jsdc, JSDSourceText* jsdsrcToFind) +{ + JSDSourceText *jsdsrc; + + for( jsdsrc = (JSDSourceText*)jsdc->sources.next; + jsdsrc != (JSDSourceText*)&jsdc->sources; + jsdsrc = (JSDSourceText*)jsdsrc->links.next ) + { + if( jsdsrc == jsdsrcToFind ) + return JS_TRUE; + } + return JS_FALSE; +} + +/* compare strings in a case insensitive manner with a length limit +*/ + +static int +strncasecomp (const char* one, const char * two, int n) +{ + const char *pA; + const char *pB; + + for(pA=one, pB=two;; pA++, pB++) + { + int tmp; + if (pA == one+n) + return 0; + if (!(*pA && *pB)) + return *pA - *pB; + tmp = tolower(*pA) - tolower(*pB); + if (tmp) + return tmp; + } +} + +static const char file_url_prefix[] = "file:"; +#define FILE_URL_PREFIX_LEN (sizeof file_url_prefix - 1) + +char* +jsd_BuildNormalizedURL( const char* url_string ) +{ + char *new_url_string; + + if( ! url_string ) + return NULL; + + if (!strncasecomp(url_string, file_url_prefix, FILE_URL_PREFIX_LEN) && + url_string[FILE_URL_PREFIX_LEN + 0] == '/' && + url_string[FILE_URL_PREFIX_LEN + 1] == '/') { + new_url_string = JS_smprintf("%s%s", + file_url_prefix, + url_string + FILE_URL_PREFIX_LEN + 2); + } else { + new_url_string = strdup(url_string); + } + return new_url_string; +} + +/***************************************************************************/ + +void +jsd_DestroyAllSources( JSDContext* jsdc ) +{ + JSDSourceText *jsdsrc; + JSDSourceText *next; + + for( jsdsrc = (JSDSourceText*)jsdc->sources.next; + jsdsrc != (JSDSourceText*)&jsdc->sources; + jsdsrc = next ) + { + next = (JSDSourceText*)jsdsrc->links.next; + _removeSource( jsdc, jsdsrc ); + } + + for( jsdsrc = (JSDSourceText*)jsdc->removedSources.next; + jsdsrc != (JSDSourceText*)&jsdc->removedSources; + jsdsrc = next ) + { + next = (JSDSourceText*)jsdsrc->links.next; + _removeSourceFromRemovedList( jsdc, jsdsrc ); + } + +} + +JSDSourceText* +jsd_IterateSources(JSDContext* jsdc, JSDSourceText **iterp) +{ + JSDSourceText *jsdsrc = *iterp; + + if( !jsdsrc ) + jsdsrc = (JSDSourceText *)jsdc->sources.next; + if( jsdsrc == (JSDSourceText *)&jsdc->sources ) + return NULL; + *iterp = (JSDSourceText *)jsdsrc->links.next; + return jsdsrc; +} + +JSDSourceText* +jsd_FindSourceForURL(JSDContext* jsdc, const char* url) +{ + JSDSourceText *jsdsrc; + + for( jsdsrc = (JSDSourceText *)jsdc->sources.next; + jsdsrc != (JSDSourceText *)&jsdc->sources; + jsdsrc = (JSDSourceText *)jsdsrc->links.next ) + { + if( 0 == strcmp(jsdsrc->url, url) ) + return jsdsrc; + } + return NULL; +} + +const char* +jsd_GetSourceURL(JSDContext* jsdc, JSDSourceText* jsdsrc) +{ + return jsdsrc->url; +} + +JSBool +jsd_GetSourceText(JSDContext* jsdc, JSDSourceText* jsdsrc, + const char** ppBuf, int* pLen ) +{ + *ppBuf = jsdsrc->text; + *pLen = jsdsrc->textLength; + return JS_TRUE; +} + +void +jsd_ClearSourceText(JSDContext* jsdc, JSDSourceText* jsdsrc) +{ + if( JSD_SOURCE_INITED != jsdsrc->status && + JSD_SOURCE_PARTIAL != jsdsrc->status ) + { + _clearText(jsdc, jsdsrc); + } +} + +JSDSourceStatus +jsd_GetSourceStatus(JSDContext* jsdc, JSDSourceText* jsdsrc) +{ + return jsdsrc->status; +} + +JSBool +jsd_IsSourceDirty(JSDContext* jsdc, JSDSourceText* jsdsrc) +{ + return jsdsrc->dirty; +} + +void +jsd_SetSourceDirty(JSDContext* jsdc, JSDSourceText* jsdsrc, JSBool dirty) +{ + jsdsrc->dirty = dirty; +} + +unsigned +jsd_GetSourceAlterCount(JSDContext* jsdc, JSDSourceText* jsdsrc) +{ + return jsdsrc->alterCount; +} + +unsigned +jsd_IncrementSourceAlterCount(JSDContext* jsdc, JSDSourceText* jsdsrc) +{ + return jsdsrc->alterCount = jsdc->sourceAlterCount++; +} + +/***************************************************************************/ + +#if defined(DEBUG) && 0 +void DEBUG_ITERATE_SOURCES( JSDContext* jsdc ) +{ + JSDSourceText* iterp = NULL; + JSDSourceText* jsdsrc = NULL; + int dummy; + + while( NULL != (jsdsrc = jsd_IterateSources(jsdc, &iterp)) ) + { + const char* url; + const char* text; + int len; + JSBool dirty; + JSDStreamStatus status; + JSBool gotSrc; + + url = JSD_GetSourceURL(jsdc, jsdsrc); + dirty = JSD_IsSourceDirty(jsdc, jsdsrc); + status = JSD_GetSourceStatus(jsdc, jsdsrc); + gotSrc = JSD_GetSourceText(jsdc, jsdsrc, &text, &len ); + + dummy = 0; /* gives us a line to set breakpoint... */ + } +} +#else +#define DEBUG_ITERATE_SOURCES(x) ((void)x) +#endif + +/***************************************************************************/ + +JSDSourceText* +jsd_NewSourceText(JSDContext* jsdc, const char* url) +{ + JSDSourceText* jsdsrc; + char* new_url_string; + + JSD_LOCK_SOURCE_TEXT(jsdc); + +#ifdef LIVEWIRE + new_url_string = url; /* we take ownership of alloc'd string */ +#else + new_url_string = jsd_BuildNormalizedURL(url); +#endif + if( ! new_url_string ) + return NULL; + + jsdsrc = jsd_FindSourceForURL(jsdc, new_url_string); + + if( jsdsrc ) + { + if( jsdsrc->doingEval ) + { + free(new_url_string); + JSD_UNLOCK_SOURCE_TEXT(jsdc); + return NULL; + } + else + _moveSourceToRemovedList(jsdc, jsdsrc); + } + + jsdsrc = _addSource( jsdc, new_url_string ); + + JSD_UNLOCK_SOURCE_TEXT(jsdc); + + return jsdsrc; +} + +JSDSourceText* +jsd_AppendSourceText(JSDContext* jsdc, + JSDSourceText* jsdsrc, + const char* text, /* *not* zero terminated */ + size_t length, + JSDSourceStatus status) +{ + JSD_LOCK_SOURCE_TEXT(jsdc); + + if( jsdsrc->doingEval ) + { + JSD_UNLOCK_SOURCE_TEXT(jsdc); + return NULL; + } + + if( ! _isSourceInSourceList( jsdc, jsdsrc ) ) + { + _removeSourceFromRemovedList( jsdc, jsdsrc ); + JSD_UNLOCK_SOURCE_TEXT(jsdc); + return NULL; + } + + if( text && length && ! _appendText( jsdc, jsdsrc, text, length ) ) + { + jsdsrc->dirty = JS_TRUE; + jsdsrc->alterCount = jsdc->sourceAlterCount++ ; + jsdsrc->status = JSD_SOURCE_FAILED; + _moveSourceToRemovedList(jsdc, jsdsrc); + JSD_UNLOCK_SOURCE_TEXT(jsdc); + return NULL; + } + + jsdsrc->dirty = JS_TRUE; + jsdsrc->alterCount = jsdc->sourceAlterCount++ ; + jsdsrc->status = status; + DEBUG_ITERATE_SOURCES(jsdc); + JSD_UNLOCK_SOURCE_TEXT(jsdc); + return jsdsrc; +} + +JSDSourceText* +jsd_AppendUCSourceText(JSDContext* jsdc, + JSDSourceText* jsdsrc, + const jschar* text, /* *not* zero terminated */ + size_t length, + JSDSourceStatus status) +{ +#define UNICODE_TRUNCATE_BUF_SIZE 1024 + static char* buf = NULL; + int remaining = length; + + if(!text || !length) + return jsd_AppendSourceText(jsdc, jsdsrc, NULL, 0, status); + + JSD_LOCK_SOURCE_TEXT(jsdc); + if(!buf) + { + buf = js_pod_malloc<char>(UNICODE_TRUNCATE_BUF_SIZE); + if(!buf) + { + JSD_UNLOCK_SOURCE_TEXT(jsdc); + return NULL; + } + } + while(remaining && jsdsrc) { + int bytes = (remaining < UNICODE_TRUNCATE_BUF_SIZE) ? remaining : UNICODE_TRUNCATE_BUF_SIZE; + int i; + for(i = 0; i < bytes; i++) + buf[i] = (const char) *(text++); + jsdsrc = jsd_AppendSourceText(jsdc,jsdsrc, + buf, bytes, + JSD_SOURCE_PARTIAL); + remaining -= bytes; + } + if(jsdsrc && status != JSD_SOURCE_PARTIAL) + jsdsrc = jsd_AppendSourceText(jsdc, jsdsrc, NULL, 0, status); + + JSD_UNLOCK_SOURCE_TEXT(jsdc); + return jsdsrc; +} + +/* convienence function for adding complete source of url in one call */ +JSBool +jsd_AddFullSourceText(JSDContext* jsdc, + const char* text, /* *not* zero terminated */ + size_t length, + const char* url) +{ + JSDSourceText* jsdsrc; + + JSD_LOCK_SOURCE_TEXT(jsdc); + + jsdsrc = jsd_NewSourceText(jsdc, url); + if( jsdsrc ) + jsdsrc = jsd_AppendSourceText(jsdc, jsdsrc, + text, length, JSD_SOURCE_PARTIAL ); + if( jsdsrc ) + jsdsrc = jsd_AppendSourceText(jsdc, jsdsrc, + NULL, 0, JSD_SOURCE_COMPLETED ); + + JSD_UNLOCK_SOURCE_TEXT(jsdc); + + return jsdsrc ? JS_TRUE : JS_FALSE; +} + +/***************************************************************************/ + +void +jsd_StartingEvalUsingFilename(JSDContext* jsdc, const char* url) +{ + JSDSourceText* jsdsrc; + + /* NOTE: We leave it locked! */ + JSD_LOCK_SOURCE_TEXT(jsdc); + + jsdsrc = jsd_FindSourceForURL(jsdc, url); + if(jsdsrc) + { +#if 0 +#ifndef JSD_LOWLEVEL_SOURCE + JS_ASSERT(! jsdsrc->doingEval); +#endif +#endif + jsdsrc->doingEval = JS_TRUE; + } +} + +void +jsd_FinishedEvalUsingFilename(JSDContext* jsdc, const char* url) +{ + JSDSourceText* jsdsrc; + + /* NOTE: We ASSUME it is locked! */ + + jsdsrc = jsd_FindSourceForURL(jsdc, url); + if(jsdsrc) + { +#if 0 +#ifndef JSD_LOWLEVEL_SOURCE + /* + * when using this low level source addition, this jsdsrc might + * not have existed before the eval, but does exist now (without + * this flag set!) + */ + JS_ASSERT(jsdsrc->doingEval); +#endif +#endif + jsdsrc->doingEval = JS_FALSE; + } + + JSD_UNLOCK_SOURCE_TEXT(jsdc); +} diff --git a/js/jsd/jsd_val.cpp b/js/jsd/jsd_val.cpp new file mode 100644 index 0000000..8853144 --- /dev/null +++ b/js/jsd/jsd_val.cpp @@ -0,0 +1,739 @@ +/* -*- 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 - Value and Property support + */ + +#include "jsd.h" +#include "jsapi.h" +#include "jsfriendapi.h" +#include "jswrapper.h" +#include "nsCxPusher.h" + +using mozilla::AutoSafeJSContext; + +#ifdef DEBUG +void JSD_ASSERT_VALID_VALUE(JSDValue* jsdval) +{ + JS_ASSERT(jsdval); + JS_ASSERT(jsdval->nref > 0); + if(!JS_CLIST_IS_EMPTY(&jsdval->props)) + { + JS_ASSERT(CHECK_BIT_FLAG(jsdval->flags, GOT_PROPS)); + JS_ASSERT(!JSVAL_IS_PRIMITIVE(jsdval->val)); + } + + if(jsdval->proto) + { + JS_ASSERT(CHECK_BIT_FLAG(jsdval->flags, GOT_PROTO)); + JS_ASSERT(jsdval->proto->nref > 0); + } + if(jsdval->parent) + { + JS_ASSERT(CHECK_BIT_FLAG(jsdval->flags, GOT_PARENT)); + JS_ASSERT(jsdval->parent->nref > 0); + } + if(jsdval->ctor) + { + JS_ASSERT(CHECK_BIT_FLAG(jsdval->flags, GOT_CTOR)); + JS_ASSERT(jsdval->ctor->nref > 0); + } +} + +void JSD_ASSERT_VALID_PROPERTY(JSDProperty* jsdprop) +{ + JS_ASSERT(jsdprop); + JS_ASSERT(jsdprop->name); + JS_ASSERT(jsdprop->name->nref > 0); + JS_ASSERT(jsdprop->val); + JS_ASSERT(jsdprop->val->nref > 0); + if(jsdprop->alias) + JS_ASSERT(jsdprop->alias->nref > 0); +} +#endif + + +JSBool +jsd_IsValueObject(JSDContext* jsdc, JSDValue* jsdval) +{ + return !JSVAL_IS_PRIMITIVE(jsdval->val) || JSVAL_IS_NULL(jsdval->val); +} + +JSBool +jsd_IsValueNumber(JSDContext* jsdc, JSDValue* jsdval) +{ + return JSVAL_IS_NUMBER(jsdval->val); +} + +JSBool +jsd_IsValueInt(JSDContext* jsdc, JSDValue* jsdval) +{ + return JSVAL_IS_INT(jsdval->val); +} + +JSBool +jsd_IsValueDouble(JSDContext* jsdc, JSDValue* jsdval) +{ + return JSVAL_IS_DOUBLE(jsdval->val); +} + +JSBool +jsd_IsValueString(JSDContext* jsdc, JSDValue* jsdval) +{ + return JSVAL_IS_STRING(jsdval->val); +} + +JSBool +jsd_IsValueBoolean(JSDContext* jsdc, JSDValue* jsdval) +{ + return JSVAL_IS_BOOLEAN(jsdval->val); +} + +JSBool +jsd_IsValueNull(JSDContext* jsdc, JSDValue* jsdval) +{ + return JSVAL_IS_NULL(jsdval->val); +} + +JSBool +jsd_IsValueVoid(JSDContext* jsdc, JSDValue* jsdval) +{ + return JSVAL_IS_VOID(jsdval->val); +} + +JSBool +jsd_IsValuePrimitive(JSDContext* jsdc, JSDValue* jsdval) +{ + return JSVAL_IS_PRIMITIVE(jsdval->val); +} + +JSBool +jsd_IsValueFunction(JSDContext* jsdc, JSDValue* jsdval) +{ + AutoSafeJSContext cx; // NB: Actually unused. + return !JSVAL_IS_PRIMITIVE(jsdval->val) && + JS_ObjectIsCallable(cx, JSVAL_TO_OBJECT(jsdval->val)); +} + +JSBool +jsd_IsValueNative(JSDContext* jsdc, JSDValue* jsdval) +{ + AutoSafeJSContext cx; + JS::RootedFunction fun(cx); + + if(jsd_IsValueFunction(jsdc, jsdval)) + { + JSAutoCompartment ac(cx, JSVAL_TO_OBJECT(jsdval->val)); + AutoSaveExceptionState as(cx); + JSBool ok = JS_FALSE; + fun = JSD_GetValueFunction(jsdc, jsdval); + if(fun) + ok = JS_GetFunctionScript(cx, fun) ? JS_FALSE : JS_TRUE; + JS_ASSERT(fun); + return ok; + } + return !JSVAL_IS_PRIMITIVE(jsdval->val); +} + +/***************************************************************************/ + +JSBool +jsd_GetValueBoolean(JSDContext* jsdc, JSDValue* jsdval) +{ + jsval val = jsdval->val; + if(!JSVAL_IS_BOOLEAN(val)) + return JS_FALSE; + return JSVAL_TO_BOOLEAN(val); +} + +int32_t +jsd_GetValueInt(JSDContext* jsdc, JSDValue* jsdval) +{ + jsval val = jsdval->val; + if(!JSVAL_IS_INT(val)) + return 0; + return JSVAL_TO_INT(val); +} + +double +jsd_GetValueDouble(JSDContext* jsdc, JSDValue* jsdval) +{ + if(!JSVAL_IS_DOUBLE(jsdval->val)) + return 0; + return JSVAL_TO_DOUBLE(jsdval->val); +} + +JSString* +jsd_GetValueString(JSDContext* jsdc, JSDValue* jsdval) +{ + AutoSafeJSContext cx; + JS::RootedValue stringval(cx); + JS::RootedString string(cx); + JS::RootedObject scopeObj(cx); + + if(jsdval->string) + return jsdval->string; + + /* Reuse the string without copying or re-rooting it */ + if(JSVAL_IS_STRING(jsdval->val)) { + jsdval->string = JSVAL_TO_STRING(jsdval->val); + return jsdval->string; + } + + /* Objects call JS_ValueToString in their own compartment. */ + scopeObj = !JSVAL_IS_PRIMITIVE(jsdval->val) ? JSVAL_TO_OBJECT(jsdval->val) : jsdc->glob; + { + JSAutoCompartment ac(cx, scopeObj); + AutoSaveExceptionState as(cx); + string = JS_ValueToString(cx, jsdval->val); + } + + JSAutoCompartment ac2(cx, jsdc->glob); + if(string) { + stringval = STRING_TO_JSVAL(string); + } + if(!string || !JS_WrapValue(cx, stringval.address())) { + return NULL; + } + + jsdval->string = JSVAL_TO_STRING(stringval); + if(!JS_AddNamedStringRoot(cx, &jsdval->string, "ValueString")) + jsdval->string = NULL; + + return jsdval->string; +} + +JSString* +jsd_GetValueFunctionId(JSDContext* jsdc, JSDValue* jsdval) +{ + AutoSafeJSContext cx; + JS::RootedFunction fun(cx); + + if(!jsdval->funName && jsd_IsValueFunction(jsdc, jsdval)) + { + JSAutoCompartment ac(cx, JSVAL_TO_OBJECT(jsdval->val)); + AutoSaveExceptionState as(cx); + fun = JSD_GetValueFunction(jsdc, jsdval); + if(!fun) + return NULL; + jsdval->funName = JS_GetFunctionId(fun); + + /* For compatibility we return "anonymous", not an empty string here. */ + if (!jsdval->funName) + jsdval->funName = JS_GetAnonymousString(jsdc->jsrt); + } + return jsdval->funName; +} + +/***************************************************************************/ + +/* + * Create a new JSD value referring to a jsval. Copy string values into the + * JSD compartment. Leave all other GCTHINGs in their native compartments + * and access them through cross-compartment calls. + */ +JSDValue* +jsd_NewValue(JSDContext* jsdc, jsval value) +{ + AutoSafeJSContext cx; + JS::RootedValue val(cx, value); + JSDValue* jsdval; + + if(!(jsdval = (JSDValue*) calloc(1, sizeof(JSDValue)))) + return NULL; + + if(JSVAL_IS_GCTHING(val)) + { + JSBool ok; + JSAutoCompartment ac(cx, jsdc->glob); + + ok = JS_AddNamedValueRoot(cx, &jsdval->val, "JSDValue"); + if(ok && JSVAL_IS_STRING(val)) { + if(!JS_WrapValue(cx, val.address())) { + ok = JS_FALSE; + } + } + + if(!ok) + { + free(jsdval); + return NULL; + } + } + jsdval->val = val; + jsdval->nref = 1; + JS_INIT_CLIST(&jsdval->props); + + return jsdval; +} + +void +jsd_DropValue(JSDContext* jsdc, JSDValue* jsdval) +{ + JS_ASSERT(jsdval->nref > 0); + if(0 == --jsdval->nref) + { + jsd_RefreshValue(jsdc, jsdval); + if(JSVAL_IS_GCTHING(jsdval->val)) + { + AutoSafeJSContext cx; + JSAutoCompartment ac(cx, jsdc->glob); + JS_RemoveValueRoot(cx, &jsdval->val); + } + free(jsdval); + } +} + +jsval +jsd_GetValueWrappedJSVal(JSDContext* jsdc, JSDValue* jsdval) +{ + AutoSafeJSContext cx; + JS::RootedObject obj(cx); + JS::RootedValue val(cx, jsdval->val); + if (!JSVAL_IS_PRIMITIVE(val)) { + JSAutoCompartment ac(cx, JSVAL_TO_OBJECT(val)); + obj = JS_ObjectToOuterObject(cx, JSVAL_TO_OBJECT(val)); + if (!obj) + { + JS_ClearPendingException(cx); + val = JSVAL_NULL; + } + else + val = OBJECT_TO_JSVAL(obj); + } + + return val; +} + +static JSDProperty* _newProperty(JSDContext* jsdc, JSPropertyDesc* pd, + unsigned additionalFlags) +{ + JSDProperty* jsdprop; + + if(!(jsdprop = (JSDProperty*) calloc(1, sizeof(JSDProperty)))) + return NULL; + + JS_INIT_CLIST(&jsdprop->links); + jsdprop->nref = 1; + jsdprop->flags = pd->flags | additionalFlags; + + if(!(jsdprop->name = jsd_NewValue(jsdc, pd->id))) + goto new_prop_fail; + + if(!(jsdprop->val = jsd_NewValue(jsdc, pd->value))) + goto new_prop_fail; + + if((jsdprop->flags & JSDPD_ALIAS) && + !(jsdprop->alias = jsd_NewValue(jsdc, pd->alias))) + goto new_prop_fail; + + return jsdprop; +new_prop_fail: + jsd_DropProperty(jsdc, jsdprop); + return NULL; +} + +static void _freeProps(JSDContext* jsdc, JSDValue* jsdval) +{ + JSDProperty* jsdprop; + + while(jsdprop = (JSDProperty*)jsdval->props.next, + jsdprop != (JSDProperty*)&jsdval->props) + { + JS_REMOVE_AND_INIT_LINK(&jsdprop->links); + jsd_DropProperty(jsdc, jsdprop); + } + JS_ASSERT(JS_CLIST_IS_EMPTY(&jsdval->props)); + CLEAR_BIT_FLAG(jsdval->flags, GOT_PROPS); +} + +static JSBool _buildProps(JSDContext* jsdc, JSDValue* jsdval) +{ + AutoSafeJSContext cx; + JS::RootedObject obj(cx); + JSPropertyDescArray pda; + unsigned i; + + JS_ASSERT(JS_CLIST_IS_EMPTY(&jsdval->props)); + JS_ASSERT(!(CHECK_BIT_FLAG(jsdval->flags, GOT_PROPS))); + JS_ASSERT(!JSVAL_IS_PRIMITIVE(jsdval->val)); + + if(JSVAL_IS_PRIMITIVE(jsdval->val)) + return JS_FALSE; + + obj = JSVAL_TO_OBJECT(jsdval->val); + + JSAutoCompartment ac(cx, obj); + + if(!JS_GetPropertyDescArray(cx, obj, &pda)) + { + return JS_FALSE; + } + + for(i = 0; i < pda.length; i++) + { + JSDProperty* prop = _newProperty(jsdc, &pda.array[i], 0); + if(!prop) + { + _freeProps(jsdc, jsdval); + break; + } + JS_APPEND_LINK(&prop->links, &jsdval->props); + } + JS_PutPropertyDescArray(cx, &pda); + SET_BIT_FLAG(jsdval->flags, GOT_PROPS); + return !JS_CLIST_IS_EMPTY(&jsdval->props); +} + +#undef DROP_CLEAR_VALUE +#define DROP_CLEAR_VALUE(jsdc, x) if(x){jsd_DropValue(jsdc,x); x = NULL;} + +void +jsd_RefreshValue(JSDContext* jsdc, JSDValue* jsdval) +{ + AutoSafeJSContext cx; + if(jsdval->string) + { + /* if the jsval is a string, then we didn't need to root the string */ + if(!JSVAL_IS_STRING(jsdval->val)) + { + JSAutoCompartment ac(cx, jsdc->glob); + JS_RemoveStringRoot(cx, &jsdval->string); + } + jsdval->string = NULL; + } + + jsdval->funName = NULL; + jsdval->className = NULL; + DROP_CLEAR_VALUE(jsdc, jsdval->proto); + DROP_CLEAR_VALUE(jsdc, jsdval->parent); + DROP_CLEAR_VALUE(jsdc, jsdval->ctor); + _freeProps(jsdc, jsdval); + jsdval->flags = 0; +} + +/***************************************************************************/ + +unsigned +jsd_GetCountOfProperties(JSDContext* jsdc, JSDValue* jsdval) +{ + JSDProperty* jsdprop; + unsigned count = 0; + + if(!(CHECK_BIT_FLAG(jsdval->flags, GOT_PROPS))) + if(!_buildProps(jsdc, jsdval)) + return 0; + + for(jsdprop = (JSDProperty*)jsdval->props.next; + jsdprop != (JSDProperty*)&jsdval->props; + jsdprop = (JSDProperty*)jsdprop->links.next) + { + count++; + } + return count; +} + +JSDProperty* +jsd_IterateProperties(JSDContext* jsdc, JSDValue* jsdval, JSDProperty **iterp) +{ + JSDProperty* jsdprop = *iterp; + if(!(CHECK_BIT_FLAG(jsdval->flags, GOT_PROPS))) + { + JS_ASSERT(!jsdprop); + if(!_buildProps(jsdc, jsdval)) + return NULL; + } + + if(!jsdprop) + jsdprop = (JSDProperty*)jsdval->props.next; + if(jsdprop == (JSDProperty*)&jsdval->props) + return NULL; + *iterp = (JSDProperty*)jsdprop->links.next; + + JS_ASSERT(jsdprop); + jsdprop->nref++; + return jsdprop; +} + +JSDProperty* +jsd_GetValueProperty(JSDContext* jsdc, JSDValue* jsdval, JSString* nameStr) +{ + AutoSafeJSContext cx; + JSAutoCompartment acBase(cx, jsdc->glob); + JSDProperty* jsdprop; + JSDProperty* iter = NULL; + JS::RootedObject obj(cx); + JS::RootedString name(cx, nameStr); + unsigned attrs = 0; + JSBool found; + JSPropertyDesc pd; + const jschar * nameChars; + size_t nameLen; + JS::RootedValue val(cx), nameval(cx); + JS::RootedId nameid(cx); + + if(!jsd_IsValueObject(jsdc, jsdval)) + return NULL; + + /* If we already have the prop, then return it */ + while(NULL != (jsdprop = jsd_IterateProperties(jsdc, jsdval, &iter))) + { + JSString* propName = jsd_GetValueString(jsdc, jsdprop->name); + if(propName) { + int result; + if (JS_CompareStrings(cx, propName, name, &result) && !result) + return jsdprop; + } + JSD_DropProperty(jsdc, jsdprop); + } + /* Not found in property list, look it up explicitly */ + + if(!(obj = JSVAL_TO_OBJECT(jsdval->val))) + return NULL; + + if (!(nameChars = JS_GetStringCharsZAndLength(cx, name, &nameLen))) + return NULL; + + { + JSAutoCompartment ac(cx, obj); + + JS_GetUCPropertyAttributes(cx, obj, nameChars, nameLen, &attrs, &found); + if (!found) + { + return NULL; + } + + JS_ClearPendingException(cx); + + if(!JS_GetUCProperty(cx, obj, nameChars, nameLen, val.address())) + { + if (JS_IsExceptionPending(cx)) + { + if (!JS_GetPendingException(cx, &pd.value)) + { + return NULL; + } + pd.flags = JSPD_EXCEPTION; + } + else + { + pd.flags = JSPD_ERROR; + pd.value = JSVAL_VOID; + } + } + else + { + pd.value = val; + } + } + + nameval = STRING_TO_JSVAL(name); + if (!JS_ValueToId(cx, nameval, nameid.address()) || + !JS_IdToValue(cx, nameid, &pd.id)) { + return NULL; + } + + pd.spare = 0; + pd.alias = JSVAL_NULL; + pd.flags |= (attrs & JSPROP_ENUMERATE) ? JSPD_ENUMERATE : 0 + | (attrs & JSPROP_READONLY) ? JSPD_READONLY : 0 + | (attrs & JSPROP_PERMANENT) ? JSPD_PERMANENT : 0; + + return _newProperty(jsdc, &pd, JSDPD_HINTED); +} + +/* + * Retrieve a JSFunction* from a JSDValue*. This differs from + * JS_ValueToFunction by fully unwrapping the object first. + */ +JSFunction* +jsd_GetValueFunction(JSDContext* jsdc, JSDValue* jsdval) +{ + AutoSafeJSContext cx; + + JS::RootedObject obj(cx); + JS::RootedFunction fun(cx); + + if (JSVAL_IS_PRIMITIVE(jsdval->val)) + return NULL; + + obj = js::UncheckedUnwrap(JSVAL_TO_OBJECT(jsdval->val)); + JSAutoCompartment ac(cx, obj); + fun = JS_ValueToFunction(cx, OBJECT_TO_JSVAL(obj)); + + return fun; +} + +JSDValue* +jsd_GetValuePrototype(JSDContext* jsdc, JSDValue* jsdval) +{ + AutoSafeJSContext cx; + if(!(CHECK_BIT_FLAG(jsdval->flags, GOT_PROTO))) + { + JS::RootedObject obj(cx); + JS::RootedObject proto(cx); + JS_ASSERT(!jsdval->proto); + SET_BIT_FLAG(jsdval->flags, GOT_PROTO); + if(JSVAL_IS_PRIMITIVE(jsdval->val)) + return NULL; + obj = JSVAL_TO_OBJECT(jsdval->val); + if(!JS_GetPrototype(cx, obj, proto.address())) + return NULL; + if(!proto) + return NULL; + jsdval->proto = jsd_NewValue(jsdc, OBJECT_TO_JSVAL(proto)); + } + if(jsdval->proto) + jsdval->proto->nref++; + return jsdval->proto; +} + +JSDValue* +jsd_GetValueParent(JSDContext* jsdc, JSDValue* jsdval) +{ + if(!(CHECK_BIT_FLAG(jsdval->flags, GOT_PARENT))) + { + AutoSafeJSContext cx; + JS::RootedObject obj(cx); + JS::RootedObject parent(cx); + JS_ASSERT(!jsdval->parent); + SET_BIT_FLAG(jsdval->flags, GOT_PARENT); + if(JSVAL_IS_PRIMITIVE(jsdval->val)) + return NULL; + obj = JSVAL_TO_OBJECT(jsdval->val); + { + JSAutoCompartment ac(cx, obj); + parent = JS_GetParentOrScopeChain(cx, obj); + } + if(!parent) + return NULL; + jsdval->parent = jsd_NewValue(jsdc, OBJECT_TO_JSVAL(parent)); + } + if(jsdval->parent) + jsdval->parent->nref++; + return jsdval->parent; +} + +JSDValue* +jsd_GetValueConstructor(JSDContext* jsdc, JSDValue* jsdval) +{ + if(!(CHECK_BIT_FLAG(jsdval->flags, GOT_CTOR))) + { + AutoSafeJSContext cx; + JS::RootedObject obj(cx); + JS::RootedObject proto(cx); + JS::RootedObject ctor(cx); + JS_ASSERT(!jsdval->ctor); + SET_BIT_FLAG(jsdval->flags, GOT_CTOR); + if(JSVAL_IS_PRIMITIVE(jsdval->val)) + return NULL; + obj = JSVAL_TO_OBJECT(jsdval->val); + if(!JS_GetPrototype(cx, obj, proto.address())) + return NULL; + if(!proto) + return NULL; + { + JSAutoCompartment ac(cx, obj); + ctor = JS_GetConstructor(cx, proto); + } + if(!ctor) + return NULL; + jsdval->ctor = jsd_NewValue(jsdc, OBJECT_TO_JSVAL(ctor)); + } + if(jsdval->ctor) + jsdval->ctor->nref++; + return jsdval->ctor; +} + +const char* +jsd_GetValueClassName(JSDContext* jsdc, JSDValue* jsdval) +{ + jsval val = jsdval->val; + if(!jsdval->className && !JSVAL_IS_PRIMITIVE(val)) + { + AutoSafeJSContext cx; + JS::RootedObject obj(cx, JSVAL_TO_OBJECT(val)); + JSAutoCompartment ac(cx, obj); + jsdval->className = JS_GetDebugClassName(obj); + } + return jsdval->className; +} + +JSDScript* +jsd_GetScriptForValue(JSDContext* jsdc, JSDValue* jsdval) +{ + AutoSafeJSContext cx; + JS::RootedValue val(cx, jsdval->val); + JSFunction* fun = NULL; + JS::RootedScript script(cx); + JSDScript* jsdscript; + + if (!jsd_IsValueFunction(jsdc, jsdval)) + return NULL; + + { + JSAutoCompartment ac(cx, JSVAL_TO_OBJECT(val)); + AutoSaveExceptionState as(cx); + fun = JSD_GetValueFunction(jsdc, jsdval); + if (fun) + script = JS_GetFunctionScript(cx, fun); + } + + if (!script) + return NULL; + + JSD_LOCK_SCRIPTS(jsdc); + jsdscript = jsd_FindJSDScript(jsdc, script); + JSD_UNLOCK_SCRIPTS(jsdc); + return jsdscript; +} + + +/***************************************************************************/ +/***************************************************************************/ + +JSDValue* +jsd_GetPropertyName(JSDContext* jsdc, JSDProperty* jsdprop) +{ + jsdprop->name->nref++; + return jsdprop->name; +} + +JSDValue* +jsd_GetPropertyValue(JSDContext* jsdc, JSDProperty* jsdprop) +{ + jsdprop->val->nref++; + return jsdprop->val; +} + +JSDValue* +jsd_GetPropertyAlias(JSDContext* jsdc, JSDProperty* jsdprop) +{ + if(jsdprop->alias) + jsdprop->alias->nref++; + return jsdprop->alias; +} + +unsigned +jsd_GetPropertyFlags(JSDContext* jsdc, JSDProperty* jsdprop) +{ + return jsdprop->flags; +} + +void +jsd_DropProperty(JSDContext* jsdc, JSDProperty* jsdprop) +{ + JS_ASSERT(jsdprop->nref > 0); + if(0 == --jsdprop->nref) + { + JS_ASSERT(JS_CLIST_IS_EMPTY(&jsdprop->links)); + DROP_CLEAR_VALUE(jsdc, jsdprop->val); + DROP_CLEAR_VALUE(jsdc, jsdprop->name); + DROP_CLEAR_VALUE(jsdc, jsdprop->alias); + free(jsdprop); + } +} diff --git a/js/jsd/jsd_xpc.cpp b/js/jsd/jsd_xpc.cpp new file mode 100644 index 0000000..9e32581 --- /dev/null +++ b/js/jsd/jsd_xpc.cpp @@ -0,0 +1,3430 @@ +/* -*- 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/. */ + +#include "jsdbgapi.h" +#include "jslock.h" +#include "jsd_xpc.h" + +#include "js/GCAPI.h" + +#include "nsIXPConnect.h" +#include "mozilla/ModuleUtils.h" +#include "nsIServiceManager.h" +#include "nsIScriptGlobalObject.h" +#include "nsIObserver.h" +#include "nsIObserverService.h" +#include "nsICategoryManager.h" +#include "nsIJSRuntimeService.h" +#include "nsIThreadInternal.h" +#include "nsTArray.h" +#include "nsThreadUtils.h" +#include "nsMemory.h" +#include "jsdebug.h" +#include "nsReadableUtils.h" +#include "nsCRT.h" +#include "nsCycleCollectionParticipant.h" +#include "mozilla/Attributes.h" + +/* XXX DOM dependency */ +#include "nsIScriptContext.h" +#include "SandboxPrivate.h" +#include "nsJSPrincipals.h" +#include "nsContentUtils.h" +#include "nsCxPusher.h" + +using mozilla::AutoSafeJSContext; +using mozilla::AutoPushJSContext; + +/* + * defining CAUTIOUS_SCRIPTHOOK makes jsds disable GC while calling out to the + * script hook. This was a hack to avoid some js engine problems that should + * be fixed now (see Mozilla bug 77636). + */ +#undef CAUTIOUS_SCRIPTHOOK + +#ifdef DEBUG_verbose +# define DEBUG_COUNT(name, count) \ + { if ((count % 10) == 0) printf (name ": %i\n", count); } +# define DEBUG_CREATE(name, count) {count++; DEBUG_COUNT ("+++++ " name,count)} +# define DEBUG_DESTROY(name, count) {count--; DEBUG_COUNT ("----- " name,count)} +#else +# define DEBUG_CREATE(name, count) +# define DEBUG_DESTROY(name, count) +#endif + +#define ASSERT_VALID_CONTEXT { if (!mCx) return NS_ERROR_NOT_AVAILABLE; } +#define ASSERT_VALID_EPHEMERAL { if (!mValid) return NS_ERROR_NOT_AVAILABLE; } + +#define JSDSERVICE_CID \ +{ /* f1299dc2-1dd1-11b2-a347-ee6b7660e048 */ \ + 0xf1299dc2, \ + 0x1dd1, \ + 0x11b2, \ + {0xa3, 0x47, 0xee, 0x6b, 0x76, 0x60, 0xe0, 0x48} \ +} + +#define JSDASO_CID \ +{ /* 2fd6b7f6-eb8c-4f32-ad26-113f2c02d0fe */ \ + 0x2fd6b7f6, \ + 0xeb8c, \ + 0x4f32, \ + {0xad, 0x26, 0x11, 0x3f, 0x2c, 0x02, 0xd0, 0xfe} \ +} + +#define JSDS_MAJOR_VERSION 1 +#define JSDS_MINOR_VERSION 2 + +#define NS_CATMAN_CTRID "@mozilla.org/categorymanager;1" +#define NS_JSRT_CTRID "@mozilla.org/js/xpc/RuntimeService;1" + +#define AUTOREG_CATEGORY "xpcom-autoregistration" +#define APPSTART_CATEGORY "app-startup" +#define JSD_AUTOREG_ENTRY "JSDebugger Startup Observer" +#define JSD_STARTUP_ENTRY "JSDebugger Startup Observer" + +static void +jsds_GCSliceCallbackProc (JSRuntime *rt, JS::GCProgress progress, const JS::GCDescription &desc); + +/******************************************************************************* + * global vars + ******************************************************************************/ + +const char implementationString[] = "Mozilla JavaScript Debugger Service"; + +const char jsdServiceCtrID[] = "@mozilla.org/js/jsd/debugger-service;1"; +const char jsdARObserverCtrID[] = "@mozilla.org/js/jsd/app-start-observer;2"; +const char jsdASObserverCtrID[] = "service,@mozilla.org/js/jsd/app-start-observer;2"; + +#ifdef DEBUG_verbose +uint32_t gScriptCount = 0; +uint32_t gValueCount = 0; +uint32_t gPropertyCount = 0; +uint32_t gContextCount = 0; +uint32_t gFrameCount = 0; +#endif + +static jsdService *gJsds = 0; +static JS::GCSliceCallback gPrevGCSliceCallback = jsds_GCSliceCallbackProc; +static bool gGCRunning = false; + +static struct DeadScript { + PRCList links; + JSDContext *jsdc; + jsdIScript *script; +} *gDeadScripts = nullptr; + +enum PatternType { + ptIgnore = 0U, + ptStartsWith = 1U, + ptEndsWith = 2U, + ptContains = 3U, + ptEquals = 4U +}; + +static struct FilterRecord { + PRCList links; + jsdIFilter *filterObject; + nsCString urlPattern; + PatternType patternType; + uint32_t startLine; + uint32_t endLine; +} *gFilters = nullptr; + +static struct LiveEphemeral *gLiveValues = nullptr; +static struct LiveEphemeral *gLiveProperties = nullptr; +static struct LiveEphemeral *gLiveContexts = nullptr; +static struct LiveEphemeral *gLiveStackFrames = nullptr; + +/******************************************************************************* + * utility functions for ephemeral lists + *******************************************************************************/ +already_AddRefed<jsdIEphemeral> +jsds_FindEphemeral (LiveEphemeral **listHead, void *key) +{ + if (!*listHead) + return nullptr; + + LiveEphemeral *lv_record = + reinterpret_cast<LiveEphemeral *> + (PR_NEXT_LINK(&(*listHead)->links)); + do + { + if (lv_record->key == key) + { + nsCOMPtr<jsdIEphemeral> ret = lv_record->value; + return ret.forget(); + } + lv_record = reinterpret_cast<LiveEphemeral *> + (PR_NEXT_LINK(&lv_record->links)); + } + while (lv_record != *listHead); + + return nullptr; +} + +void +jsds_InvalidateAllEphemerals (LiveEphemeral **listHead) +{ + LiveEphemeral *lv_record = + reinterpret_cast<LiveEphemeral *> + (PR_NEXT_LINK(&(*listHead)->links)); + do + { + LiveEphemeral *next = + reinterpret_cast<LiveEphemeral *> + (PR_NEXT_LINK(&lv_record->links)); + lv_record->value->Invalidate(); + lv_record = next; + } + while (*listHead); +} + +void +jsds_InsertEphemeral (LiveEphemeral **listHead, LiveEphemeral *item) +{ + if (*listHead) { + /* if the list exists, add to it */ + PR_APPEND_LINK(&item->links, &(*listHead)->links); + } else { + /* otherwise create the list */ + PR_INIT_CLIST(&item->links); + *listHead = item; + } +} + +void +jsds_RemoveEphemeral (LiveEphemeral **listHead, LiveEphemeral *item) +{ + LiveEphemeral *next = reinterpret_cast<LiveEphemeral *> + (PR_NEXT_LINK(&item->links)); + + if (next == item) + { + /* if the current item is also the next item, we're the only element, + * null out the list head */ + NS_ASSERTION (*listHead == item, + "How could we not be the head of a one item list?"); + *listHead = nullptr; + } + else if (item == *listHead) + { + /* otherwise, if we're currently the list head, change it */ + *listHead = next; + } + + PR_REMOVE_AND_INIT_LINK(&item->links); +} + +/******************************************************************************* + * utility functions for filters + *******************************************************************************/ +void +jsds_FreeFilter (FilterRecord *rec) +{ + NS_IF_RELEASE (rec->filterObject); + PR_Free (rec); +} + +/* copies appropriate |filter| attributes into |rec|. + * False return indicates failure, the contents of |rec| will not be changed. + */ +bool +jsds_SyncFilter (FilterRecord *rec, jsdIFilter *filter) +{ + NS_ASSERTION (rec, "jsds_SyncFilter without rec"); + NS_ASSERTION (filter, "jsds_SyncFilter without filter"); + + uint32_t startLine; + nsresult rv = filter->GetStartLine(&startLine); + if (NS_FAILED(rv)) + return false; + + uint32_t endLine; + rv = filter->GetStartLine(&endLine); + if (NS_FAILED(rv)) + return false; + + nsAutoCString urlPattern; + rv = filter->GetUrlPattern (urlPattern); + if (NS_FAILED(rv)) + return false; + + uint32_t len = urlPattern.Length(); + if (len) { + if (urlPattern[0] == '*') { + /* pattern starts with a *, shift all chars once to the left, + * including the trailing null. */ + urlPattern = Substring(urlPattern, 1, len); + + if (urlPattern[len - 2] == '*') { + /* pattern is in the format "*foo*", overwrite the final * with + * a null. */ + urlPattern.Truncate(len - 2); + rec->patternType = ptContains; + } else { + /* pattern is in the format "*foo", just make a note of the + * new length. */ + rec->patternType = ptEndsWith; + } + } else if (urlPattern[len - 1] == '*') { + /* pattern is in the format "foo*", overwrite the final * with a + * null. */ + urlPattern.Truncate(len - 1); + rec->patternType = ptStartsWith; + } else { + /* pattern is in the format "foo". */ + rec->patternType = ptEquals; + } + } else { + rec->patternType = ptIgnore; + } + + /* we got everything we need without failing, now copy it into rec. */ + + if (rec->filterObject != filter) { + NS_IF_RELEASE(rec->filterObject); + NS_ADDREF(filter); + rec->filterObject = filter; + } + + rec->startLine = startLine; + rec->endLine = endLine; + + rec->urlPattern = urlPattern; + + return true; + +} + +FilterRecord * +jsds_FindFilter (jsdIFilter *filter) +{ + if (!gFilters) + return nullptr; + + FilterRecord *current = gFilters; + + do { + if (current->filterObject == filter) + return current; + current = reinterpret_cast<FilterRecord *> + (PR_NEXT_LINK(¤t->links)); + } while (current != gFilters); + + return nullptr; +} + +/* returns true if the hook should be executed. */ +bool +jsds_FilterHook (JSDContext *jsdc, JSDThreadState *state) +{ + JSDStackFrameInfo *frame = JSD_GetStackFrame (jsdc, state); + + if (!frame) { + NS_WARNING("No frame in threadstate"); + return false; + } + + JSDScript *script = JSD_GetScriptForStackFrame (jsdc, state, frame); + if (!script) + return true; + + uintptr_t pc = JSD_GetPCForStackFrame (jsdc, state, frame); + + nsCString url(JSD_GetScriptFilename (jsdc, script)); + if (url.IsEmpty()) { + NS_WARNING ("Script with no filename"); + return false; + } + + if (!gFilters) + return true; + + uint32_t currentLine = JSD_GetClosestLine (jsdc, script, pc); + uint32_t len = 0; + FilterRecord *currentFilter = gFilters; + do { + uint32_t flags = 0; + +#ifdef DEBUG + nsresult rv = +#endif + currentFilter->filterObject->GetFlags(&flags); + NS_ASSERTION(NS_SUCCEEDED(rv), "Error getting flags for filter"); + + if (flags & jsdIFilter::FLAG_ENABLED) { + /* If there is no start line, or the start line is before + * or equal to the current */ + if ((!currentFilter->startLine || + currentFilter->startLine <= currentLine) && + /* and there is no end line, or the end line is after + * or equal to the current */ + (!currentFilter->endLine || + currentFilter->endLine >= currentLine)) { + /* then we're going to have to compare the url. */ + if (currentFilter->patternType == ptIgnore) + return !!(flags & jsdIFilter::FLAG_PASS); + + if (!len) + len = url.Length(); + nsCString urlPattern = currentFilter->urlPattern; + uint32_t patternLength = urlPattern.Length(); + if (len >= patternLength) { + switch (currentFilter->patternType) { + case ptEquals: + if (urlPattern.Equals(url)) + return !!(flags & jsdIFilter::FLAG_PASS); + break; + case ptStartsWith: + if (urlPattern.Equals(Substring(url, 0, patternLength))) + return !!(flags & jsdIFilter::FLAG_PASS); + break; + case ptEndsWith: + if (urlPattern.Equals(Substring(url, len - patternLength))) + return !!(flags & jsdIFilter::FLAG_PASS); + break; + case ptContains: + { + nsACString::const_iterator start, end; + url.BeginReading(start); + url.EndReading(end); + if (FindInReadable(currentFilter->urlPattern, start, end)) + return !!(flags & jsdIFilter::FLAG_PASS); + } + break; + default: + NS_ERROR("Invalid pattern type"); + } + } + } + } + currentFilter = reinterpret_cast<FilterRecord *> + (PR_NEXT_LINK(¤tFilter->links)); + } while (currentFilter != gFilters); + + return true; + +} + +/******************************************************************************* + * c callbacks + *******************************************************************************/ + +static void +jsds_NotifyPendingDeadScripts (JSRuntime *rt) +{ + jsdService *jsds = gJsds; + + nsCOMPtr<jsdIScriptHook> hook; + if (jsds) { + NS_ADDREF(jsds); + jsds->GetScriptHook (getter_AddRefs(hook)); + jsds->DoPause(nullptr, true); + } + + DeadScript *deadScripts = gDeadScripts; + gDeadScripts = nullptr; + while (deadScripts) { + DeadScript *ds = deadScripts; + /* get next deleted script */ + deadScripts = reinterpret_cast<DeadScript *> + (PR_NEXT_LINK(&ds->links)); + if (deadScripts == ds) + deadScripts = nullptr; + + if (hook) + { + /* tell the user this script has been destroyed */ +#ifdef CAUTIOUS_SCRIPTHOOK + JS_UNKEEP_ATOMS(rt); +#endif + hook->OnScriptDestroyed (ds->script); +#ifdef CAUTIOUS_SCRIPTHOOK + JS_KEEP_ATOMS(rt); +#endif + } + + /* take it out of the circular list */ + PR_REMOVE_LINK(&ds->links); + + /* addref came from the FromPtr call in jsds_ScriptHookProc */ + NS_RELEASE(ds->script); + /* free the struct! */ + PR_Free(ds); + } + + if (jsds) { + jsds->DoUnPause(nullptr, true); + NS_RELEASE(jsds); + } +} + +static void +jsds_GCSliceCallbackProc (JSRuntime *rt, JS::GCProgress progress, const JS::GCDescription &desc) +{ + if (progress == JS::GC_CYCLE_END || progress == JS::GC_SLICE_END) { + NS_ASSERTION(gGCRunning, "GC slice callback was missed"); + + while (gDeadScripts) + jsds_NotifyPendingDeadScripts (rt); + + gGCRunning = false; + } else { + NS_ASSERTION(!gGCRunning, "should not re-enter GC"); + gGCRunning = true; + } + + if (gPrevGCSliceCallback) + (*gPrevGCSliceCallback)(rt, progress, desc); +} + +static unsigned +jsds_ErrorHookProc (JSDContext *jsdc, JSContext *cx, const char *message, + JSErrorReport *report, void *callerdata) +{ + static bool running = false; + + nsCOMPtr<jsdIErrorHook> hook; + gJsds->GetErrorHook(getter_AddRefs(hook)); + if (!hook) + return JSD_ERROR_REPORTER_PASS_ALONG; + + if (running) + return JSD_ERROR_REPORTER_PASS_ALONG; + + running = true; + + nsCOMPtr<jsdIValue> val; + if (JS_IsExceptionPending(cx)) { + jsval jv; + JS_GetPendingException(cx, &jv); + JSDValue *jsdv = JSD_NewValue (jsdc, jv); + val = getter_AddRefs(jsdValue::FromPtr(jsdc, jsdv)); + } + + nsAutoCString fileName; + uint32_t line; + uint32_t pos; + uint32_t flags; + uint32_t errnum; + bool rval; + if (report) { + fileName.Assign(report->filename); + line = report->lineno; + pos = report->tokenptr - report->linebuf; + flags = report->flags; + errnum = report->errorNumber; + } + else + { + line = 0; + pos = 0; + flags = 0; + errnum = 0; + } + + gJsds->DoPause(nullptr, true); + hook->OnError (nsDependentCString(message), fileName, line, pos, flags, errnum, val, &rval); + gJsds->DoUnPause(nullptr, true); + + running = false; + if (!rval) + return JSD_ERROR_REPORTER_DEBUG; + + return JSD_ERROR_REPORTER_PASS_ALONG; +} + +static JSBool +jsds_CallHookProc (JSDContext* jsdc, JSDThreadState* jsdthreadstate, + unsigned type, void* callerdata) +{ + nsCOMPtr<jsdICallHook> hook; + + switch (type) + { + case JSD_HOOK_TOPLEVEL_START: + case JSD_HOOK_TOPLEVEL_END: + gJsds->GetTopLevelHook(getter_AddRefs(hook)); + break; + + case JSD_HOOK_FUNCTION_CALL: + case JSD_HOOK_FUNCTION_RETURN: + gJsds->GetFunctionHook(getter_AddRefs(hook)); + break; + + default: + NS_ASSERTION (0, "Unknown hook type."); + } + + if (!hook) + return JS_TRUE; + + if (!jsds_FilterHook (jsdc, jsdthreadstate)) + return JS_FALSE; + + JSDStackFrameInfo *native_frame = JSD_GetStackFrame (jsdc, jsdthreadstate); + nsCOMPtr<jsdIStackFrame> frame = + getter_AddRefs(jsdStackFrame::FromPtr(jsdc, jsdthreadstate, + native_frame)); + gJsds->DoPause(nullptr, true); + hook->OnCall(frame, type); + gJsds->DoUnPause(nullptr, true); + jsdStackFrame::InvalidateAll(); + + return JS_TRUE; +} + +static uint32_t +jsds_ExecutionHookProc (JSDContext* jsdc, JSDThreadState* jsdthreadstate, + unsigned type, void* callerdata, jsval* rval) +{ + nsCOMPtr<jsdIExecutionHook> hook(0); + uint32_t hook_rv = JSD_HOOK_RETURN_CONTINUE; + nsCOMPtr<jsdIValue> js_rv; + + switch (type) + { + case JSD_HOOK_INTERRUPTED: + gJsds->GetInterruptHook(getter_AddRefs(hook)); + break; + case JSD_HOOK_DEBUG_REQUESTED: + gJsds->GetDebugHook(getter_AddRefs(hook)); + break; + case JSD_HOOK_DEBUGGER_KEYWORD: + gJsds->GetDebuggerHook(getter_AddRefs(hook)); + break; + case JSD_HOOK_BREAKPOINT: + { + /* we can't pause breakpoints the way we pause the other + * execution hooks (at least, not easily.) Instead we bail + * here if the service is paused. */ + uint32_t level; + gJsds->GetPauseDepth(&level); + if (!level) + gJsds->GetBreakpointHook(getter_AddRefs(hook)); + } + break; + case JSD_HOOK_THROW: + { + hook_rv = JSD_HOOK_RETURN_CONTINUE_THROW; + gJsds->GetThrowHook(getter_AddRefs(hook)); + if (hook) { + JSDValue *jsdv = JSD_GetException (jsdc, jsdthreadstate); + js_rv = getter_AddRefs(jsdValue::FromPtr (jsdc, jsdv)); + } + break; + } + default: + NS_ASSERTION (0, "Unknown hook type."); + } + + if (!hook) + return hook_rv; + + if (!jsds_FilterHook (jsdc, jsdthreadstate)) + return JSD_HOOK_RETURN_CONTINUE; + + JSDStackFrameInfo *native_frame = JSD_GetStackFrame (jsdc, jsdthreadstate); + nsCOMPtr<jsdIStackFrame> frame = + getter_AddRefs(jsdStackFrame::FromPtr(jsdc, jsdthreadstate, + native_frame)); + gJsds->DoPause(nullptr, true); + jsdIValue *inout_rv = js_rv; + NS_IF_ADDREF(inout_rv); + hook->OnExecute (frame, type, &inout_rv, &hook_rv); + js_rv = inout_rv; + NS_IF_RELEASE(inout_rv); + gJsds->DoUnPause(nullptr, true); + jsdStackFrame::InvalidateAll(); + + if (hook_rv == JSD_HOOK_RETURN_RET_WITH_VAL || + hook_rv == JSD_HOOK_RETURN_THROW_WITH_VAL) { + *rval = JSVAL_VOID; + if (js_rv) { + JSDValue *jsdv; + if (NS_SUCCEEDED(js_rv->GetJSDValue (&jsdv))) + *rval = JSD_GetValueWrappedJSVal(jsdc, jsdv); + } + } + + return hook_rv; +} + +static void +jsds_ScriptHookProc (JSDContext* jsdc, JSDScript* jsdscript, JSBool creating, + void* callerdata) +{ +#ifdef CAUTIOUS_SCRIPTHOOK + JSRuntime *rt = JS_GetRuntime(nsContentUtils::GetSafeJSContext()); +#endif + + if (creating) { + nsCOMPtr<jsdIScriptHook> hook; + gJsds->GetScriptHook(getter_AddRefs(hook)); + + /* a script is being created */ + if (!hook) { + /* nobody cares, just exit */ + return; + } + + nsCOMPtr<jsdIScript> script = + getter_AddRefs(jsdScript::FromPtr(jsdc, jsdscript)); +#ifdef CAUTIOUS_SCRIPTHOOK + JS_UNKEEP_ATOMS(rt); +#endif + gJsds->DoPause(nullptr, true); + hook->OnScriptCreated (script); + gJsds->DoUnPause(nullptr, true); +#ifdef CAUTIOUS_SCRIPTHOOK + JS_KEEP_ATOMS(rt); +#endif + } else { + /* a script is being destroyed. even if there is no registered hook + * we'll still need to invalidate the jsdIScript record, in order + * to remove the reference held in the JSDScript private data. */ + nsCOMPtr<jsdIScript> jsdis = + static_cast<jsdIScript *>(JSD_GetScriptPrivate(jsdscript)); + if (!jsdis) + return; + + jsdis->Invalidate(); + + if (!gGCRunning) { + nsCOMPtr<jsdIScriptHook> hook; + gJsds->GetScriptHook(getter_AddRefs(hook)); + if (!hook) + return; + + /* if GC *isn't* running, we can tell the user about the script + * delete now. */ +#ifdef CAUTIOUS_SCRIPTHOOK + JS_UNKEEP_ATOMS(rt); +#endif + + gJsds->DoPause(nullptr, true); + hook->OnScriptDestroyed (jsdis); + gJsds->DoUnPause(nullptr, true); +#ifdef CAUTIOUS_SCRIPTHOOK + JS_KEEP_ATOMS(rt); +#endif + } else { + /* if a GC *is* running, we've got to wait until it's done before + * we can execute any JS, so we queue the notification in a PRCList + * until GC tells us it's done. See jsds_GCCallbackProc(). */ + DeadScript *ds = PR_NEW(DeadScript); + if (!ds) + return; /* NS_ERROR_OUT_OF_MEMORY */ + + ds->jsdc = jsdc; + ds->script = jsdis; + NS_ADDREF(ds->script); + if (gDeadScripts) + /* if the queue exists, add to it */ + PR_APPEND_LINK(&ds->links, &gDeadScripts->links); + else { + /* otherwise create the queue */ + PR_INIT_CLIST(&ds->links); + gDeadScripts = ds; + } + } + } +} + +/******************************************************************************* + * reflected jsd data structures + *******************************************************************************/ + +/* Contexts */ +/* +NS_IMPL_THREADSAFE_ISUPPORTS2(jsdContext, jsdIContext, jsdIEphemeral); + +NS_IMETHODIMP +jsdContext::GetJSDContext(JSDContext **_rval) +{ + *_rval = mCx; + return NS_OK; +} +*/ + +/* Objects */ +NS_IMPL_THREADSAFE_ISUPPORTS1(jsdObject, jsdIObject) + +NS_IMETHODIMP +jsdObject::GetJSDContext(JSDContext **_rval) +{ + *_rval = mCx; + return NS_OK; +} + +NS_IMETHODIMP +jsdObject::GetJSDObject(JSDObject **_rval) +{ + *_rval = mObject; + return NS_OK; +} + +NS_IMETHODIMP +jsdObject::GetCreatorURL(nsACString &_rval) +{ + _rval.Assign(JSD_GetObjectNewURL(mCx, mObject)); + return NS_OK; +} + +NS_IMETHODIMP +jsdObject::GetCreatorLine(uint32_t *_rval) +{ + *_rval = JSD_GetObjectNewLineNumber(mCx, mObject); + return NS_OK; +} + +NS_IMETHODIMP +jsdObject::GetConstructorURL(nsACString &_rval) +{ + _rval.Assign(JSD_GetObjectConstructorURL(mCx, mObject)); + return NS_OK; +} + +NS_IMETHODIMP +jsdObject::GetConstructorLine(uint32_t *_rval) +{ + *_rval = JSD_GetObjectConstructorLineNumber(mCx, mObject); + return NS_OK; +} + +NS_IMETHODIMP +jsdObject::GetValue(jsdIValue **_rval) +{ + JSDValue *jsdv = JSD_GetValueForObject (mCx, mObject); + + *_rval = jsdValue::FromPtr (mCx, jsdv); + return NS_OK; +} + +/* Properties */ +NS_IMPL_THREADSAFE_ISUPPORTS2(jsdProperty, jsdIProperty, jsdIEphemeral) + +jsdProperty::jsdProperty (JSDContext *aCx, JSDProperty *aProperty) : + mCx(aCx), mProperty(aProperty) +{ + DEBUG_CREATE ("jsdProperty", gPropertyCount); + mValid = (aCx && aProperty); + mLiveListEntry.value = this; + jsds_InsertEphemeral (&gLiveProperties, &mLiveListEntry); +} + +jsdProperty::~jsdProperty () +{ + DEBUG_DESTROY ("jsdProperty", gPropertyCount); + if (mValid) + Invalidate(); +} + +NS_IMETHODIMP +jsdProperty::Invalidate() +{ + ASSERT_VALID_EPHEMERAL; + mValid = false; + jsds_RemoveEphemeral (&gLiveProperties, &mLiveListEntry); + JSD_DropProperty (mCx, mProperty); + return NS_OK; +} + +void +jsdProperty::InvalidateAll() +{ + if (gLiveProperties) + jsds_InvalidateAllEphemerals (&gLiveProperties); +} + +NS_IMETHODIMP +jsdProperty::GetJSDContext(JSDContext **_rval) +{ + *_rval = mCx; + return NS_OK; +} + +NS_IMETHODIMP +jsdProperty::GetJSDProperty(JSDProperty **_rval) +{ + *_rval = mProperty; + return NS_OK; +} + +NS_IMETHODIMP +jsdProperty::GetIsValid(bool *_rval) +{ + *_rval = mValid; + return NS_OK; +} + +NS_IMETHODIMP +jsdProperty::GetAlias(jsdIValue **_rval) +{ + JSDValue *jsdv = JSD_GetPropertyValue (mCx, mProperty); + + *_rval = jsdValue::FromPtr (mCx, jsdv); + return NS_OK; +} + +NS_IMETHODIMP +jsdProperty::GetFlags(uint32_t *_rval) +{ + *_rval = JSD_GetPropertyFlags (mCx, mProperty); + return NS_OK; +} + +NS_IMETHODIMP +jsdProperty::GetName(jsdIValue **_rval) +{ + JSDValue *jsdv = JSD_GetPropertyName (mCx, mProperty); + + *_rval = jsdValue::FromPtr (mCx, jsdv); + return NS_OK; +} + +NS_IMETHODIMP +jsdProperty::GetValue(jsdIValue **_rval) +{ + JSDValue *jsdv = JSD_GetPropertyValue (mCx, mProperty); + + *_rval = jsdValue::FromPtr (mCx, jsdv); + return NS_OK; +} + +/* Scripts */ +NS_IMPL_THREADSAFE_ISUPPORTS2(jsdScript, jsdIScript, jsdIEphemeral) + +static NS_IMETHODIMP +AssignToJSString(JSDContext *aCx, nsACString *x, JSString *str) +{ + if (!str) { + x->SetLength(0); + return NS_OK; + } + AutoSafeJSContext cx; + JSAutoCompartment ac(cx, JSD_GetDefaultGlobal(aCx)); // Just in case. + size_t length = JS_GetStringEncodingLength(cx, str); + if (length == size_t(-1)) + return NS_ERROR_FAILURE; + x->SetLength(uint32_t(length)); + if (x->Length() != uint32_t(length)) + return NS_ERROR_OUT_OF_MEMORY; + JS_EncodeStringToBuffer(cx, str, x->BeginWriting(), length); + return NS_OK; +} + +jsdScript::jsdScript (JSDContext *aCx, JSDScript *aScript) : mValid(false), + mTag(0), + mCx(aCx), + mScript(aScript), + mFileName(0), + mFunctionName(0), + mBaseLineNumber(0), + mLineExtent(0), + mPPLineMap(0), + mFirstPC(0) +{ + DEBUG_CREATE ("jsdScript", gScriptCount); + + if (mScript) { + /* copy the script's information now, so we have it later, when it + * gets destroyed. */ + JSD_LockScriptSubsystem(mCx); + mFileName = new nsCString(JSD_GetScriptFilename(mCx, mScript)); + mFunctionName = new nsCString(); + if (mFunctionName) { + JSString *str = JSD_GetScriptFunctionId(mCx, mScript); + if (str) + AssignToJSString(mCx, mFunctionName, str); + } + mBaseLineNumber = JSD_GetScriptBaseLineNumber(mCx, mScript); + mLineExtent = JSD_GetScriptLineExtent(mCx, mScript); + mFirstPC = JSD_GetClosestPC(mCx, mScript, 0); + JSD_UnlockScriptSubsystem(mCx); + + mValid = true; + } +} + +jsdScript::~jsdScript () +{ + DEBUG_DESTROY ("jsdScript", gScriptCount); + delete mFileName; + delete mFunctionName; + + if (mPPLineMap) + PR_Free(mPPLineMap); + + /* Invalidate() needs to be called to release an owning reference to + * ourselves, so if we got here without being invalidated, something + * has gone wrong with our ref count. */ + NS_ASSERTION (!mValid, "Script destroyed without being invalidated."); +} + +/* + * This method populates a line <-> pc map for a pretty printed version of this + * script. It does this by decompiling, and then recompiling the script. The + * resulting script is scanned for the line map, and then left as GC fodder. + */ +PCMapEntry * +jsdScript::CreatePPLineMap() +{ + AutoSafeJSContext cx; + JSAutoCompartment ac(cx, JSD_GetDefaultGlobal (mCx)); // Just in case. + JS::RootedObject obj(cx, JS_NewObject(cx, NULL, NULL, NULL)); + if (!obj) + return nullptr; + JS::RootedFunction fun(cx, JSD_GetJSFunction (mCx, mScript)); + JS::RootedScript script(cx); /* In JSD compartment */ + uint32_t baseLine; + JS::RootedString jsstr(cx); + size_t length; + const jschar *chars; + + if (fun) { + unsigned nargs; + + { + JSAutoCompartment ac(cx, JS_GetFunctionObject(fun)); + nargs = JS_GetFunctionArgumentCount(cx, fun); + if (nargs > 12) + return nullptr; + jsstr = JS_DecompileFunctionBody (cx, fun, 4); + if (!jsstr) + return nullptr; + + if (!(chars = JS_GetStringCharsAndLength(cx, jsstr, &length))) + return nullptr; + } + + JS::Anchor<JSString *> kungFuDeathGrip(jsstr); + const char *argnames[] = {"arg1", "arg2", "arg3", "arg4", + "arg5", "arg6", "arg7", "arg8", + "arg9", "arg10", "arg11", "arg12" }; + fun = JS_CompileUCFunction (cx, obj, "ppfun", nargs, argnames, chars, + length, "x-jsd:ppbuffer?type=function", 3); + if (!fun || !(script = JS_GetFunctionScript(cx, fun))) + return nullptr; + baseLine = 3; + } else { + script = JSD_GetJSScript(mCx, mScript); + JSString *jsstr; + + { + JSAutoCompartment ac(cx, script); + + jsstr = JS_DecompileScript (cx, script, "ppscript", 4); + if (!jsstr) + return nullptr; + + if (!(chars = JS_GetStringCharsAndLength(cx, jsstr, &length))) + return nullptr; + } + + JS::Anchor<JSString *> kungFuDeathGrip(jsstr); + script = JS_CompileUCScript (cx, obj, chars, length, "x-jsd:ppbuffer?type=script", 1); + if (!script) + return nullptr; + baseLine = 1; + } + + uint32_t scriptExtent = JS_GetScriptLineExtent (cx, script); + jsbytecode* firstPC = JS_LineNumberToPC (cx, script, 0); + /* allocate worst case size of map (number of lines in script + 1 + * for our 0 record), we'll shrink it with a realloc later. */ + PCMapEntry *lineMap = + static_cast<PCMapEntry *> + (PR_Malloc((scriptExtent + 1) * sizeof (PCMapEntry))); + uint32_t lineMapSize = 0; + + if (lineMap) { + for (uint32_t line = baseLine; line < scriptExtent + baseLine; ++line) { + jsbytecode* pc = JS_LineNumberToPC (cx, script, line); + if (line == JS_PCToLineNumber (cx, script, pc)) { + lineMap[lineMapSize].line = line; + lineMap[lineMapSize].pc = pc - firstPC; + ++lineMapSize; + } + } + if (scriptExtent != lineMapSize) { + lineMap = + static_cast<PCMapEntry *> + (PR_Realloc(mPPLineMap = lineMap, + lineMapSize * sizeof(PCMapEntry))); + if (!lineMap) { + PR_Free(mPPLineMap); + lineMapSize = 0; + } + } + } + + mPCMapSize = lineMapSize; + return mPPLineMap = lineMap; +} + +uint32_t +jsdScript::PPPcToLine (uint32_t aPC) +{ + if (!mPPLineMap && !CreatePPLineMap()) + return 0; + uint32_t i; + for (i = 1; i < mPCMapSize; ++i) { + if (mPPLineMap[i].pc > aPC) + return mPPLineMap[i - 1].line; + } + + return mPPLineMap[mPCMapSize - 1].line; +} + +uint32_t +jsdScript::PPLineToPc (uint32_t aLine) +{ + if (!mPPLineMap && !CreatePPLineMap()) + return 0; + uint32_t i; + for (i = 1; i < mPCMapSize; ++i) { + if (mPPLineMap[i].line > aLine) + return mPPLineMap[i - 1].pc; + } + + return mPPLineMap[mPCMapSize - 1].pc; +} + +NS_IMETHODIMP +jsdScript::GetJSDContext(JSDContext **_rval) +{ + ASSERT_VALID_EPHEMERAL; + *_rval = mCx; + return NS_OK; +} + +NS_IMETHODIMP +jsdScript::GetJSDScript(JSDScript **_rval) +{ + ASSERT_VALID_EPHEMERAL; + *_rval = mScript; + return NS_OK; +} + +NS_IMETHODIMP +jsdScript::GetVersion (int32_t *_rval) +{ + ASSERT_VALID_EPHEMERAL; + AutoSafeJSContext cx; + JS::RootedScript script(cx, JSD_GetJSScript(mCx, mScript)); + JSAutoCompartment ac(cx, script); + *_rval = static_cast<int32_t>(JS_GetScriptVersion(cx, script)); + return NS_OK; +} + +NS_IMETHODIMP +jsdScript::GetTag(uint32_t *_rval) +{ + if (!mTag) + mTag = ++jsdScript::LastTag; + + *_rval = mTag; + return NS_OK; +} + +NS_IMETHODIMP +jsdScript::Invalidate() +{ + ASSERT_VALID_EPHEMERAL; + mValid = false; + + /* release the addref we do in FromPtr */ + jsdIScript *script = static_cast<jsdIScript *> + (JSD_GetScriptPrivate(mScript)); + NS_ASSERTION (script == this, "That's not my script!"); + NS_RELEASE(script); + JSD_SetScriptPrivate(mScript, NULL); + return NS_OK; +} + +void +jsdScript::InvalidateAll () +{ + JSDContext *cx; + if (NS_FAILED(gJsds->GetJSDContext (&cx))) + return; + + JSDScript *script; + JSDScript *iter = NULL; + + JSD_LockScriptSubsystem(cx); + while((script = JSD_IterateScripts(cx, &iter)) != NULL) { + nsCOMPtr<jsdIScript> jsdis = + static_cast<jsdIScript *>(JSD_GetScriptPrivate(script)); + if (jsdis) + jsdis->Invalidate(); + } + JSD_UnlockScriptSubsystem(cx); +} + +NS_IMETHODIMP +jsdScript::GetIsValid(bool *_rval) +{ + *_rval = mValid; + return NS_OK; +} + +NS_IMETHODIMP +jsdScript::SetFlags(uint32_t flags) +{ + ASSERT_VALID_EPHEMERAL; + JSD_SetScriptFlags(mCx, mScript, flags); + return NS_OK; +} + +NS_IMETHODIMP +jsdScript::GetFlags(uint32_t *_rval) +{ + ASSERT_VALID_EPHEMERAL; + *_rval = JSD_GetScriptFlags(mCx, mScript); + return NS_OK; +} + +NS_IMETHODIMP +jsdScript::GetFileName(nsACString &_rval) +{ + _rval.Assign(*mFileName); + return NS_OK; +} + +NS_IMETHODIMP +jsdScript::GetFunctionName(nsACString &_rval) +{ + _rval.Assign(*mFunctionName); + return NS_OK; +} + +NS_IMETHODIMP +jsdScript::GetParameterNames(uint32_t* count, PRUnichar*** paramNames) +{ + ASSERT_VALID_EPHEMERAL; + AutoSafeJSContext cx; + JS::RootedFunction fun(cx, JSD_GetJSFunction (mCx, mScript)); + if (!fun) { + *count = 0; + *paramNames = nullptr; + return NS_OK; + } + + JSAutoCompartment ac(cx, JS_GetFunctionObject(fun)); + + unsigned nargs; + if (!JS_FunctionHasLocalNames(cx, fun) || + (nargs = JS_GetFunctionArgumentCount(cx, fun)) == 0) { + *count = 0; + *paramNames = nullptr; + return NS_OK; + } + + PRUnichar **ret = + static_cast<PRUnichar**>(NS_Alloc(nargs * sizeof(PRUnichar*))); + if (!ret) + return NS_ERROR_OUT_OF_MEMORY; + + void *mark; + uintptr_t *names = JS_GetFunctionLocalNameArray(cx, fun, &mark); + if (!names) { + NS_Free(ret); + return NS_ERROR_OUT_OF_MEMORY; + } + + nsresult rv = NS_OK; + for (unsigned i = 0; i < nargs; ++i) { + JSAtom *atom = JS_LocalNameToAtom(names[i]); + if (!atom) { + ret[i] = 0; + } else { + JSString *str = JS_AtomKey(atom); + ret[i] = NS_strndup(JS_GetInternedStringChars(str), JS_GetStringLength(str)); + if (!ret[i]) { + NS_FREE_XPCOM_ALLOCATED_POINTER_ARRAY(i, ret); + rv = NS_ERROR_OUT_OF_MEMORY; + break; + } + } + } + JS_ReleaseFunctionLocalNameArray(cx, mark); + if (NS_FAILED(rv)) + return rv; + *count = nargs; + *paramNames = ret; + return NS_OK; +} + +NS_IMETHODIMP +jsdScript::GetFunctionObject(jsdIValue **_rval) +{ + JSFunction *fun = JSD_GetJSFunction(mCx, mScript); + if (!fun) + return NS_ERROR_NOT_AVAILABLE; + + AutoSafeJSContext jsContext; + JS::RootedObject obj(jsContext, JS_GetFunctionObject(fun)); + if (!obj) + return NS_ERROR_FAILURE; + + JSDContext *cx; + if (NS_FAILED(gJsds->GetJSDContext (&cx))) + return NS_ERROR_NOT_INITIALIZED; + + JSDValue *jsdv = JSD_NewValue(cx, OBJECT_TO_JSVAL(obj)); + if (!jsdv) + return NS_ERROR_OUT_OF_MEMORY; + + *_rval = jsdValue::FromPtr(cx, jsdv); + if (!*_rval) { + JSD_DropValue(cx, jsdv); + return NS_ERROR_OUT_OF_MEMORY; + } + + return NS_OK; +} + +NS_IMETHODIMP +jsdScript::GetFunctionSource(nsAString & aFunctionSource) +{ + ASSERT_VALID_EPHEMERAL; + AutoSafeJSContext cx_; + JSContext *cx = cx_; // Appease the type system with Maybe<>s below. + JS::RootedFunction fun(cx, JSD_GetJSFunction (mCx, mScript)); + + JSString *jsstr; + mozilla::Maybe<JSAutoCompartment> ac; + if (fun) { + ac.construct(cx, JS_GetFunctionObject(fun)); + jsstr = JS_DecompileFunction (cx, fun, 4); + } else { + JS::RootedScript script(cx, JSD_GetJSScript (mCx, mScript)); + ac.construct(cx, script); + jsstr = JS_DecompileScript (cx, script, "ppscript", 4); + } + if (!jsstr) + return NS_ERROR_FAILURE; + + size_t length; + const jschar *chars = JS_GetStringCharsZAndLength(cx, jsstr, &length); + if (!chars) + return NS_ERROR_FAILURE; + + aFunctionSource = nsDependentString(chars, length); + return NS_OK; +} + +NS_IMETHODIMP +jsdScript::GetBaseLineNumber(uint32_t *_rval) +{ + *_rval = mBaseLineNumber; + return NS_OK; +} + +NS_IMETHODIMP +jsdScript::GetLineExtent(uint32_t *_rval) +{ + *_rval = mLineExtent; + return NS_OK; +} + +NS_IMETHODIMP +jsdScript::GetCallCount(uint32_t *_rval) +{ + ASSERT_VALID_EPHEMERAL; + *_rval = JSD_GetScriptCallCount (mCx, mScript); + return NS_OK; +} + +NS_IMETHODIMP +jsdScript::GetMaxRecurseDepth(uint32_t *_rval) +{ + ASSERT_VALID_EPHEMERAL; + *_rval = JSD_GetScriptMaxRecurseDepth (mCx, mScript); + return NS_OK; +} + +NS_IMETHODIMP +jsdScript::GetMinExecutionTime(double *_rval) +{ + ASSERT_VALID_EPHEMERAL; + *_rval = JSD_GetScriptMinExecutionTime (mCx, mScript); + return NS_OK; +} + +NS_IMETHODIMP +jsdScript::GetMaxExecutionTime(double *_rval) +{ + ASSERT_VALID_EPHEMERAL; + *_rval = JSD_GetScriptMaxExecutionTime (mCx, mScript); + return NS_OK; +} + +NS_IMETHODIMP +jsdScript::GetTotalExecutionTime(double *_rval) +{ + ASSERT_VALID_EPHEMERAL; + *_rval = JSD_GetScriptTotalExecutionTime (mCx, mScript); + return NS_OK; +} + +NS_IMETHODIMP +jsdScript::GetMinOwnExecutionTime(double *_rval) +{ + ASSERT_VALID_EPHEMERAL; + *_rval = JSD_GetScriptMinOwnExecutionTime (mCx, mScript); + return NS_OK; +} + +NS_IMETHODIMP +jsdScript::GetMaxOwnExecutionTime(double *_rval) +{ + ASSERT_VALID_EPHEMERAL; + *_rval = JSD_GetScriptMaxOwnExecutionTime (mCx, mScript); + return NS_OK; +} + +NS_IMETHODIMP +jsdScript::GetTotalOwnExecutionTime(double *_rval) +{ + ASSERT_VALID_EPHEMERAL; + *_rval = JSD_GetScriptTotalOwnExecutionTime (mCx, mScript); + return NS_OK; +} + +NS_IMETHODIMP +jsdScript::ClearProfileData() +{ + ASSERT_VALID_EPHEMERAL; + JSD_ClearScriptProfileData(mCx, mScript); + return NS_OK; +} + +NS_IMETHODIMP +jsdScript::PcToLine(uint32_t aPC, uint32_t aPcmap, uint32_t *_rval) +{ + ASSERT_VALID_EPHEMERAL; + if (aPcmap == PCMAP_SOURCETEXT) { + *_rval = JSD_GetClosestLine (mCx, mScript, mFirstPC + aPC); + } else if (aPcmap == PCMAP_PRETTYPRINT) { + *_rval = PPPcToLine(aPC); + } else { + return NS_ERROR_INVALID_ARG; + } + + return NS_OK; +} + +NS_IMETHODIMP +jsdScript::LineToPc(uint32_t aLine, uint32_t aPcmap, uint32_t *_rval) +{ + ASSERT_VALID_EPHEMERAL; + if (aPcmap == PCMAP_SOURCETEXT) { + uintptr_t pc = JSD_GetClosestPC (mCx, mScript, aLine); + *_rval = pc - mFirstPC; + } else if (aPcmap == PCMAP_PRETTYPRINT) { + *_rval = PPLineToPc(aLine); + } else { + return NS_ERROR_INVALID_ARG; + } + + return NS_OK; +} + +NS_IMETHODIMP +jsdScript::EnableSingleStepInterrupts(bool enable) +{ + ASSERT_VALID_EPHEMERAL; + + /* Must have set interrupt hook before enabling */ + if (enable && !jsdService::GetService()->CheckInterruptHook()) + return NS_ERROR_NOT_INITIALIZED; + + return (JSD_EnableSingleStepInterrupts(mCx, mScript, enable) ? NS_OK : NS_ERROR_FAILURE); +} + +NS_IMETHODIMP +jsdScript::GetExecutableLines(uint32_t aPcmap, uint32_t aStartLine, uint32_t aMaxLines, + uint32_t* aCount, uint32_t** aExecutableLines) +{ + ASSERT_VALID_EPHEMERAL; + if (aPcmap == PCMAP_SOURCETEXT) { + uintptr_t start = JSD_GetClosestPC(mCx, mScript, 0); + unsigned lastLine = JSD_GetScriptBaseLineNumber(mCx, mScript) + + JSD_GetScriptLineExtent(mCx, mScript) - 1; + uintptr_t end = JSD_GetClosestPC(mCx, mScript, lastLine + 1); + + *aExecutableLines = static_cast<uint32_t*>(NS_Alloc((end - start + 1) * sizeof(uint32_t))); + if (!JSD_GetLinePCs(mCx, mScript, aStartLine, aMaxLines, aCount, aExecutableLines, NULL)) + return NS_ERROR_OUT_OF_MEMORY; + + return NS_OK; + } + + if (aPcmap == PCMAP_PRETTYPRINT) { + if (!mPPLineMap) { + if (!CreatePPLineMap()) + return NS_ERROR_OUT_OF_MEMORY; + } + + nsTArray<uint32_t> lines; + uint32_t i; + + for (i = 0; i < mPCMapSize; ++i) { + if (mPPLineMap[i].line >= aStartLine) + break; + } + + for (; i < mPCMapSize && lines.Length() < aMaxLines; ++i) { + lines.AppendElement(mPPLineMap[i].line); + } + + if (aCount) + *aCount = lines.Length(); + + *aExecutableLines = static_cast<uint32_t*>(NS_Alloc(lines.Length() * sizeof(uint32_t))); + if (!*aExecutableLines) + return NS_ERROR_OUT_OF_MEMORY; + + for (i = 0; i < lines.Length(); ++i) + (*aExecutableLines)[i] = lines[i]; + + return NS_OK; + } + + return NS_ERROR_INVALID_ARG; +} + +NS_IMETHODIMP +jsdScript::IsLineExecutable(uint32_t aLine, uint32_t aPcmap, bool *_rval) +{ + ASSERT_VALID_EPHEMERAL; + if (aPcmap == PCMAP_SOURCETEXT) { + uintptr_t pc = JSD_GetClosestPC (mCx, mScript, aLine); + *_rval = (aLine == JSD_GetClosestLine (mCx, mScript, pc)); + } else if (aPcmap == PCMAP_PRETTYPRINT) { + if (!mPPLineMap && !CreatePPLineMap()) + return NS_ERROR_OUT_OF_MEMORY; + *_rval = false; + for (uint32_t i = 0; i < mPCMapSize; ++i) { + if (mPPLineMap[i].line >= aLine) { + *_rval = (mPPLineMap[i].line == aLine); + break; + } + } + } else { + return NS_ERROR_INVALID_ARG; + } + + return NS_OK; +} + +NS_IMETHODIMP +jsdScript::SetBreakpoint(uint32_t aPC) +{ + ASSERT_VALID_EPHEMERAL; + uintptr_t pc = mFirstPC + aPC; + JSD_SetExecutionHook (mCx, mScript, pc, jsds_ExecutionHookProc, NULL); + return NS_OK; +} + +NS_IMETHODIMP +jsdScript::ClearBreakpoint(uint32_t aPC) +{ + ASSERT_VALID_EPHEMERAL; + uintptr_t pc = mFirstPC + aPC; + JSD_ClearExecutionHook (mCx, mScript, pc); + return NS_OK; +} + +NS_IMETHODIMP +jsdScript::ClearAllBreakpoints() +{ + ASSERT_VALID_EPHEMERAL; + JSD_LockScriptSubsystem(mCx); + JSD_ClearAllExecutionHooksForScript (mCx, mScript); + JSD_UnlockScriptSubsystem(mCx); + return NS_OK; +} + +/* Contexts */ +NS_IMPL_THREADSAFE_ISUPPORTS2(jsdContext, jsdIContext, jsdIEphemeral) + +jsdIContext * +jsdContext::FromPtr (JSDContext *aJSDCx, JSContext *aJSCx) +{ + if (!aJSDCx || !aJSCx) + return nullptr; + + nsCOMPtr<jsdIContext> jsdicx; + nsCOMPtr<jsdIEphemeral> eph = + jsds_FindEphemeral (&gLiveContexts, static_cast<void *>(aJSCx)); + if (eph) + { + jsdicx = do_QueryInterface(eph); + } + else + { + nsCOMPtr<nsISupports> iscx; + if (JS_GetOptions(aJSCx) & JSOPTION_PRIVATE_IS_NSISUPPORTS) + iscx = static_cast<nsISupports *>(JS_GetContextPrivate(aJSCx)); + jsdicx = new jsdContext (aJSDCx, aJSCx, iscx); + } + + jsdIContext *ctx = nullptr; + jsdicx.swap(ctx); + return ctx; +} + +jsdContext::jsdContext (JSDContext *aJSDCx, JSContext *aJSCx, + nsISupports *aISCx) : mValid(true), mTag(0), + mJSDCx(aJSDCx), + mJSCx(aJSCx), mISCx(aISCx) +{ + DEBUG_CREATE ("jsdContext", gContextCount); + mLiveListEntry.value = this; + mLiveListEntry.key = static_cast<void *>(aJSCx); + jsds_InsertEphemeral (&gLiveContexts, &mLiveListEntry); +} + +jsdContext::~jsdContext() +{ + DEBUG_DESTROY ("jsdContext", gContextCount); + if (mValid) + { + /* call Invalidate() to take ourselves out of the live list */ + Invalidate(); + } +} + +NS_IMETHODIMP +jsdContext::GetIsValid(bool *_rval) +{ + *_rval = mValid; + return NS_OK; +} + +NS_IMETHODIMP +jsdContext::Invalidate() +{ + ASSERT_VALID_EPHEMERAL; + mValid = false; + jsds_RemoveEphemeral (&gLiveContexts, &mLiveListEntry); + return NS_OK; +} + +void +jsdContext::InvalidateAll() +{ + if (gLiveContexts) + jsds_InvalidateAllEphemerals (&gLiveContexts); +} + +NS_IMETHODIMP +jsdContext::GetJSContext(JSContext **_rval) +{ + ASSERT_VALID_EPHEMERAL; + *_rval = mJSCx; + return NS_OK; +} + +NS_IMETHODIMP +jsdContext::GetOptions(uint32_t *_rval) +{ + ASSERT_VALID_EPHEMERAL; + *_rval = JS_GetOptions(mJSCx); + return NS_OK; +} + +NS_IMETHODIMP +jsdContext::SetOptions(uint32_t options) +{ + ASSERT_VALID_EPHEMERAL; + uint32_t lastOptions = JS_GetOptions(mJSCx); + + /* don't let users change this option, they'd just be shooting themselves + * in the foot. */ + if ((options ^ lastOptions) & JSOPTION_PRIVATE_IS_NSISUPPORTS) + return NS_ERROR_ILLEGAL_VALUE; + + JS_SetOptions(mJSCx, options); + return NS_OK; +} + +NS_IMETHODIMP +jsdContext::GetPrivateData(nsISupports **_rval) +{ + ASSERT_VALID_EPHEMERAL; + uint32_t options = JS_GetOptions(mJSCx); + if (options & JSOPTION_PRIVATE_IS_NSISUPPORTS) + { + *_rval = static_cast<nsISupports*>(JS_GetContextPrivate(mJSCx)); + NS_IF_ADDREF(*_rval); + } + else + { + *_rval = nullptr; + } + + return NS_OK; +} + +NS_IMETHODIMP +jsdContext::GetWrappedContext(nsISupports **_rval) +{ + ASSERT_VALID_EPHEMERAL; + NS_IF_ADDREF(*_rval = mISCx); + return NS_OK; +} + +NS_IMETHODIMP +jsdContext::GetTag(uint32_t *_rval) +{ + ASSERT_VALID_EPHEMERAL; + if (!mTag) + mTag = ++jsdContext::LastTag; + + *_rval = mTag; + return NS_OK; +} + +NS_IMETHODIMP +jsdContext::GetGlobalObject (jsdIValue **_rval) +{ + ASSERT_VALID_EPHEMERAL; + JSObject *glob = js::GetDefaultGlobalForContext(mJSCx); + JSDValue *jsdv = JSD_NewValue (mJSDCx, OBJECT_TO_JSVAL(glob)); + if (!jsdv) + return NS_ERROR_FAILURE; + *_rval = jsdValue::FromPtr (mJSDCx, jsdv); + if (!*_rval) + return NS_ERROR_FAILURE; + return NS_OK; +} + +NS_IMETHODIMP +jsdContext::GetScriptsEnabled (bool *_rval) +{ + ASSERT_VALID_EPHEMERAL; + if (!mISCx) { + *_rval = true; + return NS_OK; + } + + nsCOMPtr<nsIScriptContext> context = do_QueryInterface(mISCx); + if (!context) + return NS_ERROR_NO_INTERFACE; + + *_rval = context->GetScriptsEnabled(); + + return NS_OK; +} + +NS_IMETHODIMP +jsdContext::SetScriptsEnabled (bool _rval) +{ + ASSERT_VALID_EPHEMERAL; + if (!mISCx) { + if (_rval) + return NS_OK; + return NS_ERROR_NO_INTERFACE; + } + + nsCOMPtr<nsIScriptContext> context = do_QueryInterface(mISCx); + if (!context) + return NS_ERROR_NO_INTERFACE; + + context->SetScriptsEnabled(_rval, true); + + return NS_OK; +} + +/* Stack Frames */ +NS_IMPL_THREADSAFE_ISUPPORTS2(jsdStackFrame, jsdIStackFrame, jsdIEphemeral) + +jsdStackFrame::jsdStackFrame (JSDContext *aCx, JSDThreadState *aThreadState, + JSDStackFrameInfo *aStackFrameInfo) : + mCx(aCx), mThreadState(aThreadState), mStackFrameInfo(aStackFrameInfo) +{ + DEBUG_CREATE ("jsdStackFrame", gFrameCount); + mValid = (aCx && aThreadState && aStackFrameInfo); + if (mValid) { + mLiveListEntry.key = aStackFrameInfo; + mLiveListEntry.value = this; + jsds_InsertEphemeral (&gLiveStackFrames, &mLiveListEntry); + } +} + +jsdStackFrame::~jsdStackFrame() +{ + DEBUG_DESTROY ("jsdStackFrame", gFrameCount); + if (mValid) + { + /* call Invalidate() to take ourselves out of the live list */ + Invalidate(); + } +} + +jsdIStackFrame * +jsdStackFrame::FromPtr (JSDContext *aCx, JSDThreadState *aThreadState, + JSDStackFrameInfo *aStackFrameInfo) +{ + if (!aStackFrameInfo) + return nullptr; + + jsdIStackFrame *rv; + nsCOMPtr<jsdIStackFrame> frame; + + nsCOMPtr<jsdIEphemeral> eph = + jsds_FindEphemeral (&gLiveStackFrames, + reinterpret_cast<void *>(aStackFrameInfo)); + + if (eph) + { + frame = do_QueryInterface(eph); + rv = frame; + } + else + { + rv = new jsdStackFrame (aCx, aThreadState, aStackFrameInfo); + } + + NS_IF_ADDREF(rv); + return rv; +} + +NS_IMETHODIMP +jsdStackFrame::Invalidate() +{ + ASSERT_VALID_EPHEMERAL; + mValid = false; + jsds_RemoveEphemeral (&gLiveStackFrames, &mLiveListEntry); + return NS_OK; +} + +void +jsdStackFrame::InvalidateAll() +{ + if (gLiveStackFrames) + jsds_InvalidateAllEphemerals (&gLiveStackFrames); +} + +NS_IMETHODIMP +jsdStackFrame::GetJSDContext(JSDContext **_rval) +{ + ASSERT_VALID_EPHEMERAL; + *_rval = mCx; + return NS_OK; +} + +NS_IMETHODIMP +jsdStackFrame::GetJSDThreadState(JSDThreadState **_rval) +{ + ASSERT_VALID_EPHEMERAL; + *_rval = mThreadState; + return NS_OK; +} + +NS_IMETHODIMP +jsdStackFrame::GetJSDStackFrameInfo(JSDStackFrameInfo **_rval) +{ + ASSERT_VALID_EPHEMERAL; + *_rval = mStackFrameInfo; + return NS_OK; +} + +NS_IMETHODIMP +jsdStackFrame::GetIsValid(bool *_rval) +{ + *_rval = mValid; + return NS_OK; +} + +NS_IMETHODIMP +jsdStackFrame::GetCallingFrame(jsdIStackFrame **_rval) +{ + ASSERT_VALID_EPHEMERAL; + JSDStackFrameInfo *sfi = JSD_GetCallingStackFrame (mCx, mThreadState, + mStackFrameInfo); + *_rval = jsdStackFrame::FromPtr (mCx, mThreadState, sfi); + return NS_OK; +} + +NS_IMETHODIMP +jsdStackFrame::GetExecutionContext(jsdIContext **_rval) +{ + ASSERT_VALID_EPHEMERAL; + JSContext *cx = JSD_GetJSContext (mCx, mThreadState); + *_rval = jsdContext::FromPtr (mCx, cx); + return NS_OK; +} + +NS_IMETHODIMP +jsdStackFrame::GetFunctionName(nsACString &_rval) +{ + ASSERT_VALID_EPHEMERAL; + JSString *str = JSD_GetIdForStackFrame(mCx, mThreadState, mStackFrameInfo); + if (str) + return AssignToJSString(mCx, &_rval, str); + + _rval.Assign("anonymous"); + return NS_OK; +} + +NS_IMETHODIMP +jsdStackFrame::GetIsDebugger(bool *_rval) +{ + ASSERT_VALID_EPHEMERAL; + *_rval = JSD_IsStackFrameDebugger (mCx, mThreadState, mStackFrameInfo); + return NS_OK; +} + +NS_IMETHODIMP +jsdStackFrame::GetIsConstructing(bool *_rval) +{ + ASSERT_VALID_EPHEMERAL; + *_rval = JSD_IsStackFrameConstructing (mCx, mThreadState, mStackFrameInfo); + return NS_OK; +} + +NS_IMETHODIMP +jsdStackFrame::GetScript(jsdIScript **_rval) +{ + ASSERT_VALID_EPHEMERAL; + JSDScript *script = JSD_GetScriptForStackFrame (mCx, mThreadState, + mStackFrameInfo); + *_rval = jsdScript::FromPtr (mCx, script); + return NS_OK; +} + +NS_IMETHODIMP +jsdStackFrame::GetPc(uint32_t *_rval) +{ + ASSERT_VALID_EPHEMERAL; + JSDScript *script = JSD_GetScriptForStackFrame (mCx, mThreadState, + mStackFrameInfo); + if (!script) + return NS_ERROR_FAILURE; + uintptr_t pcbase = JSD_GetClosestPC(mCx, script, 0); + + uintptr_t pc = JSD_GetPCForStackFrame (mCx, mThreadState, mStackFrameInfo); + if (pc) + *_rval = pc - pcbase; + else + *_rval = pcbase; + return NS_OK; +} + +NS_IMETHODIMP +jsdStackFrame::GetLine(uint32_t *_rval) +{ + ASSERT_VALID_EPHEMERAL; + JSDScript *script = JSD_GetScriptForStackFrame (mCx, mThreadState, + mStackFrameInfo); + if (script) { + uintptr_t pc = JSD_GetPCForStackFrame (mCx, mThreadState, mStackFrameInfo); + *_rval = JSD_GetClosestLine (mCx, script, pc); + } else { + return NS_ERROR_FAILURE; + } + return NS_OK; +} + +NS_IMETHODIMP +jsdStackFrame::GetCallee(jsdIValue **_rval) +{ + ASSERT_VALID_EPHEMERAL; + JSDValue *jsdv = JSD_GetCallObjectForStackFrame (mCx, mThreadState, + mStackFrameInfo); + + *_rval = jsdValue::FromPtr (mCx, jsdv); + return NS_OK; +} + +NS_IMETHODIMP +jsdStackFrame::GetScope(jsdIValue **_rval) +{ + ASSERT_VALID_EPHEMERAL; + JSDValue *jsdv = JSD_GetScopeChainForStackFrame (mCx, mThreadState, + mStackFrameInfo); + + *_rval = jsdValue::FromPtr (mCx, jsdv); + return NS_OK; +} + +NS_IMETHODIMP +jsdStackFrame::GetThisValue(jsdIValue **_rval) +{ + ASSERT_VALID_EPHEMERAL; + JSDValue *jsdv = JSD_GetThisForStackFrame (mCx, mThreadState, + mStackFrameInfo); + + *_rval = jsdValue::FromPtr (mCx, jsdv); + return NS_OK; +} + + +NS_IMETHODIMP +jsdStackFrame::Eval (const nsAString &bytes, const nsACString &fileName, + uint32_t line, jsdIValue **result, bool *_rval) +{ + ASSERT_VALID_EPHEMERAL; + + if (bytes.IsEmpty()) + return NS_ERROR_INVALID_ARG; + + // get pointer to buffer contained in |bytes| + nsAString::const_iterator h; + bytes.BeginReading(h); + const jschar *char_bytes = reinterpret_cast<const jschar *>(h.get()); + + JSExceptionState *estate = 0; + + AutoPushJSContext cx(JSD_GetJSContext (mCx, mThreadState)); + + JS::RootedValue jv(cx); + + estate = JS_SaveExceptionState (cx); + JS_ClearPendingException (cx); + + *_rval = JSD_AttemptUCScriptInStackFrame (mCx, mThreadState, + mStackFrameInfo, + char_bytes, bytes.Length(), + PromiseFlatCString(fileName).get(), + line, &jv); + if (!*_rval) { + if (JS_IsExceptionPending(cx)) + JS_GetPendingException (cx, jv.address()); + else + jv = JSVAL_NULL; + } + + JS_RestoreExceptionState (cx, estate); + + JSDValue *jsdv = JSD_NewValue (mCx, jv); + if (!jsdv) + return NS_ERROR_FAILURE; + *result = jsdValue::FromPtr (mCx, jsdv); + if (!*result) + return NS_ERROR_FAILURE; + + return NS_OK; +} + +/* Values */ +NS_IMPL_THREADSAFE_ISUPPORTS2(jsdValue, jsdIValue, jsdIEphemeral) +jsdIValue * +jsdValue::FromPtr (JSDContext *aCx, JSDValue *aValue) +{ + /* value will be dropped by te jsdValue destructor. */ + + if (!aValue) + return nullptr; + + jsdIValue *rv = new jsdValue (aCx, aValue); + NS_IF_ADDREF(rv); + return rv; +} + +jsdValue::jsdValue (JSDContext *aCx, JSDValue *aValue) : mValid(true), + mCx(aCx), + mValue(aValue) +{ + DEBUG_CREATE ("jsdValue", gValueCount); + mLiveListEntry.value = this; + jsds_InsertEphemeral (&gLiveValues, &mLiveListEntry); +} + +jsdValue::~jsdValue() +{ + DEBUG_DESTROY ("jsdValue", gValueCount); + if (mValid) + /* call Invalidate() to take ourselves out of the live list */ + Invalidate(); +} + +NS_IMETHODIMP +jsdValue::GetIsValid(bool *_rval) +{ + *_rval = mValid; + return NS_OK; +} + +NS_IMETHODIMP +jsdValue::Invalidate() +{ + ASSERT_VALID_EPHEMERAL; + mValid = false; + jsds_RemoveEphemeral (&gLiveValues, &mLiveListEntry); + JSD_DropValue (mCx, mValue); + return NS_OK; +} + +void +jsdValue::InvalidateAll() +{ + if (gLiveValues) + jsds_InvalidateAllEphemerals (&gLiveValues); +} + +NS_IMETHODIMP +jsdValue::GetJSDContext(JSDContext **_rval) +{ + ASSERT_VALID_EPHEMERAL; + *_rval = mCx; + return NS_OK; +} + +NS_IMETHODIMP +jsdValue::GetJSDValue (JSDValue **_rval) +{ + ASSERT_VALID_EPHEMERAL; + *_rval = mValue; + return NS_OK; +} + +NS_IMETHODIMP +jsdValue::GetIsNative (bool *_rval) +{ + ASSERT_VALID_EPHEMERAL; + *_rval = JSD_IsValueNative (mCx, mValue); + return NS_OK; +} + +NS_IMETHODIMP +jsdValue::GetIsNumber (bool *_rval) +{ + ASSERT_VALID_EPHEMERAL; + *_rval = JSD_IsValueNumber (mCx, mValue); + return NS_OK; +} + +NS_IMETHODIMP +jsdValue::GetIsPrimitive (bool *_rval) +{ + ASSERT_VALID_EPHEMERAL; + *_rval = JSD_IsValuePrimitive (mCx, mValue); + return NS_OK; +} + +NS_IMETHODIMP +jsdValue::GetJsType (uint32_t *_rval) +{ + ASSERT_VALID_EPHEMERAL; + jsval val; + + val = JSD_GetValueWrappedJSVal (mCx, mValue); + + if (JSVAL_IS_NULL(val)) + *_rval = TYPE_NULL; + else if (JSVAL_IS_BOOLEAN(val)) + *_rval = TYPE_BOOLEAN; + else if (JSVAL_IS_DOUBLE(val)) + *_rval = TYPE_DOUBLE; + else if (JSVAL_IS_INT(val)) + *_rval = TYPE_INT; + else if (JSVAL_IS_STRING(val)) + *_rval = TYPE_STRING; + else if (JSVAL_IS_VOID(val)) + *_rval = TYPE_VOID; + else if (JSD_IsValueFunction (mCx, mValue)) + *_rval = TYPE_FUNCTION; + else if (!JSVAL_IS_PRIMITIVE(val)) + *_rval = TYPE_OBJECT; + else + NS_ASSERTION (0, "Value has no discernible type."); + + return NS_OK; +} + +NS_IMETHODIMP +jsdValue::GetJsPrototype (jsdIValue **_rval) +{ + ASSERT_VALID_EPHEMERAL; + JSDValue *jsdv = JSD_GetValuePrototype (mCx, mValue); + *_rval = jsdValue::FromPtr (mCx, jsdv); + return NS_OK; +} + +NS_IMETHODIMP +jsdValue::GetJsParent (jsdIValue **_rval) +{ + ASSERT_VALID_EPHEMERAL; + JSDValue *jsdv = JSD_GetValueParent (mCx, mValue); + *_rval = jsdValue::FromPtr (mCx, jsdv); + return NS_OK; +} + +NS_IMETHODIMP +jsdValue::GetJsClassName(nsACString &_rval) +{ + ASSERT_VALID_EPHEMERAL; + _rval.Assign(JSD_GetValueClassName(mCx, mValue)); + + return NS_OK; +} + +NS_IMETHODIMP +jsdValue::GetJsConstructor (jsdIValue **_rval) +{ + ASSERT_VALID_EPHEMERAL; + JSDValue *jsdv = JSD_GetValueConstructor (mCx, mValue); + *_rval = jsdValue::FromPtr (mCx, jsdv); + return NS_OK; +} + +NS_IMETHODIMP +jsdValue::GetJsFunctionName(nsACString &_rval) +{ + ASSERT_VALID_EPHEMERAL; + return AssignToJSString(mCx, &_rval, JSD_GetValueFunctionId(mCx, mValue)); +} + +NS_IMETHODIMP +jsdValue::GetBooleanValue(bool *_rval) +{ + ASSERT_VALID_EPHEMERAL; + *_rval = JSD_GetValueBoolean (mCx, mValue); + return NS_OK; +} + +NS_IMETHODIMP +jsdValue::GetDoubleValue(double *_rval) +{ + ASSERT_VALID_EPHEMERAL; + *_rval = JSD_GetValueDouble (mCx, mValue); + return NS_OK; +} + +NS_IMETHODIMP +jsdValue::GetIntValue(int32_t *_rval) +{ + ASSERT_VALID_EPHEMERAL; + *_rval = JSD_GetValueInt (mCx, mValue); + return NS_OK; +} + +NS_IMETHODIMP +jsdValue::GetObjectValue(jsdIObject **_rval) +{ + ASSERT_VALID_EPHEMERAL; + JSDObject *obj; + obj = JSD_GetObjectForValue (mCx, mValue); + *_rval = jsdObject::FromPtr (mCx, obj); + if (!*_rval) + return NS_ERROR_FAILURE; + return NS_OK; +} + +NS_IMETHODIMP +jsdValue::GetStringValue(nsACString &_rval) +{ + ASSERT_VALID_EPHEMERAL; + AutoSafeJSContext cx; + JSString *jstr_val = JSD_GetValueString(mCx, mValue); + if (jstr_val) { + size_t length; + const jschar *chars = JS_GetStringCharsZAndLength(cx, jstr_val, &length); + if (!chars) + return NS_ERROR_FAILURE; + nsDependentString depStr(chars, length); + CopyUTF16toUTF8(depStr, _rval); + } else { + _rval.Truncate(); + } + return NS_OK; +} + +NS_IMETHODIMP +jsdValue::GetPropertyCount (int32_t *_rval) +{ + ASSERT_VALID_EPHEMERAL; + if (JSD_IsValueObject(mCx, mValue)) + *_rval = JSD_GetCountOfProperties (mCx, mValue); + else + *_rval = -1; + return NS_OK; +} + +NS_IMETHODIMP +jsdValue::GetProperties (jsdIProperty ***propArray, uint32_t *length) +{ + ASSERT_VALID_EPHEMERAL; + *propArray = nullptr; + if (length) + *length = 0; + + uint32_t prop_count = JSD_IsValueObject(mCx, mValue) + ? JSD_GetCountOfProperties (mCx, mValue) + : 0; + NS_ENSURE_TRUE(prop_count, NS_OK); + + jsdIProperty **pa_temp = + static_cast<jsdIProperty **> + (nsMemory::Alloc(sizeof (jsdIProperty *) * + prop_count)); + NS_ENSURE_TRUE(pa_temp, NS_ERROR_OUT_OF_MEMORY); + + uint32_t i = 0; + JSDProperty *iter = NULL; + JSDProperty *prop; + while ((prop = JSD_IterateProperties (mCx, mValue, &iter))) { + pa_temp[i] = jsdProperty::FromPtr (mCx, prop); + ++i; + } + + NS_ASSERTION (prop_count == i, "property count mismatch"); + + /* if caller doesn't care about length, don't bother telling them */ + *propArray = pa_temp; + if (length) + *length = prop_count; + + return NS_OK; +} + +NS_IMETHODIMP +jsdValue::GetProperty (const nsACString &name, jsdIProperty **_rval) +{ + ASSERT_VALID_EPHEMERAL; + AutoSafeJSContext cx; + JSAutoCompartment ac(cx, JSD_GetDefaultGlobal (mCx)); // Just in case. + + /* not rooting this */ + JSString *jstr_name = JS_NewStringCopyZ(cx, PromiseFlatCString(name).get()); + if (!jstr_name) + return NS_ERROR_OUT_OF_MEMORY; + + JSDProperty *prop = JSD_GetValueProperty (mCx, mValue, jstr_name); + + *_rval = jsdProperty::FromPtr (mCx, prop); + return NS_OK; +} + +NS_IMETHODIMP +jsdValue::Refresh() +{ + ASSERT_VALID_EPHEMERAL; + JSD_RefreshValue (mCx, mValue); + return NS_OK; +} + +NS_IMETHODIMP +jsdValue::GetWrappedValue(JSContext* aCx, JS::Value* aRetval) +{ + ASSERT_VALID_EPHEMERAL; + + *aRetval = JSD_GetValueWrappedJSVal(mCx, mValue); + if (!JS_WrapValue(aCx, aRetval)) { + return NS_ERROR_FAILURE; + } + + return NS_OK; +} + +NS_IMETHODIMP +jsdValue::GetScript(jsdIScript **_rval) +{ + ASSERT_VALID_EPHEMERAL; + JSDScript *script = JSD_GetScriptForValue(mCx, mValue); + *_rval = jsdScript::FromPtr(mCx, script); + return NS_OK; +} + +/****************************************************************************** + * debugger service implementation + ******************************************************************************/ + +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(jsdService) + NS_INTERFACE_MAP_ENTRY(jsdIDebuggerService) + NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, jsdIDebuggerService) +NS_INTERFACE_MAP_END + +NS_IMPL_CYCLE_COLLECTION_10(jsdService, + mErrorHook, mBreakpointHook, mDebugHook, + mDebuggerHook, mInterruptHook, mScriptHook, + mThrowHook, mTopLevelHook, mFunctionHook, + mActivationCallback) +NS_IMPL_CYCLE_COLLECTING_ADDREF(jsdService) +NS_IMPL_CYCLE_COLLECTING_RELEASE(jsdService) + +NS_IMETHODIMP +jsdService::GetJSDContext(JSDContext **_rval) +{ + *_rval = mCx; + return NS_OK; +} + +NS_IMETHODIMP +jsdService::GetFlags (uint32_t *_rval) +{ + ASSERT_VALID_CONTEXT; + *_rval = JSD_GetContextFlags (mCx); + return NS_OK; +} + +NS_IMETHODIMP +jsdService::SetFlags (uint32_t flags) +{ + ASSERT_VALID_CONTEXT; + JSD_SetContextFlags (mCx, flags); + return NS_OK; +} + +NS_IMETHODIMP +jsdService::GetImplementationString(nsACString &aImplementationString) +{ + aImplementationString.AssignLiteral(implementationString); + return NS_OK; +} + +NS_IMETHODIMP +jsdService::GetImplementationMajor(uint32_t *_rval) +{ + *_rval = JSDS_MAJOR_VERSION; + return NS_OK; +} + +NS_IMETHODIMP +jsdService::GetImplementationMinor(uint32_t *_rval) +{ + *_rval = JSDS_MINOR_VERSION; + return NS_OK; +} + +NS_IMETHODIMP +jsdService::GetIsOn (bool *_rval) +{ + *_rval = mOn; + return NS_OK; +} + +NS_IMETHODIMP +jsdService::On (void) +{ + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP +jsdService::AsyncOn (jsdIActivationCallback *activationCallback) +{ + nsresult rv; + + nsCOMPtr<nsIXPConnect> xpc = do_GetService(nsIXPConnect::GetCID(), &rv); + if (NS_FAILED(rv)) return rv; + + mActivationCallback = activationCallback; + + return xpc->SetDebugModeWhenPossible(true, true); +} + +NS_IMETHODIMP +jsdService::RecompileForDebugMode (JSContext *cx, JSCompartment *comp, bool mode) { + NS_ASSERTION(NS_IsMainThread(), "wrong thread"); + /* XPConnect now does this work itself, so this IDL entry point is no longer used. */ + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP +jsdService::DeactivateDebugger () +{ + if (!mCx) + return NS_OK; + + jsdContext::InvalidateAll(); + jsdScript::InvalidateAll(); + jsdValue::InvalidateAll(); + jsdProperty::InvalidateAll(); + jsdStackFrame::InvalidateAll(); + ClearAllBreakpoints(); + + JSD_SetErrorReporter (mCx, NULL, NULL); + JSD_SetScriptHook (mCx, NULL, NULL); + JSD_ClearThrowHook (mCx); + JSD_ClearInterruptHook (mCx); + JSD_ClearDebuggerHook (mCx); + JSD_ClearDebugBreakHook (mCx); + JSD_ClearTopLevelHook (mCx); + JSD_ClearFunctionHook (mCx); + + JSD_DebuggerOff (mCx); + + mCx = nullptr; + mRuntime = nullptr; + mOn = false; + + return NS_OK; +} + + +NS_IMETHODIMP +jsdService::ActivateDebugger (JSRuntime *rt) +{ + if (mOn) + return (rt == mRuntime) ? NS_OK : NS_ERROR_ALREADY_INITIALIZED; + + mRuntime = rt; + + if (gPrevGCSliceCallback == jsds_GCSliceCallbackProc) + /* condition indicates that the callback proc has not been set yet */ + gPrevGCSliceCallback = JS::SetGCSliceCallback (rt, jsds_GCSliceCallbackProc); + + mCx = JSD_DebuggerOnForUser (rt, NULL, NULL); + if (!mCx) + return NS_ERROR_FAILURE; + + AutoSafeJSContext cx; + JS::RootedObject glob(cx, JSD_GetDefaultGlobal (mCx)); + JSAutoCompartment ac(cx, glob); + + /* init xpconnect on the debugger's context in case xpconnect tries to + * use it for stuff. */ + nsresult rv; + nsCOMPtr<nsIXPConnect> xpc = do_GetService(nsIXPConnect::GetCID(), &rv); + if (NS_FAILED(rv)) + return rv; + + xpc->InitClasses (cx, glob); + + /* Start watching for script creation/destruction and manage jsdScript + * objects accordingly + */ + JSD_SetScriptHook (mCx, jsds_ScriptHookProc, NULL); + + /* If any of these mFooHook objects are installed, do the required JSD + * hookup now. See also, jsdService::SetFooHook(). + */ + if (mErrorHook) + JSD_SetErrorReporter (mCx, jsds_ErrorHookProc, NULL); + if (mThrowHook) + JSD_SetThrowHook (mCx, jsds_ExecutionHookProc, NULL); + /* can't ignore script callbacks, as we need to |Release| the wrapper + * stored in private data when a script is deleted. */ + if (mInterruptHook) + JSD_SetInterruptHook (mCx, jsds_ExecutionHookProc, NULL); + if (mDebuggerHook) + JSD_SetDebuggerHook (mCx, jsds_ExecutionHookProc, NULL); + if (mDebugHook) + JSD_SetDebugBreakHook (mCx, jsds_ExecutionHookProc, NULL); + if (mTopLevelHook) + JSD_SetTopLevelHook (mCx, jsds_CallHookProc, NULL); + else + JSD_ClearTopLevelHook (mCx); + if (mFunctionHook) + JSD_SetFunctionHook (mCx, jsds_CallHookProc, NULL); + else + JSD_ClearFunctionHook (mCx); + mOn = true; + +#ifdef DEBUG + printf ("+++ JavaScript debugging hooks installed.\n"); +#endif + + nsCOMPtr<jsdIActivationCallback> activationCallback; + mActivationCallback.swap(activationCallback); + if (activationCallback) + return activationCallback->OnDebuggerActivated(); + + return NS_OK; +} + +NS_IMETHODIMP +jsdService::Off (void) +{ + if (!mOn) + return NS_OK; + + if (!mCx || !mRuntime) + return NS_ERROR_NOT_INITIALIZED; + + if (gDeadScripts) { + if (gGCRunning) + return NS_ERROR_NOT_AVAILABLE; + + while (gDeadScripts) + jsds_NotifyPendingDeadScripts (JS_GetRuntime(nsContentUtils::GetSafeJSContext())); + } + + DeactivateDebugger(); + +#ifdef DEBUG + printf ("+++ JavaScript debugging hooks removed.\n"); +#endif + + nsresult rv; + nsCOMPtr<nsIXPConnect> xpc = do_GetService(nsIXPConnect::GetCID(), &rv); + if (NS_FAILED(rv)) + return rv; + + xpc->SetDebugModeWhenPossible(false, true); + + return NS_OK; +} + +NS_IMETHODIMP +jsdService::GetPauseDepth(uint32_t *_rval) +{ + NS_ENSURE_ARG_POINTER(_rval); + *_rval = mPauseLevel; + return NS_OK; +} + +NS_IMETHODIMP +jsdService::Pause(uint32_t *_rval) +{ + return DoPause(_rval, false); +} + +nsresult +jsdService::DoPause(uint32_t *_rval, bool internalCall) +{ + if (!mCx) + return NS_ERROR_NOT_INITIALIZED; + + if (++mPauseLevel == 1) { + JSD_SetErrorReporter (mCx, NULL, NULL); + JSD_ClearThrowHook (mCx); + JSD_ClearInterruptHook (mCx); + JSD_ClearDebuggerHook (mCx); + JSD_ClearDebugBreakHook (mCx); + JSD_ClearTopLevelHook (mCx); + JSD_ClearFunctionHook (mCx); + JSD_DebuggerPause (mCx); + + nsresult rv; + nsCOMPtr<nsIXPConnect> xpc = do_GetService(nsIXPConnect::GetCID(), &rv); + if (NS_FAILED(rv)) return rv; + + if (!internalCall) { + rv = xpc->SetDebugModeWhenPossible(false, false); + NS_ENSURE_SUCCESS(rv, rv); + } + } + + if (_rval) + *_rval = mPauseLevel; + + return NS_OK; +} + +NS_IMETHODIMP +jsdService::UnPause(uint32_t *_rval) +{ + return DoUnPause(_rval, false); +} + +nsresult +jsdService::DoUnPause(uint32_t *_rval, bool internalCall) +{ + if (!mCx) + return NS_ERROR_NOT_INITIALIZED; + + if (mPauseLevel == 0) + return NS_ERROR_NOT_AVAILABLE; + + /* check mOn before we muck with this stuff, it's possible the debugger + * was turned off while we were paused. + */ + if (--mPauseLevel == 0 && mOn) { + JSD_DebuggerUnpause (mCx); + if (mErrorHook) + JSD_SetErrorReporter (mCx, jsds_ErrorHookProc, NULL); + if (mThrowHook) + JSD_SetThrowHook (mCx, jsds_ExecutionHookProc, NULL); + if (mInterruptHook) + JSD_SetInterruptHook (mCx, jsds_ExecutionHookProc, NULL); + if (mDebuggerHook) + JSD_SetDebuggerHook (mCx, jsds_ExecutionHookProc, NULL); + if (mDebugHook) + JSD_SetDebugBreakHook (mCx, jsds_ExecutionHookProc, NULL); + if (mTopLevelHook) + JSD_SetTopLevelHook (mCx, jsds_CallHookProc, NULL); + else + JSD_ClearTopLevelHook (mCx); + if (mFunctionHook) + JSD_SetFunctionHook (mCx, jsds_CallHookProc, NULL); + else + JSD_ClearFunctionHook (mCx); + + nsresult rv; + nsCOMPtr<nsIXPConnect> xpc = do_GetService(nsIXPConnect::GetCID(), &rv); + if (NS_FAILED(rv)) return rv; + + if (!internalCall) { + rv = xpc->SetDebugModeWhenPossible(true, false); + NS_ENSURE_SUCCESS(rv, rv); + } + } + + if (_rval) + *_rval = mPauseLevel; + + return NS_OK; +} + +NS_IMETHODIMP +jsdService::EnumerateContexts (jsdIContextEnumerator *enumerator) +{ + ASSERT_VALID_CONTEXT; + + if (!enumerator) + return NS_OK; + + JSContext *iter = NULL; + JSContext *cx; + + while ((cx = JS_ContextIterator (mRuntime, &iter))) + { + nsCOMPtr<jsdIContext> jsdicx = + getter_AddRefs(jsdContext::FromPtr(mCx, cx)); + if (jsdicx) + { + if (NS_FAILED(enumerator->EnumerateContext(jsdicx))) + break; + } + } + + return NS_OK; +} + +NS_IMETHODIMP +jsdService::EnumerateScripts (jsdIScriptEnumerator *enumerator) +{ + ASSERT_VALID_CONTEXT; + + JSDScript *script; + JSDScript *iter = NULL; + nsresult rv = NS_OK; + + JSD_LockScriptSubsystem(mCx); + while((script = JSD_IterateScripts(mCx, &iter))) { + nsCOMPtr<jsdIScript> jsdis = + getter_AddRefs(jsdScript::FromPtr(mCx, script)); + rv = enumerator->EnumerateScript (jsdis); + if (NS_FAILED(rv)) + break; + } + JSD_UnlockScriptSubsystem(mCx); + + return rv; +} + +NS_IMETHODIMP +jsdService::GC (void) +{ + ASSERT_VALID_CONTEXT; + JSRuntime *rt = JSD_GetJSRuntime (mCx); + JS_GC(rt); + return NS_OK; +} + +NS_IMETHODIMP +jsdService::DumpHeap(const nsACString &fileName) +{ + ASSERT_VALID_CONTEXT; +#ifndef DEBUG + return NS_ERROR_NOT_IMPLEMENTED; +#else + nsresult rv = NS_OK; + FILE *file = !fileName.IsEmpty() ? fopen(PromiseFlatCString(fileName).get(), "w") : stdout; + if (!file) { + rv = NS_ERROR_FAILURE; + } else { + if (!JS_DumpHeap(JS_GetRuntime(nsContentUtils::GetSafeJSContext()), file, NULL, JSTRACE_OBJECT, NULL, (size_t)-1, NULL)) + rv = NS_ERROR_FAILURE; + if (file != stdout) + fclose(file); + } + return rv; +#endif +} + +NS_IMETHODIMP +jsdService::ClearProfileData () +{ + ASSERT_VALID_CONTEXT; + JSD_ClearAllProfileData (mCx); + return NS_OK; +} + +NS_IMETHODIMP +jsdService::InsertFilter (jsdIFilter *filter, jsdIFilter *after) +{ + NS_ENSURE_ARG_POINTER (filter); + if (jsds_FindFilter (filter)) + return NS_ERROR_INVALID_ARG; + + FilterRecord *rec = PR_NEWZAP (FilterRecord); + if (!rec) + return NS_ERROR_OUT_OF_MEMORY; + + if (!jsds_SyncFilter (rec, filter)) { + PR_Free (rec); + return NS_ERROR_FAILURE; + } + + if (gFilters) { + if (!after) { + /* insert at head of list */ + PR_INSERT_LINK(&rec->links, &gFilters->links); + gFilters = rec; + } else { + /* insert somewhere in the list */ + FilterRecord *afterRecord = jsds_FindFilter (after); + if (!afterRecord) { + jsds_FreeFilter(rec); + return NS_ERROR_INVALID_ARG; + } + PR_INSERT_AFTER(&rec->links, &afterRecord->links); + } + } else { + if (after) { + /* user asked to insert into the middle of an empty list, bail. */ + jsds_FreeFilter(rec); + return NS_ERROR_NOT_INITIALIZED; + } + PR_INIT_CLIST(&rec->links); + gFilters = rec; + } + + return NS_OK; +} + +NS_IMETHODIMP +jsdService::AppendFilter (jsdIFilter *filter) +{ + NS_ENSURE_ARG_POINTER (filter); + if (jsds_FindFilter (filter)) + return NS_ERROR_INVALID_ARG; + FilterRecord *rec = PR_NEWZAP (FilterRecord); + + if (!jsds_SyncFilter (rec, filter)) { + PR_Free (rec); + return NS_ERROR_FAILURE; + } + + if (gFilters) { + PR_INSERT_BEFORE(&rec->links, &gFilters->links); + } else { + PR_INIT_CLIST(&rec->links); + gFilters = rec; + } + + return NS_OK; +} + +NS_IMETHODIMP +jsdService::RemoveFilter (jsdIFilter *filter) +{ + NS_ENSURE_ARG_POINTER(filter); + FilterRecord *rec = jsds_FindFilter (filter); + if (!rec) + return NS_ERROR_INVALID_ARG; + + if (gFilters == rec) { + gFilters = reinterpret_cast<FilterRecord *> + (PR_NEXT_LINK(&rec->links)); + /* If we're the only filter left, null out the list head. */ + if (gFilters == rec) + gFilters = nullptr; + } + + + PR_REMOVE_LINK(&rec->links); + jsds_FreeFilter (rec); + + return NS_OK; +} + +NS_IMETHODIMP +jsdService::SwapFilters (jsdIFilter *filter_a, jsdIFilter *filter_b) +{ + NS_ENSURE_ARG_POINTER(filter_a); + NS_ENSURE_ARG_POINTER(filter_b); + + FilterRecord *rec_a = jsds_FindFilter (filter_a); + if (!rec_a) + return NS_ERROR_INVALID_ARG; + + if (filter_a == filter_b) { + /* just a refresh */ + if (!jsds_SyncFilter (rec_a, filter_a)) + return NS_ERROR_FAILURE; + return NS_OK; + } + + FilterRecord *rec_b = jsds_FindFilter (filter_b); + if (!rec_b) { + /* filter_b is not in the list, replace filter_a with filter_b. */ + if (!jsds_SyncFilter (rec_a, filter_b)) + return NS_ERROR_FAILURE; + } else { + /* both filters are in the list, swap. */ + if (!jsds_SyncFilter (rec_a, filter_b)) + return NS_ERROR_FAILURE; + if (!jsds_SyncFilter (rec_b, filter_a)) + return NS_ERROR_FAILURE; + } + + return NS_OK; +} + +NS_IMETHODIMP +jsdService::EnumerateFilters (jsdIFilterEnumerator *enumerator) +{ + if (!gFilters) + return NS_OK; + + FilterRecord *current = gFilters; + do { + jsds_SyncFilter (current, current->filterObject); + /* SyncFilter failure would be bad, but what would we do about it? */ + if (enumerator) { + nsresult rv = enumerator->EnumerateFilter (current->filterObject); + if (NS_FAILED(rv)) + return rv; + } + current = reinterpret_cast<FilterRecord *> + (PR_NEXT_LINK (¤t->links)); + } while (current != gFilters); + + return NS_OK; +} + +NS_IMETHODIMP +jsdService::RefreshFilters () +{ + return EnumerateFilters(nullptr); +} + +NS_IMETHODIMP +jsdService::ClearFilters () +{ + if (!gFilters) + return NS_OK; + + FilterRecord *current = reinterpret_cast<FilterRecord *> + (PR_NEXT_LINK (&gFilters->links)); + do { + FilterRecord *next = reinterpret_cast<FilterRecord *> + (PR_NEXT_LINK (¤t->links)); + PR_REMOVE_AND_INIT_LINK(¤t->links); + jsds_FreeFilter(current); + current = next; + } while (current != gFilters); + + jsds_FreeFilter(current); + gFilters = nullptr; + + return NS_OK; +} + +NS_IMETHODIMP +jsdService::ClearAllBreakpoints (void) +{ + ASSERT_VALID_CONTEXT; + + JSD_LockScriptSubsystem(mCx); + JSD_ClearAllExecutionHooks (mCx); + JSD_UnlockScriptSubsystem(mCx); + return NS_OK; +} + +NS_IMETHODIMP +jsdService::WrapValue(const JS::Value &value, jsdIValue **_rval) +{ + ASSERT_VALID_CONTEXT; + JSDValue *jsdv = JSD_NewValue(mCx, value); + if (!jsdv) + return NS_ERROR_FAILURE; + + *_rval = jsdValue::FromPtr (mCx, jsdv); + return NS_OK; +} + + +NS_IMETHODIMP +jsdService::EnterNestedEventLoop (jsdINestCallback *callback, uint32_t *_rval) +{ + // Nesting event queues is a thing of the past. Now, we just spin the + // current event loop. + nsresult rv = NS_OK; + nsCxPusher pusher; + pusher.PushNull(); + uint32_t nestLevel = ++mNestedLoopLevel; + nsCOMPtr<nsIThread> thread = do_GetCurrentThread(); + + if (callback) { + DoPause(nullptr, true); + rv = callback->OnNest(); + DoUnPause(nullptr, true); + } + + while (NS_SUCCEEDED(rv) && mNestedLoopLevel >= nestLevel) { + if (!NS_ProcessNextEvent(thread)) + rv = NS_ERROR_UNEXPECTED; + } + + NS_ASSERTION (mNestedLoopLevel <= nestLevel, + "nested event didn't unwind properly"); + if (mNestedLoopLevel == nestLevel) + --mNestedLoopLevel; + + *_rval = mNestedLoopLevel; + return rv; +} + +NS_IMETHODIMP +jsdService::ExitNestedEventLoop (uint32_t *_rval) +{ + if (mNestedLoopLevel > 0) + --mNestedLoopLevel; + else + return NS_ERROR_FAILURE; + + *_rval = mNestedLoopLevel; + return NS_OK; +} + +/* hook attribute get/set functions */ + +NS_IMETHODIMP +jsdService::SetErrorHook (jsdIErrorHook *aHook) +{ + mErrorHook = aHook; + + /* if the debugger isn't initialized, that's all we can do for now. The + * ActivateDebugger() method will do the rest when the coast is clear. + */ + if (!mCx || mPauseLevel) + return NS_OK; + + if (aHook) + JSD_SetErrorReporter (mCx, jsds_ErrorHookProc, NULL); + else + JSD_SetErrorReporter (mCx, NULL, NULL); + + return NS_OK; +} + +NS_IMETHODIMP +jsdService::GetErrorHook (jsdIErrorHook **aHook) +{ + *aHook = mErrorHook; + NS_IF_ADDREF(*aHook); + + return NS_OK; +} + +NS_IMETHODIMP +jsdService::SetBreakpointHook (jsdIExecutionHook *aHook) +{ + mBreakpointHook = aHook; + return NS_OK; +} + +NS_IMETHODIMP +jsdService::GetBreakpointHook (jsdIExecutionHook **aHook) +{ + *aHook = mBreakpointHook; + NS_IF_ADDREF(*aHook); + + return NS_OK; +} + +NS_IMETHODIMP +jsdService::SetDebugHook (jsdIExecutionHook *aHook) +{ + mDebugHook = aHook; + + /* if the debugger isn't initialized, that's all we can do for now. The + * ActivateDebugger() method will do the rest when the coast is clear. + */ + if (!mCx || mPauseLevel) + return NS_OK; + + if (aHook) + JSD_SetDebugBreakHook (mCx, jsds_ExecutionHookProc, NULL); + else + JSD_ClearDebugBreakHook (mCx); + + return NS_OK; +} + +NS_IMETHODIMP +jsdService::GetDebugHook (jsdIExecutionHook **aHook) +{ + *aHook = mDebugHook; + NS_IF_ADDREF(*aHook); + + return NS_OK; +} + +NS_IMETHODIMP +jsdService::SetDebuggerHook (jsdIExecutionHook *aHook) +{ + mDebuggerHook = aHook; + + /* if the debugger isn't initialized, that's all we can do for now. The + * ActivateDebugger() method will do the rest when the coast is clear. + */ + if (!mCx || mPauseLevel) + return NS_OK; + + if (aHook) + JSD_SetDebuggerHook (mCx, jsds_ExecutionHookProc, NULL); + else + JSD_ClearDebuggerHook (mCx); + + return NS_OK; +} + +NS_IMETHODIMP +jsdService::GetDebuggerHook (jsdIExecutionHook **aHook) +{ + *aHook = mDebuggerHook; + NS_IF_ADDREF(*aHook); + + return NS_OK; +} + +NS_IMETHODIMP +jsdService::SetInterruptHook (jsdIExecutionHook *aHook) +{ + mInterruptHook = aHook; + + /* if the debugger isn't initialized, that's all we can do for now. The + * ActivateDebugger() method will do the rest when the coast is clear. + */ + if (!mCx || mPauseLevel) + return NS_OK; + + if (aHook) + JSD_SetInterruptHook (mCx, jsds_ExecutionHookProc, NULL); + else + JSD_ClearInterruptHook (mCx); + + return NS_OK; +} + +NS_IMETHODIMP +jsdService::GetInterruptHook (jsdIExecutionHook **aHook) +{ + *aHook = mInterruptHook; + NS_IF_ADDREF(*aHook); + + return NS_OK; +} + +NS_IMETHODIMP +jsdService::SetScriptHook (jsdIScriptHook *aHook) +{ + mScriptHook = aHook; + + /* if the debugger isn't initialized, that's all we can do for now. The + * ActivateDebugger() method will do the rest when the coast is clear. + */ + if (!mCx || mPauseLevel) + return NS_OK; + + if (aHook) + JSD_SetScriptHook (mCx, jsds_ScriptHookProc, NULL); + /* we can't unset it if !aHook, because we still need to see script + * deletes in order to Release the jsdIScripts held in JSDScript + * private data. */ + return NS_OK; +} + +NS_IMETHODIMP +jsdService::GetScriptHook (jsdIScriptHook **aHook) +{ + *aHook = mScriptHook; + NS_IF_ADDREF(*aHook); + + return NS_OK; +} + +NS_IMETHODIMP +jsdService::SetThrowHook (jsdIExecutionHook *aHook) +{ + mThrowHook = aHook; + + /* if the debugger isn't initialized, that's all we can do for now. The + * ActivateDebugger() method will do the rest when the coast is clear. + */ + if (!mCx || mPauseLevel) + return NS_OK; + + if (aHook) + JSD_SetThrowHook (mCx, jsds_ExecutionHookProc, NULL); + else + JSD_ClearThrowHook (mCx); + + return NS_OK; +} + +NS_IMETHODIMP +jsdService::GetThrowHook (jsdIExecutionHook **aHook) +{ + *aHook = mThrowHook; + NS_IF_ADDREF(*aHook); + + return NS_OK; +} + +NS_IMETHODIMP +jsdService::SetTopLevelHook (jsdICallHook *aHook) +{ + mTopLevelHook = aHook; + + /* if the debugger isn't initialized, that's all we can do for now. The + * ActivateDebugger() method will do the rest when the coast is clear. + */ + if (!mCx || mPauseLevel) + return NS_OK; + + if (aHook) + JSD_SetTopLevelHook (mCx, jsds_CallHookProc, NULL); + else + JSD_ClearTopLevelHook (mCx); + + return NS_OK; +} + +NS_IMETHODIMP +jsdService::GetTopLevelHook (jsdICallHook **aHook) +{ + *aHook = mTopLevelHook; + NS_IF_ADDREF(*aHook); + + return NS_OK; +} + +NS_IMETHODIMP +jsdService::SetFunctionHook (jsdICallHook *aHook) +{ + mFunctionHook = aHook; + + /* if the debugger isn't initialized, that's all we can do for now. The + * ActivateDebugger() method will do the rest when the coast is clear. + */ + if (!mCx || mPauseLevel) + return NS_OK; + + if (aHook) + JSD_SetFunctionHook (mCx, jsds_CallHookProc, NULL); + else + JSD_ClearFunctionHook (mCx); + + return NS_OK; +} + +NS_IMETHODIMP +jsdService::GetFunctionHook (jsdICallHook **aHook) +{ + *aHook = mFunctionHook; + NS_IF_ADDREF(*aHook); + + return NS_OK; +} + +/* virtual */ +jsdService::~jsdService() +{ + ClearFilters(); + mErrorHook = nullptr; + mBreakpointHook = nullptr; + mDebugHook = nullptr; + mDebuggerHook = nullptr; + mInterruptHook = nullptr; + mScriptHook = nullptr; + mThrowHook = nullptr; + mTopLevelHook = nullptr; + mFunctionHook = nullptr; + Off(); + gJsds = nullptr; +} + +jsdService * +jsdService::GetService () +{ + if (!gJsds) + gJsds = new jsdService(); + + NS_IF_ADDREF(gJsds); + return gJsds; +} + +NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(jsdService, jsdService::GetService) + +/* app-start observer. turns on the debugger at app-start. this is inserted + * and/or removed from the app-start category by the jsdService::initAtStartup + * property. + */ +class jsdASObserver MOZ_FINAL : public nsIObserver +{ + public: + NS_DECL_ISUPPORTS + NS_DECL_NSIOBSERVER + + jsdASObserver () {} +}; + +NS_IMPL_THREADSAFE_ISUPPORTS1(jsdASObserver, nsIObserver) + +NS_IMETHODIMP +jsdASObserver::Observe (nsISupports *aSubject, const char *aTopic, + const PRUnichar *aData) +{ + nsresult rv; + + // Hmm. Why is the app-startup observer called multiple times? + //NS_ASSERTION(!gJsds, "app startup observer called twice"); + nsCOMPtr<jsdIDebuggerService> jsds = do_GetService(jsdServiceCtrID, &rv); + if (NS_FAILED(rv)) + return rv; + + bool on; + rv = jsds->GetIsOn(&on); + if (NS_FAILED(rv) || on) + return rv; + + nsCOMPtr<nsIJSRuntimeService> rts = do_GetService(NS_JSRT_CTRID, &rv); + if (NS_FAILED(rv)) + return rv; + + JSRuntime *rt; + rts->GetRuntime (&rt); + if (NS_FAILED(rv)) + return rv; + + rv = jsds->ActivateDebugger(rt); + if (NS_FAILED(rv)) + return rv; + + return NS_OK; +} + +NS_GENERIC_FACTORY_CONSTRUCTOR(jsdASObserver) +NS_DEFINE_NAMED_CID(JSDSERVICE_CID); +NS_DEFINE_NAMED_CID(JSDASO_CID); + +static const mozilla::Module::CIDEntry kJSDCIDs[] = { + { &kJSDSERVICE_CID, false, NULL, jsdServiceConstructor }, + { &kJSDASO_CID, false, NULL, jsdASObserverConstructor }, + { NULL } +}; + +static const mozilla::Module::ContractIDEntry kJSDContracts[] = { + { jsdServiceCtrID, &kJSDSERVICE_CID }, + { jsdARObserverCtrID, &kJSDASO_CID }, + { NULL } +}; + +static const mozilla::Module kJSDModule = { + mozilla::Module::kVersion, + kJSDCIDs, + kJSDContracts +}; + +NSMODULE_DEFN(JavaScript_Debugger) = &kJSDModule; + +void +global_finalize(JSFreeOp *aFop, JSObject *aObj) +{ + nsIScriptObjectPrincipal *sop = + static_cast<nsIScriptObjectPrincipal *>(js::GetObjectPrivate(aObj)); + MOZ_ASSERT(sop); + static_cast<SandboxPrivate *>(sop)->ForgetGlobalObject(); + NS_IF_RELEASE(sop); +} + +JSObject * +CreateJSDGlobal(JSContext *aCx, JSClass *aClasp) +{ + nsresult rv; + nsCOMPtr<nsIPrincipal> nullPrin = do_CreateInstance("@mozilla.org/nullprincipal;1", &rv); + NS_ENSURE_SUCCESS(rv, nullptr); + + JSPrincipals *jsPrin = nsJSPrincipals::get(nullPrin); + JSObject *global = JS_NewGlobalObject(aCx, aClasp, jsPrin); + NS_ENSURE_TRUE(global, nullptr); + + // We have created a new global let's attach a private to it + // that implements nsIGlobalObject. + nsCOMPtr<nsIScriptObjectPrincipal> sbp = + new SandboxPrivate(nullPrin, global); + JS_SetPrivate(global, sbp.forget().get()); + + return global; +} + +/******************************************************************************** + ******************************************************************************** + * graveyard + */ + +#if 0 +/* Thread States */ +NS_IMPL_THREADSAFE_ISUPPORTS1(jsdThreadState, jsdIThreadState); + +NS_IMETHODIMP +jsdThreadState::GetJSDContext(JSDContext **_rval) +{ + *_rval = mCx; + return NS_OK; +} + +NS_IMETHODIMP +jsdThreadState::GetJSDThreadState(JSDThreadState **_rval) +{ + *_rval = mThreadState; + return NS_OK; +} + +NS_IMETHODIMP +jsdThreadState::GetFrameCount (uint32_t *_rval) +{ + *_rval = JSD_GetCountOfStackFrames (mCx, mThreadState); + return NS_OK; +} + +NS_IMETHODIMP +jsdThreadState::GetTopFrame (jsdIStackFrame **_rval) +{ + JSDStackFrameInfo *sfi = JSD_GetStackFrame (mCx, mThreadState); + + *_rval = jsdStackFrame::FromPtr (mCx, mThreadState, sfi); + return NS_OK; +} + +NS_IMETHODIMP +jsdThreadState::GetPendingException(jsdIValue **_rval) +{ + JSDValue *jsdv = JSD_GetException (mCx, mThreadState); + + *_rval = jsdValue::FromPtr (mCx, jsdv); + return NS_OK; +} + +NS_IMETHODIMP +jsdThreadState::SetPendingException(jsdIValue *aException) +{ + JSDValue *jsdv; + + nsresult rv = aException->GetJSDValue (&jsdv); + if (NS_FAILED(rv)) + return NS_ERROR_FAILURE; + + if (!JSD_SetException (mCx, mThreadState, jsdv)) + return NS_ERROR_FAILURE; + + return NS_OK; +} + +#endif diff --git a/js/jsd/jsd_xpc.h b/js/jsd/jsd_xpc.h new file mode 100644 index 0000000..4dbfe7c --- /dev/null +++ b/js/jsd/jsd_xpc.h @@ -0,0 +1,372 @@ +/* -*- 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/. */ + +#ifndef JSDSERVICE_H___ +#define JSDSERVICE_H___ + +#include "jsdIDebuggerService.h" +#include "jsdebug.h" +#include "nsString.h" +#include "nsCOMPtr.h" +#include "nspr.h" +#include "nsCycleCollectionParticipant.h" +#include "mozilla/Attributes.h" + +// #if defined(DEBUG_rginda_l) +// # define DEBUG_verbose +// #endif + +struct LiveEphemeral { + /* link in a chain of live values list */ + PRCList links; + jsdIEphemeral *value; + void *key; +}; + +struct PCMapEntry { + uint32_t pc, line; +}; + +/******************************************************************************* + * reflected jsd data structures + *******************************************************************************/ + +class jsdObject MOZ_FINAL : public jsdIObject +{ + public: + NS_DECL_ISUPPORTS + NS_DECL_JSDIOBJECT + + /* you'll normally use use FromPtr() instead of directly constructing one */ + jsdObject (JSDContext *aCx, JSDObject *aObject) : + mCx(aCx), mObject(aObject) + { + } + + static jsdIObject *FromPtr (JSDContext *aCx, + JSDObject *aObject) + { + if (!aObject) + return nullptr; + + jsdIObject *rv = new jsdObject (aCx, aObject); + NS_IF_ADDREF(rv); + return rv; + } + + private: + jsdObject(); /* no implementation */ + jsdObject(const jsdObject&); /* no implementation */ + + JSDContext *mCx; + JSDObject *mObject; +}; + + +class jsdProperty : public jsdIProperty +{ + public: + NS_DECL_ISUPPORTS + NS_DECL_JSDIPROPERTY + NS_DECL_JSDIEPHEMERAL + + jsdProperty (JSDContext *aCx, JSDProperty *aProperty); + virtual ~jsdProperty (); + + static jsdIProperty *FromPtr (JSDContext *aCx, + JSDProperty *aProperty) + { + if (!aProperty) + return nullptr; + + jsdIProperty *rv = new jsdProperty (aCx, aProperty); + NS_IF_ADDREF(rv); + return rv; + } + + static void InvalidateAll(); + + private: + jsdProperty(); /* no implementation */ + jsdProperty(const jsdProperty&); /* no implementation */ + + bool mValid; + LiveEphemeral mLiveListEntry; + JSDContext *mCx; + JSDProperty *mProperty; +}; + +class jsdScript : public jsdIScript +{ + public: + NS_DECL_ISUPPORTS + NS_DECL_JSDISCRIPT + NS_DECL_JSDIEPHEMERAL + + /* you'll normally use use FromPtr() instead of directly constructing one */ + jsdScript (JSDContext *aCx, JSDScript *aScript); + virtual ~jsdScript(); + + static jsdIScript *FromPtr (JSDContext *aCx, JSDScript *aScript) + { + if (!aScript) + return nullptr; + + void *data = JSD_GetScriptPrivate (aScript); + jsdIScript *rv; + + if (data) { + rv = static_cast<jsdIScript *>(data); + } else { + rv = new jsdScript (aCx, aScript); + NS_IF_ADDREF(rv); /* addref for the SetScriptPrivate, released in + * Invalidate() */ + JSD_SetScriptPrivate (aScript, static_cast<void *>(rv)); + } + + NS_IF_ADDREF(rv); /* addref for return value */ + return rv; + } + + static void InvalidateAll(); + + private: + static uint32_t LastTag; + + jsdScript(); /* no implementation */ + jsdScript (const jsdScript&); /* no implementation */ + PCMapEntry* CreatePPLineMap(); + uint32_t PPPcToLine(uint32_t aPC); + uint32_t PPLineToPc(uint32_t aLine); + + bool mValid; + uint32_t mTag; + JSDContext *mCx; + JSDScript *mScript; + nsCString *mFileName; + nsCString *mFunctionName; + uint32_t mBaseLineNumber, mLineExtent; + PCMapEntry *mPPLineMap; + uint32_t mPCMapSize; + uintptr_t mFirstPC; +}; + +uint32_t jsdScript::LastTag = 0; + +class jsdContext : public jsdIContext +{ + public: + NS_DECL_ISUPPORTS + NS_DECL_JSDICONTEXT + NS_DECL_JSDIEPHEMERAL + + jsdContext (JSDContext *aJSDCx, JSContext *aJSCx, nsISupports *aISCx); + virtual ~jsdContext(); + + static void InvalidateAll(); + static jsdIContext *FromPtr (JSDContext *aJSDCx, JSContext *aJSCx); + private: + static uint32_t LastTag; + + jsdContext (); /* no implementation */ + jsdContext (const jsdContext&); /* no implementation */ + + bool mValid; + LiveEphemeral mLiveListEntry; + uint32_t mTag; + JSDContext *mJSDCx; + JSContext *mJSCx; + nsCOMPtr<nsISupports> mISCx; +}; + +uint32_t jsdContext::LastTag = 0; + +class jsdStackFrame : public jsdIStackFrame +{ + public: + NS_DECL_ISUPPORTS + NS_DECL_JSDISTACKFRAME + NS_DECL_JSDIEPHEMERAL + + /* you'll normally use use FromPtr() instead of directly constructing one */ + jsdStackFrame (JSDContext *aCx, JSDThreadState *aThreadState, + JSDStackFrameInfo *aStackFrameInfo); + virtual ~jsdStackFrame(); + + static void InvalidateAll(); + static jsdIStackFrame* FromPtr (JSDContext *aCx, + JSDThreadState *aThreadState, + JSDStackFrameInfo *aStackFrameInfo); + + private: + jsdStackFrame(); /* no implementation */ + jsdStackFrame(const jsdStackFrame&); /* no implementation */ + + bool mValid; + LiveEphemeral mLiveListEntry; + JSDContext *mCx; + JSDThreadState *mThreadState; + JSDStackFrameInfo *mStackFrameInfo; +}; + +class jsdValue : public jsdIValue +{ + public: + NS_DECL_ISUPPORTS + NS_DECL_JSDIVALUE + NS_DECL_JSDIEPHEMERAL + + /* you'll normally use use FromPtr() instead of directly constructing one */ + jsdValue (JSDContext *aCx, JSDValue *aValue); + virtual ~jsdValue(); + + static jsdIValue *FromPtr (JSDContext *aCx, JSDValue *aValue); + static void InvalidateAll(); + + private: + jsdValue(); /* no implementation */ + jsdValue (const jsdScript&); /* no implementation */ + + bool mValid; + LiveEphemeral mLiveListEntry; + JSDContext *mCx; + JSDValue *mValue; +}; + +/****************************************************************************** + * debugger service + ******************************************************************************/ + +class jsdService : public jsdIDebuggerService +{ + public: + NS_DECL_CYCLE_COLLECTING_ISUPPORTS + NS_DECL_JSDIDEBUGGERSERVICE + + NS_DECL_CYCLE_COLLECTION_CLASS(jsdService) + + jsdService() : mOn(false), mPauseLevel(0), + mNestedLoopLevel(0), mCx(0), mRuntime(0), mErrorHook(0), + mBreakpointHook(0), mDebugHook(0), mDebuggerHook(0), + mInterruptHook(0), mScriptHook(0), mThrowHook(0), + mTopLevelHook(0), mFunctionHook(0) + { + } + + virtual ~jsdService(); + + static jsdService *GetService (); + + bool CheckInterruptHook() { return !!mInterruptHook; } + + nsresult DoPause(uint32_t *_rval, bool internalCall); + nsresult DoUnPause(uint32_t *_rval, bool internalCall); + + private: + bool mOn; + uint32_t mPauseLevel; + uint32_t mNestedLoopLevel; + JSDContext *mCx; + JSRuntime *mRuntime; + + nsCOMPtr<jsdIErrorHook> mErrorHook; + nsCOMPtr<jsdIExecutionHook> mBreakpointHook; + nsCOMPtr<jsdIExecutionHook> mDebugHook; + nsCOMPtr<jsdIExecutionHook> mDebuggerHook; + nsCOMPtr<jsdIExecutionHook> mInterruptHook; + nsCOMPtr<jsdIScriptHook> mScriptHook; + nsCOMPtr<jsdIExecutionHook> mThrowHook; + nsCOMPtr<jsdICallHook> mTopLevelHook; + nsCOMPtr<jsdICallHook> mFunctionHook; + nsCOMPtr<jsdIActivationCallback> mActivationCallback; +}; + +#endif /* JSDSERVICE_H___ */ + + +/* graveyard */ + +#if 0 + +class jsdContext : public jsdIContext +{ + public: + NS_DECL_ISUPPORTS + NS_DECL_JSDICONTEXT + + /* you'll normally use use FromPtr() instead of directly constructing one */ + jsdContext (JSDContext *aCx) : mCx(aCx) + { + printf ("++++++ jsdContext\n"); + } + + static jsdIContext *FromPtr (JSDContext *aCx) + { + if (!aCx) + return nullptr; + + void *data = JSD_GetContextPrivate (aCx); + jsdIContext *rv; + + if (data) { + rv = static_cast<jsdIContext *>(data); + } else { + rv = new jsdContext (aCx); + NS_IF_ADDREF(rv); // addref for the SetContextPrivate + JSD_SetContextPrivate (aCx, static_cast<void *>(rv)); + } + + NS_IF_ADDREF(rv); // addref for the return value + return rv; + } + + virtual ~jsdContext() { printf ("------ ~jsdContext\n"); } + private: + jsdContext(); /* no implementation */ + jsdContext(const jsdContext&); /* no implementation */ + + JSDContext *mCx; +}; + +class jsdThreadState : public jsdIThreadState +{ + public: + NS_DECL_ISUPPORTS + NS_DECL_JSDITHREADSTATE + + /* you'll normally use use FromPtr() instead of directly constructing one */ + jsdThreadState (JSDContext *aCx, JSDThreadState *aThreadState) : + mCx(aCx), mThreadState(aThreadState) + { + } + + /* XXX These things are only valid for a short period of time, they reflect + * state in the js engine that will go away after stepping past wherever + * we were stopped at when this was created. We could keep a list of every + * instance of this we've created, and "invalidate" them before we let the + * engine continue. The next time we need a threadstate, we can search the + * list to find an invalidated one, and just reuse it. + */ + static jsdIThreadState *FromPtr (JSDContext *aCx, + JSDThreadState *aThreadState) + { + if (!aThreadState) + return nullptr; + + jsdIThreadState *rv = new jsdThreadState (aCx, aThreadState); + NS_IF_ADDREF(rv); + return rv; + } + + private: + jsdThreadState(); /* no implementation */ + jsdThreadState(const jsdThreadState&); /* no implementation */ + + JSDContext *mCx; + JSDThreadState *mThreadState; +}; + +#endif diff --git a/js/jsd/jsdebug.cpp b/js/jsd/jsdebug.cpp new file mode 100644 index 0000000..0355ca3 --- /dev/null +++ b/js/jsd/jsdebug.cpp @@ -0,0 +1,1371 @@ +/* -*- 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 - All public functions + */ + +#include "jsd.h" + +/***************************************************************************/ +/* High Level calls */ + +JSD_PUBLIC_API(JSDContext*) +JSD_DebuggerOnForUser(JSRuntime* jsrt, + JSD_UserCallbacks* callbacks, + void* user) +{ + return jsd_DebuggerOnForUser(jsrt, callbacks, user, NULL); +} + +JSD_PUBLIC_API(JSDContext*) +JSD_DebuggerOn(void) +{ + return jsd_DebuggerOn(); +} + +JSD_PUBLIC_API(void) +JSD_DebuggerOff(JSDContext* jsdc) +{ + JSD_ASSERT_VALID_CONTEXT(jsdc); + jsd_DebuggerOff(jsdc); +} + +JSD_PUBLIC_API(void) +JSD_DebuggerPause(JSDContext* jsdc) +{ + JSD_ASSERT_VALID_CONTEXT(jsdc); + jsd_DebuggerPause(jsdc, JS_FALSE); +} + +JSD_PUBLIC_API(void) +JSD_DebuggerUnpause(JSDContext* jsdc) +{ + JSD_ASSERT_VALID_CONTEXT(jsdc); + jsd_DebuggerUnpause(jsdc); +} + +JSD_PUBLIC_API(unsigned) +JSD_GetMajorVersion(void) +{ + return JSD_MAJOR_VERSION; +} + +JSD_PUBLIC_API(unsigned) +JSD_GetMinorVersion(void) +{ + return JSD_MINOR_VERSION; +} + +JSD_PUBLIC_API(JSObject*) +JSD_GetDefaultGlobal(JSDContext* jsdc) +{ + JSD_ASSERT_VALID_CONTEXT(jsdc); + return jsdc->glob; +} + +JSD_PUBLIC_API(JSRuntime*) +JSD_GetJSRuntime(JSDContext* jsdc) +{ + JSD_ASSERT_VALID_CONTEXT(jsdc); + return jsdc->jsrt; +} + +JSD_PUBLIC_API(void) +JSD_SetUserCallbacks(JSRuntime* jsrt, JSD_UserCallbacks* callbacks, void* user) +{ + jsd_SetUserCallbacks(jsrt, callbacks, user); +} + +JSD_PUBLIC_API(void) +JSD_JSContextInUse(JSDContext* jsdc, JSContext* context) +{ + JSD_ASSERT_VALID_CONTEXT(jsdc); + /* we no longer need this information - may need it again in the future */ +} + +JSD_PUBLIC_API(void *) +JSD_SetContextPrivate(JSDContext *jsdc, void *data) +{ + JSD_ASSERT_VALID_CONTEXT(jsdc); + return jsd_SetContextPrivate (jsdc, data); +} + +JSD_PUBLIC_API(void *) +JSD_GetContextPrivate(JSDContext *jsdc) +{ + JSD_ASSERT_VALID_CONTEXT(jsdc); + return jsd_GetContextPrivate (jsdc); +} + +JSD_PUBLIC_API(void) +JSD_ClearAllProfileData(JSDContext *jsdc) +{ + JSD_ASSERT_VALID_CONTEXT(jsdc); + jsd_ClearAllProfileData(jsdc); +} + +JSD_PUBLIC_API(void) +JSD_SetContextFlags(JSDContext *jsdc, uint32_t flags) +{ + JSD_ASSERT_VALID_CONTEXT(jsdc); + jsdc->flags = flags; + if (flags & JSD_COLLECT_PROFILE_DATA) { + /* Need to reenable our call hooks now */ + JS_SetExecuteHook(jsdc->jsrt, jsd_TopLevelCallHook, jsdc); + JS_SetCallHook(jsdc->jsrt, jsd_FunctionCallHook, jsdc); + } +} + +JSD_PUBLIC_API(uint32_t) +JSD_GetContextFlags(JSDContext *jsdc) +{ + JSD_ASSERT_VALID_CONTEXT(jsdc); + return jsdc->flags; +} + +JSD_PUBLIC_API(JSDContext*) +JSD_JSDContextForJSContext(JSContext* context) +{ + return jsd_JSDContextForJSContext(context); +} + +/***************************************************************************/ +/* Script functions */ + +JSD_PUBLIC_API(void) +JSD_LockScriptSubsystem(JSDContext* jsdc) +{ + JSD_ASSERT_VALID_CONTEXT(jsdc); + JSD_LOCK_SCRIPTS(jsdc); +} + +JSD_PUBLIC_API(void) +JSD_UnlockScriptSubsystem(JSDContext* jsdc) +{ + JSD_ASSERT_VALID_CONTEXT(jsdc); + JSD_UNLOCK_SCRIPTS(jsdc); +} + +JSD_PUBLIC_API(JSDScript*) +JSD_IterateScripts(JSDContext* jsdc, JSDScript **iterp) +{ + JSD_ASSERT_VALID_CONTEXT(jsdc); + return jsd_IterateScripts(jsdc, iterp); +} + +JSD_PUBLIC_API(uint32_t) +JSD_GetScriptFlags(JSDContext *jsdc, JSDScript *script) +{ + JSD_ASSERT_VALID_CONTEXT(jsdc); + return jsd_GetScriptFlags(jsdc, script); +} + +JSD_PUBLIC_API(void) +JSD_SetScriptFlags(JSDContext *jsdc, JSDScript *script, uint32_t flags) +{ + JSD_ASSERT_VALID_CONTEXT(jsdc); + jsd_SetScriptFlags(jsdc, script, flags); +} + +JSD_PUBLIC_API(unsigned) +JSD_GetScriptCallCount(JSDContext* jsdc, JSDScript *script) +{ + JSD_ASSERT_VALID_CONTEXT(jsdc); + return jsd_GetScriptCallCount(jsdc, script); +} + +JSD_PUBLIC_API(unsigned) +JSD_GetScriptMaxRecurseDepth(JSDContext* jsdc, JSDScript *script) +{ + JSD_ASSERT_VALID_CONTEXT(jsdc); + return jsd_GetScriptMaxRecurseDepth(jsdc, script); +} + + +JSD_PUBLIC_API(double) +JSD_GetScriptMinExecutionTime(JSDContext* jsdc, JSDScript *script) +{ + JSD_ASSERT_VALID_CONTEXT(jsdc); + return jsd_GetScriptMinExecutionTime(jsdc, script); +} + +JSD_PUBLIC_API(double) +JSD_GetScriptMaxExecutionTime(JSDContext* jsdc, JSDScript *script) +{ + JSD_ASSERT_VALID_CONTEXT(jsdc); + return jsd_GetScriptMaxExecutionTime(jsdc, script); +} + +JSD_PUBLIC_API(double) +JSD_GetScriptTotalExecutionTime(JSDContext* jsdc, JSDScript *script) +{ + JSD_ASSERT_VALID_CONTEXT(jsdc); + return jsd_GetScriptTotalExecutionTime(jsdc, script); +} + +JSD_PUBLIC_API(double) +JSD_GetScriptMinOwnExecutionTime(JSDContext* jsdc, JSDScript *script) +{ + JSD_ASSERT_VALID_CONTEXT(jsdc); + return jsd_GetScriptMinOwnExecutionTime(jsdc, script); +} + +JSD_PUBLIC_API(double) +JSD_GetScriptMaxOwnExecutionTime(JSDContext* jsdc, JSDScript *script) +{ + JSD_ASSERT_VALID_CONTEXT(jsdc); + return jsd_GetScriptMaxOwnExecutionTime(jsdc, script); +} + +JSD_PUBLIC_API(double) +JSD_GetScriptTotalOwnExecutionTime(JSDContext* jsdc, JSDScript *script) +{ + JSD_ASSERT_VALID_CONTEXT(jsdc); + return jsd_GetScriptTotalOwnExecutionTime(jsdc, script); +} + +JSD_PUBLIC_API(void) +JSD_ClearScriptProfileData(JSDContext* jsdc, JSDScript *script) +{ + JSD_ASSERT_VALID_CONTEXT(jsdc); + jsd_ClearScriptProfileData(jsdc, script); +} + +JSD_PUBLIC_API(JSScript*) +JSD_GetJSScript(JSDContext* jsdc, JSDScript *script) +{ + return jsd_GetJSScript(jsdc, script); +} + +JSD_PUBLIC_API(JSFunction*) +JSD_GetJSFunction(JSDContext* jsdc, JSDScript *script) +{ + return jsd_GetJSFunction (jsdc, script); +} + +JSD_PUBLIC_API(void *) +JSD_SetScriptPrivate(JSDScript *jsdscript, void *data) +{ + JSD_ASSERT_VALID_SCRIPT(jsdscript); + return jsd_SetScriptPrivate (jsdscript, data); +} + +JSD_PUBLIC_API(void *) +JSD_GetScriptPrivate(JSDScript *jsdscript) +{ + JSD_ASSERT_VALID_SCRIPT(jsdscript); + return jsd_GetScriptPrivate (jsdscript); +} + + +JSD_PUBLIC_API(JSBool) +JSD_IsActiveScript(JSDContext* jsdc, JSDScript *jsdscript) +{ + JSD_ASSERT_VALID_CONTEXT(jsdc); + return jsd_IsActiveScript(jsdc, jsdscript); +} + +JSD_PUBLIC_API(const char*) +JSD_GetScriptFilename(JSDContext* jsdc, JSDScript *jsdscript) +{ + JSD_ASSERT_VALID_CONTEXT(jsdc); + JSD_ASSERT_VALID_SCRIPT(jsdscript); + return jsd_GetScriptFilename(jsdc, jsdscript); +} + +JSD_PUBLIC_API(JSString *) +JSD_GetScriptFunctionId(JSDContext* jsdc, JSDScript *jsdscript) +{ + JSD_ASSERT_VALID_CONTEXT(jsdc); + JSD_ASSERT_VALID_SCRIPT(jsdscript); + return jsd_GetScriptFunctionId(jsdc, jsdscript); +} + +JSD_PUBLIC_API(unsigned) +JSD_GetScriptBaseLineNumber(JSDContext* jsdc, JSDScript *jsdscript) +{ + JSD_ASSERT_VALID_CONTEXT(jsdc); + JSD_ASSERT_VALID_SCRIPT(jsdscript); + return jsd_GetScriptBaseLineNumber(jsdc, jsdscript); +} + +JSD_PUBLIC_API(unsigned) +JSD_GetScriptLineExtent(JSDContext* jsdc, JSDScript *jsdscript) +{ + JSD_ASSERT_VALID_CONTEXT(jsdc); + JSD_ASSERT_VALID_SCRIPT(jsdscript); + return jsd_GetScriptLineExtent(jsdc, jsdscript); +} + +JSD_PUBLIC_API(JSBool) +JSD_SetScriptHook(JSDContext* jsdc, JSD_ScriptHookProc hook, void* callerdata) +{ + JSD_ASSERT_VALID_CONTEXT(jsdc); + return jsd_SetScriptHook(jsdc, hook, callerdata); +} + +JSD_PUBLIC_API(JSBool) +JSD_GetScriptHook(JSDContext* jsdc, JSD_ScriptHookProc* hook, void** callerdata) +{ + JSD_ASSERT_VALID_CONTEXT(jsdc); + return jsd_GetScriptHook(jsdc, hook, callerdata); +} + +JSD_PUBLIC_API(uintptr_t) +JSD_GetClosestPC(JSDContext* jsdc, JSDScript* jsdscript, unsigned line) +{ + JSD_ASSERT_VALID_CONTEXT(jsdc); + JSD_ASSERT_VALID_SCRIPT(jsdscript); + return jsd_GetClosestPC(jsdc, jsdscript, line); +} + +JSD_PUBLIC_API(unsigned) +JSD_GetClosestLine(JSDContext* jsdc, JSDScript* jsdscript, uintptr_t pc) +{ + JSD_ASSERT_VALID_CONTEXT(jsdc); + JSD_ASSERT_VALID_SCRIPT(jsdscript); + return jsd_GetClosestLine(jsdc, jsdscript, pc); +} + +JSD_PUBLIC_API(JSBool) +JSD_GetLinePCs(JSDContext* jsdc, JSDScript* jsdscript, + unsigned startLine, unsigned maxLines, + unsigned* count, unsigned** lines, uintptr_t** pcs) +{ + JSD_ASSERT_VALID_CONTEXT(jsdc); + JSD_ASSERT_VALID_SCRIPT(jsdscript); + return jsd_GetLinePCs(jsdc, jsdscript, startLine, maxLines, count, lines, pcs); +} + +JSD_PUBLIC_API(void) +JSD_ScriptCreated(JSDContext* jsdc, + JSContext *cx, + const char *filename, /* URL this script loads from */ + unsigned lineno, /* line where this script starts */ + JSScript *script, + JSFunction *fun) +{ + JSD_ASSERT_VALID_CONTEXT(jsdc); + jsd_ScriptCreated(jsdc, cx, filename, lineno, script, fun); +} + +JSD_PUBLIC_API(void) +JSD_ScriptDestroyed(JSDContext* jsdc, + JSFreeOp *fop, + JSScript *script) +{ + JSD_ASSERT_VALID_CONTEXT(jsdc); + jsd_ScriptDestroyed(jsdc, fop, script); +} + +/***************************************************************************/ +/* Source Text functions */ + +JSD_PUBLIC_API(void) +JSD_LockSourceTextSubsystem(JSDContext* jsdc) +{ + JSD_ASSERT_VALID_CONTEXT(jsdc); + JSD_LOCK_SOURCE_TEXT(jsdc); +} + +JSD_PUBLIC_API(void) +JSD_UnlockSourceTextSubsystem(JSDContext* jsdc) +{ + JSD_ASSERT_VALID_CONTEXT(jsdc); + JSD_UNLOCK_SOURCE_TEXT(jsdc); +} + +JSD_PUBLIC_API(JSDSourceText*) +JSD_IterateSources(JSDContext* jsdc, JSDSourceText **iterp) +{ + JSD_ASSERT_VALID_CONTEXT(jsdc); + return jsd_IterateSources(jsdc, iterp); +} + +JSD_PUBLIC_API(JSDSourceText*) +JSD_FindSourceForURL(JSDContext* jsdc, const char* url) +{ + JSD_ASSERT_VALID_CONTEXT(jsdc); + JS_ASSERT(url); + return jsd_FindSourceForURL(jsdc, url); +} + +JSD_PUBLIC_API(const char*) +JSD_GetSourceURL(JSDContext* jsdc, JSDSourceText* jsdsrc) +{ + JSD_ASSERT_VALID_CONTEXT(jsdc); + JSD_ASSERT_VALID_SOURCE_TEXT(jsdsrc); + return jsd_GetSourceURL(jsdc,jsdsrc); +} + +JSD_PUBLIC_API(JSBool) +JSD_GetSourceText(JSDContext* jsdc, JSDSourceText* jsdsrc, + const char** ppBuf, int* pLen) +{ + JSD_ASSERT_VALID_CONTEXT(jsdc); + JSD_ASSERT_VALID_SOURCE_TEXT(jsdsrc); + JS_ASSERT(ppBuf); + JS_ASSERT(pLen); + return jsd_GetSourceText(jsdc, jsdsrc, ppBuf, pLen); +} + +JSD_PUBLIC_API(void) +JSD_ClearSourceText(JSDContext* jsdc, JSDSourceText* jsdsrc) +{ + JSD_ASSERT_VALID_CONTEXT(jsdc); + JSD_ASSERT_VALID_SOURCE_TEXT(jsdsrc); + jsd_ClearSourceText(jsdc, jsdsrc); +} + + +JSD_PUBLIC_API(JSDSourceStatus) +JSD_GetSourceStatus(JSDContext* jsdc, JSDSourceText* jsdsrc) +{ + JSD_ASSERT_VALID_CONTEXT(jsdc); + JSD_ASSERT_VALID_SOURCE_TEXT(jsdsrc); + return jsd_GetSourceStatus(jsdc, jsdsrc); +} + +JSD_PUBLIC_API(JSBool) +JSD_IsSourceDirty(JSDContext* jsdc, JSDSourceText* jsdsrc) +{ + JSD_ASSERT_VALID_CONTEXT(jsdc); + JSD_ASSERT_VALID_SOURCE_TEXT(jsdsrc); + return jsd_IsSourceDirty(jsdc, jsdsrc); +} + +JSD_PUBLIC_API(void) +JSD_SetSourceDirty(JSDContext* jsdc, JSDSourceText* jsdsrc, JSBool dirty) +{ + JSD_ASSERT_VALID_CONTEXT(jsdc); + JSD_ASSERT_VALID_SOURCE_TEXT(jsdsrc); + jsd_SetSourceDirty(jsdc, jsdsrc, dirty); +} + +JSD_PUBLIC_API(unsigned) +JSD_GetSourceAlterCount(JSDContext* jsdc, JSDSourceText* jsdsrc) +{ + JSD_ASSERT_VALID_CONTEXT(jsdc); + JSD_ASSERT_VALID_SOURCE_TEXT(jsdsrc); + return jsd_GetSourceAlterCount(jsdc, jsdsrc); +} + +JSD_PUBLIC_API(unsigned) +JSD_IncrementSourceAlterCount(JSDContext* jsdc, JSDSourceText* jsdsrc) +{ + JSD_ASSERT_VALID_CONTEXT(jsdc); + JSD_ASSERT_VALID_SOURCE_TEXT(jsdsrc); + return jsd_IncrementSourceAlterCount(jsdc, jsdsrc); +} + +JSD_PUBLIC_API(void) +JSD_DestroyAllSources( JSDContext* jsdc ) +{ + JSD_ASSERT_VALID_CONTEXT(jsdc); + jsd_DestroyAllSources(jsdc); +} + +JSD_PUBLIC_API(JSDSourceText*) +JSD_NewSourceText(JSDContext* jsdc, const char* url) +{ + JSD_ASSERT_VALID_CONTEXT(jsdc); + JS_ASSERT(url); + return jsd_NewSourceText(jsdc, url); +} + +JSD_PUBLIC_API(JSDSourceText*) +JSD_AppendSourceText(JSDContext* jsdc, + JSDSourceText* jsdsrc, + const char* text, /* *not* zero terminated */ + size_t length, + JSDSourceStatus status) +{ + JSD_ASSERT_VALID_CONTEXT(jsdc); + JSD_ASSERT_VALID_SOURCE_TEXT(jsdsrc); + return jsd_AppendSourceText(jsdc, jsdsrc, text, length, status); +} + +extern JSD_PUBLIC_API(JSDSourceText*) +JSD_AppendUCSourceText(JSDContext* jsdc, + JSDSourceText* jsdsrc, + const jschar* text, /* *not* zero terminated */ + size_t length, + JSDSourceStatus status) +{ + JSD_ASSERT_VALID_CONTEXT(jsdc); + JSD_ASSERT_VALID_SOURCE_TEXT(jsdsrc); + return jsd_AppendUCSourceText(jsdc, jsdsrc, text, length, status); +} + +JSD_PUBLIC_API(JSBool) +JSD_AddFullSourceText(JSDContext* jsdc, + const char* text, /* *not* zero terminated */ + size_t length, + const char* url) +{ + JSD_ASSERT_VALID_CONTEXT(jsdc); + JS_ASSERT(url); + return jsd_AddFullSourceText(jsdc, text, length, url); +} + +/***************************************************************************/ +/* Execution/Interrupt Hook functions */ + +JSD_PUBLIC_API(JSBool) +JSD_SetExecutionHook(JSDContext* jsdc, + JSDScript* jsdscript, + uintptr_t pc, + JSD_ExecutionHookProc hook, + void* callerdata) +{ + JSD_ASSERT_VALID_CONTEXT(jsdc); + JSD_ASSERT_VALID_SCRIPT(jsdscript); + return jsd_SetExecutionHook(jsdc, jsdscript, pc, hook, callerdata); +} + +JSD_PUBLIC_API(JSBool) +JSD_ClearExecutionHook(JSDContext* jsdc, + JSDScript* jsdscript, + uintptr_t pc) +{ + JSD_ASSERT_VALID_CONTEXT(jsdc); + JSD_ASSERT_VALID_SCRIPT(jsdscript); + return jsd_ClearExecutionHook(jsdc, jsdscript, pc); +} + +JSD_PUBLIC_API(JSBool) +JSD_ClearAllExecutionHooksForScript(JSDContext* jsdc, JSDScript* jsdscript) +{ + JSD_ASSERT_VALID_CONTEXT(jsdc); + JSD_ASSERT_VALID_SCRIPT(jsdscript); + return jsd_ClearAllExecutionHooksForScript(jsdc, jsdscript); +} + +JSD_PUBLIC_API(JSBool) +JSD_ClearAllExecutionHooks(JSDContext* jsdc) +{ + JSD_ASSERT_VALID_CONTEXT(jsdc); + return jsd_ClearAllExecutionHooks(jsdc); +} + +JSD_PUBLIC_API(JSBool) +JSD_SetInterruptHook(JSDContext* jsdc, + JSD_ExecutionHookProc hook, + void* callerdata) +{ + JSD_ASSERT_VALID_CONTEXT(jsdc); + return jsd_SetInterruptHook(jsdc, hook, callerdata); +} + +JSD_PUBLIC_API(JSBool) +JSD_EnableSingleStepInterrupts(JSDContext* jsdc, JSDScript* jsdscript, JSBool enable) +{ + JSD_ASSERT_VALID_CONTEXT(jsdc); + JSD_ASSERT_VALID_SCRIPT(jsdscript); + return jsd_EnableSingleStepInterrupts(jsdc, jsdscript, enable); +} + +JSD_PUBLIC_API(JSBool) +JSD_ClearInterruptHook(JSDContext* jsdc) +{ + JSD_ASSERT_VALID_CONTEXT(jsdc); + return jsd_ClearInterruptHook(jsdc); +} + +JSD_PUBLIC_API(JSBool) +JSD_SetDebugBreakHook(JSDContext* jsdc, + JSD_ExecutionHookProc hook, + void* callerdata) +{ + JSD_ASSERT_VALID_CONTEXT(jsdc); + return jsd_SetDebugBreakHook(jsdc, hook, callerdata); +} + +JSD_PUBLIC_API(JSBool) +JSD_ClearDebugBreakHook(JSDContext* jsdc) +{ + JSD_ASSERT_VALID_CONTEXT(jsdc); + return jsd_ClearDebugBreakHook(jsdc); +} + +JSD_PUBLIC_API(JSBool) +JSD_SetDebuggerHook(JSDContext* jsdc, + JSD_ExecutionHookProc hook, + void* callerdata) +{ + JSD_ASSERT_VALID_CONTEXT(jsdc); + return jsd_SetDebuggerHook(jsdc, hook, callerdata); +} + +JSD_PUBLIC_API(JSBool) +JSD_ClearDebuggerHook(JSDContext* jsdc) +{ + JSD_ASSERT_VALID_CONTEXT(jsdc); + return jsd_ClearDebuggerHook(jsdc); +} + +JSD_PUBLIC_API(JSBool) +JSD_SetThrowHook(JSDContext* jsdc, + JSD_ExecutionHookProc hook, + void* callerdata) +{ + JSD_ASSERT_VALID_CONTEXT(jsdc); + return jsd_SetThrowHook(jsdc, hook, callerdata); +} + +JSD_PUBLIC_API(JSBool) +JSD_ClearThrowHook(JSDContext* jsdc) +{ + JSD_ASSERT_VALID_CONTEXT(jsdc); + return jsd_ClearThrowHook(jsdc); +} + +JSD_PUBLIC_API(JSBool) +JSD_SetTopLevelHook(JSDContext* jsdc, + JSD_CallHookProc hook, + void* callerdata) +{ + JSD_ASSERT_VALID_CONTEXT(jsdc); + return jsd_SetTopLevelHook(jsdc, hook, callerdata); +} + +JSD_PUBLIC_API(JSBool) +JSD_ClearTopLevelHook(JSDContext* jsdc) +{ + JSD_ASSERT_VALID_CONTEXT(jsdc); + return jsd_ClearTopLevelHook(jsdc); +} + +JSD_PUBLIC_API(JSBool) +JSD_SetFunctionHook(JSDContext* jsdc, + JSD_CallHookProc hook, + void* callerdata) +{ + JSD_ASSERT_VALID_CONTEXT(jsdc); + return jsd_SetFunctionHook(jsdc, hook, callerdata); +} + +JSD_PUBLIC_API(JSBool) +JSD_ClearFunctionHook(JSDContext* jsdc) +{ + JSD_ASSERT_VALID_CONTEXT(jsdc); + return jsd_ClearFunctionHook(jsdc); +} + +/***************************************************************************/ +/* Stack Frame functions */ + +JSD_PUBLIC_API(unsigned) +JSD_GetCountOfStackFrames(JSDContext* jsdc, JSDThreadState* jsdthreadstate) +{ + JSD_ASSERT_VALID_CONTEXT(jsdc); + return jsd_GetCountOfStackFrames(jsdc, jsdthreadstate); +} + +JSD_PUBLIC_API(JSDStackFrameInfo*) +JSD_GetStackFrame(JSDContext* jsdc, JSDThreadState* jsdthreadstate) +{ + JSD_ASSERT_VALID_CONTEXT(jsdc); + return jsd_GetStackFrame(jsdc, jsdthreadstate); +} + +JSD_PUBLIC_API(JSContext*) +JSD_GetJSContext(JSDContext* jsdc, JSDThreadState* jsdthreadstate) +{ + JSD_ASSERT_VALID_CONTEXT(jsdc); + return jsd_GetJSContext(jsdc, jsdthreadstate); +} + +JSD_PUBLIC_API(JSDStackFrameInfo*) +JSD_GetCallingStackFrame(JSDContext* jsdc, + JSDThreadState* jsdthreadstate, + JSDStackFrameInfo* jsdframe) +{ + JSD_ASSERT_VALID_CONTEXT(jsdc); + return jsd_GetCallingStackFrame(jsdc, jsdthreadstate, jsdframe); +} + +JSD_PUBLIC_API(JSDScript*) +JSD_GetScriptForStackFrame(JSDContext* jsdc, + JSDThreadState* jsdthreadstate, + JSDStackFrameInfo* jsdframe) +{ + JSD_ASSERT_VALID_CONTEXT(jsdc); + return jsd_GetScriptForStackFrame(jsdc, jsdthreadstate, jsdframe); +} + +JSD_PUBLIC_API(uintptr_t) +JSD_GetPCForStackFrame(JSDContext* jsdc, + JSDThreadState* jsdthreadstate, + JSDStackFrameInfo* jsdframe) +{ + JSD_ASSERT_VALID_CONTEXT(jsdc); + return jsd_GetPCForStackFrame(jsdc, jsdthreadstate, jsdframe); +} + +JSD_PUBLIC_API(JSDValue*) +JSD_GetCallObjectForStackFrame(JSDContext* jsdc, + JSDThreadState* jsdthreadstate, + JSDStackFrameInfo* jsdframe) +{ + JSD_ASSERT_VALID_CONTEXT(jsdc); + return jsd_GetCallObjectForStackFrame(jsdc, jsdthreadstate, jsdframe); +} + +JSD_PUBLIC_API(JSDValue*) +JSD_GetScopeChainForStackFrame(JSDContext* jsdc, + JSDThreadState* jsdthreadstate, + JSDStackFrameInfo* jsdframe) +{ + JSD_ASSERT_VALID_CONTEXT(jsdc); + return jsd_GetScopeChainForStackFrame(jsdc, jsdthreadstate, jsdframe); +} + +JSD_PUBLIC_API(JSDValue*) +JSD_GetThisForStackFrame(JSDContext* jsdc, + JSDThreadState* jsdthreadstate, + JSDStackFrameInfo* jsdframe) +{ + JSD_ASSERT_VALID_CONTEXT(jsdc); + return jsd_GetThisForStackFrame(jsdc, jsdthreadstate, jsdframe); +} + +JSD_PUBLIC_API(JSString *) +JSD_GetIdForStackFrame(JSDContext* jsdc, + JSDThreadState* jsdthreadstate, + JSDStackFrameInfo* jsdframe) +{ + JSD_ASSERT_VALID_CONTEXT(jsdc); + return jsd_GetIdForStackFrame(jsdc, jsdthreadstate, jsdframe); +} + +JSD_PUBLIC_API(JSBool) +JSD_IsStackFrameDebugger(JSDContext* jsdc, + JSDThreadState* jsdthreadstate, + JSDStackFrameInfo* jsdframe) +{ + JSD_ASSERT_VALID_CONTEXT(jsdc); + return jsd_IsStackFrameDebugger(jsdc, jsdthreadstate, jsdframe); +} + +JSD_PUBLIC_API(JSBool) +JSD_IsStackFrameConstructing(JSDContext* jsdc, + JSDThreadState* jsdthreadstate, + JSDStackFrameInfo* jsdframe) +{ + JSD_ASSERT_VALID_CONTEXT(jsdc); + return jsd_IsStackFrameConstructing(jsdc, jsdthreadstate, jsdframe); +} + +JSD_PUBLIC_API(JSBool) +JSD_EvaluateUCScriptInStackFrame(JSDContext* jsdc, + JSDThreadState* jsdthreadstate, + JSDStackFrameInfo* jsdframe, + const jschar *bytes, unsigned length, + const char *filename, unsigned lineno, JS::MutableHandleValue rval) +{ + JSD_ASSERT_VALID_CONTEXT(jsdc); + JS_ASSERT(bytes); + JS_ASSERT(length); + JS_ASSERT(filename); + + return jsd_EvaluateUCScriptInStackFrame(jsdc, jsdthreadstate,jsdframe, + bytes, length, filename, lineno, + JS_TRUE, rval); +} + +JSD_PUBLIC_API(JSBool) +JSD_AttemptUCScriptInStackFrame(JSDContext* jsdc, + JSDThreadState* jsdthreadstate, + JSDStackFrameInfo* jsdframe, + const jschar *bytes, unsigned length, + const char *filename, unsigned lineno, + JS::MutableHandleValue rval) +{ + JSD_ASSERT_VALID_CONTEXT(jsdc); + JS_ASSERT(bytes); + JS_ASSERT(length); + JS_ASSERT(filename); + + return jsd_EvaluateUCScriptInStackFrame(jsdc, jsdthreadstate,jsdframe, + bytes, length, filename, lineno, + JS_FALSE, rval); +} + +JSD_PUBLIC_API(JSBool) +JSD_EvaluateScriptInStackFrame(JSDContext* jsdc, + JSDThreadState* jsdthreadstate, + JSDStackFrameInfo* jsdframe, + const char *bytes, unsigned length, + const char *filename, unsigned lineno, JS::MutableHandleValue rval) +{ + JSD_ASSERT_VALID_CONTEXT(jsdc); + JS_ASSERT(bytes); + JS_ASSERT(length); + JS_ASSERT(filename); + + return jsd_EvaluateScriptInStackFrame(jsdc, jsdthreadstate,jsdframe, + bytes, length, + filename, lineno, JS_TRUE, rval); +} + +JSD_PUBLIC_API(JSBool) +JSD_AttemptScriptInStackFrame(JSDContext* jsdc, + JSDThreadState* jsdthreadstate, + JSDStackFrameInfo* jsdframe, + const char *bytes, unsigned length, + const char *filename, unsigned lineno, JS::MutableHandleValue rval) +{ + JSD_ASSERT_VALID_CONTEXT(jsdc); + JS_ASSERT(bytes); + JS_ASSERT(length); + JS_ASSERT(filename); + + return jsd_EvaluateScriptInStackFrame(jsdc, jsdthreadstate,jsdframe, + bytes, length, + filename, lineno, JS_FALSE, rval); +} + +JSD_PUBLIC_API(JSString*) +JSD_ValToStringInStackFrame(JSDContext* jsdc, + JSDThreadState* jsdthreadstate, + JSDStackFrameInfo* jsdframe, + jsval val) +{ + JSD_ASSERT_VALID_CONTEXT(jsdc); + return jsd_ValToStringInStackFrame(jsdc, jsdthreadstate, jsdframe, val); +} + +JSD_PUBLIC_API(JSDValue*) +JSD_GetException(JSDContext* jsdc, JSDThreadState* jsdthreadstate) +{ + JSD_ASSERT_VALID_CONTEXT(jsdc); + return jsd_GetException(jsdc, jsdthreadstate); +} + +extern JSD_PUBLIC_API(JSBool) +JSD_SetException(JSDContext* jsdc, JSDThreadState* jsdthreadstate, + JSDValue* jsdval) +{ + JSD_ASSERT_VALID_CONTEXT(jsdc); + return jsd_SetException(jsdc, jsdthreadstate, jsdval); +} + +/***************************************************************************/ + +JSD_PUBLIC_API(JSBool) +JSD_SetErrorReporter(JSDContext* jsdc, + JSD_ErrorReporter reporter, + void* callerdata) +{ + JSD_ASSERT_VALID_CONTEXT(jsdc); + return jsd_SetErrorReporter(jsdc, reporter, callerdata); +} + +JSD_PUBLIC_API(JSBool) +JSD_GetErrorReporter(JSDContext* jsdc, + JSD_ErrorReporter* reporter, + void** callerdata) +{ + JSD_ASSERT_VALID_CONTEXT(jsdc); + return jsd_GetErrorReporter(jsdc, reporter, callerdata); +} + +/***************************************************************************/ + +JSD_PUBLIC_API(JSBool) +JSD_IsLockingAndThreadIdSupported() +{ +#ifdef JSD_THREADSAFE + return JS_TRUE; +#else + return JS_FALSE; +#endif +} + +JSD_PUBLIC_API(JSDStaticLock*) +JSD_CreateLock() +{ +#ifdef JSD_THREADSAFE + return jsd_CreateLock(); +#else + return (void*)1; +#endif +} + +JSD_PUBLIC_API(void) +JSD_Lock(JSDStaticLock* lock) +{ +#ifdef JSD_THREADSAFE + jsd_Lock(lock); +#endif +} + +JSD_PUBLIC_API(void) +JSD_Unlock(JSDStaticLock* lock) +{ +#ifdef JSD_THREADSAFE + jsd_Unlock(lock); +#endif +} + +JSD_PUBLIC_API(JSBool) +JSD_IsLocked(JSDStaticLock* lock) +{ +#if defined(JSD_THREADSAFE) && defined(DEBUG) + return jsd_IsLocked(lock); +#else + return JS_TRUE; +#endif +} + +JSD_PUBLIC_API(JSBool) +JSD_IsUnlocked(JSDStaticLock* lock) +{ +#if defined(JSD_THREADSAFE) && defined(DEBUG) + return ! jsd_IsLocked(lock); +#else + return JS_TRUE; +#endif +} + +JSD_PUBLIC_API(void*) +JSD_CurrentThread() +{ + return JSD_CURRENT_THREAD(); +} + +/***************************************************************************/ +/* Value and Property Functions */ + +JSD_PUBLIC_API(JSDValue*) +JSD_NewValue(JSDContext* jsdc, jsval val) +{ + JSD_ASSERT_VALID_CONTEXT(jsdc); + return jsd_NewValue(jsdc, val); +} + +JSD_PUBLIC_API(void) +JSD_DropValue(JSDContext* jsdc, JSDValue* jsdval) +{ + JSD_ASSERT_VALID_CONTEXT(jsdc); + JSD_ASSERT_VALID_VALUE(jsdval); + jsd_DropValue(jsdc, jsdval); +} + +JSD_PUBLIC_API(jsval) +JSD_GetValueWrappedJSVal(JSDContext* jsdc, JSDValue* jsdval) +{ + JSD_ASSERT_VALID_CONTEXT(jsdc); + JSD_ASSERT_VALID_VALUE(jsdval); + return jsd_GetValueWrappedJSVal(jsdc, jsdval); +} + +JSD_PUBLIC_API(void) +JSD_RefreshValue(JSDContext* jsdc, JSDValue* jsdval) +{ + JSD_ASSERT_VALID_CONTEXT(jsdc); + JSD_ASSERT_VALID_VALUE(jsdval); + jsd_RefreshValue(jsdc, jsdval); +} + +/**************************************************/ + +JSD_PUBLIC_API(JSBool) +JSD_IsValueObject(JSDContext* jsdc, JSDValue* jsdval) +{ + JSD_ASSERT_VALID_CONTEXT(jsdc); + JSD_ASSERT_VALID_VALUE(jsdval); + return jsd_IsValueObject(jsdc, jsdval); +} + +JSD_PUBLIC_API(JSBool) +JSD_IsValueNumber(JSDContext* jsdc, JSDValue* jsdval) +{ + JSD_ASSERT_VALID_CONTEXT(jsdc); + JSD_ASSERT_VALID_VALUE(jsdval); + return jsd_IsValueNumber(jsdc, jsdval); +} + +JSD_PUBLIC_API(JSBool) +JSD_IsValueInt(JSDContext* jsdc, JSDValue* jsdval) +{ + JSD_ASSERT_VALID_CONTEXT(jsdc); + JSD_ASSERT_VALID_VALUE(jsdval); + return jsd_IsValueInt(jsdc, jsdval); +} + +JSD_PUBLIC_API(JSBool) +JSD_IsValueDouble(JSDContext* jsdc, JSDValue* jsdval) +{ + JSD_ASSERT_VALID_CONTEXT(jsdc); + JSD_ASSERT_VALID_VALUE(jsdval); + return jsd_IsValueDouble(jsdc, jsdval); +} + +JSD_PUBLIC_API(JSBool) +JSD_IsValueString(JSDContext* jsdc, JSDValue* jsdval) +{ + JSD_ASSERT_VALID_CONTEXT(jsdc); + JSD_ASSERT_VALID_VALUE(jsdval); + return jsd_IsValueString(jsdc, jsdval); +} + +JSD_PUBLIC_API(JSBool) +JSD_IsValueBoolean(JSDContext* jsdc, JSDValue* jsdval) +{ + JSD_ASSERT_VALID_CONTEXT(jsdc); + JSD_ASSERT_VALID_VALUE(jsdval); + return jsd_IsValueBoolean(jsdc, jsdval); +} + +JSD_PUBLIC_API(JSBool) +JSD_IsValueNull(JSDContext* jsdc, JSDValue* jsdval) +{ + JSD_ASSERT_VALID_CONTEXT(jsdc); + JSD_ASSERT_VALID_VALUE(jsdval); + return jsd_IsValueNull(jsdc, jsdval); +} + +JSD_PUBLIC_API(JSBool) +JSD_IsValueVoid(JSDContext* jsdc, JSDValue* jsdval) +{ + JSD_ASSERT_VALID_CONTEXT(jsdc); + JSD_ASSERT_VALID_VALUE(jsdval); + return jsd_IsValueVoid(jsdc, jsdval); +} + +JSD_PUBLIC_API(JSBool) +JSD_IsValuePrimitive(JSDContext* jsdc, JSDValue* jsdval) +{ + JSD_ASSERT_VALID_CONTEXT(jsdc); + JSD_ASSERT_VALID_VALUE(jsdval); + return jsd_IsValuePrimitive(jsdc, jsdval); +} + +JSD_PUBLIC_API(JSBool) +JSD_IsValueFunction(JSDContext* jsdc, JSDValue* jsdval) +{ + JSD_ASSERT_VALID_CONTEXT(jsdc); + JSD_ASSERT_VALID_VALUE(jsdval); + return jsd_IsValueFunction(jsdc, jsdval); +} + +JSD_PUBLIC_API(JSBool) +JSD_IsValueNative(JSDContext* jsdc, JSDValue* jsdval) +{ + JSD_ASSERT_VALID_CONTEXT(jsdc); + JSD_ASSERT_VALID_VALUE(jsdval); + return jsd_IsValueNative(jsdc, jsdval); +} + +/**************************************************/ + +JSD_PUBLIC_API(JSBool) +JSD_GetValueBoolean(JSDContext* jsdc, JSDValue* jsdval) +{ + JSD_ASSERT_VALID_CONTEXT(jsdc); + JSD_ASSERT_VALID_VALUE(jsdval); + return jsd_GetValueBoolean(jsdc, jsdval); +} + +JSD_PUBLIC_API(int32_t) +JSD_GetValueInt(JSDContext* jsdc, JSDValue* jsdval) +{ + JSD_ASSERT_VALID_CONTEXT(jsdc); + JSD_ASSERT_VALID_VALUE(jsdval); + return jsd_GetValueInt(jsdc, jsdval); +} + +JSD_PUBLIC_API(double) +JSD_GetValueDouble(JSDContext* jsdc, JSDValue* jsdval) +{ + JSD_ASSERT_VALID_CONTEXT(jsdc); + JSD_ASSERT_VALID_VALUE(jsdval); + return jsd_GetValueDouble(jsdc, jsdval); +} + +JSD_PUBLIC_API(JSString*) +JSD_GetValueString(JSDContext* jsdc, JSDValue* jsdval) +{ + JSD_ASSERT_VALID_CONTEXT(jsdc); + JSD_ASSERT_VALID_VALUE(jsdval); + return jsd_GetValueString(jsdc, jsdval); +} + +JSD_PUBLIC_API(JSString *) +JSD_GetValueFunctionId(JSDContext* jsdc, JSDValue* jsdval) +{ + JSD_ASSERT_VALID_CONTEXT(jsdc); + JSD_ASSERT_VALID_VALUE(jsdval); + return jsd_GetValueFunctionId(jsdc, jsdval); +} + +JSD_PUBLIC_API(JSFunction*) +JSD_GetValueFunction(JSDContext* jsdc, JSDValue* jsdval) +{ + JSD_ASSERT_VALID_CONTEXT(jsdc); + JSD_ASSERT_VALID_VALUE(jsdval); + return jsd_GetValueFunction(jsdc, jsdval); +} + +/**************************************************/ + +JSD_PUBLIC_API(unsigned) +JSD_GetCountOfProperties(JSDContext* jsdc, JSDValue* jsdval) +{ + JSD_ASSERT_VALID_CONTEXT(jsdc); + JSD_ASSERT_VALID_VALUE(jsdval); + return jsd_GetCountOfProperties(jsdc, jsdval); +} + +JSD_PUBLIC_API(JSDProperty*) +JSD_IterateProperties(JSDContext* jsdc, JSDValue* jsdval, JSDProperty **iterp) +{ + JSD_ASSERT_VALID_CONTEXT(jsdc); + JSD_ASSERT_VALID_VALUE(jsdval); + JS_ASSERT(iterp); + return jsd_IterateProperties(jsdc, jsdval, iterp); +} + +JSD_PUBLIC_API(JSDProperty*) +JSD_GetValueProperty(JSDContext* jsdc, JSDValue* jsdval, JSString* name) +{ + JSD_ASSERT_VALID_CONTEXT(jsdc); + JSD_ASSERT_VALID_VALUE(jsdval); + JS_ASSERT(name); + return jsd_GetValueProperty(jsdc, jsdval, name); +} + +JSD_PUBLIC_API(JSDValue*) +JSD_GetValuePrototype(JSDContext* jsdc, JSDValue* jsdval) +{ + JSD_ASSERT_VALID_CONTEXT(jsdc); + JSD_ASSERT_VALID_VALUE(jsdval); + return jsd_GetValuePrototype(jsdc, jsdval); +} + +JSD_PUBLIC_API(JSDValue*) +JSD_GetValueParent(JSDContext* jsdc, JSDValue* jsdval) +{ + JSD_ASSERT_VALID_CONTEXT(jsdc); + JSD_ASSERT_VALID_VALUE(jsdval); + return jsd_GetValueParent(jsdc, jsdval); +} + +JSD_PUBLIC_API(JSDValue*) +JSD_GetValueConstructor(JSDContext* jsdc, JSDValue* jsdval) +{ + JSD_ASSERT_VALID_CONTEXT(jsdc); + JSD_ASSERT_VALID_VALUE(jsdval); + return jsd_GetValueConstructor(jsdc, jsdval); +} + +JSD_PUBLIC_API(const char*) +JSD_GetValueClassName(JSDContext* jsdc, JSDValue* jsdval) +{ + JSD_ASSERT_VALID_CONTEXT(jsdc); + JSD_ASSERT_VALID_VALUE(jsdval); + return jsd_GetValueClassName(jsdc, jsdval); +} + +JSD_PUBLIC_API(JSDScript*) +JSD_GetScriptForValue(JSDContext* jsdc, JSDValue* jsdval) +{ + JSD_ASSERT_VALID_CONTEXT(jsdc); + return jsd_GetScriptForValue(jsdc, jsdval); +} +/**************************************************/ + +JSD_PUBLIC_API(void) +JSD_DropProperty(JSDContext* jsdc, JSDProperty* jsdprop) +{ + JSD_ASSERT_VALID_CONTEXT(jsdc); + JSD_ASSERT_VALID_PROPERTY(jsdprop); + jsd_DropProperty(jsdc, jsdprop); +} + + +JSD_PUBLIC_API(JSDValue*) +JSD_GetPropertyName(JSDContext* jsdc, JSDProperty* jsdprop) +{ + JSD_ASSERT_VALID_CONTEXT(jsdc); + JSD_ASSERT_VALID_PROPERTY(jsdprop); + return jsd_GetPropertyName(jsdc, jsdprop); +} + +JSD_PUBLIC_API(JSDValue*) +JSD_GetPropertyValue(JSDContext* jsdc, JSDProperty* jsdprop) +{ + JSD_ASSERT_VALID_CONTEXT(jsdc); + JSD_ASSERT_VALID_PROPERTY(jsdprop); + return jsd_GetPropertyValue(jsdc, jsdprop); +} + +JSD_PUBLIC_API(JSDValue*) +JSD_GetPropertyAlias(JSDContext* jsdc, JSDProperty* jsdprop) +{ + JSD_ASSERT_VALID_CONTEXT(jsdc); + JSD_ASSERT_VALID_PROPERTY(jsdprop); + return jsd_GetPropertyAlias(jsdc, jsdprop); +} + +JSD_PUBLIC_API(unsigned) +JSD_GetPropertyFlags(JSDContext* jsdc, JSDProperty* jsdprop) +{ + JSD_ASSERT_VALID_CONTEXT(jsdc); + JSD_ASSERT_VALID_PROPERTY(jsdprop); + return jsd_GetPropertyFlags(jsdc, jsdprop); +} + +/**************************************************/ +/* Object Functions */ + +JSD_PUBLIC_API(void) +JSD_LockObjectSubsystem(JSDContext* jsdc) +{ + JSD_ASSERT_VALID_CONTEXT(jsdc); + JSD_LOCK_OBJECTS(jsdc); +} + +JSD_PUBLIC_API(void) +JSD_UnlockObjectSubsystem(JSDContext* jsdc) +{ + JSD_ASSERT_VALID_CONTEXT(jsdc); + JSD_UNLOCK_OBJECTS(jsdc); +} + +JSD_PUBLIC_API(JSDObject*) +JSD_IterateObjects(JSDContext* jsdc, JSDObject** iterp) +{ + JSD_ASSERT_VALID_CONTEXT(jsdc); + return jsd_IterateObjects(jsdc, iterp); +} + +JSD_PUBLIC_API(JSObject*) +JSD_GetWrappedObject(JSDContext* jsdc, JSDObject* jsdobj) +{ + JSD_ASSERT_VALID_CONTEXT(jsdc); + JSD_ASSERT_VALID_OBJECT(jsdobj); + return jsd_GetWrappedObject(jsdc, jsdobj); + +} + +JSD_PUBLIC_API(const char*) +JSD_GetObjectNewURL(JSDContext* jsdc, JSDObject* jsdobj) +{ + JSD_ASSERT_VALID_CONTEXT(jsdc); + JSD_ASSERT_VALID_OBJECT(jsdobj); + return jsd_GetObjectNewURL(jsdc, jsdobj); +} + +JSD_PUBLIC_API(unsigned) +JSD_GetObjectNewLineNumber(JSDContext* jsdc, JSDObject* jsdobj) +{ + JSD_ASSERT_VALID_CONTEXT(jsdc); + JSD_ASSERT_VALID_OBJECT(jsdobj); + return jsd_GetObjectNewLineNumber(jsdc, jsdobj); +} + +JSD_PUBLIC_API(const char*) +JSD_GetObjectConstructorURL(JSDContext* jsdc, JSDObject* jsdobj) +{ + JSD_ASSERT_VALID_CONTEXT(jsdc); + JSD_ASSERT_VALID_OBJECT(jsdobj); + return jsd_GetObjectConstructorURL(jsdc, jsdobj); +} + +JSD_PUBLIC_API(unsigned) +JSD_GetObjectConstructorLineNumber(JSDContext* jsdc, JSDObject* jsdobj) +{ + JSD_ASSERT_VALID_CONTEXT(jsdc); + JSD_ASSERT_VALID_OBJECT(jsdobj); + return jsd_GetObjectConstructorLineNumber(jsdc, jsdobj); +} + +JSD_PUBLIC_API(const char*) +JSD_GetObjectConstructorName(JSDContext* jsdc, JSDObject* jsdobj) +{ + JSD_ASSERT_VALID_CONTEXT(jsdc); + JSD_ASSERT_VALID_OBJECT(jsdobj); + return jsd_GetObjectConstructorName(jsdc, jsdobj); +} + +JSD_PUBLIC_API(JSDObject*) +JSD_GetJSDObjectForJSObject(JSDContext* jsdc, JSObject* jsobj) +{ + JSD_ASSERT_VALID_CONTEXT(jsdc); + JS_ASSERT(jsobj); + return jsd_GetJSDObjectForJSObject(jsdc, jsobj); +} + +JSD_PUBLIC_API(JSDObject*) +JSD_GetObjectForValue(JSDContext* jsdc, JSDValue* jsdval) +{ + JSD_ASSERT_VALID_CONTEXT(jsdc); + JSD_ASSERT_VALID_VALUE(jsdval); + return jsd_GetObjectForValue(jsdc, jsdval); +} + +JSD_PUBLIC_API(JSDValue*) +JSD_GetValueForObject(JSDContext* jsdc, JSDObject* jsdobj) +{ + JSD_ASSERT_VALID_CONTEXT(jsdc); + JSD_ASSERT_VALID_OBJECT(jsdobj); + return jsd_GetValueForObject(jsdc, jsdobj); +} + +/***************************************************************************/ +/* Livewire specific API */ +#ifdef LIVEWIRE + +JSD_PUBLIC_API(LWDBGScript*) +JSDLW_GetLWScript(JSDContext* jsdc, JSDScript* jsdscript) +{ + JSD_ASSERT_VALID_CONTEXT(jsdc); + JSD_ASSERT_VALID_SCRIPT(jsdscript); + return jsdlw_GetLWScript(jsdc, jsdscript); +} + +JSD_PUBLIC_API(JSDSourceText*) +JSDLW_PreLoadSource( JSDContext* jsdc, LWDBGApp* app, + const char* filename, JSBool clear ) +{ + JSD_ASSERT_VALID_CONTEXT(jsdc); + JS_ASSERT(app); + JS_ASSERT(filename); + return jsdlw_PreLoadSource(jsdc, app, filename, clear); +} + +JSD_PUBLIC_API(JSDSourceText*) +JSDLW_ForceLoadSource( JSDContext* jsdc, JSDSourceText* jsdsrc ) +{ + JSD_ASSERT_VALID_CONTEXT(jsdc); + JSD_ASSERT_VALID_SOURCE_TEXT(jsdsrc); + return jsdlw_ForceLoadSource(jsdc, jsdsrc); +} + +JSD_PUBLIC_API(JSBool) +JSDLW_RawToProcessedLineNumber(JSDContext* jsdc, JSDScript* jsdscript, + unsigned lineIn, unsigned* lineOut) +{ + JSD_ASSERT_VALID_CONTEXT(jsdc); + JSD_ASSERT_VALID_SCRIPT(jsdscript); + return jsdlw_RawToProcessedLineNumber(jsdc, jsdscript, lineIn, lineOut); +} + +JSD_PUBLIC_API(JSBool) +JSDLW_ProcessedToRawLineNumber(JSDContext* jsdc, JSDScript* jsdscript, + unsigned lineIn, unsigned* lineOut) +{ + JSD_ASSERT_VALID_CONTEXT(jsdc); + JSD_ASSERT_VALID_SCRIPT(jsdscript); + return jsdlw_ProcessedToRawLineNumber(jsdc, jsdscript, lineIn, lineOut); +} + +#endif +/***************************************************************************/ diff --git a/js/jsd/jsdebug.h b/js/jsd/jsdebug.h new file mode 100644 index 0000000..ddb2fa0 --- /dev/null +++ b/js/jsd/jsdebug.h @@ -0,0 +1,1564 @@ +/* -*- 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/. */ + +/* + * Header for JavaScript Debugging support - All public functions + */ + +#ifndef jsdebug_h___ +#define jsdebug_h___ + +/* Get jstypes.h included first. After that we can use PR macros for doing +* this extern "C" stuff! +*/ +#ifdef __cplusplus +extern "C" +{ +#endif +#include "jstypes.h" +#ifdef __cplusplus +} +#endif + +#include "jsapi.h" +#include "jsdbgapi.h" +#ifdef LIVEWIRE +#include "lwdbgapi.h" +#endif + +JS_BEGIN_EXTERN_C + +/* + * The linkage of JSD API functions differs depending on whether the file is + * used within the JSD library or not. Any source file within the JSD + * libraray should define EXPORT_JSD_API whereas any client of the library + * should not. + */ +#ifdef EXPORT_JSD_API +#define JSD_PUBLIC_API(t) JS_EXPORT_API(t) +#define JSD_PUBLIC_DATA(t) JS_EXPORT_DATA(t) +#else +#define JSD_PUBLIC_API(t) JS_IMPORT_API(t) +#define JSD_PUBLIC_DATA(t) JS_IMPORT_DATA(t) +#endif + +#define JSD_FRIEND_API(t) JSD_PUBLIC_API(t) +#define JSD_FRIEND_DATA(t) JSD_PUBLIC_DATA(t) + +/***************************************************************************/ +/* Opaque typedefs for handles */ + +typedef struct JSDContext JSDContext; +typedef struct JSDScript JSDScript; +typedef struct JSDSourceText JSDSourceText; +typedef struct JSDThreadState JSDThreadState; +typedef struct JSDStackFrameInfo JSDStackFrameInfo; +typedef struct JSDValue JSDValue; +typedef struct JSDProperty JSDProperty; +typedef struct JSDObject JSDObject; + +/***************************************************************************/ +/* High Level calls */ + +/* +* callback stuff for calls in EXE to be accessed by this code +* when it lives in an explicitly loaded DLL +*/ + +/* +* This callback allows JSD to inform the embedding when JSD has been +* turned on or off. This is especially useful in the Java-based debugging +* system used in mozilla because the debugger applet controls starting +* up the JSD system. +*/ +typedef void +(* JSD_SetContextProc)(JSDContext* jsdc, void* user); + +/* This struct could have more fields in future versions */ +typedef struct +{ + unsigned size; /* size of this struct (init before use)*/ + JSD_SetContextProc setContext; +} JSD_UserCallbacks; + +/* +* Used by an embedding to tell JSD what JSRuntime to use and to set +* callbacks without starting up JSD. This assumes only one JSRuntime +* will be used. This exists to support the mozilla Java-based debugger +* system. +*/ +extern JSD_PUBLIC_API(void) +JSD_SetUserCallbacks(JSRuntime* jsrt, + JSD_UserCallbacks* callbacks, + void* user); + +/* +* Startup JSD. +* This version of the init function requires that JSD_SetUserCallbacks() +* has been previously called (with a non-NULL callback struct pointer) +*/ +extern JSD_PUBLIC_API(JSDContext*) +JSD_DebuggerOn(void); + +/* +* Startup JSD on a particular JSRuntime with (optional) callbacks +*/ +extern JSD_PUBLIC_API(JSDContext*) +JSD_DebuggerOnForUser(JSRuntime* jsrt, + JSD_UserCallbacks* callbacks, + void* user); + +/* + * Startup JSD in an application that uses compartments. Debugger + * objects will be allocated in the same compartment as scopeobj. + */ +extern JSD_PUBLIC_API(JSDContext*) +JSD_DebuggerOnForUserWithCompartment(JSRuntime* jsrt, + JSD_UserCallbacks* callbacks, + void* user, + JSObject* scopeobj); + +/* +* Shutdown JSD for this JSDContext +*/ +extern JSD_PUBLIC_API(void) +JSD_DebuggerOff(JSDContext* jsdc); + +/* + * Pause JSD for this JSDContext + */ +extern JSD_PUBLIC_API(void) +JSD_DebuggerPause(JSDContext* jsdc); + +/* + * Unpause JSD for this JSDContext + */ +extern JSD_PUBLIC_API(void) +JSD_DebuggerUnpause(JSDContext* jsdc); + +/* +* Get the Major Version (initial JSD release used major version = 1) +*/ +extern JSD_PUBLIC_API(unsigned) +JSD_GetMajorVersion(void); + +/* +* Get the Minor Version (initial JSD release used minor version = 0) +*/ +extern JSD_PUBLIC_API(unsigned) +JSD_GetMinorVersion(void); + +/* +* Returns the default JSD global associated with a given JSDContext. +*/ +extern JSD_PUBLIC_API(JSObject*) +JSD_GetDefaultGlobal(JSDContext* jsdc); + +/* +* Returns a JSRuntime this context is associated with +*/ +extern JSD_PUBLIC_API(JSRuntime*) +JSD_GetJSRuntime(JSDContext* jsdc); + +/* +* Set the private data for this context, returns previous value +*/ +extern JSD_PUBLIC_API(void *) +JSD_SetContextPrivate(JSDContext *jsdc, void *data); + +/* +* Get the private data for this context +*/ +extern JSD_PUBLIC_API(void *) +JSD_GetContextPrivate(JSDContext *jsdc); + +/* +* Clear profile data for all scripts +*/ +extern JSD_PUBLIC_API(void) +JSD_ClearAllProfileData(JSDContext* jsdc); + +/* +* Context flags. +*/ + +/* Include native frames in JSDThreadStates. */ +#define JSD_INCLUDE_NATIVE_FRAMES 0x01 +/* +* Normally, if a script has a 0 in JSD_SCRIPT_PROFILE_BIT it is included in +* profile data, otherwise it is not profiled. Setting the JSD_PROFILE_WHEN_SET +* flag reverses this convention. +*/ +#define JSD_PROFILE_WHEN_SET 0x02 +/* +* Normally, when the script in the top frame of a thread state has a 1 in +* JSD_SCRIPT_DEBUG_BIT, the execution hook is ignored. Setting the +* JSD_DEBUG_WHEN_SET flag reverses this convention. +*/ +#define JSD_DEBUG_WHEN_SET 0x04 +/* +* When this flag is set the internal call hook will collect profile data. +*/ +#define JSD_COLLECT_PROFILE_DATA 0x08 +/* +* When this flag is set, stack frames that are disabled for debugging +* will not appear in the call stack chain. +*/ +#define JSD_HIDE_DISABLED_FRAMES 0x10 +/* +* When this flag is set, the debugger will only check the +* JSD_SCRIPT_DEBUG_BIT on the top (most recent) stack frame. This +* makes it possible to stop in an enabled frame which was called from +* a stack that contains a disabled frame. +* +* When this flag is *not* set, any stack that contains a disabled frame +* will not be debugged (the execution hook will not be invoked.) +* +* This only applies when the reason for calling the hook would have +* been JSD_HOOK_INTERRUPTED or JSD_HOOK_THROW. JSD_HOOK_BREAKPOINT, +* JSD_HOOK_DEBUG_REQUESTED, and JSD_HOOK_DEBUGGER_KEYWORD always stop, +* regardless of this setting, as long as the top frame is not disabled. +* +* If JSD_HIDE_DISABLED_FRAMES is set, this is effectively set as well. +*/ +#define JSD_MASK_TOP_FRAME_ONLY 0x20 + +/* +* 0x40 was formerly used to hook into object creation. +*/ +#define JSD_DISABLE_OBJECT_TRACE_RETIRED 0x40 + + +extern JSD_PUBLIC_API(void) +JSD_SetContextFlags (JSDContext* jsdc, uint32_t flags); + +extern JSD_PUBLIC_API(uint32_t) +JSD_GetContextFlags (JSDContext* jsdc); + +/* +* Notify JSD that this JSContext is 'in use'. This allows JSD to hook the +* ErrorReporter. For the most part this is done automatically whenever +* events like script loading happen. But, it is a good idea to call this +* from the embedding when new contexts come into use. +*/ +extern JSD_PUBLIC_API(void) +JSD_JSContextInUse(JSDContext* jsdc, JSContext* context); + +/* +* Find the JSDContext (if any) associated with the JSRuntime of a +* given JSContext. +*/ +extern JSD_PUBLIC_API(JSDContext*) +JSD_JSDContextForJSContext(JSContext* context); + +/***************************************************************************/ +/* Script functions */ + +/* +* Lock the entire script subsystem. This grabs a highlevel lock that +* protects the JSD internal information about scripts. It is important +* to wrap script related calls in this lock in multithreaded situations +* -- i.e. where the debugger is running on a different thread than the +* interpreter -- or when multiple debugger threads may be accessing this +* subsystem. It is safe (and best) to use this locking even if the +* environment might not be multi-threaded. Safely nestable. +*/ +extern JSD_PUBLIC_API(void) +JSD_LockScriptSubsystem(JSDContext* jsdc); + +/* +* Unlock the entire script subsystem -- see JSD_LockScriptSubsystem +*/ +extern JSD_PUBLIC_API(void) +JSD_UnlockScriptSubsystem(JSDContext* jsdc); + +/* +* Iterate through all the active scripts for this JSDContext. +* NOTE: must initialize iterp to NULL to start iteration. +* NOTE: must lock and unlock the subsystem +* example: +* +* JSDScript jsdscript; +* JSDScript iter = NULL; +* +* JSD_LockScriptSubsystem(jsdc); +* while((jsdscript = JSD_IterateScripts(jsdc, &iter)) != NULL) { +* *** use jsdscript here *** +* } +* JSD_UnlockScriptSubsystem(jsdc); +*/ +extern JSD_PUBLIC_API(JSDScript*) +JSD_IterateScripts(JSDContext* jsdc, JSDScript **iterp); + +/* +* Get the number of times this script has been called. +*/ +extern JSD_PUBLIC_API(unsigned) +JSD_GetScriptCallCount(JSDContext* jsdc, JSDScript *script); + +/* +* Get the max number of times this script called itself, directly or indirectly. +*/ +extern JSD_PUBLIC_API(unsigned) +JSD_GetScriptMaxRecurseDepth(JSDContext* jsdc, JSDScript *script); + +/* +* Get the shortest execution time recorded. +*/ +extern JSD_PUBLIC_API(double) +JSD_GetScriptMinExecutionTime(JSDContext* jsdc, JSDScript *script); + +/* +* Get the longest execution time recorded. +*/ +extern JSD_PUBLIC_API(double) +JSD_GetScriptMaxExecutionTime(JSDContext* jsdc, JSDScript *script); + +/* +* Get the total amount of time spent in this script. +*/ +extern JSD_PUBLIC_API(double) +JSD_GetScriptTotalExecutionTime(JSDContext* jsdc, JSDScript *script); + +/* +* Get the shortest execution time recorded, excluding time spent in called +* functions. +*/ +extern JSD_PUBLIC_API(double) +JSD_GetScriptMinOwnExecutionTime(JSDContext* jsdc, JSDScript *script); + +/* +* Get the longest execution time recorded, excluding time spent in called +* functions. +*/ +extern JSD_PUBLIC_API(double) +JSD_GetScriptMaxOwnExecutionTime(JSDContext* jsdc, JSDScript *script); + +/* +* Get the total amount of time spent in this script, excluding time spent +* in called functions. +*/ +extern JSD_PUBLIC_API(double) +JSD_GetScriptTotalOwnExecutionTime(JSDContext* jsdc, JSDScript *script); + +/* +* Clear profile data for this script. +*/ +extern JSD_PUBLIC_API(void) +JSD_ClearScriptProfileData(JSDContext* jsdc, JSDScript *script); + +/* +* Get the JSScript for a JSDScript +*/ +extern JSD_PUBLIC_API(JSScript*) +JSD_GetJSScript(JSDContext* jsdc, JSDScript *script); + +/* +* Get the JSFunction for a JSDScript +*/ +extern JSD_PUBLIC_API(JSFunction*) +JSD_GetJSFunction(JSDContext* jsdc, JSDScript *script); + +/* +* Determines whether or not to collect profile information for this +* script. The context flag JSD_PROFILE_WHEN_SET decides the logic. +*/ +#define JSD_SCRIPT_PROFILE_BIT 0x01 +/* +* Determines whether or not to ignore breakpoints, etc. in this script. +* The context flag JSD_DEBUG_WHEN_SET decides the logic. +*/ +#define JSD_SCRIPT_DEBUG_BIT 0x02 +/* + * Determines whether to invoke the onScriptDestroy callback for this + * script. The default is for this to be true if the onScriptCreated + * callback was invoked for this script. + */ +#define JSD_SCRIPT_CALL_DESTROY_HOOK_BIT 0x04 + +extern JSD_PUBLIC_API(uint32_t) +JSD_GetScriptFlags(JSDContext *jsdc, JSDScript* jsdscript); + +extern JSD_PUBLIC_API(void) +JSD_SetScriptFlags(JSDContext *jsdc, JSDScript* jsdscript, uint32_t flags); + +/* +* Set the private data for this script, returns previous value +*/ +extern JSD_PUBLIC_API(void *) +JSD_SetScriptPrivate(JSDScript* jsdscript, void *data); + +/* +* Get the private data for this script +*/ +extern JSD_PUBLIC_API(void *) +JSD_GetScriptPrivate(JSDScript* jsdscript); + +/* +* Determine if this script is still loaded in the interpreter +*/ +extern JSD_PUBLIC_API(JSBool) +JSD_IsActiveScript(JSDContext* jsdc, JSDScript *jsdscript); + +/* +* Get the filename associated with this script +*/ +extern JSD_PUBLIC_API(const char*) +JSD_GetScriptFilename(JSDContext* jsdc, JSDScript *jsdscript); + +/* +* Get the function name associated with this script (NULL if not a function). +* If the function does not have a name the result is the string "anonymous". +* *** new for gecko 2.0 **** +*/ +extern JSD_PUBLIC_API(JSString *) +JSD_GetScriptFunctionId(JSDContext* jsdc, JSDScript *jsdscript); + +/* +* Get the base linenumber of the sourcefile from which this script was loaded. +* This is one-based -- i.e. the first line of a file is line '1'. This may +* return 0 if this infomation is unknown. +*/ +extern JSD_PUBLIC_API(unsigned) +JSD_GetScriptBaseLineNumber(JSDContext* jsdc, JSDScript *jsdscript); + +/* +* Get the count of source lines associated with this script (1 or greater) +*/ +extern JSD_PUBLIC_API(unsigned) +JSD_GetScriptLineExtent(JSDContext* jsdc, JSDScript *jsdscript); + +/* +* Declaration of callback for notification of script creation and destruction. +* 'creating' is JS_TRUE if creating new script, JS_FALSE if destroying existing +* script (callback called just before actual destruction). +* 'callerdata' is what was passed to JSD_SetScriptHook to set the hook. +*/ +typedef void +(* JSD_ScriptHookProc)(JSDContext* jsdc, + JSDScript* jsdscript, + JSBool creating, + void* callerdata); + +/* +* Set a hook to be called when scripts are created or destroyed (loaded or +* unloaded). +* 'callerdata' can be whatever you want it to be. +*/ +extern JSD_PUBLIC_API(JSBool) +JSD_SetScriptHook(JSDContext* jsdc, JSD_ScriptHookProc hook, void* callerdata); + +/* +* Get the current script hook. +*/ +extern JSD_PUBLIC_API(JSBool) +JSD_GetScriptHook(JSDContext* jsdc, JSD_ScriptHookProc* hook, void** callerdata); + +/* +* Get a 'Program Counter' value for a given line. This represents the location +* of the first bit of executable code for this line of source. This 'pc' should +* be considered an opaque handle. +* 0 is returned for invalid scripts, or lines that lie outside the script. +* If no code is on the given line, then the returned pc represents the first +* code within the script (if any) after the given line. +* This function can be used to set breakpoints -- see JSD_SetExecutionHook +*/ +extern JSD_PUBLIC_API(uintptr_t) +JSD_GetClosestPC(JSDContext* jsdc, JSDScript* jsdscript, unsigned line); + +/* +* Get the source line number for a given 'Program Counter' location. +* Returns 0 if no source line information is appropriate (or available) for +* the given pc. +*/ +extern JSD_PUBLIC_API(unsigned) +JSD_GetClosestLine(JSDContext* jsdc, JSDScript* jsdscript, uintptr_t pc); + +/* + * Get a list of lines and the corresponding earliest PC for each (see + * JSD_GetClosestPC). Lines with no PCs associated will not be returned. NULL + * may be passed for either lines or pcs to avoid filling anything in for that + * argument. + */ +extern JSD_PUBLIC_API(JSBool) +JSD_GetLinePCs(JSDContext* jsdc, JSDScript* jsdscript, + unsigned startLine, unsigned maxLines, + unsigned* count, unsigned** lines, uintptr_t** pcs); + +/* these are only used in cases where scripts are created outside of JS*/ + +/* +* Direct call to notify JSD that a script has been created. +* Embeddings that use the normal jsapi script functions need not call this. +* Any embedding that follows the (discouraged!) practice of contructing script +* structures manually should call this function to inform JSD. (older ssjs +* systems do this). +*/ +extern JSD_PUBLIC_API(void) +JSD_ScriptCreated(JSDContext* jsdc, + JSContext *cx, + const char *filename, /* URL this script loads from */ + unsigned lineno, /* line where this script starts */ + JSScript *script, + JSFunction *fun); + +/* +* see JSD_ScriptCreated +*/ +extern JSD_PUBLIC_API(void) +JSD_ScriptDestroyed(JSDContext* jsdc, + JSFreeOp *fop, + JSScript *script); + +/***************************************************************************/ +/* Source Text functions */ + +/* +* In some embeddings (e.g. mozilla) JavaScript source code from a 'file' may be +* execute before the entire 'file' has even been loaded. This system supports +* access to such incrmentally loaded source. It also allows for the possibility +* that source loading may fail or be aborted (though the source that did load +* may still be usable). Remember that this source is the entire 'file' +* contents and that the JavaScript code may only be part of that source. +* +* For any given URL there can only be one source text item (the most recently +* loaded). +*/ + +/* these coorespond to netscape.jsdebug.SourceTextItem.java values - +* change in both places if anywhere +*/ + +typedef enum +{ + JSD_SOURCE_INITED = 0, /* initialized, but contains no source yet */ + JSD_SOURCE_PARTIAL = 1, /* some source loaded, more expected */ + JSD_SOURCE_COMPLETED = 2, /* all source finished loading */ + JSD_SOURCE_ABORTED = 3, /* user aborted loading, some may be loaded */ + JSD_SOURCE_FAILED = 4, /* loading failed, some may be loaded */ + JSD_SOURCE_CLEARED = 5 /* text has been cleared by debugger */ +} JSDSourceStatus; + +/* +* Lock the entire source text subsystem. This grabs a highlevel lock that +* protects the JSD internal information about sources. It is important to +* wrap source text related calls in this lock in multithreaded situations +* -- i.e. where the debugger is running on a different thread than the +* interpreter (or the loader of sources) -- or when multiple debugger +* threads may be accessing this subsystem. It is safe (and best) to use +* this locking even if the environment might not be multi-threaded. +* Safely Nestable. +*/ +extern JSD_PUBLIC_API(void) +JSD_LockSourceTextSubsystem(JSDContext* jsdc); + +/* +* Unlock the entire source text subsystem. see JSD_LockSourceTextSubsystem. +*/ +extern JSD_PUBLIC_API(void) +JSD_UnlockSourceTextSubsystem(JSDContext* jsdc); + +/* +* Iterate the source test items. Use the same pattern of calls shown in +* the example for JSD_IterateScripts - NOTE that the SourceTextSubsystem. +* must be locked before and unlocked after iterating. +*/ +extern JSD_PUBLIC_API(JSDSourceText*) +JSD_IterateSources(JSDContext* jsdc, JSDSourceText **iterp); + +/* +* Find the source text item for the given URL (or filename - or whatever +* string the given embedding uses to describe source locations). +* Returns NULL is not found. +*/ +extern JSD_PUBLIC_API(JSDSourceText*) +JSD_FindSourceForURL(JSDContext* jsdc, const char* url); + +/* +* Get the URL string associated with the given source text item +*/ +extern JSD_PUBLIC_API(const char*) +JSD_GetSourceURL(JSDContext* jsdc, JSDSourceText* jsdsrc); + +/* +* Get the actual source text. This gives access to the actual storage of +* the source - it sHould *not* be written to. +* The buffer is NOT zero terminated (nor is it guaranteed to have space to +* hold a zero terminating char). +* XXX this is 8-bit character data. Unicode source is not yet supported. +*/ +extern JSD_PUBLIC_API(JSBool) +JSD_GetSourceText(JSDContext* jsdc, JSDSourceText* jsdsrc, + const char** ppBuf, int* pLen); + +/* +* Clear the text -- delete the text and set the status to JSD_SOURCE_CLEARED. +* This is useful if source is done loading and the debugger wishes to store +* the text data itself (e.g. in a Java String). This allows avoidance of +* storing the same text in multiple places. +*/ +extern JSD_PUBLIC_API(void) +JSD_ClearSourceText(JSDContext* jsdc, JSDSourceText* jsdsrc); + +/* +* Return the status of the source text item. see JSDSourceStatus enum. +*/ +extern JSD_PUBLIC_API(JSDSourceStatus) +JSD_GetSourceStatus(JSDContext* jsdc, JSDSourceText* jsdsrc); + +/* +* Has the source been altered since the last call to JSD_SetSourceDirty? +* Use of JSD_IsSourceDirty and JSD_SetSourceDirty is still supported, but +* discouraged in favor of the JSD_GetSourceAlterCount system. This dirty +* scheme ASSUMES that there is only one consumer of the data. +*/ +extern JSD_PUBLIC_API(JSBool) +JSD_IsSourceDirty(JSDContext* jsdc, JSDSourceText* jsdsrc); + +/* +* Clear the dirty flag +*/ +extern JSD_PUBLIC_API(void) +JSD_SetSourceDirty(JSDContext* jsdc, JSDSourceText* jsdsrc, JSBool dirty); + +/* +* Each time a source text item is altered this value is incremented. Any +* consumer can store this value when they retieve other data about the +* source text item and then check later to see if the current value is +* different from their stored value. Thus they can know if they have stale +* data or not. NOTE: this value is not gauranteed to start at any given number. +*/ +extern JSD_PUBLIC_API(unsigned) +JSD_GetSourceAlterCount(JSDContext* jsdc, JSDSourceText* jsdsrc); + +/* +* Force an increment in the alter count for a source text item. This is +* normally automatic when the item changes, but a give consumer may want to +* force this to amke an item appear to have changed even if it has not. +*/ +extern JSD_PUBLIC_API(unsigned) +JSD_IncrementSourceAlterCount(JSDContext* jsdc, JSDSourceText* jsdsrc); + +/* +* Destroy *all* the source text items +* (new for server-side USE WITH CARE) +*/ +extern JSD_PUBLIC_API(void) +JSD_DestroyAllSources( JSDContext* jsdc ); + +/* functions for adding source items */ + +/* +* Add a new item for a given URL. If an iten already exists for the given URL +* then the old item is removed. +* 'url' may not be NULL. +* +* ifdef LIVEWIRE url is treated as a char* and ownership is claimed by jsd +*/ +extern JSD_PUBLIC_API(JSDSourceText*) +JSD_NewSourceText(JSDContext* jsdc, const char* url); + +/* +* Append text (or change status -- e.g. set completed) for a source text +* item. Text need not be zero terminated. Callers should consider the returned +* JSDSourceText to be the 'current' item for future use. This may return NULL +* if called after this item has been replaced by a call to JSD_NewSourceText. +*/ +extern JSD_PUBLIC_API(JSDSourceText*) +JSD_AppendSourceText(JSDContext* jsdc, + JSDSourceText* jsdsrc, + const char* text, /* *not* zero terminated */ + size_t length, + JSDSourceStatus status); + +/* +* Unicode varient of JSD_AppendSourceText. +* +* NOTE: At this point text is stored in 8bit ASCII so this function just +* extracts the bottom 8bits from each jschar. At some future point we may +* switch to storing and exposing 16bit Unicode. +*/ +extern JSD_PUBLIC_API(JSDSourceText*) +JSD_AppendUCSourceText(JSDContext* jsdc, + JSDSourceText* jsdsrc, + const jschar* text, /* *not* zero terminated */ + size_t length, + JSDSourceStatus status); +/* + * Convienence function for adding complete source of url in one call. + * same as: + * JSDSourceText* jsdsrc; + * JSD_LOCK_SOURCE_TEXT(jsdc); + * jsdsrc = jsd_NewSourceText(jsdc, url); + * if(jsdsrc) + * jsdsrc = jsd_AppendSourceText(jsdc, jsdsrc, + * text, length, JSD_SOURCE_PARTIAL); + * if(jsdsrc) + * jsdsrc = jsd_AppendSourceText(jsdc, jsdsrc, + * NULL, 0, JSD_SOURCE_COMPLETED); + * JSD_UNLOCK_SOURCE_TEXT(jsdc); + * return jsdsrc ? JS_TRUE : JS_FALSE; + */ +extern JSD_PUBLIC_API(JSBool) +JSD_AddFullSourceText(JSDContext* jsdc, + const char* text, /* *not* zero terminated */ + size_t length, + const char* url); + +/***************************************************************************/ +/* Execution/Interrupt Hook functions */ + +/* possible 'type' params for JSD_ExecutionHookProc */ +#define JSD_HOOK_INTERRUPTED 0 +#define JSD_HOOK_BREAKPOINT 1 +#define JSD_HOOK_DEBUG_REQUESTED 2 +#define JSD_HOOK_DEBUGGER_KEYWORD 3 +#define JSD_HOOK_THROW 4 + +/* legal return values for JSD_ExecutionHookProc */ +#define JSD_HOOK_RETURN_HOOK_ERROR 0 +#define JSD_HOOK_RETURN_CONTINUE 1 +#define JSD_HOOK_RETURN_ABORT 2 +#define JSD_HOOK_RETURN_RET_WITH_VAL 3 +#define JSD_HOOK_RETURN_THROW_WITH_VAL 4 +#define JSD_HOOK_RETURN_CONTINUE_THROW 5 + +/* +* Implement a callback of this form in order to hook execution. +*/ +typedef unsigned +(* JSD_ExecutionHookProc)(JSDContext* jsdc, + JSDThreadState* jsdthreadstate, + unsigned type, + void* callerdata, + jsval* rval); + +/* possible 'type' params for JSD_CallHookProc */ +#define JSD_HOOK_TOPLEVEL_START 0 /* about to evaluate top level script */ +#define JSD_HOOK_TOPLEVEL_END 1 /* done evaluting top level script */ +#define JSD_HOOK_FUNCTION_CALL 2 /* about to call a function */ +#define JSD_HOOK_FUNCTION_RETURN 3 /* done calling function */ + +/* +* Implement a callback of this form in order to hook function call/returns. +* Return JS_TRUE from a TOPLEVEL_START or FUNCTION_CALL type call hook if you +* want to hear about the TOPLEVEL_END or FUNCTION_RETURN too. Return value is +* ignored to TOPLEVEL_END and FUNCTION_RETURN type hooks. +*/ +typedef JSBool +(* JSD_CallHookProc)(JSDContext* jsdc, + JSDThreadState* jsdthreadstate, + unsigned type, + void* callerdata); + +/* +* Set Hook to be called whenever the given pc is about to be executed -- +* i.e. for 'trap' or 'breakpoint' +*/ +extern JSD_PUBLIC_API(JSBool) +JSD_SetExecutionHook(JSDContext* jsdc, + JSDScript* jsdscript, + uintptr_t pc, + JSD_ExecutionHookProc hook, + void* callerdata); + +/* +* Clear the hook for this pc +*/ +extern JSD_PUBLIC_API(JSBool) +JSD_ClearExecutionHook(JSDContext* jsdc, + JSDScript* jsdscript, + uintptr_t pc); + +/* +* Clear all the pc specific hooks for this script +*/ +extern JSD_PUBLIC_API(JSBool) +JSD_ClearAllExecutionHooksForScript(JSDContext* jsdc, JSDScript* jsdscript); + +/* +* Clear all the pc specific hooks for the entire JSRuntime associated with +* this JSDContext +*/ +extern JSD_PUBLIC_API(JSBool) +JSD_ClearAllExecutionHooks(JSDContext* jsdc); + +/* +* Set a hook to be called before the next instruction is executed. Depending +* on the threading situation and whether or not an JS code is currently +* executing the hook might be called before this call returns, or at some +* future time. The hook will continue to be called as each instruction +* executes until cleared. +*/ +extern JSD_PUBLIC_API(JSBool) +JSD_SetInterruptHook(JSDContext* jsdc, + JSD_ExecutionHookProc hook, + void* callerdata); + +/* +* Call the interrupt hook at least once per source line +*/ +extern JSD_PUBLIC_API(JSBool) +JSD_EnableSingleStepInterrupts(JSDContext* jsdc, JSDScript *jsdscript, JSBool enable); + +/* +* Clear the current interrupt hook. +*/ +extern JSD_PUBLIC_API(JSBool) +JSD_ClearInterruptHook(JSDContext* jsdc); + +/* +* Set the hook that should be called whenever a JSD_ErrorReporter hook +* returns JSD_ERROR_REPORTER_DEBUG. +*/ +extern JSD_PUBLIC_API(JSBool) +JSD_SetDebugBreakHook(JSDContext* jsdc, + JSD_ExecutionHookProc hook, + void* callerdata); + +/* +* Clear the debug break hook +*/ +extern JSD_PUBLIC_API(JSBool) +JSD_ClearDebugBreakHook(JSDContext* jsdc); + +/* +* Set the hook that should be called when the 'debugger' keyword is +* encountered by the JavaScript interpreter during execution. +*/ +extern JSD_PUBLIC_API(JSBool) +JSD_SetDebuggerHook(JSDContext* jsdc, + JSD_ExecutionHookProc hook, + void* callerdata); + +/* +* Clear the 'debugger' keyword hook +*/ +extern JSD_PUBLIC_API(JSBool) +JSD_ClearDebuggerHook(JSDContext* jsdc); + +/* +* Set the hook that should be called when a JS exception is thrown. +* NOTE: the 'do default' return value is: JSD_HOOK_RETURN_CONTINUE_THROW +*/ +extern JSD_PUBLIC_API(JSBool) +JSD_SetThrowHook(JSDContext* jsdc, + JSD_ExecutionHookProc hook, + void* callerdata); +/* +* Clear the throw hook +*/ +extern JSD_PUBLIC_API(JSBool) +JSD_ClearThrowHook(JSDContext* jsdc); + +/* +* Set the hook that should be called when a toplevel script begins or completes. +*/ +extern JSD_PUBLIC_API(JSBool) +JSD_SetTopLevelHook(JSDContext* jsdc, + JSD_CallHookProc hook, + void* callerdata); +/* +* Clear the toplevel call hook +*/ +extern JSD_PUBLIC_API(JSBool) +JSD_ClearTopLevelHook(JSDContext* jsdc); + +/* +* Set the hook that should be called when a function call or return happens. +*/ +extern JSD_PUBLIC_API(JSBool) +JSD_SetFunctionHook(JSDContext* jsdc, + JSD_CallHookProc hook, + void* callerdata); +/* +* Clear the function call hook +*/ +extern JSD_PUBLIC_API(JSBool) +JSD_ClearFunctionHook(JSDContext* jsdc); + +/***************************************************************************/ +/* Stack Frame functions */ + +/* +* Get the count of call stack frames for the given JSDThreadState +*/ +extern JSD_PUBLIC_API(unsigned) +JSD_GetCountOfStackFrames(JSDContext* jsdc, JSDThreadState* jsdthreadstate); + +/* +* Get the 'current' call stack frame for the given JSDThreadState +*/ +extern JSD_PUBLIC_API(JSDStackFrameInfo*) +JSD_GetStackFrame(JSDContext* jsdc, JSDThreadState* jsdthreadstate); + +/* +* Get the JSContext for the given JSDThreadState +*/ +extern JSD_PUBLIC_API(JSContext*) +JSD_GetJSContext(JSDContext* jsdc, JSDThreadState* jsdthreadstate); + +/* +* Get the calling call stack frame for the given frame +*/ +extern JSD_PUBLIC_API(JSDStackFrameInfo*) +JSD_GetCallingStackFrame(JSDContext* jsdc, + JSDThreadState* jsdthreadstate, + JSDStackFrameInfo* jsdframe); + +/* +* Get the script for the given call stack frame +*/ +extern JSD_PUBLIC_API(JSDScript*) +JSD_GetScriptForStackFrame(JSDContext* jsdc, + JSDThreadState* jsdthreadstate, + JSDStackFrameInfo* jsdframe); + +/* +* Get the 'Program Counter' for the given call stack frame +*/ +extern JSD_PUBLIC_API(uintptr_t) +JSD_GetPCForStackFrame(JSDContext* jsdc, + JSDThreadState* jsdthreadstate, + JSDStackFrameInfo* jsdframe); + +/* +* Get the JavaScript Call Object for the given call stack frame. +* *** new for version 1.1 **** +*/ +extern JSD_PUBLIC_API(JSDValue*) +JSD_GetCallObjectForStackFrame(JSDContext* jsdc, + JSDThreadState* jsdthreadstate, + JSDStackFrameInfo* jsdframe); + +/* +* Get the head of the scope chain for the given call stack frame. +* the chain can be traversed using JSD_GetValueParent. +* *** new for version 1.1 **** +*/ +extern JSD_PUBLIC_API(JSDValue*) +JSD_GetScopeChainForStackFrame(JSDContext* jsdc, + JSDThreadState* jsdthreadstate, + JSDStackFrameInfo* jsdframe); + +/* +* Get the 'this' Object for the given call stack frame. +* *** new for version 1.1 **** +*/ +extern JSD_PUBLIC_API(JSDValue*) +JSD_GetThisForStackFrame(JSDContext* jsdc, + JSDThreadState* jsdthreadstate, + JSDStackFrameInfo* jsdframe); + +/* +* Get the name of the function executing in this stack frame. Especially useful +* for native frames (without script objects.) +* *** new for gecko 2.0 **** +*/ +extern JSD_PUBLIC_API(JSString *) +JSD_GetIdForStackFrame(JSDContext* jsdc, + JSDThreadState* jsdthreadstate, + JSDStackFrameInfo* jsdframe); + +/* +* True if stack frame represents a frame created as a result of a debugger +* evaluation. +*/ +extern JSD_PUBLIC_API(JSBool) +JSD_IsStackFrameDebugger(JSDContext* jsdc, + JSDThreadState* jsdthreadstate, + JSDStackFrameInfo* jsdframe); + +/* +* True if stack frame is constructing a new object. +*/ +extern JSD_PUBLIC_API(JSBool) +JSD_IsStackFrameConstructing(JSDContext* jsdc, + JSDThreadState* jsdthreadstate, + JSDStackFrameInfo* jsdframe); + +/* +* Evaluate the given unicode source code in the context of the given stack frame. +* returns JS_TRUE and puts result in rval on success, JS_FALSE on failure. +* NOTE: The ErrorReporter hook might be called if this fails. +*/ +extern JSD_PUBLIC_API(JSBool) +JSD_EvaluateUCScriptInStackFrame(JSDContext* jsdc, + JSDThreadState* jsdthreadstate, + JSDStackFrameInfo* jsdframe, + const jschar *bytes, unsigned length, + const char *filename, unsigned lineno, + JS::MutableHandleValue rval); + +/* +* Same as above, but does not eat exceptions. +*/ +extern JSD_PUBLIC_API(JSBool) +JSD_AttemptUCScriptInStackFrame(JSDContext* jsdc, + JSDThreadState* jsdthreadstate, + JSDStackFrameInfo* jsdframe, + const jschar *bytes, unsigned length, + const char *filename, unsigned lineno, + JS::MutableHandleValue rval); + +/* single byte character version of JSD_EvaluateUCScriptInStackFrame */ +extern JSD_PUBLIC_API(JSBool) +JSD_EvaluateScriptInStackFrame(JSDContext* jsdc, + JSDThreadState* jsdthreadstate, + JSDStackFrameInfo* jsdframe, + const char *bytes, unsigned length, + const char *filename, unsigned lineno, JS::MutableHandleValue rval); + +/* +* Same as above, but does not eat exceptions. +*/ +extern JSD_PUBLIC_API(JSBool) +JSD_AttemptScriptInStackFrame(JSDContext* jsdc, + JSDThreadState* jsdthreadstate, + JSDStackFrameInfo* jsdframe, + const char *bytes, unsigned length, + const char *filename, unsigned lineno, JS::MutableHandleValue rval); + +/* +* Convert the given jsval to a string +* NOTE: The ErrorReporter hook might be called if this fails. +*/ +extern JSD_PUBLIC_API(JSString*) +JSD_ValToStringInStackFrame(JSDContext* jsdc, + JSDThreadState* jsdthreadstate, + JSDStackFrameInfo* jsdframe, + jsval val); + +/* +* Get the JSDValue currently being thrown as an exception (may be NULL). +* NOTE: must eventually release by calling JSD_DropValue (if not NULL) +* *** new for version 1.1 **** +*/ +extern JSD_PUBLIC_API(JSDValue*) +JSD_GetException(JSDContext* jsdc, JSDThreadState* jsdthreadstate); + +/* +* Set the JSDValue currently being thrown as an exception. +* *** new for version 1.1 **** +*/ +extern JSD_PUBLIC_API(JSBool) +JSD_SetException(JSDContext* jsdc, JSDThreadState* jsdthreadstate, + JSDValue* jsdval); + +/***************************************************************************/ +/* Error Reporter functions */ + +/* +* XXX The ErrorReporter Hook scheme is going to change soon to more +* Fully support exceptions. +*/ + +/* legal return values for JSD_ErrorReporter */ +#define JSD_ERROR_REPORTER_PASS_ALONG 0 /* pass along to regular reporter */ +#define JSD_ERROR_REPORTER_RETURN 1 /* don't pass to error reporter */ +#define JSD_ERROR_REPORTER_DEBUG 2 /* force call to DebugBreakHook */ +#define JSD_ERROR_REPORTER_CLEAR_RETURN 3 /* clear exception and don't pass */ + +/* +* Implement a callback of this form in order to hook the ErrorReporter +*/ +typedef unsigned +(* JSD_ErrorReporter)(JSDContext* jsdc, + JSContext* cx, + const char* message, + JSErrorReport* report, + void* callerdata); + +/* Set ErrorReporter hook */ +extern JSD_PUBLIC_API(JSBool) +JSD_SetErrorReporter(JSDContext* jsdc, + JSD_ErrorReporter reporter, + void* callerdata); + +/* Get Current ErrorReporter hook */ +extern JSD_PUBLIC_API(JSBool) +JSD_GetErrorReporter(JSDContext* jsdc, + JSD_ErrorReporter* reporter, + void** callerdata); + +/***************************************************************************/ +/* Generic locks that callers can use for their own purposes */ + +struct JSDStaticLock; + +/* +* Is Locking and GetThread supported in this build? +*/ +extern JSD_PUBLIC_API(JSBool) +JSD_IsLockingAndThreadIdSupported(); + +/* +* Create a reentrant/nestable lock +*/ +extern JSD_PUBLIC_API(JSDStaticLock*) +JSD_CreateLock(); + +/* +* Aquire lock for this thread (or block until available). Increments a +* counter if this thread already owns the lock. +*/ +extern JSD_PUBLIC_API(void) +JSD_Lock(JSDStaticLock* lock); + +/* +* Release lock for this thread (or decrement the counter if JSD_Lock +* was previous called more than once). +*/ +extern JSD_PUBLIC_API(void) +JSD_Unlock(JSDStaticLock* lock); + +/* +* For debugging only if not (JS_THREADSAFE AND DEBUG) then returns JS_TRUE +* So JSD_IsLocked(lock) may not equal !JSD_IsUnlocked(lock) +*/ +extern JSD_PUBLIC_API(JSBool) +JSD_IsLocked(JSDStaticLock* lock); + +/* +* See above... +*/ +extern JSD_PUBLIC_API(JSBool) +JSD_IsUnlocked(JSDStaticLock* lock); + +/* +* return an ID uniquely identifying this thread. +*/ +extern JSD_PUBLIC_API(void*) +JSD_CurrentThread(); + +/***************************************************************************/ +/* Value and Property Functions --- All NEW for 1.1 --- */ + +/* +* NOTE: JSDValue and JSDProperty objects are reference counted. This allows +* for rooting these objects AND any underlying garbage collected jsvals. +* ALL JSDValue and JSDProperty objects returned by the functions below +* MUST eventually be released using the appropriate JSD_Dropxxx function. +*/ + +/* +* Create a new JSDValue to wrap the given jsval +* NOTE: must eventually release by calling JSD_DropValue (if not NULL) +* *** new for version 1.1 **** +*/ +extern JSD_PUBLIC_API(JSDValue*) +JSD_NewValue(JSDContext* jsdc, jsval val); + +/* +* Release the JSDValue. After this call the object MUST not be referenced again! +* *** new for version 1.1 **** +*/ +extern JSD_PUBLIC_API(void) +JSD_DropValue(JSDContext* jsdc, JSDValue* jsdval); + +/* +* Get the jsval wrapped by this JSDValue +* *** new for version 1.1 **** +*/ +extern JSD_PUBLIC_API(jsval) +JSD_GetValueWrappedJSVal(JSDContext* jsdc, JSDValue* jsdval); + +/* +* Clear all property and association information about the given JSDValue. +* Such information will be lazily regenerated when later accessed. This +* function must be called to make changes to the properties of an object +* visible to the accessor functions below (if the properties et.al. have +* changed since a previous call to those accessors). +* *** new for version 1.1 **** +*/ +extern JSD_PUBLIC_API(void) +JSD_RefreshValue(JSDContext* jsdc, JSDValue* jsdval); + +/**************************************************/ + +/* +* Does the JSDValue wrap a JSObject? +* *** new for version 1.1 **** +*/ +extern JSD_PUBLIC_API(JSBool) +JSD_IsValueObject(JSDContext* jsdc, JSDValue* jsdval); + +/* +* Does the JSDValue wrap a number (int or double)? +* *** new for version 1.1 **** +*/ +extern JSD_PUBLIC_API(JSBool) +JSD_IsValueNumber(JSDContext* jsdc, JSDValue* jsdval); + +/* +* Does the JSDValue wrap an int? +* *** new for version 1.1 **** +*/ +extern JSD_PUBLIC_API(JSBool) +JSD_IsValueInt(JSDContext* jsdc, JSDValue* jsdval); + +/* +* Does the JSDValue wrap a double? +* *** new for version 1.1 **** +*/ +extern JSD_PUBLIC_API(JSBool) +JSD_IsValueDouble(JSDContext* jsdc, JSDValue* jsdval); + +/* +* Does the JSDValue wrap a JSString? +* *** new for version 1.1 **** +*/ +extern JSD_PUBLIC_API(JSBool) +JSD_IsValueString(JSDContext* jsdc, JSDValue* jsdval); + +/* +* Does the JSDValue wrap a JSBool? +* *** new for version 1.1 **** +*/ +extern JSD_PUBLIC_API(JSBool) +JSD_IsValueBoolean(JSDContext* jsdc, JSDValue* jsdval); + +/* +* Does the JSDValue wrap a JSVAL_NULL? +* *** new for version 1.1 **** +*/ +extern JSD_PUBLIC_API(JSBool) +JSD_IsValueNull(JSDContext* jsdc, JSDValue* jsdval); + +/* +* Does the JSDValue wrap a JSVAL_VOID? +* *** new for version 1.1 **** +*/ +extern JSD_PUBLIC_API(JSBool) +JSD_IsValueVoid(JSDContext* jsdc, JSDValue* jsdval); + +/* +* Does the JSDValue wrap a primative (not a JSObject)? +* *** new for version 1.1 **** +*/ +extern JSD_PUBLIC_API(JSBool) +JSD_IsValuePrimitive(JSDContext* jsdc, JSDValue* jsdval); + +/* +* Does the JSDValue wrap a function? +* *** new for version 1.1 **** +*/ +extern JSD_PUBLIC_API(JSBool) +JSD_IsValueFunction(JSDContext* jsdc, JSDValue* jsdval); + +/* +* Does the JSDValue wrap a native function? +* *** new for version 1.1 **** +*/ +extern JSD_PUBLIC_API(JSBool) +JSD_IsValueNative(JSDContext* jsdc, JSDValue* jsdval); + +/**************************************************/ + +/* +* Return JSBool value (does NOT do conversion). +* *** new for version 1.1 **** +*/ +extern JSD_PUBLIC_API(JSBool) +JSD_GetValueBoolean(JSDContext* jsdc, JSDValue* jsdval); + +/* +* Return int32_t value (does NOT do conversion). +* *** new for version 1.1 **** +*/ +extern JSD_PUBLIC_API(int32_t) +JSD_GetValueInt(JSDContext* jsdc, JSDValue* jsdval); + +/* +* Return double value (does NOT do conversion). +* *** new for version 1.1 **** +*/ +extern JSD_PUBLIC_API(double) +JSD_GetValueDouble(JSDContext* jsdc, JSDValue* jsdval); + +/* +* Return JSString value (DOES do conversion if necessary). +* NOTE that the JSString returned is not protected from garbage +* collection. It should be immediately read or wrapped using +* JSD_NewValue(jsdc,STRING_TO_JSVAL(str)) if necessary. +* *** new for version 1.1 **** +*/ +extern JSD_PUBLIC_API(JSString*) +JSD_GetValueString(JSDContext* jsdc, JSDValue* jsdval); + +/* +* Return name of function IFF JSDValue represents a function. +* *** new for gecko 2.0 **** +*/ +extern JSD_PUBLIC_API(JSString *) +JSD_GetValueFunctionId(JSDContext* jsdc, JSDValue* jsdval); + +/* +* Return function object IFF JSDValue represents a function or an object +* wrapping a function. +* *** new for version 1.1 **** +*/ +extern JSD_PUBLIC_API(JSFunction*) +JSD_GetValueFunction(JSDContext* jsdc, JSDValue* jsdval); + +/**************************************************/ + +/* +* Return the number of properties for the JSDValue. +* *** new for version 1.1 **** +*/ +extern JSD_PUBLIC_API(unsigned) +JSD_GetCountOfProperties(JSDContext* jsdc, JSDValue* jsdval); + +/* +* Iterate through the properties of the JSDValue. +* Use form similar to that shown for JSD_IterateScripts (no locking required). +* NOTE: each JSDProperty returned must eventually be released by calling +* JSD_DropProperty. +* *** new for version 1.1 **** +*/ +extern JSD_PUBLIC_API(JSDProperty*) +JSD_IterateProperties(JSDContext* jsdc, JSDValue* jsdval, JSDProperty **iterp); + +/* +* Get the JSDProperty for the property of this JSDVal with this name. +* NOTE: must eventually release by calling JSD_DropProperty (if not NULL) +* *** new for version 1.1 **** +*/ +extern JSD_PUBLIC_API(JSDProperty*) +JSD_GetValueProperty(JSDContext* jsdc, JSDValue* jsdval, JSString* name); + +/* +* Get the prototype object for this JSDValue. +* NOTE: must eventually release by calling JSD_DropValue (if not NULL) +* *** new for version 1.1 **** +*/ +extern JSD_PUBLIC_API(JSDValue*) +JSD_GetValuePrototype(JSDContext* jsdc, JSDValue* jsdval); + +/* +* Get the parent object for this JSDValue. +* NOTE: must eventually release by calling JSD_DropValue (if not NULL) +* *** new for version 1.1 **** +*/ +extern JSD_PUBLIC_API(JSDValue*) +JSD_GetValueParent(JSDContext* jsdc, JSDValue* jsdval); + +/* +* Get the ctor object for this JSDValue (or likely its prototype's ctor). +* NOTE: must eventually release by calling JSD_DropValue (if not NULL) +* *** new for version 1.1 **** +*/ +extern JSD_PUBLIC_API(JSDValue*) +JSD_GetValueConstructor(JSDContext* jsdc, JSDValue* jsdval); + +/* +* Get the name of the class for this object. +* *** new for version 1.1 **** +*/ +extern JSD_PUBLIC_API(const char*) +JSD_GetValueClassName(JSDContext* jsdc, JSDValue* jsdval); + +/* +* Get the script for the given value if the given value represents a +* scripted function. Otherwise, return null. +*/ +extern JSD_PUBLIC_API(JSDScript*) +JSD_GetScriptForValue(JSDContext* jsdc, JSDValue* jsdval); + +/**************************************************/ + +/* possible or'd together bitflags returned by JSD_GetPropertyFlags + * + * XXX these must stay the same as the JSPD_ flags in jsdbgapi.h + */ +#define JSDPD_ENUMERATE JSPD_ENUMERATE /* visible to for/in loop */ +#define JSDPD_READONLY JSPD_READONLY /* assignment is error */ +#define JSDPD_PERMANENT JSPD_PERMANENT /* property cannot be deleted */ +#define JSDPD_ALIAS JSPD_ALIAS /* property has an alias id */ +#define JSDPD_EXCEPTION JSPD_EXCEPTION /* exception occurred looking up */ + /* proprety, value is exception */ +#define JSDPD_ERROR JSPD_ERROR /* native getter returned JS_FALSE */ + /* without throwing an exception */ +/* this is not one of the JSPD_ flags in jsdbgapi.h - careful not to overlap*/ +#define JSDPD_HINTED 0x800 /* found via explicit lookup */ + +/* +* Release this JSDProperty +* *** new for version 1.1 **** +*/ +extern JSD_PUBLIC_API(void) +JSD_DropProperty(JSDContext* jsdc, JSDProperty* jsdprop); + +/* +* Get the JSDValue represeting the name of this property (int or string) +* NOTE: must eventually release by calling JSD_DropValue +* *** new for version 1.1 **** +*/ +extern JSD_PUBLIC_API(JSDValue*) +JSD_GetPropertyName(JSDContext* jsdc, JSDProperty* jsdprop); + +/* +* Get the JSDValue represeting the current value of this property +* NOTE: must eventually release by calling JSD_DropValue (if not NULL) +* *** new for version 1.1 **** +*/ +extern JSD_PUBLIC_API(JSDValue*) +JSD_GetPropertyValue(JSDContext* jsdc, JSDProperty* jsdprop); + +/* +* Get the JSDValue represeting the alias of this property (if JSDPD_ALIAS set) +* NOTE: must eventually release by calling JSD_DropValue (if not NULL) +* *** new for version 1.1 **** +*/ +extern JSD_PUBLIC_API(JSDValue*) +JSD_GetPropertyAlias(JSDContext* jsdc, JSDProperty* jsdprop); + +/* +* Get the flags for this property +* *** new for version 1.1 **** +*/ +extern JSD_PUBLIC_API(unsigned) +JSD_GetPropertyFlags(JSDContext* jsdc, JSDProperty* jsdprop); + +/***************************************************************************/ +/* Object Functions --- All NEW for 1.1 --- */ + +/* +* JSDObjects exist to allow a means of iterating through all JSObjects in the +* engine. They are created and destroyed as the wrapped JSObjects are created +* and destroyed in the engine. JSDObjects additionally track the location in +* the JavaScript source where their wrapped JSObjects were created and the name +* and location of the (non-native) constructor used. +* +* NOTE: JSDObjects are NOT reference counted. The have only weak links to +* jsObjects - thus they do not inhibit garbage collection of JSObjects. If +* you need a JSDObject to safely persist then wrap it in a JSDValue (using +* jsd_GetValueForObject). +*/ + +/* +* Lock the entire Object subsystem -- see JSD_UnlockObjectSubsystem +* *** new for version 1.1 **** +*/ +extern JSD_PUBLIC_API(void) +JSD_LockObjectSubsystem(JSDContext* jsdc); + +/* +* Unlock the entire Object subsystem -- see JSD_LockObjectSubsystem +* *** new for version 1.1 **** +*/ +extern JSD_PUBLIC_API(void) +JSD_UnlockObjectSubsystem(JSDContext* jsdc); + +/* +* Iterate through the known objects +* Use form similar to that shown for JSD_IterateScripts. +* NOTE: the ObjectSubsystem must be locked before and unlocked after iterating. +* *** new for version 1.1 **** +*/ +extern JSD_PUBLIC_API(JSDObject*) +JSD_IterateObjects(JSDContext* jsdc, JSDObject** iterp); + +/* +* Get the JSObject represented by this JSDObject +* *** new for version 1.1 **** +*/ +extern JSD_PUBLIC_API(JSObject*) +JSD_GetWrappedObject(JSDContext* jsdc, JSDObject* jsdobj); + +/* +* Get the URL of the line of source that caused this object to be created. +* May be NULL. +* *** new for version 1.1 **** +*/ +extern JSD_PUBLIC_API(const char*) +JSD_GetObjectNewURL(JSDContext* jsdc, JSDObject* jsdobj); + +/* +* Get the line number of the line of source that caused this object to be +* created. May be 0 indicating that the line number is unknown. +* *** new for version 1.1 **** +*/ +extern JSD_PUBLIC_API(unsigned) +JSD_GetObjectNewLineNumber(JSDContext* jsdc, JSDObject* jsdobj); + +/* +* Get the URL of the line of source of the constructor for this object. +* May be NULL. +* *** new for version 1.1 **** +*/ +extern JSD_PUBLIC_API(const char*) +JSD_GetObjectConstructorURL(JSDContext* jsdc, JSDObject* jsdobj); + +/* +* Get the line number of the line of source of the constructor for this object. +* created. May be 0 indicating that the line number is unknown. +* *** new for version 1.1 **** +*/ +extern JSD_PUBLIC_API(unsigned) +JSD_GetObjectConstructorLineNumber(JSDContext* jsdc, JSDObject* jsdobj); + +/* +* Get the name of the constructor for this object. +* May be NULL. +* *** new for version 1.1 **** +*/ +extern JSD_PUBLIC_API(const char*) +JSD_GetObjectConstructorName(JSDContext* jsdc, JSDObject* jsdobj); + +/* +* Get JSDObject representing this JSObject. +* May return NULL. +* *** new for version 1.1 **** +*/ +extern JSD_PUBLIC_API(JSDObject*) +JSD_GetJSDObjectForJSObject(JSDContext* jsdc, JSObject* jsobj); + +/* +* Get JSDObject representing this JSDValue. +* May return NULL. +* *** new for version 1.1 **** +*/ +extern JSD_PUBLIC_API(JSDObject*) +JSD_GetObjectForValue(JSDContext* jsdc, JSDValue* jsdval); + +/* +* Create a JSDValue to wrap (and root) this JSDObject. +* NOTE: must eventually release by calling JSD_DropValue (if not NULL) +* *** new for version 1.1 **** +*/ +extern JSD_PUBLIC_API(JSDValue*) +JSD_GetValueForObject(JSDContext* jsdc, JSDObject* jsdobj); + +/***************************************************************************/ +/* Livewire specific API */ +#ifdef LIVEWIRE + +extern JSD_PUBLIC_API(LWDBGScript*) +JSDLW_GetLWScript(JSDContext* jsdc, JSDScript* jsdscript); + +extern JSD_PUBLIC_API(JSDSourceText*) +JSDLW_PreLoadSource(JSDContext* jsdc, LWDBGApp* app, + const char* filename, JSBool clear); + +extern JSD_PUBLIC_API(JSDSourceText*) +JSDLW_ForceLoadSource(JSDContext* jsdc, JSDSourceText* jsdsrc); + +extern JSD_PUBLIC_API(JSBool) +JSDLW_RawToProcessedLineNumber(JSDContext* jsdc, JSDScript* jsdscript, + unsigned lineIn, unsigned* lineOut); + +extern JSD_PUBLIC_API(JSBool) +JSDLW_ProcessedToRawLineNumber(JSDContext* jsdc, JSDScript* jsdscript, + unsigned lineIn, unsigned* lineOut); + +#endif +/***************************************************************************/ + +JS_END_EXTERN_C + +#endif /* jsdebug_h___ */ diff --git a/js/jsd/jsdstubs.cpp b/js/jsd/jsdstubs.cpp new file mode 100644 index 0000000..2242041 --- /dev/null +++ b/js/jsd/jsdstubs.cpp @@ -0,0 +1,18 @@ +/* -*- 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/. */ + +/* this is all going away... replaced by code in js/jsd/java */ + +#if 0 + +#include "_stubs/netscape_jsdebug_Script.c" +#include "_stubs/netscape_jsdebug_DebugController.c" +#include "_stubs/netscape_jsdebug_JSThreadState.c" +#include "_stubs/netscape_jsdebug_JSStackFrameInfo.c" +#include "_stubs/netscape_jsdebug_JSPC.c" +#include "_stubs/netscape_jsdebug_JSSourceTextProvider.c" + +#endif diff --git a/js/jsd/jshash.cpp b/js/jsd/jshash.cpp new file mode 100644 index 0000000..d4caf2c --- /dev/null +++ b/js/jsd/jshash.cpp @@ -0,0 +1,444 @@ +/* -*- 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/. */ + +/* + * PR hash table package. + */ +#include <stdlib.h> +#include <string.h> +#include "jstypes.h" +#include "jsutil.h" +#include "jshash.h" + +using namespace js; + +/* Compute the number of buckets in ht */ +#define NBUCKETS(ht) JS_BIT(JS_HASH_BITS - (ht)->shift) + +/* The smallest table has 16 buckets */ +#define MINBUCKETSLOG2 4 +#define MINBUCKETS JS_BIT(MINBUCKETSLOG2) + +/* Compute the maximum entries given n buckets that we will tolerate, ~90% */ +#define OVERLOADED(n) ((n) - ((n) >> 3)) + +/* Compute the number of entries below which we shrink the table by half */ +#define UNDERLOADED(n) (((n) > MINBUCKETS) ? ((n) >> 2) : 0) + +/* +** Stubs for default hash allocator ops. +*/ +static void * +DefaultAllocTable(void *pool, size_t size) +{ + return js_malloc(size); +} + +static void +DefaultFreeTable(void *pool, void *item, size_t size) +{ + js_free(item); +} + +static JSHashEntry * +DefaultAllocEntry(void *pool, const void *key) +{ + return (JSHashEntry*) js_malloc(sizeof(JSHashEntry)); +} + +static void +DefaultFreeEntry(void *pool, JSHashEntry *he, unsigned flag) +{ + if (flag == HT_FREE_ENTRY) + js_free(he); +} + +static JSHashAllocOps defaultHashAllocOps = { + DefaultAllocTable, DefaultFreeTable, + DefaultAllocEntry, DefaultFreeEntry +}; + +JSHashTable * +JS_NewHashTable(uint32_t n, JSHashFunction keyHash, + JSHashComparator keyCompare, JSHashComparator valueCompare, + JSHashAllocOps *allocOps, void *allocPriv) +{ + JSHashTable *ht; + size_t nb; + + if (n <= MINBUCKETS) { + n = MINBUCKETSLOG2; + } else { + n = JS_CEILING_LOG2W(n); + if (int32_t(n) < 0) + return NULL; + } + + if (!allocOps) allocOps = &defaultHashAllocOps; + + ht = (JSHashTable*) allocOps->allocTable(allocPriv, sizeof *ht); + if (!ht) + return NULL; + memset(ht, 0, sizeof *ht); + ht->shift = JS_HASH_BITS - n; + n = JS_BIT(n); + nb = n * sizeof(JSHashEntry *); + ht->buckets = (JSHashEntry**) allocOps->allocTable(allocPriv, nb); + if (!ht->buckets) { + allocOps->freeTable(allocPriv, ht, nb); + return NULL; + } + memset(ht->buckets, 0, nb); + + ht->keyHash = keyHash; + ht->keyCompare = keyCompare; + ht->valueCompare = valueCompare; + ht->allocOps = allocOps; + ht->allocPriv = allocPriv; + return ht; +} + +void +JS_HashTableDestroy(JSHashTable *ht) +{ + uint32_t i, n; + JSHashEntry *he, **hep; + JSHashAllocOps *allocOps = ht->allocOps; + void *allocPriv = ht->allocPriv; + + n = NBUCKETS(ht); + for (i = 0; i < n; i++) { + hep = &ht->buckets[i]; + while ((he = *hep) != NULL) { + *hep = he->next; + allocOps->freeEntry(allocPriv, he, HT_FREE_ENTRY); + } + } +#ifdef DEBUG + memset(ht->buckets, 0xDB, n * sizeof ht->buckets[0]); +#endif + allocOps->freeTable(allocPriv, ht->buckets, n * sizeof ht->buckets[0]); +#ifdef DEBUG + memset(ht, 0xDB, sizeof *ht); +#endif + allocOps->freeTable(allocPriv, ht, sizeof *ht); +} + +/* + * Multiplicative hash, from Knuth 6.4. + */ +#define BUCKET_HEAD(ht, keyHash) \ + (&(ht)->buckets[((keyHash) * JS_GOLDEN_RATIO) >> (ht)->shift]) + +JSHashEntry ** +JS_HashTableRawLookup(JSHashTable *ht, JSHashNumber keyHash, const void *key) +{ + JSHashEntry *he, **hep, **hep0; + +#ifdef JS_HASHMETER + ht->nlookups++; +#endif + hep = hep0 = BUCKET_HEAD(ht, keyHash); + while ((he = *hep) != NULL) { + if (he->keyHash == keyHash && ht->keyCompare(key, he->key)) { + /* Move to front of chain if not already there */ + if (hep != hep0) { + *hep = he->next; + he->next = *hep0; + *hep0 = he; + } + return hep0; + } + hep = &he->next; +#ifdef JS_HASHMETER + ht->nsteps++; +#endif + } + return hep; +} + +static JSBool +Resize(JSHashTable *ht, uint32_t newshift) +{ + size_t nb, nentries, i; + JSHashEntry **oldbuckets, *he, *next, **hep; + size_t nold = NBUCKETS(ht); + + JS_ASSERT(newshift < JS_HASH_BITS); + + nb = (size_t)1 << (JS_HASH_BITS - newshift); + + /* Integer overflow protection. */ + if (nb > (size_t)-1 / sizeof(JSHashEntry*)) + return JS_FALSE; + nb *= sizeof(JSHashEntry*); + + oldbuckets = ht->buckets; + ht->buckets = (JSHashEntry**)ht->allocOps->allocTable(ht->allocPriv, nb); + if (!ht->buckets) { + ht->buckets = oldbuckets; + return JS_FALSE; + } + memset(ht->buckets, 0, nb); + + ht->shift = newshift; + nentries = ht->nentries; + + for (i = 0; nentries != 0; i++) { + for (he = oldbuckets[i]; he; he = next) { + JS_ASSERT(nentries != 0); + --nentries; + next = he->next; + hep = BUCKET_HEAD(ht, he->keyHash); + + /* + * We do not require unique entries, instead appending he to the + * chain starting at hep. + */ + while (*hep) + hep = &(*hep)->next; + he->next = NULL; + *hep = he; + } + } +#ifdef DEBUG + memset(oldbuckets, 0xDB, nold * sizeof oldbuckets[0]); +#endif + ht->allocOps->freeTable(ht->allocPriv, oldbuckets, + nold * sizeof oldbuckets[0]); + return JS_TRUE; +} + +JSHashEntry * +JS_HashTableRawAdd(JSHashTable *ht, JSHashEntry **&hep, + JSHashNumber keyHash, const void *key, void *value) +{ + uint32_t n; + JSHashEntry *he; + + /* Grow the table if it is overloaded */ + n = NBUCKETS(ht); + if (ht->nentries >= OVERLOADED(n)) { + if (!Resize(ht, ht->shift - 1)) + return NULL; +#ifdef JS_HASHMETER + ht->ngrows++; +#endif + hep = JS_HashTableRawLookup(ht, keyHash, key); + } + + /* Make a new key value entry */ + he = ht->allocOps->allocEntry(ht->allocPriv, key); + if (!he) + return NULL; + he->keyHash = keyHash; + he->key = key; + he->value = value; + he->next = *hep; + *hep = he; + ht->nentries++; + return he; +} + +JSHashEntry * +JS_HashTableAdd(JSHashTable *ht, const void *key, void *value) +{ + JSHashNumber keyHash; + JSHashEntry *he, **hep; + + keyHash = ht->keyHash(key); + hep = JS_HashTableRawLookup(ht, keyHash, key); + if ((he = *hep) != NULL) { + /* Hit; see if values match */ + if (ht->valueCompare(he->value, value)) { + /* key,value pair is already present in table */ + return he; + } + if (he->value) + ht->allocOps->freeEntry(ht->allocPriv, he, HT_FREE_VALUE); + he->value = value; + return he; + } + return JS_HashTableRawAdd(ht, hep, keyHash, key, value); +} + +void +JS_HashTableRawRemove(JSHashTable *ht, JSHashEntry **hep, JSHashEntry *he) +{ + uint32_t n; + + *hep = he->next; + ht->allocOps->freeEntry(ht->allocPriv, he, HT_FREE_ENTRY); + + /* Shrink table if it's underloaded */ + n = NBUCKETS(ht); + if (--ht->nentries < UNDERLOADED(n)) { + Resize(ht, ht->shift + 1); +#ifdef JS_HASHMETER + ht->nshrinks++; +#endif + } +} + +JSBool +JS_HashTableRemove(JSHashTable *ht, const void *key) +{ + JSHashNumber keyHash; + JSHashEntry *he, **hep; + + keyHash = ht->keyHash(key); + hep = JS_HashTableRawLookup(ht, keyHash, key); + if ((he = *hep) == NULL) + return JS_FALSE; + + /* Hit; remove element */ + JS_HashTableRawRemove(ht, hep, he); + return JS_TRUE; +} + +void * +JS_HashTableLookup(JSHashTable *ht, const void *key) +{ + JSHashNumber keyHash; + JSHashEntry *he, **hep; + + keyHash = ht->keyHash(key); + hep = JS_HashTableRawLookup(ht, keyHash, key); + if ((he = *hep) != NULL) { + return he->value; + } + return NULL; +} + +/* +** Iterate over the entries in the hash table calling func for each +** entry found. Stop if "f" says to (return value & JS_ENUMERATE_STOP). +** Return a count of the number of elements scanned. +*/ +int +JS_HashTableEnumerateEntries(JSHashTable *ht, JSHashEnumerator f, void *arg) +{ + JSHashEntry *he, **hep, **bucket; + uint32_t nlimit, n, nbuckets, newlog2; + int rv; + + nlimit = ht->nentries; + n = 0; + for (bucket = ht->buckets; n != nlimit; ++bucket) { + hep = bucket; + while ((he = *hep) != NULL) { + JS_ASSERT(n < nlimit); + rv = f(he, n, arg); + n++; + if (rv & HT_ENUMERATE_REMOVE) { + *hep = he->next; + ht->allocOps->freeEntry(ht->allocPriv, he, HT_FREE_ENTRY); + --ht->nentries; + } else { + hep = &he->next; + } + if (rv & HT_ENUMERATE_STOP) { + goto out; + } + } + } + +out: + /* Shrink table if removal of entries made it underloaded */ + if (ht->nentries != nlimit) { + JS_ASSERT(ht->nentries < nlimit); + nbuckets = NBUCKETS(ht); + if (MINBUCKETS < nbuckets && ht->nentries < UNDERLOADED(nbuckets)) { + newlog2 = JS_CEILING_LOG2W(ht->nentries); + if (newlog2 < MINBUCKETSLOG2) + newlog2 = MINBUCKETSLOG2; + + /* Check that we really shrink the table. */ + JS_ASSERT(JS_HASH_BITS - ht->shift > newlog2); + Resize(ht, JS_HASH_BITS - newlog2); + } + } + return (int)n; +} + +#ifdef JS_HASHMETER +#include <stdio.h> + +void +JS_HashTableDumpMeter(JSHashTable *ht, JSHashEnumerator dump, FILE *fp) +{ + double sqsum, mean, sigma; + uint32_t nchains, nbuckets; + uint32_t i, n, maxChain, maxChainLen; + JSHashEntry *he; + + sqsum = 0; + nchains = 0; + maxChain = maxChainLen = 0; + nbuckets = NBUCKETS(ht); + for (i = 0; i < nbuckets; i++) { + he = ht->buckets[i]; + if (!he) + continue; + nchains++; + for (n = 0; he; he = he->next) + n++; + sqsum += n * n; + if (n > maxChainLen) { + maxChainLen = n; + maxChain = i; + } + } + + mean = JS_MeanAndStdDev(nchains, ht->nentries, sqsum, &sigma); + + fprintf(fp, "\nHash table statistics:\n"); + fprintf(fp, " number of lookups: %u\n", ht->nlookups); + fprintf(fp, " number of entries: %u\n", ht->nentries); + fprintf(fp, " number of grows: %u\n", ht->ngrows); + fprintf(fp, " number of shrinks: %u\n", ht->nshrinks); + fprintf(fp, " mean steps per hash: %g\n", (double)ht->nsteps + / ht->nlookups); + fprintf(fp, "mean hash chain length: %g\n", mean); + fprintf(fp, " standard deviation: %g\n", sigma); + fprintf(fp, " max hash chain length: %u\n", maxChainLen); + fprintf(fp, " max hash chain: [%u]\n", maxChain); + + for (he = ht->buckets[maxChain], i = 0; he; he = he->next, i++) + if (dump(he, i, fp) != HT_ENUMERATE_NEXT) + break; +} +#endif /* JS_HASHMETER */ + +int +JS_HashTableDump(JSHashTable *ht, JSHashEnumerator dump, FILE *fp) +{ + int count; + + count = JS_HashTableEnumerateEntries(ht, dump, fp); +#ifdef JS_HASHMETER + JS_HashTableDumpMeter(ht, dump, fp); +#endif + return count; +} + +JSHashNumber +JS_HashString(const void *key) +{ + JSHashNumber h; + const unsigned char *s; + + h = 0; + for (s = (const unsigned char *)key; *s; s++) + h = JS_ROTATE_LEFT32(h, 4) ^ *s; + return h; +} + +int +JS_CompareValues(const void *v1, const void *v2) +{ + return v1 == v2; +} diff --git a/js/jsd/jshash.h b/js/jsd/jshash.h new file mode 100644 index 0000000..e21c7b7 --- /dev/null +++ b/js/jsd/jshash.h @@ -0,0 +1,120 @@ +/* -*- 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/. */ + +#ifndef jshash_h___ +#define jshash_h___ + +/* + * API to portable hash table code. + */ +#include <stddef.h> +#include <stdio.h> +#include "jstypes.h" + +JS_BEGIN_EXTERN_C + +typedef uint32_t JSHashNumber; +typedef struct JSHashEntry JSHashEntry; +typedef struct JSHashTable JSHashTable; + +#define JS_HASH_BITS 32 +#define JS_GOLDEN_RATIO 0x9E3779B9U + +typedef JSHashNumber (* JSHashFunction)(const void *key); +typedef int (* JSHashComparator)(const void *v1, const void *v2); +typedef int (* JSHashEnumerator)(JSHashEntry *he, int i, void *arg); + +/* Flag bits in JSHashEnumerator's return value */ +#define HT_ENUMERATE_NEXT 0 /* continue enumerating entries */ +#define HT_ENUMERATE_STOP 1 /* stop enumerating entries */ +#define HT_ENUMERATE_REMOVE 2 /* remove and free the current entry */ + +typedef struct JSHashAllocOps { + void * (*allocTable)(void *pool, size_t size); + void (*freeTable)(void *pool, void *item, size_t size); + JSHashEntry * (*allocEntry)(void *pool, const void *key); + void (*freeEntry)(void *pool, JSHashEntry *he, unsigned flag); +} JSHashAllocOps; + +#define HT_FREE_VALUE 0 /* just free the entry's value */ +#define HT_FREE_ENTRY 1 /* free value and entire entry */ + +struct JSHashEntry { + JSHashEntry *next; /* hash chain linkage */ + JSHashNumber keyHash; /* key hash function result */ + const void *key; /* ptr to opaque key */ + void *value; /* ptr to opaque value */ +}; + +struct JSHashTable { + JSHashEntry **buckets; /* vector of hash buckets */ + uint32_t nentries; /* number of entries in table */ + uint32_t shift; /* multiplicative hash shift */ + JSHashFunction keyHash; /* key hash function */ + JSHashComparator keyCompare; /* key comparison function */ + JSHashComparator valueCompare; /* value comparison function */ + JSHashAllocOps *allocOps; /* allocation operations */ + void *allocPriv; /* allocation private data */ +#ifdef JS_HASHMETER + uint32_t nlookups; /* total number of lookups */ + uint32_t nsteps; /* number of hash chains traversed */ + uint32_t ngrows; /* number of table expansions */ + uint32_t nshrinks; /* number of table contractions */ +#endif +}; + +/* + * Create a new hash table. + * If allocOps is null, use default allocator ops built on top of malloc(). + */ +extern JSHashTable * +JS_NewHashTable(uint32_t n, JSHashFunction keyHash, + JSHashComparator keyCompare, JSHashComparator valueCompare, + JSHashAllocOps *allocOps, void *allocPriv); + +extern void +JS_HashTableDestroy(JSHashTable *ht); + +/* Low level access methods */ +extern JSHashEntry ** +JS_HashTableRawLookup(JSHashTable *ht, JSHashNumber keyHash, const void *key); + +#ifdef __cplusplus +extern JSHashEntry * +JS_HashTableRawAdd(JSHashTable *ht, JSHashEntry **&hep, JSHashNumber keyHash, + const void *key, void *value); +#endif + +extern void +JS_HashTableRawRemove(JSHashTable *ht, JSHashEntry **hep, JSHashEntry *he); + +/* Higher level access methods */ +extern JSHashEntry * +JS_HashTableAdd(JSHashTable *ht, const void *key, void *value); + +extern JSBool +JS_HashTableRemove(JSHashTable *ht, const void *key); + +extern int +JS_HashTableEnumerateEntries(JSHashTable *ht, JSHashEnumerator f, void *arg); + +extern void * +JS_HashTableLookup(JSHashTable *ht, const void *key); + +extern int +JS_HashTableDump(JSHashTable *ht, JSHashEnumerator dump, FILE *fp); + +/* General-purpose C string hash function. */ +extern JSHashNumber +JS_HashString(const void *key); + +/* Stub function just returns v1 == v2 */ +extern int +JS_CompareValues(const void *v1, const void *v2); + +JS_END_EXTERN_C + +#endif /* jshash_h___ */ diff --git a/js/jsd/mkshell.bat b/js/jsd/mkshell.bat new file mode 100755 index 0000000..a00f468 --- /dev/null +++ b/js/jsd/mkshell.bat @@ -0,0 +1,8 @@ +@echo off +REM This Source Code Form is subject to the terms of the Mozilla Public +REM License, v. 2.0. If a copy of the MPL was not distributed with this +REM file, You can obtain one at http://mozilla.org/MPL/2.0/. + +REM nmake -f jsdshell.mak JSDEBUGGER_JAVA_UI=1 %1 %2 %3 %4 %5 +@echo on +nmake -f jsdshell.mak JSDEBUGGER_JAVA_UI=1 %1 %2 %3 %4 %5 diff --git a/js/jsd/moz.build b/js/jsd/moz.build new file mode 100644 index 0000000..1cb5e02 --- /dev/null +++ b/js/jsd/moz.build @@ -0,0 +1,35 @@ +# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*- +# vim: set filetype=python: +# 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/. + +DIRS += ['idl'] +TEST_TOOL_DIRS += ['test'] + +MODULE = 'jsdebug' + +EXPORTS += [ + 'jsdebug.h', +] + +XPCSHELL_TESTS_MANIFESTS += ['test/xpcshell.ini'] + +CPP_SOURCES += [ + 'jsd_atom.cpp', + 'jsd_high.cpp', + 'jsd_hook.cpp', + 'jsd_lock.cpp', + 'jsd_obj.cpp', + 'jsd_scpt.cpp', + 'jsd_stak.cpp', + 'jsd_step.cpp', + 'jsd_text.cpp', + 'jsd_val.cpp', + 'jsd_xpc.cpp', + 'jsdebug.cpp', + 'jshash.cpp', +] + +LIBRARY_NAME = 'jsd' + diff --git a/js/jsd/resource.h b/js/jsd/resource.h new file mode 100644 index 0000000..69874fe --- /dev/null +++ b/js/jsd/resource.h @@ -0,0 +1,21 @@ +/* -*- 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/. */ + +//{{NO_DEPENDENCIES}} +// Microsoft Developer Studio generated include file. +// Used by jsd3240.rc +// + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 101 +#define _APS_NEXT_COMMAND_VALUE 40001 +#define _APS_NEXT_CONTROL_VALUE 1000 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif diff --git a/js/jsd/test/Makefile.in b/js/jsd/test/Makefile.in new file mode 100644 index 0000000..bf3273d --- /dev/null +++ b/js/jsd/test/Makefile.in @@ -0,0 +1,19 @@ +# +# 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/. + +DEPTH = @DEPTH@ +topsrcdir = @top_srcdir@ +srcdir = @srcdir@ +VPATH = @srcdir@ +relativesrcdir = @relativesrcdir@ + +include $(DEPTH)/config/autoconf.mk + +MOCHITEST_FILES = test_bug507448.html bug507448.js \ + test_bug617870-callhooks.html test-bug617870-callhooks.js jsd-test.js \ + test_bug638178-execlines.html test-bug638178-execlines.js \ + $(NULL) + +include $(topsrcdir)/config/rules.mk diff --git a/js/jsd/test/bug507448.js b/js/jsd/test/bug507448.js new file mode 100644 index 0000000..9ef2241 --- /dev/null +++ b/js/jsd/test/bug507448.js @@ -0,0 +1,25 @@ +netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect"); + +function f() {} +function g(a,b) {} +function h(me, too, here) { var x = 1; } +function annoying(a, b, a, b, b, a) {} +function manyLocals(a, b, c, d, e, f, g, h, i, j, k, l, m) { + var n, o, p, q, r, s, t, u, v, w, x, y, z; +} + +assertArraysEqual(jsd.wrapValue(f).script.getParameterNames(), []); +assertArraysEqual(jsd.wrapValue(g).script.getParameterNames(), ["a", "b"]); +assertArraysEqual(jsd.wrapValue(h).script.getParameterNames(), ["me", "too", "here"]); +assertArraysEqual(jsd.wrapValue(annoying).script.getParameterNames(), + ["a", "b", "a", "b", "b", "a"]); +assertArraysEqual(jsd.wrapValue(manyLocals).script.getParameterNames(), + "abcdefghijklm".split("")); + +if (!jsdOnAtStart) { + // turn JSD off if it wasn't on when this test started + jsd.off(); + ok(!jsd.isOn, "JSD shouldn't be running at the end of this test."); +} + +SimpleTest.finish();
\ No newline at end of file diff --git a/js/jsd/test/jsd-test.js b/js/jsd/test/jsd-test.js new file mode 100644 index 0000000..cb6480c --- /dev/null +++ b/js/jsd/test/jsd-test.js @@ -0,0 +1,119 @@ +netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect"); +const Cc = Components.classes; +const Ci = Components.interfaces; +const RETURN_CONTINUE = Ci.jsdIExecutionHook.RETURN_CONTINUE; +const DebuggerService = Cc["@mozilla.org/js/jsd/debugger-service;1"]; + +var jsd = Components.classes['@mozilla.org/js/jsd/debugger-service;1'] + .getService(Ci.jsdIDebuggerService); +var jsdOnAtStart = false; + +function setupJSD(test) { + netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect"); + jsdOnAtStart = jsd.isOn; + if (jsdOnAtStart) { + runTest(); + } else { + jsd.asyncOn({ onDebuggerActivated: function() { runTest(); } }); + } +} + +// Ugly workaround: when you turn the debugger on, it will only see scripts +// compiled after that point. And it may be turned on asynchronously. So +// we put the debugged code into a separate script that gets loaded after +// the debugger is on. +function loadScript(url, element) { + var script = document.createElement('script'); + script.type = 'text/javascript'; + script.src = url; + script.defer = false; + element.appendChild(script); +} + +function findScriptByFunction(name) { + var script; + jsd.enumerateScripts({ enumerateScript: + function(script_) { + if (script_.functionName === name) { + script = script_; + } + } + }); + + if (typeof(script) === "undefined") { + throw("Cannot find function named '" + name + "'"); + } + + return script; +} + +// Pass in a JSD script +function breakOnAllLines(script) { + // Map each line to a PC, and collect that set of PCs (removing + // duplicates.) + var pcs = {}; + for (i = 0; i < script.lineExtent; i++) { + var jsdLine = script.baseLineNumber + i; + var pc = script.lineToPc(jsdLine, Ci.jsdIScript.PCMAP_SOURCETEXT); + pcs[pc] = 1; + } + + // Set a breakpoint on each of those PCs. + for (pc in pcs) { + try { + script.setBreakpoint(pc); + } catch(e) { + alert("Error setting breakpoint: " + e); + } + } +} + +// Set a breakpoint on a script, where lineno is relative to the beginning +// of the script (NOT the absolute line number within the file). +function breakOnLine(script, lineno) { + breakOnAbsoluteLine(script, script.baseLineNumber + lineno); +} + +function breakOnAbsoluteLine(script, lineno) { + var pc = script.lineToPc(lineno, Ci.jsdIScript.PCMAP_SOURCETEXT); + script.setBreakpoint(pc); +} + +function loadPage(page) { + var url; + if (page.match(/^\w+:/)) { + // Full URI, so just use it + url = page; + } else { + // Treat as relative to previous page + url = document.location.href.replace(/\/[^\/]*$/, "/" + page); + } + + dump("Switching to URL " + url + "\n"); + + gURLBar.value = url; + gURLBar.handleCommand(); +} + +function breakpointObserver(lines, interesting, callback) { + jsd.breakpointHook = { onExecute: function(frame, type, rv) { + breakpoints_hit.push(frame.line); + if (frame.line in interesting) { + return callback(frame, type, breakpoints_hit); + } else { + return RETURN_CONTINUE; + } + } }; +} + +function dumpStack(frame, msg) { + netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect"); + dump(msg + ":\n"); + while(frame) { + var callee = frame.callee; + if (callee !== null) + callee = callee.jsClassName; + dump(" " + frame.script.fileName + ":" + frame.line + " func=" + frame.script.functionName + " ffunc=" + frame.functionName + " callee=" + callee + " pc=" + frame.pc + "\n"); + frame = frame.callingFrame; + } +} diff --git a/js/jsd/test/moz.build b/js/jsd/test/moz.build new file mode 100644 index 0000000..219f3f3 --- /dev/null +++ b/js/jsd/test/moz.build @@ -0,0 +1,8 @@ +# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*- +# vim: set filetype=python: +# 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/. + +MODULE = 'jsdebug' + diff --git a/js/jsd/test/test-bug617870-callhooks.js b/js/jsd/test/test-bug617870-callhooks.js new file mode 100644 index 0000000..bfe97a3 --- /dev/null +++ b/js/jsd/test/test-bug617870-callhooks.js @@ -0,0 +1,52 @@ +g = { 'global noneval': 1 }; +netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect"); + +eval("g['global eval'] = 1"); + +// Function to step through and set breakpoints on +function f1() { + g['function noneval'] = 1; + eval("g['function eval'] = 1"); + + x = 1; + for (y = 0; y < 10; y++) { + x++; + } + for (y = 0; y < 3; y++) { + x++; + } + z = 3; +} + +var f2 = new Function("g['function noneval'] = 2; eval(\"g['function eval'] = 2\")"); + +function testJSD(jsd) { + netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect"); + ok(jsd.isOn, "JSD needs to be running for this test."); + + var numBreakpoints = 0; + + f1(); + f2(); + jsd.topLevelHook = null; + jsd.functionHook = null; + dump("numGlobalNonevals="+numGlobalNonevals+"\n"); + dump("numFunctionNonevals="+numFunctionNonevals+"\n"); + dump("numGlobalEvals="+numGlobalEvals+"\n"); + dump("numFunctionEvals="+numFunctionEvals+"\n"); + + ok(numFunctionNonevals == 3, "(fn) Should have hit f1(), testJSD(), and f2(); hit " + hits.fn); + ok(numGlobalNonevals == 1, "(gn) Overall script, hit " + hits.gn); + ok(numGlobalEvals == 1, "(ge) Eval in global area, hit " + hits.ge); + ok(numFunctionEvals == 2, "(fe) Evals within f1() and f2(), hit " + hits.fe); + + if (!jsdOnAtStart) { + // turn JSD off if it wasn't on when this test started + jsd.off(); + ok(!jsd.isOn, "JSD shouldn't be running at the end of this test."); + } + + SimpleTest.finish(); +} + +testJSD(jsd); diff --git a/js/jsd/test/test-bug638178-execlines.js b/js/jsd/test/test-bug638178-execlines.js new file mode 100644 index 0000000..a35811d --- /dev/null +++ b/js/jsd/test/test-bug638178-execlines.js @@ -0,0 +1,95 @@ +netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect"); + +var jsdIScript = Components.interfaces.jsdIScript; + +function f1() { + var x; +} + +function f2() { + + + var x; var y; x = 1; +} + +function f3() { + + + var x; + + var y; var y2; y = 1; + var z; + +} + +var jsdIFilter = Components.interfaces.jsdIFilter; + +function testJSD(jsd) { + netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect"); + ok(jsd.isOn, "JSD needs to be running for this test."); + + jsd.functionHook = ({ + onCall: function(frame, type) { + //console.log("Got " + type); + console.log("Got " + frame.script.fileName); + } + }); + + console.log("Triggering functions"); + f1(); + f2(); + f3(); + console.log("Done with functions"); + + var linemap = {}; + var firsts = {}; + var rests = {}; + var startlines = {}; + jsd.enumerateScripts({ + enumerateScript: function(script) { + netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect"); + if (/execlines\.js$/.test(script.fileName)) { + console.log("script: " + script.fileName + " " + script.functionName); + var execLines = script.getExecutableLines(jsdIScript.PCMAP_SOURCETEXT, 0, 10000); + console.log(execLines.toSource()); + linemap[script.functionName] = execLines; + startlines[script.functionName] = script.baseLineNumber; + + execLines = script.getExecutableLines(jsdIScript.PCMAP_SOURCETEXT, 0, 1); + firsts[script.functionName] = execLines; + execLines = script.getExecutableLines(jsdIScript.PCMAP_SOURCETEXT, execLines[0]+1, 10000); + rests[script.functionName] = execLines; + } + } + }); + + var checklines = function (funcname, linemap, rellines) { + var base = startlines[funcname]; + var b = []; + for (var i = 0; i < rellines.length; ++i) { + b[i] = rellines[i] + base; + } + is(linemap[funcname].toSource(), b.toSource(), funcname + " lines"); + }; + + checklines('f1', linemap, [ 1 ]); + checklines('f2', linemap, [ 3 ]); + checklines('f3', linemap, [ 3, 5, 6 ]); + + checklines('f1', firsts, [ 1 ]); + checklines('f1', rests, []); + checklines('f3', firsts, [ 3 ]); + checklines('f3', rests, [ 5, 6 ]); + + jsd.functionHook = null; + + if (!jsdOnAtStart) { + // turn JSD off if it wasn't on when this test started + jsd.off(); + ok(!jsd.isOn, "JSD shouldn't be running at the end of this test."); + } + + SimpleTest.finish(); +} + +testJSD(jsd); diff --git a/js/jsd/test/test_bug507448.html b/js/jsd/test/test_bug507448.html new file mode 100644 index 0000000..1df24fd --- /dev/null +++ b/js/jsd/test/test_bug507448.html @@ -0,0 +1,112 @@ +<!DOCTYPE HTML> +<html> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=507448 +--> +<head> + <title>Test for Bug 507448</title> + <script type="application/javascript" src="/MochiKit/Base.js"></script> + <script type="application/javascript" src="/MochiKit/Async.js"></script> + <script type="application/javascript" src="/MochiKit/DOM.js"></script> + <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> +</head> +<body> +<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=507448">Mozilla Bug 507448</a> +<p id="display"></p> +<div id="content" style="display: none"> + +</div> +<pre id="test"> +<script type="application/javascript"> +function f() {} +function g(a,b) {} +function h(me, too, here) { var x = 1; } +function annoying(a, b, a, b, b, a) {} +function manyLocals(a, b, c, d, e, f, g, h, i, j, k, l, m) { + var n, o, p, q, r, s, t, u, v, w, x, y, z; +} +</script> +<script type="application/javascript"> + +SimpleTest.waitForExplicitFinish(); + +function loadScript(url) { + var d = new MochiKit.Async.Deferred(); + var head = document.getElementsByTagName("head")[0]; + var script = MochiKit.DOM.createDOM("script", { type: "text/javascript", src: url }); + script.onload = function() { + script.onload = null; + script.onerror = null; + script.onreadystatechange = null; + d.callback(); + }; + script.onerror = function(msg) { + script.onload = null; + script.onerror = null; + script.onreadystatechange = null; + msg = "Failed to load script at " + url + ": " + msg; + d.errback(new URIError(msg, url)); + } + script.onreadystatechange = function() { + if (script.readyState == "loaded" || script.readyState == "complete") { + script.onload(); + } else { + // IE doesn't bother to report errors... + MochiKit.Async.callLater(10, script.onerror, "Script loading timed out") + } + }; + head.appendChild(script); + return d; +} + +/** Test for Bug 507448 **/ +function assertArraysEqual(arr1, arr2) { + is(arr1.length, arr2.length, "Lengths not equal"); + for (var i = 0 ; i < arr1.length; ++i) { + is(arr1[i], arr2[i], "Element " + i + " not equal"); + } +} + +netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect"); +var jsdIDebuggerService = Components.interfaces.jsdIDebuggerService; +var jsd = Components.classes['@mozilla.org/js/jsd/debugger-service;1'] + .getService(jsdIDebuggerService); +var jsdOnAtStart = false; + +function setupJSD() { + // This is somewhat unfortunate: jsd only deals with scripts that have a + // nonzero line number, so we can't just createElement a script here. + // So break the test up into three <script>s, of which the middle one has our test functions. + + netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect"); + + jsdOnAtStart = jsd.isOn; + if (jsdOnAtStart) { + testJSD(); + } else { + jsd.asyncOn( + { + onDebuggerActivated: function() { + testJSD(); + } + } + ); + } +} + +addLoadEvent(setupJSD); + +</script> +<script> +function testJSD() { + netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect"); + + ok(jsd.isOn, "JSD needs to be running for this test."); + + var deferred = loadScript("bug507448.js"); +} +</script> +</pre> +</body> +</html> diff --git a/js/jsd/test/test_bug617870-callhooks.html b/js/jsd/test/test_bug617870-callhooks.html new file mode 100644 index 0000000..e9f5b2e --- /dev/null +++ b/js/jsd/test/test_bug617870-callhooks.html @@ -0,0 +1,83 @@ +<!DOCTYPE HTML> +<html> +<head> + <!-- The bug number is pulled from the test URL --> + <title>JSD Test for Bug AUTOFILLED</title> + <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> + <script type="application/javascript" src="jsd-test.js"></script> + <script type="application/javascript"> +var BUG = 617870; +var TEST_SCRIPT = "test-bug617870-callhooks.js"; +document.getElementsByTagName("title")[0].innerHTML = "JSD Test for Bug " + BUG; + +netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect"); + +SimpleTest.waitForExplicitFinish(); + +var hits = { gn: [], ge: [], fn: [], fe: [] }; +var numGlobalNonevals = 0; +var numFunctionNonevals = 0; +var numGlobalEvals = 0; +var numFunctionEvals = 0; +function runTest() { + netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect"); + jsd.topLevelHook = { + onCall: function(frame,type) { + netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect"); + if (frame.script.fileName.indexOf(TEST_SCRIPT) != -1) { + var desc = frame.script.fileName + ":" + frame.line + " (" + frame.functionName + ")"; + if (type == Ci.jsdICallHook.TYPE_TOPLEVEL_START) { + if (frame.callingFrame === null) { + numGlobalNonevals++; + hits.gn.push(desc); + } else if (frame.callee === null) { + numGlobalEvals++; + hits.ge.push(desc); + } else { + numFunctionEvals++; + hits.fe.push(desc); + } + } + dumpStack(frame, "TOPLEVEL(" + type + ")"); + } + } + }; + jsd.functionHook = { + onCall: function(frame,type) { + netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect"); + if (frame.script.fileName.indexOf(TEST_SCRIPT) != -1) { + if (type == Ci.jsdICallHook.TYPE_FUNCTION_CALL) { + var desc = frame.script.fileName + ":" + frame.line + " (" + frame.functionName + ")"; + numFunctionNonevals++; + hits.fn.push(desc); + } + dumpStack(frame, "FUNCTION(" + type + ")"); + } + } + }; + loadScript(TEST_SCRIPT, document.getElementById("test")); +} + +function setupTest() { + var buglink = document.getElementById("buglink"); + buglink.href = "https://bugzilla.mozilla.org/show_bug.cgi?id=" + BUG; + buglink.innerHTML = "Mozilla Bug " + BUG; +} + </script> +</head> +<body onLoad='setupTest(); setupJSD();'> + +<a id="buglink" target="_blank"></a> +<p id="display"></p> + +<div id="content" style="display: none"> + <pre id='test'> + </pre> +</div> + +<div id='test-output'> +</div> + +</body> +</html> diff --git a/js/jsd/test/test_bug638178-execlines.html b/js/jsd/test/test_bug638178-execlines.html new file mode 100644 index 0000000..75cd1b0 --- /dev/null +++ b/js/jsd/test/test_bug638178-execlines.html @@ -0,0 +1,46 @@ +<!DOCTYPE HTML> +<html> +<head> + <!-- The bug number is pulled from the test URL --> + <title>JSD Test for Bug AUTOFILLED</title> + <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> + <script type="application/javascript" src="jsd-test.js"></script> + <script type="application/javascript"> +var BUG = 638178; +var TEST_SCRIPT = "test-bug638178-execlines.js"; +document.getElementsByTagName("title")[0].innerHTML = "JSD Test for Bug " + BUG; + +netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect"); + +SimpleTest.waitForExplicitFinish(); + +function runTest() { + netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect"); + console.log("start of runTest, loading script"); + loadScript(TEST_SCRIPT, document.getElementById("test")); + console.log("end of runTest"); +} + +function setupTest() { + var buglink = document.getElementById("buglink"); + buglink.href = "https://bugzilla.mozilla.org/show_bug.cgi?id=" + BUG; + buglink.innerHTML = "Mozilla Bug " + BUG; +} + </script> +</head> +<body onLoad='setupTest(); setupJSD();'> + +<a id="buglink" target="_blank"></a> +<p id="display"></p> + +<div id="content" style="display: none"> + <pre id='test'> + </pre> +</div> + +<div id='test-output'> +</div> + +</body> +</html> diff --git a/js/jsd/test/test_evalCached.js b/js/jsd/test/test_evalCached.js new file mode 100644 index 0000000..4c56ef2 --- /dev/null +++ b/js/jsd/test/test_evalCached.js @@ -0,0 +1,23 @@ +// This test must be run with debugging already enabled + +function run_test() { + const Cc = Components.classes; + const Ci = Components.interfaces; + const DebuggerService = Cc["@mozilla.org/js/jsd/debugger-service;1"]; + const jsdIDebuggerService = Ci.jsdIDebuggerService; + var jsd = DebuggerService.getService(jsdIDebuggerService); + + do_check_true(jsd.isOn); + + jsd.scriptHook = { + onScriptCreated: function(script) { + // Just the presence of this will trigger the script to be handed + // to JSD and trigger the crash + }, + onScriptDestroyed: function(script) { + } + } + + eval("4+4"); + eval("4+4"); // Will be found in the eval cache +} diff --git a/js/jsd/test/test_jsval_retval.js b/js/jsd/test/test_jsval_retval.js new file mode 100644 index 0000000..a2ddba8 --- /dev/null +++ b/js/jsd/test/test_jsval_retval.js @@ -0,0 +1,42 @@ +// Bug 689101 - if the binary layout of jsval does not match between C and C++ +// code, then calls to functions returning jsval may get compiled differently +// than the callee, resulting in parameters being shifted over by one. +// +// An example is where on Windows, calling jsdValue.getWrappedValue() will +// return a random floating point number instead of an object. +// +// This test must be run with debugging already enabled + +function run_test() { + const Cc = Components.classes; + const Ci = Components.interfaces; + const DebuggerService = Cc["@mozilla.org/js/jsd/debugger-service;1"]; + const jsdIDebuggerService = Ci.jsdIDebuggerService; + var jsd = DebuggerService.getService(jsdIDebuggerService); + + do_check_true(jsd.isOn); + + var n = 0; + function f() { + n++; + } + + jsd.enumerateScripts({ enumerateScript: function(script) { + script.setBreakpoint(0); + } }); + + jsd.breakpointHook = function(frame, type, dummy) { + var scope = frame.scope; + var parent = scope.jsParent; // Probably does not need to be called + var wrapped = scope.getWrappedValue(); + // Do not try to print 'wrapped'; it may be an internal Call object + // that will crash when you toString it. Different bug. + do_check_eq(typeof(wrapped), "object"); + return Ci.jsdIExecutionHook.RETURN_CONTINUE; + }; + + f(); + + jsd.breakpointHook = null; + jsd = null; +} diff --git a/js/jsd/test/xpcshell.ini b/js/jsd/test/xpcshell.ini new file mode 100644 index 0000000..4681adf --- /dev/null +++ b/js/jsd/test/xpcshell.ini @@ -0,0 +1,9 @@ +[DEFAULT] +head = +tail = + +[test_jsval_retval.js] +debug = 1 + +[test_evalCached.js] +debug = 1 |