Confirmed users
496
edits
(→Detaching From a Thread: Detaching loses breakpoints, etc.) |
(Use StudlyCaps for literal string values and metavariables; this isn't emacs-lisp, you know.) |
||
Line 45: | Line 45: | ||
If a packet is directed to an actor that no longer exists, the server sends a packet to the client of the following form: | If a packet is directed to an actor that no longer exists, the server sends a packet to the client of the following form: | ||
{ "from":<i>actor</i>, "error":" | { "from":<i>actor</i>, "error":"noSuchActor" } | ||
where <i>actor</i> is the name of the non-existent actor. (It is strange to receive messages from actors that do not exist, but the client evidently believes that actor exists, and this reply allows the client to pair up the error report with the source of the problem.) | where <i>actor</i> is the name of the non-existent actor. (It is strange to receive messages from actors that do not exist, but the client evidently believes that actor exists, and this reply allows the client to pair up the error report with the source of the problem.) | ||
Line 67: | Line 67: | ||
If an actor receives a packet whose type it does not recognize, it sends an error reply of the form: | If an actor receives a packet whose type it does not recognize, it sends an error reply of the form: | ||
{ "from":<i>actor</i>, "error":" | { "from":<i>actor</i>, "error":"unrecognizedPacketType", "message":<i>message</i> } | ||
where <i>message</i> provides details to help debugger developers understand what went wrong: what kind of actor <i>actor</i> is; the packet received; and so on. | where <i>message</i> provides details to help debugger developers understand what went wrong: what kind of actor <i>actor</i> is; the packet received; and so on. | ||
Line 91: | Line 91: | ||
This represents the JavaScript <tt>undefined</tt> value. (<tt>undefined</tt> has no direct representation in JSON.) | This represents the JavaScript <tt>undefined</tt> value. (<tt>undefined</tt> has no direct representation in JSON.) | ||
{ "type":"object", "class":<i> | { "type":"object", "class":<i>className</i>, "actor":<i>actor</i> } | ||
This represents a JavaScript object whose class is <i> | This represents a JavaScript object whose class is <i>className</i>. (Arrays and functions are treated as objects for the sake of forming grips.) <i>Actor</i> can be consulted for the object's contents, as explained below. | ||
{ "type":" | { "type":"longString", "initial":<i>initial</i>, "length":<i>length</i>, "actor":<i>actor</i> } | ||
This represents a very long string, where "very long" is defined at the server's discretion. <i>Initial</i> is some initial portion of the string, <i>length</i> is the string's full length, and <i>actor</i> can be consulted for the rest of the string, as explained below. | This represents a very long string, where "very long" is defined at the server's discretion. <i>Initial</i> is some initial portion of the string, <i>length</i> is the string's full length, and <i>actor</i> can be consulted for the rest of the string, as explained below. | ||
Line 159: | Line 159: | ||
{ "type":"object", "class":"Object", "actor":<i>actor</i> } | { "type":"object", "class":"Object", "actor":<i>actor</i> } | ||
and sending a [[#Finding_An_Object's_Prototype_And_Properties|" | and sending a [[#Finding_An_Object's_Prototype_And_Properties|"prototypeAndProperties"]] request to <i>actor</i> would produce the following reply: | ||
{ "from":<i>actor</i>, "prototype":{ "type":"object", "class":"Object", "actor":<i> | { "from":<i>actor</i>, "prototype":{ "type":"object", "class":"Object", "actor":<i>objprotoActor</i> }, | ||
" | "ownProperties":{ "x":{ "enumerable":true, "configurable":true, "writeable":true, "value":10 }, | ||
"y":{ "enumerable":true, "configurable":true, "writeable":true, "value":"kaiju" }, | "y":{ "enumerable":true, "configurable":true, "writeable":true, "value":"kaiju" }, | ||
"a":{ "enumerable":true, "configurable":true, | "a":{ "enumerable":true, "configurable":true, | ||
"get":{ "type":"object", "class":"Function", "actor":<i> | "get":{ "type":"object", "class":"Function", "actor":<i>getterActor</i> }, | ||
"set":{ "type":"undefined" } | "set":{ "type":"undefined" } | ||
} | } | ||
Line 175: | Line 175: | ||
To examine an object's prototype and properties, a client can send the object's grip's actor a request of the form: | To examine an object's prototype and properties, a client can send the object's grip's actor a request of the form: | ||
{ "to":<i> | { "to":<i>gripActor</i>, "type":"prototypeAndProperties" } | ||
to which the grip actor replies: | to which the grip actor replies: | ||
{ "from":<i> | { "from":<i>gripActor</i>, "prototype":<i>prototype</i>, "ownProperties":<i>ownProperties</i> } | ||
where <i>prototype</i> is a grip on the object's prototype (possibly <tt>{ "type":"null" }</tt>), and <i> | where <i>prototype</i> is a grip on the object's prototype (possibly <tt>{ "type":"null" }</tt>), and <i>ownProperties</i> has the form: | ||
{ <i>name</i>:<i>descriptor</i>, ... } | { <i>name</i>:<i>descriptor</i>, ... } | ||
Line 193: | Line 193: | ||
To find an object's prototype, a client can send the object's grip's actor a request of the form: | To find an object's prototype, a client can send the object's grip's actor a request of the form: | ||
{ "to":<i> | { "to":<i>gripActor</i>, "type":"prototype" } | ||
to which the grip actor replies: | to which the grip actor replies: | ||
{ "from":<i> | { "from":<i>gripActor</i>, "prototype":<i>prototype</i> } | ||
where <i>prototype</i> is a grip on the object's prototype (possibly <tt>{ "type":"null" }</tt>). | where <i>prototype</i> is a grip on the object's prototype (possibly <tt>{ "type":"null" }</tt>). | ||
Line 205: | Line 205: | ||
To list an object's own properties' names, a client can send the object's grip's actor a request of the form: | To list an object's own properties' names, a client can send the object's grip's actor a request of the form: | ||
{ "to":<i> | { "to":<i>gripActor</i>, "type":"ownPropertyNames" } | ||
to which the grip actor replies: | to which the grip actor replies: | ||
{ "from":<i> | { "from":<i>gripActor</i>, "ownPropertyNames":[ <i>name</i>, ... ] } | ||
where each <i>name</i> is a string naming an own property of the object. | where each <i>name</i> is a string naming an own property of the object. | ||
Line 217: | Line 217: | ||
To obtain a descriptor for a particular property of an object, a client can send the object's grip's actor a request of the form: | To obtain a descriptor for a particular property of an object, a client can send the object's grip's actor a request of the form: | ||
{ "to":<i> | { "to":<i>gripActor</i>, "type":"property" } | ||
to which the grip actor replies: | to which the grip actor replies: | ||
{ "from":<i> | { "from":<i>gripActor</i>, "descriptor":<i>descriptor</i> } | ||
where <i>descriptor</i> is a descriptor for the given property, or <tt>null</tt> if there is no such property on the object. | where <i>descriptor</i> is a descriptor for the given property, or <tt>null</tt> if there is no such property on the object. | ||
Line 241: | Line 241: | ||
If an object's class as given in the grip is <code>"Function"</code>, then the grip's actor responds to the messages given here. | If an object's class as given in the grip is <code>"Function"</code>, then the grip's actor responds to the messages given here. | ||
{ "to":<i> | { "to":<i>functionGripActor</i>, "type":"nameAndParameters" } | ||
This requests the name of the function represented by <i> | This requests the name of the function represented by <i>functionGripActor</i>, and the names of its parameters. The reply has the form: | ||
{ "from":<i> | { "from":<i>functionGripActor</i>, "name":<i>name</i>, "parameters":[ <i>parameter</i>, ... ] } | ||
where <i>name</i> is the name of the function, or <code>null</code> if the function is anonymous, and each <i>parameter</i> is the name of a formal parameter to the function as a string. If the function takes destructuring arguments, the <i>parameter</i> is a structure of JSON array and object forms matching the form of the destructuring arguments. | where <i>name</i> is the name of the function, or <code>null</code> if the function is anonymous, and each <i>parameter</i> is the name of a formal parameter to the function as a string. If the function takes destructuring arguments, the <i>parameter</i> is a structure of JSON array and object forms matching the form of the destructuring arguments. | ||
{ "to":<i> | { "to":<i>functionGripActor</i>, "type":"scope" } | ||
Return the lexical environment over which the function has closed. The reply has the form: | Return the lexical environment over which the function has closed. The reply has the form: | ||
{ "from":<i> | { "from":<i>functionGripActor</i>, "scope":<i>environment</i> } | ||
where <i>environment</i> is a [[#Lexical_Environments|lexical environment]]. | where <i>environment</i> is a [[#Lexical_Environments|lexical environment]]. | ||
{ "to":<i> | { "to":<i>functionGripActor</i>, "type":"decompile", "pretty":<i>pretty</i> } | ||
Return JavaScript source code for a function equivalent to the one represented by <i> | Return JavaScript source code for a function equivalent to the one represented by <i>functionGripActor</i>. If the optional <code>pretty</code> parameter is present and <i>pretty</i> is <code>true</code>, then produce indented source code with line breaks. The reply has the form: | ||
{ "from":<i> | { "from":<i>functionGripActor</i>, "decompiledCode":<i>code</i> } | ||
where <i>code</i> is a string. | where <i>code</i> is a string. | ||
Line 269: | Line 269: | ||
The client can find the full contents of a long string by sending a request to the long string grip actor of the form: | The client can find the full contents of a long string by sending a request to the long string grip actor of the form: | ||
{ "to":<i> | { "to":<i>gripActor</i>, "type":"substring", "start":<i>start</i>, "length":<i>length</i> } | ||
where <i>start</i> and <i>length</i> are integers. This requests the substring <i>length</i> characters long, starting at the <i>start</i>'th character. The actor replies as follows: | where <i>start</i> and <i>length</i> are integers. This requests the substring <i>length</i> characters long, starting at the <i>start</i>'th character. The actor replies as follows: | ||
{ "from":<i> | { "from":<i>gripActor</i>, "substring":<i>string</i> } | ||
where <i>string</i> is the requested portion of the string the actor represents. | where <i>string</i> is the requested portion of the string the actor represents. | ||
Line 281: | Line 281: | ||
== Grip Lifetimes == | == Grip Lifetimes == | ||
Most grips are <b> | Most grips are <b>pauseLifetime</b> grips: they last only while the JavaScript thread is paused, and become invalid as soon as the debugger allows the thread to resume execution. (The actors in pause-lifetime grips are children of an actor that is closed when the thread resumes, or is detached from.) This arrangement allows the protocol to use grips freely in responses without requiring the client to remember and close them all. | ||
However, in some cases the client may wish to retain a reference to an object while the debuggee runs. For example, a panel displaying objects selected by the user must update its view of the objects each time the debuggee pauses. To carry this out, the client can promote a pause-lifetime grip to a <b> | However, in some cases the client may wish to retain a reference to an object while the debuggee runs. For example, a panel displaying objects selected by the user must update its view of the objects each time the debuggee pauses. To carry this out, the client can promote a pause-lifetime grip to a <b>threadLifetime</b> grip, which lasts until the thread is detached from or exits. Actors in thread-lifetime grips are children of the thread actor. When the client no longer needs a thread-lifetime grip, it can explicitly release it. | ||
Both pause-lifetime and thread-lifetime grips are garbage collection roots. | Both pause-lifetime and thread-lifetime grips are garbage collection roots. | ||
Line 289: | Line 289: | ||
To promote a pause-lifetime grip to a thread-lifetime grip, the client sends a packet of the form: | To promote a pause-lifetime grip to a thread-lifetime grip, the client sends a packet of the form: | ||
{ "to":<i> | { "to":<i>gripActor</i>, "type":"threadGrip" } | ||
where <i> | where <i>gripActor</i> is the actor from the existing pause-lifetime grip. The grip actor will reply: | ||
{ "from":<i> | { "from":<i>gripActor</i>, "threadGrip":<i>threadGrip</i> } | ||
where <i> | where <i>threadGrip</i> is a new grip on the same object, but whose actor is parented by the thread actor, not the pause actor. | ||
The client can release a thread-lifetime grip by sending the grip actor a request of the form: | The client can release a thread-lifetime grip by sending the grip actor a request of the form: | ||
{ "to":<i> | { "to":<i>gripActor</i>, "type":"release" } | ||
The grip actor will reply, simply: | The grip actor will reply, simply: | ||
{ "from":<i> | { "from":<i>gripActor</i> } | ||
This closes the grip actor. | This closes the grip actor. | ||
Line 309: | Line 309: | ||
The client can release many thread-lifetime grips in a single operation by sending the thread actor a request of the form: | The client can release many thread-lifetime grips in a single operation by sending the thread actor a request of the form: | ||
{ "to":<i>thread</i>, "type":" | { "to":<i>thread</i>, "type":"releaseMany", "actors":[ <i>gripActor</i>, ... ] } | ||
where each <i> | where each <i>gripActor</i> is the name of a child of <i>thread</i> that should be freed. The thread actor will reply, simply: | ||
{ "from":<i>thread</i> } | { "from":<i>thread</i> } | ||
Line 347: | Line 347: | ||
When the connection to the server is opened, the root actor opens the conversation with the following packet: | When the connection to the server is opened, the root actor opens the conversation with the following packet: | ||
{ "from":"root", " | { "from":"root", "applicationType":<i>appType</i>, "traits":<i>traits</i>, ...} | ||
The root actor's name is always zero. <i> | The root actor's name is always zero. <i>appType</i> is a string indicating what sort of program the server represents. There may be more properties present, depending on <i>appType</i>. | ||
<i>traits</i> is an object describing protocol variants this server supports that are not convenient for the client to detect otherwise. The property names present indicate what traits the server has; the properties' values depend on their names. This version of the protocol defines no traits, so <i>traits</i> must be an object with no properties, <tt>{}</tt>. | <i>traits</i> is an object describing protocol variants this server supports that are not convenient for the client to detect otherwise. The property names present indicate what traits the server has; the properties' values depend on their names. This version of the protocol defines no traits, so <i>traits</i> must be an object with no properties, <tt>{}</tt>. | ||
Line 355: | Line 355: | ||
For web browsers, the introductory packet should have the following form: | For web browsers, the introductory packet should have the following form: | ||
{ "from":"root", " | { "from":"root", "applicationType":"browser", "traits":<i>traits</i> } | ||
== Listing Top-Level Browsing Contexts == | == Listing Top-Level Browsing Contexts == | ||
Line 361: | Line 361: | ||
To get a list of the top-level browsing contexts (tabs) present in a browser, a client should send a request like the following to the root actor: | To get a list of the top-level browsing contexts (tabs) present in a browser, a client should send a request like the following to the root actor: | ||
{ "to":"root", "type":" | { "to":"root", "type":"listContexts" } | ||
The reply should have the form: | The reply should have the form: | ||
Line 373: | Line 373: | ||
<i>actor</i> is the actor representing that top-level browsing context; <i>title</i> is the context's document's title, and <i>url</i> is the context's document's URL. | <i>actor</i> is the actor representing that top-level browsing context; <i>title</i> is the context's document's title, and <i>url</i> is the context's document's URL. | ||
Clients should send " | Clients should send "listContexts" requests only to root actors that have identified themselves as browsers. | ||
Actor names given in a list-contexts reply are children of the root actor. They remain valid at least until the next list-contexts request is received. If the client attaches to a context actor, its name is valid at least until the client detaches from the context and receives a "detached" packet from the context, or until the client sends a "release" packet to the context. (These packets are described in detail in [[#Interacting_with_Thread-Like_Actors|Interacting with Thread-Like Actors]].) | Actor names given in a list-contexts reply are children of the root actor. They remain valid at least until the next list-contexts request is received. If the client attaches to a context actor, its name is valid at least until the client detaches from the context and receives a "detached" packet from the context, or until the client sends a "release" packet to the context. (These packets are described in detail in [[#Interacting_with_Thread-Like_Actors|Interacting with Thread-Like Actors]].) | ||
Line 379: | Line 379: | ||
For example, upon connection to a web browser visiting two pages at example.com, the root actor's introductory packet might look like this: | For example, upon connection to a web browser visiting two pages at example.com, the root actor's introductory packet might look like this: | ||
{ "from":"root", " | { "from":"root", "applicationType":"browser", | ||
"contexts":[ { "actor":"context1", "title":"Fruits", | "contexts":[ { "actor":"context1", "title":"Fruits", | ||
"url":"http://www.example.com/fruits/" }, | "url":"http://www.example.com/fruits/" }, | ||
Line 421: | Line 421: | ||
{ "to":<i>thread</i>, "type":"attach" } | { "to":<i>thread</i>, "type":"attach" } | ||
Here, <i>thread</i> is the actor representing the thread, perhaps a browsing context from a " | Here, <i>thread</i> is the actor representing the thread, perhaps a browsing context from a "listContexts" reply. This packet causes the thread to pause its execution, if it does not exit of its own accord first. The thread responds in one of two ways: | ||
{ "from":<i>thread</i>, "type":"paused", "why":{ "type":"attached" }, ... } | { "from":<i>thread</i>, "type":"paused", "why":{ "type":"attached" }, ... } | ||
Line 433: | Line 433: | ||
If the client sends an <code>"attach"</code> packet to a thread that is not in the <b>Detached</b> or <b>Exited</b> state, the actor sends an error reply of the form: | If the client sends an <code>"attach"</code> packet to a thread that is not in the <b>Detached</b> or <b>Exited</b> state, the actor sends an error reply of the form: | ||
{ "from":<i>thread</i>, "error":" | { "from":<i>thread</i>, "error":"wrongState", "message":<i>message</i> } | ||
where <i>message</i> details which state the thread was in instead (to make debugging debuggers easier). In this case, the thread's state is unaffected. | where <i>message</i> details which state the thread was in instead (to make debugging debuggers easier). In this case, the thread's state is unaffected. | ||
Line 462: | Line 462: | ||
If the client sends a <code>"detach"</code> packet to a thread that is not in the <b>Running</b>, <b>Paused</b>, or <b>Exited</b> state, the actor sends an error reply of the form: | If the client sends a <code>"detach"</code> packet to a thread that is not in the <b>Running</b>, <b>Paused</b>, or <b>Exited</b> state, the actor sends an error reply of the form: | ||
{ "from":<i>thread</i>, "error":" | { "from":<i>thread</i>, "error":"wrongState", "message":<i>message</i> } | ||
where <i>message</i> details which state the thread was in instead (to make debugging debuggers easier). In this case, the thread's state is unaffected. | where <i>message</i> details which state the thread was in instead (to make debugging debuggers easier). In this case, the thread's state is unaffected. | ||
Line 482: | Line 482: | ||
{ "from":<i>thread</i>, "type":"paused", "actor":<i>actor</i>, "why":<i>reason</i>, | { "from":<i>thread</i>, "type":"paused", "actor":<i>actor</i>, "why":<i>reason</i>, | ||
" | "currentFrame":<i>frame</i>, "poppedFrames":[<i>poppedFrame</i>...] } | ||
This indicates that the thread has entered the <b>Paused</b> state, and explains where and why. | This indicates that the thread has entered the <b>Paused</b> state, and explains where and why. | ||
Line 490: | Line 490: | ||
Since actors in value grips are parented by the pause actor, this means that those grips become invalid when the thread resumes, or is detached from; it is not possible to take a grip from one pause and use it in the next. To create a grip that remains valid between pauses, see [[#Holding_Values_Between_Pauses|Holding Values Between Pauses]]. | Since actors in value grips are parented by the pause actor, this means that those grips become invalid when the thread resumes, or is detached from; it is not possible to take a grip from one pause and use it in the next. To create a grip that remains valid between pauses, see [[#Holding_Values_Between_Pauses|Holding Values Between Pauses]]. | ||
The <i> | The <i>currentFrame</i> value describes the top frame on the JavaScript stack; see [[#Listing_Stack_Frames|Listing Stack Frames]], below. | ||
The <code>" | The <code>"poppedFrames"</code> property is an array of frame actor names, listing the actors for all frames that were live as of the last pause, but have since been popped. If no frames have been popped, or if this is the first pause for this thread, then this property's value is the empty array. | ||
The <i>reason</i> value describes why the thread paused. It has one of the following forms: | The <i>reason</i> value describes why the thread paused. It has one of the following forms: | ||
Line 504: | Line 504: | ||
The thread stopped because it received an "interrupt" packet from the client. | The thread stopped because it received an "interrupt" packet from the client. | ||
{ "type":" | { "type":"resumeLimit" } | ||
The client resumed the thread with a "resume" packet that included a <tt> | The client resumed the thread with a "resume" packet that included a <tt>resumeLimit</tt> property, and the thread paused because the given <i>limit</i> was met. | ||
{ "type":" | { "type":"debuggerStatement" } | ||
The thread stopped because it executed a JavaScript "debugger" statement. | The thread stopped because it executed a JavaScript "debugger" statement. | ||
Line 522: | Line 522: | ||
<i>TODO: This should provide more details about the watchpoint in the packet, instead of incurring another round-trip before we can display anything helpful.</i> | <i>TODO: This should provide more details about the watchpoint in the packet, instead of incurring another round-trip before we can display anything helpful.</i> | ||
{ "type":" | { "type":"clientEvaluated", "value":<i>grip</i> } | ||
The client's prior <tt> | The client's prior <tt>clientEvaluate</tt> command has completed normally; <i>grip</i> is a grip on the expression's value. See [[#Evaluating_Source-Language_Expressions|Evaluating Source-Language Expressions]] for details. | ||
{ "type":" | { "type":"clientEvaluated", "exception":<i>grip</i> } | ||
The client's prior <tt> | The client's prior <tt>clientEvaluate</tt> command has completed abruptly; <i>grip</i> is the uncaught exception value. See [[#Evaluating_Source-Language_Expressions|Evaluating Source-Language Expressions]] for details. | ||
== Resuming a Thread == | == Resuming a Thread == | ||
Line 534: | Line 534: | ||
If a thread is in the <b>Paused</b> state, the client can resume it by sending a packet of the following form: | If a thread is in the <b>Paused</b> state, the client can resume it by sending a packet of the following form: | ||
{ "to":<i>thread</i>, "type":"resume", " | { "to":<i>thread</i>, "type":"resume", "resumeLimit":<i>limit</i> } | ||
This puts the thread in the <b>Running</b> state. The thread will pause again for breakpoint hits, watchpoint hits, throw watches, frame pop watches, and other standing pause requests. | This puts the thread in the <b>Running</b> state. The thread will pause again for breakpoint hits, watchpoint hits, throw watches, frame pop watches, and other standing pause requests. | ||
In addition, if the optional <tt> | In addition, if the optional <tt>resumeLimit</tt> property is present, then the thread should also pause when <i>limit</i> is reached. <i>Limit</i> must have one of the following forms: | ||
{ "type":"next" } | { "type":"next" } | ||
Line 564: | Line 564: | ||
The thread should pause just before the current frame is popped, whether by throwing an exception or returning a value. | The thread should pause just before the current frame is popped, whether by throwing an exception or returning a value. | ||
When a thread pauses because a limit was reached, the "paused" packet's <i>reason</i> will have a type of <tt>" | When a thread pauses because a limit was reached, the "paused" packet's <i>reason</i> will have a type of <tt>"resumeLimit"</tt>. | ||
A resume limit applies only to the current resumption; once the thread pauses, whether because the limit was reached or some other event occurred—a breakpoint hit, for example—the resume limit is no longer in effect. | A resume limit applies only to the current resumption; once the thread pauses, whether because the limit was reached or some other event occurred—a breakpoint hit, for example—the resume limit is no longer in effect. | ||
If no <tt>" | If no <tt>"resumeLimit"</tt> property appears in the "resume" packet, then the thread should run until some standing pause condition is met (a breakpoint is hit; a watchpoint triggers; or the like). | ||
A "resume" packet closes the pause actor the client provided in the "paused" packet that began the pause. | A "resume" packet closes the pause actor the client provided in the "paused" packet that began the pause. | ||
Line 574: | Line 574: | ||
If the client sends a <code>"resume"</code> packet to a thread that is not in the <b>Paused</b> state, the actor sends an error reply of the form: | If the client sends a <code>"resume"</code> packet to a thread that is not in the <b>Paused</b> state, the actor sends an error reply of the form: | ||
{ "from":<i>thread</i>, "error":" | { "from":<i>thread</i>, "error":"wrongState", "message":<i>message</i> } | ||
where <i>message</i> details which state the thread was in instead (to make debugging debuggers easier). In this case, the thread's state is unaffected. | where <i>message</i> details which state the thread was in instead (to make debugging debuggers easier). In this case, the thread's state is unaffected. | ||
Line 596: | Line 596: | ||
If the client sends an <code>"interrupt"</code> packet to a thread that is not in the <b>Running</b>, <b>Paused</b>, or <b>Exited</b> state, the actor sends an error reply of the form: | If the client sends an <code>"interrupt"</code> packet to a thread that is not in the <b>Running</b>, <b>Paused</b>, or <b>Exited</b> state, the actor sends an error reply of the form: | ||
{ "from":<i>thread</i>, "error":" | { "from":<i>thread</i>, "error":"wrongState", "message":<i>message</i> } | ||
where <i>message</i> details which state the thread was in instead (to make debugging debuggers easier). In this case, the thread's state is unaffected. | where <i>message</i> details which state the thread was in instead (to make debugging debuggers easier). In this case, the thread's state is unaffected. | ||
Line 663: | Line 663: | ||
{ "actor":<i>actor</i>, "depth":<i>depth</i>, "type":"call", "this":<i>this</i>, | { "actor":<i>actor</i>, "depth":<i>depth</i>, "type":"call", "this":<i>this</i>, | ||
"where":<i>location</i>, "environment",<i>environment</i>, | "where":<i>location</i>, "environment",<i>environment</i>, | ||
"callee":<i>callee</i>, " | "callee":<i>callee</i>, "calleeName":<i>calleeName</i>, "arguments":<i>arguments</i> } | ||
where: | where: | ||
* <i>callee</i> is a grip on the function value being called; | * <i>callee</i> is a grip on the function value being called; | ||
* <i> | * <i>calleeName</i> is the name of the callee, a string (this property is omitted for anonymous functions); | ||
* <i>arguments</i> is an array of grips on the actual values passed to the function; | * <i>arguments</i> is an array of grips on the actual values passed to the function; | ||
Line 688: | Line 688: | ||
=== Client Evaluation Frames === | === Client Evaluation Frames === | ||
When the client evaluates an expression with an <tt> | When the client evaluates an expression with an <tt>clientEvaluate</tt> packet, the evaluation appears on the stack as a special kind of frame, of the form: | ||
{ "actor":<i>actor</i>, "depth":<i>depth</i>, "type":" | { "actor":<i>actor</i>, "depth":<i>depth</i>, "type":"clientEvaluate", "this":<i>this</i>, | ||
"where":<i>location</i>, "environment",<i>environment</i> } | "where":<i>location</i>, "environment",<i>environment</i> } | ||
Line 699: | Line 699: | ||
The client can remove frames from the stack by sending a request of the form: | The client can remove frames from the stack by sending a request of the form: | ||
{ "to":<i> | { "to":<i>frameActor</i>, "type":"pop", "value":<i>value</i> } | ||
where <i> | where <i>frameActor</i> is the actor representing the stack frame to pop, and <i>value</i> is a grip on the value that should be returned as the value of the frame. All younger stack frames are also popped. The frame actor will reply: | ||
{ "from":<i> | { "from":<i>frameActor</i>, "watches":[<i>watchActor</i> ...] } | ||
where each <i> | where each <i>watchActor</i> is the name of a frame pop watch actor that has been triggered in the process of popping the given frame. If no frame pop watches are triggered, the <tt>watches</tt> property may be omitted. | ||
<i>TODO: specify the error to return if the frame cannot be popped --- can host (C++) function frames be popped?</i> | <i>TODO: specify the error to return if the frame cannot be popped --- can host (C++) function frames be popped?</i> | ||
Line 713: | Line 713: | ||
To evaluate a source-language expression in a thread, the client sends a specialized <tt>resume</tt> packet of the form: | To evaluate a source-language expression in a thread, the client sends a specialized <tt>resume</tt> packet of the form: | ||
{ "to":<i>thread</i>, "type":" | { "to":<i>thread</i>, "type":"clientEvaluate", "expression":<i>expr</i>, "frame":<i>frame</i> } | ||
This resumes the thread just as an ordinary <tt>resume</tt> packet does, but rather than continuing execution where the pause took place, has the thread begin evaluation of the source-language expression given by <i>expr</i>, a string. The evaluation takes place in a new [[#Client_Evaluation_Frames|Client Evaluation Frame]], pushed on the stack. When evaluation of <i>expr</i> completes, the client will report a <tt> | This resumes the thread just as an ordinary <tt>resume</tt> packet does, but rather than continuing execution where the pause took place, has the thread begin evaluation of the source-language expression given by <i>expr</i>, a string. The evaluation takes place in a new [[#Client_Evaluation_Frames|Client Evaluation Frame]], pushed on the stack. When evaluation of <i>expr</i> completes, the client will report a <tt>clientEvaluate</tt> pause containing the expression's value. | ||
If evaluating <i>expr</i> completes abruptly, this outcome is still reported via an <tt> | If evaluating <i>expr</i> completes abruptly, this outcome is still reported via an <tt>clientEvaluated</tt> pause, so it is not necessary for the client to take explicit steps to catch exceptions thrown by the expression. | ||
<i>TODO: evaluate with given grips bound to given identifiers</i> | <i>TODO: evaluate with given grips bound to given identifiers</i> | ||
Line 725: | Line 725: | ||
A lexical environment (written as <i>environment</i> in packet descriptions) records the identifier bindings visible at a particular point in the program. An environment has one of the following forms: | A lexical environment (written as <i>environment</i> in packet descriptions) records the identifier bindings visible at a particular point in the program. An environment has one of the following forms: | ||
{ "type":"object", "actor":<i>actor</i>, "object":<i>object</i>, "parent":<i> | { "type":"object", "actor":<i>actor</i>, "object":<i>object</i>, "parent":<i>parentEnvironment</i> } | ||
This represents a scope chain element whose identifier bindings reflect the properties of <i>object</i> (a grip). This could be the global object (<tt>window</tt> in a browser), or a DOM element (for event handler content attributes, which have the input element, form, and document on their scope chain along with the <tt>window</tt>). | This represents a scope chain element whose identifier bindings reflect the properties of <i>object</i> (a grip). This could be the global object (<tt>window</tt> in a browser), or a DOM element (for event handler content attributes, which have the input element, form, and document on their scope chain along with the <tt>window</tt>). | ||
Line 731: | Line 731: | ||
<i>Actor</i> is the name of an actor representing this lexical environment. The requests it can answer are described below. | <i>Actor</i> is the name of an actor representing this lexical environment. The requests it can answer are described below. | ||
<i> | <i>ParentEnvironment</i> describes the next enclosing lexical environment; the <tt>parent</tt> property is omitted on the outermost environment. | ||
{ "type":"function", "actor":<i>actor</i>, "function":<i>function</i>, " | { "type":"function", "actor":<i>actor</i>, "function":<i>function</i>, "functionName":<i>functionName</i>, | ||
"bindings":<i>bindings</i>, "parent":<i> | "bindings":<i>bindings</i>, "parent":<i>parentEnvironment</i> } | ||
This represents the variable environment created by a call to <i>function</i> (a grip), whose name is <i> | This represents the variable environment created by a call to <i>function</i> (a grip), whose name is <i>functionName</i> (a string). <i>Bindings</i> describes the bindings in scope, including the function's arguments, the <tt>arguments</tt> object, and local <tt>var</tt> and function bindings; its form is described in detail below. The <tt>functionName</tt> property is omitted if the function is anonymous. The other properties are as described above. | ||
{ "type":"with", "actor":<i>actor</i>, "object":<i>object</i>, "parent":<i> | { "type":"with", "actor":<i>actor</i>, "object":<i>object</i>, "parent":<i>parentEnvironment</i> } | ||
This represents bindings introduced by a <tt>with</tt> statement whose operand is <i>object</i> (a grip). The other properties are as described above. | This represents bindings introduced by a <tt>with</tt> statement whose operand is <i>object</i> (a grip). The other properties are as described above. | ||
{ "type":"block", "actor":<i>actor</i>, "bindings":<i>bindings</i>, "parent":<i> | { "type":"block", "actor":<i>actor</i>, "bindings":<i>bindings</i>, "parent":<i>parentEnvironment</i> } | ||
This represents bindings introduced by a <tt>let</tt> block, <tt> | This represents bindings introduced by a <tt>let</tt> block, <tt>forIn</tt> statement, <tt>catch</tt> block, or the like. The properties are as described above. | ||
A <i>bindings</i> value has the form: | A <i>bindings</i> value has the form: | ||
Line 759: | Line 759: | ||
To fully enumerate the bindings introduced by any lexical environment, the client can send a request of the following form to the environment's actor: | To fully enumerate the bindings introduced by any lexical environment, the client can send a request of the following form to the environment's actor: | ||
{ "to":<i> | { "to":<i>envActor</i>, "type":"bindings" } | ||
The actor will reply as follows: | The actor will reply as follows: | ||
{ "from":<i> | { "from":<i>envActor</i>, "bindings":<i>bindings</i> } | ||
To change the value of a variable bound in a particular lexical environment, the client can send a request to the environment's actor: | To change the value of a variable bound in a particular lexical environment, the client can send a request to the environment's actor: | ||
{ "to":<i> | { "to":<i>envActor</i>, "type":"assign", "name":<i>name</i>, "value":<i>value</i> } | ||
This changes the value of the identifier whose name is <i>name</i> (a string) to that represented by <i>value</i> (a grip). The actor will reply as follows, simply: | This changes the value of the identifier whose name is <i>name</i> (a string) to that represented by <i>value</i> (a grip). The actor will reply as follows, simply: | ||
{ "from":<i> | { "from":<i>envActor</i> } | ||
If the named identifier is immutable, the actor will send an error reply of the form: | If the named identifier is immutable, the actor will send an error reply of the form: | ||
{ "from":<i> | { "from":<i>envActor</i>, "error":"immutableBinding", "message":<i>message</i> } | ||
== Lexical Environment Examples == | == Lexical Environment Examples == | ||
Line 793: | Line 793: | ||
then we would hit that breakpoint, eliciting a packet like the following: | then we would hit that breakpoint, eliciting a packet like the following: | ||
{ "from":<i>thread</i>, "type":"paused", "actor":<i> | { "from":<i>thread</i>, "type":"paused", "actor":<i>pauseActor</i>, | ||
"why":{ "type":"breakpoint", "actors":[<i> | "why":{ "type":"breakpoint", "actors":[<i>breakpointActor</i>] }, | ||
"frame":{ "actor":<i> | "frame":{ "actor":<i>frameActor</i>, "depth":1, | ||
"type":"call", "where":{ "url":"sample.js", "line":3 }, | "type":"call", "where":{ "url":"sample.js", "line":3 }, | ||
"environment":{ "type":"function", "actor":<i> | "environment":{ "type":"function", "actor":<i>gFrameActor</i>, | ||
"function":{ "type":"object", "class":"Function", "actor":<i> | "function":{ "type":"object", "class":"Function", "actor":<i>gActor</i> }, | ||
" | "functionName":"g", | ||
"bindings":{ "mutable":{ "y":"argument to g" } }, | "bindings":{ "mutable":{ "y":"argument to g" } }, | ||
"parent":{ "type":"function", "actor":<i> | "parent":{ "type":"function", "actor":<i>fFrameActor</i>, | ||
"function":{ "type":"object", "class":"Function", "actor":<i> | "function":{ "type":"object", "class":"Function", "actor":<i>fActor</i> }, | ||
" | "functionName":"f", | ||
"bindings":{ "mutable":{ "x":"argument to f" } }, | "bindings":{ "mutable":{ "x":"argument to f" } }, | ||
"parent":{ "type":"object", "actor":<i> | "parent":{ "type":"object", "actor":<i>globalCodeActor</i>, | ||
"object":{ "type":"object", "class":"Global", | "object":{ "type":"object", "class":"Global", | ||
"actor":<i> | "actor":<i>globalObjectActor</i> } | ||
} | } | ||
} | } | ||
}, | }, | ||
"callee":<i> | "callee":<i>gActor</i>, "calleeName":"g", | ||
"this":{ "type":"object", "class":"Function", "actor":<i> | "this":{ "type":"object", "class":"Function", "actor":<i>gActor</i> }, | ||
"arguments":["argument to g"] | "arguments":["argument to g"] | ||
} | } | ||
Line 828: | Line 828: | ||
While a thread is paused, a client can set breakpoints in the thread's code by sending requests of the form: | While a thread is paused, a client can set breakpoints in the thread's code by sending requests of the form: | ||
{ "to":<i>thread</i>, "type":" | { "to":<i>thread</i>, "type":"setBreakpoint", "location":<i>location</i> } | ||
where <i>location</i> is a [[#Source_Locations|source location]]. If the thread is able to establish a breakpoint at the given location, it replies: | where <i>location</i> is a [[#Source_Locations|source location]]. If the thread is able to establish a breakpoint at the given location, it replies: | ||
{ "from":<i>thread</i>, "actor":<i>actor</i>, " | { "from":<i>thread</i>, "actor":<i>actor</i>, "actualLocation":<i>actualLocation</i> } | ||
where <i>actor</i> is an actor representing the breakpoint (a child of the thread actor), and <i> | where <i>actor</i> is an actor representing the breakpoint (a child of the thread actor), and <i>actualLocation</i> is the location at which the breakpoint was really set. If <i>location</i> and <i>actualLocation</i> are the same, then the <tt>actualLocation</tt> property can be omitted. | ||
If the thread cannot find the script referred to in <i>location</i>, it sends an error reply of the form: | If the thread cannot find the script referred to in <i>location</i>, it sends an error reply of the form: | ||
{ "from":<i>thread</i>, "error":" | { "from":<i>thread</i>, "error":"noScript" } | ||
If <i>location</i> refers to a line and column at which the given script has no program code, and no reasonable alternative location can be chosen (say, by skipping forward), then the thread sends an error reply of the form: | If <i>location</i> refers to a line and column at which the given script has no program code, and no reasonable alternative location can be chosen (say, by skipping forward), then the thread sends an error reply of the form: | ||
{ "from":<i>thread</i>, "error":" | { "from":<i>thread</i>, "error":"noCodeAtLineColumn" } | ||
To delete a breakpoint, the client can send the breakpoint's actor a message of the form: | To delete a breakpoint, the client can send the breakpoint's actor a message of the form: | ||
{ "to":<i> | { "to":<i>breakpointActor</i>, "type":"delete" } | ||
to which the breakpoint actor will reply, simply: | to which the breakpoint actor will reply, simply: | ||
{ "from":<i> | { "from":<i>breakpointActor</i> } | ||
This closes communications with <i> | This closes communications with <i>breakpointActor</i>. | ||
= Watchpoints = | = Watchpoints = |