Subj : Re: [Q] Possible to share prototypes accross contexts ? To : t o b e From : Brendan Eich Date : Thu Jan 29 2004 02:26 pm t o b e wrote: >Hi, > >I have a DOM implementation using SM for a limited resource platform browser >which uses prototypes to fake inheritance. > >Since JS_InitClass requires a context this implies that the returned protype >is only valid for that context and that I would have to call JS_InitClass >for each new context I create > Actually, it doesn't imply that. It depends on what object (obj, the 2nd param) you pass. That's the object in which the class name is bound to the constructor function (or if the constructor param is null, to the class prototype object, e.g. Math). The cx is just an execution vehicle, with the special case that for JS_InitStandardClasses, if you haven't yet called JS_SetGlobalObject on that cx, JS_InitStandardClasses will do it for you, passing its obj param along. You can have one context and many global objects, provided you have only one thread at a time using the cx. You would want to JS_SetGlobalObject repeatedly as each global took its turn using the context. > (in fact this is the case since destroying the >context in my app causes the GC to collect the prototypes... is this correct >behaviour or a symptom of a bug on my part ?). > It means you are relying on the context's globalObject member, set by JS_SetGlobalObject and conditionally by JS_InitStandardClasses, to protect the global object from GC. If you don't want the global to go away, but do want the context to be destroyed (or to be reused for another global, as sketched in my previous paragraph), then you need a GC root to protect the global, whether or not cx->globalObject happens to protect it. > There are 83 'classes' in my >DOM implementation so I'm a little concerned that just blindly creating all >prototypes when I create the context might be somewhat wasteful of memory, >particularly as most of them are likely not to be used for any given >context. > > Have you looked at JS_ResolveStandardClass/JS_EnumerateStandardClasses? You can use your global object's class resolve hook similarly to lazily create your classes. >So.. a couple of direct questions: > >Are prototypes 'large' objects and should I even be concerned that I'm >initialising 83 of them every time I create a new context (plus an >associative array to map the context to a table of prototypes). > > Maybe. Why do you create a new context? You *should* be able to minimize context population so that contexts are 1:1 with threads, and you should be able to pool contexts so that context lifetime === thread-using-js lifetime, plus some extra time as a free context in the pool's LRU list. >and > >Is there a way to share prototypes amongst contexts if they *are* large >enough to be of concern. > > Context is for execution -- think thread, not scope (which is what objects are for). You could share prototypes among contexts if you sealed them (JS_SealObject) first, in isolation, before hooking them up to any global object (you'll need a dummy global in which to init the classes whose prototypes you're sealing; it will be sealed too, so you can't use it as a real global, of course). But such prototypes would be readonly and couldn't have properties added, removed, or set. That might not match the semantics you want to present to scripts running in the different contexts sharing the sealed prototypes. So, do you really need more than one context? If so, why? How many global objects do you need? Does each need its own context because of multi-threading, or for some other reason? If you use lazy class creation, you can minimize costs to what each context's scripts demand, while still giving each context/global pair its own copy of a given, demanded class constructor, prototype, properties, and methods. >and > >Would introducing a 'higher' global object to that currently used by each >context and then only ever having one context throughout the applications >lifetime seem like a workable approach (this is a browser, the current >globals are windows on a 1-1 relationship with documents). > > > That's the so-called "superglobal" idea, where your global objects all delegate, via their __proto__ slots, to a sealed superglobal that has one set of standard classes. This idea was developed well before JS_SealObject appeared, and it worked in embeddings where there was one trust domain, and scripts didn't contend to set and use pigeon-holes in the shared superglobal (e.g., two scripts fighting to add different Array.prototype.top methods -- see below). You can use the superglobal approach, but if you're doing anything like a web browser embedding, you can't trust scripts not to interfere or even attack one another through the shared superglobal. So if you seal the superglobal, then no script can do anything like this: Array.prototype.top = function () { return this.length && this[this.length-1]; } (i.e., extend a standard class prototype with methods to complement the existing ones, here namely push and pop). I personally think lazy class creation wins, because JS has always allowed scripts to decorate class prototypes to extend the set of standard methods, and that capability shouldn't be broken if you can help it. /be .