Sfink/Contexts and Compartments: Difference between revisions

no edit summary
No edit summary
No edit summary
 
(2 intermediate revisions by the same user not shown)
Line 1: Line 1:
See also http://www.christianwimmer.at/Publications/Wagner11a/Wagner11a.pdf
See also http://www.christianwimmer.at/Publications/Wagner11a/Wagner11a.pdf


This is my attempt to make sense of a couple of fundamental SpiderMonkey data structures, compartments and contexts. Please note that I don't actually know what I'm talking about, so you shouldn't really believe anything you read here. But enough of that...
This is my attempt to make sense of a couple of fundamental SpiderMonkey data structures, compartments and contexts. I am far from an expert, but mrbkap (who *is* the expert) has read through this and pointed out only one glaring mistake, since fixed. So it should be more or less correct now.


== Contexts are Control, Compartments are Data ==
== Contexts are Control, Compartments are Data ==
Line 7: Line 7:
JSContexts are control, JSCompartments are data.
JSContexts are control, JSCompartments are data.


A JSContext (from here on, just ''context'') represents the execution of JS code. A context contains a JS stack and is associated with a thread. (A thread may use multiple contexts, but a given context will only execute on a single thread at a time.)
A JSContext (from here on, just ''context'') represents the execution of JS code. A context contains a JS stack and is associated with a runtime. A thread may use multiple contexts, but a given context will only execute on a single thread at a time.


A JSCompartment (''compartment'') is a memory space that objects and other garbage-collected things (''GCthings'') are stored within.
A JSCompartment (''compartment'') is a memory space that objects and other garbage-collected things (''GCthings'') are stored within.
Line 21: Line 21:
is required to always be within the same compartment, and a "pending exception" object which, if set, will also be in the same compartment. Any object created using a context will be created inside the context's current compartment, and the object's scope chain will be initialized to a scope object within that same compartment. (That scope object ''might'' be cx->globalObject, but really that's just the ultimate fallback. Usually the scope object will be found via the stack instead.)
is required to always be within the same compartment, and a "pending exception" object which, if set, will also be in the same compartment. Any object created using a context will be created inside the context's current compartment, and the object's scope chain will be initialized to a scope object within that same compartment. (That scope object ''might'' be cx->globalObject, but really that's just the ultimate fallback. Usually the scope object will be found via the stack instead.)


To make a cross-compartment call, cx->compartment is updated to the new compartment. The scope object must also be updated, and for that reason you must pass in a target object in the destination compartment. The scope object will be set to the target object's global object. (There's a hacky special case when you're using a JSScript for the target object, since they don't have global objects, but ignore that.) If an exception is pending, it will be set to a wrapper (really, a proxy) inside the new compartment. The wrapper mediates access to the original exception object that lives in the origin compartment.
To make a cross-compartment call, cx->compartment is updated to the new compartment. The scope object must also be updated, and for that reason you must pass in a target object in the destination compartment. The scope object will be set to the target object's global object. If an exception is pending, it will be set to a wrapper (really, a proxy) inside the new compartment. The wrapper mediates access to the original exception object that lives in the origin compartment.


Finally, a dummy frame that represents the compartment transition is pushed onto the JS stack. This frame is used for setting the scope object of anything created while executing within the new compartment. Also, the security privileges of executing code are determined by the current stack -- eg, if your chrome code in a chrome compartment calls a content script in a content compartment, that script will execute with content privileges until it returns, then will revert to chrome privileges.
The security privileges of executing code are determined by the current stack -- eg, if your chrome code in a chrome compartment calls a content script in a content compartment, that script will execute with content privileges until it returns, then will revert to chrome privileges.


When debugging, it is helpful to know that a compartment is associated with a "JSPrincipals" object that represents the "security information" for the contents of that compartment. This is used to decide who can access what, and is mostly opaque to the JS engine. But for Gecko, it'll typically contain a human-understandable URL, which makes it much easier to figure out what's going on:
When debugging, it is helpful to know that a compartment is associated with a "JSPrincipals" object that represents the "security information" for the contents of that compartment. This is used to decide who can access what, and is mostly opaque to the JS engine. But for Gecko, it'll typically contain a human-understandable URL, which makes it much easier to figure out what's going on:
Line 38: Line 38:
   $4 = 0x7fffd120 "http://angryhippos.com/accounts/"
   $4 = 0x7fffd120 "http://angryhippos.com/accounts/"


Anything within a single compartment can freely and directly access anything else in that same compartment. No locking or wrappers are necessary (or possible). The overall model is thus a partitioning of all (garbage collectible) data into separate compartments, with controlled access from one compartment to another but lockless, direct access between objects within a compartment. Cross-compartment access is handled via "wrappers", which is the subject of the next section.
Anything within a single compartment can freely and directly access anything else in that same compartment. No locking or wrappers are necessary (or possible). The overall model is thus a partitioning of all (garbage collectible) data into separate compartments, with controlled access from one compartment to another but lockless, direct access between objects within a compartment. Cross-compartment access is handled via "wrappers", the topic of the next section.


== Wrappers ==
== Wrappers ==
Line 88: Line 88:
something in another compartment, the engine will interpose a cross-compartment
something in another compartment, the engine will interpose a cross-compartment
wrapper for you. It's up to the embedding -- the user of the JS engine -- to
wrapper for you. It's up to the embedding -- the user of the JS engine -- to
decide how to divide up data into different compartments, and what the behavior
decide how to divide up data into different compartments, and what behavior
is triggered when you cross between compartments. You could have a "home"
is triggered when you cross between compartments. You could have a "home"
compartment and a "bigger" compartment, and the cross-compartment wrapper could
compartment and a "bigger" compartment, and the cross-compartment wrapper could
Line 106: Line 106:
other, etc. See js/src/xpconnect/wrappers/WrapperFactory.cpp for the gruesome
other, etc. See js/src/xpconnect/wrappers/WrapperFactory.cpp for the gruesome
details.
details.
== Threads ==
A compartment can only be accessed by a single thread at a time, though it
doesn't have to be the same thread every time. Additionally, only one thread
may have stack frames running a given compartment's code at a time.
(Equivalently, "access compartment X" is defined as "currently executing or
potentially returning to a frame from compartment X").
A context is associated with a thread. (JSContext has a thread_ field with
thread-specific data.) Multiple contexts may be associated with the same
thread. A context is associated with a compartment whenever it is running code
(equivalently, when it is in a request). A context may move between
compartments, and have multiple compartments' code on its stack(s).
...to be finished...
Confirmed users
328

edits