Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

planning to remove non-fast natives

17 views
Skip to first unread message

Luke Wagner

unread,
Aug 14, 2010, 8:55:54 PM8/14/10
to
The JSAPI has had JSFastNative for a while now. Fast natives have
important advantages over slow natives:
- no stack frame is pushed for the call
- 'this' may be computed lazily.

Until recently, slow natives (JSNative) had two important uses:
1. extra rooting slows (via 'extra' slots)
2. constructors (where the under-construction object was passed as this

With the addition of conservative stack scanning, (1) should no longer
be necessary since live locals on the stack may be used instead. With
to-be-public-API addition of "fast constructors", (2) is not necessary
either. Furthermore, slow natives impose a complexity and performance
burden on the JS engine as a whole. Thus, the plan (bug 581263) is to
remove slow natives.

More specifically, the planned changes to JSAPI are:
1. mv JSFastNative JSNative
2. rm JSFUN_FAST_NATIVE; JS_FN will create a JSNative (JS_FN will still
add JSFUN_STUB_GSOPS to the given flags)
3. rm JSFunctionSpec::extra and drop the 5th 'extra' arg from JS_FS
4. add JSFUN_CONSTRUCTOR / JSCLASS_CONSTRUCTOR

Setting JSCLASS_CONSTRUCTOR in JSClass::flags will cause the constructor
function created by JS_InitClass to have the JSFUN_CONSTRUCTOR flag.
JSFUN_CONSTRUCTOR may also be set for functions created directly by
JS_DefineFunction, through the attrs argument, or by JS_DefineFunctions,
through JSFunctionSpec::flags.

The meaning of the JSFUN_CONSTRUCTOR flag is as follows:

If JSFUN_CONSTRUCTOR is not set on a native function, the native will
behave like a JSFastNative does now when called with 'new'. (That is, a
new object will be constructed with the appropriate prototype and passed
as 'this'. As before with JSFastNative, a native must access the 'this'
parameter via JS_THIS or JS_THIS_OBJECT and JS_SET_RVAL if returning
JS_TRUE.)

If JSFUN_CONSTRUCTOR is set on a native function (which will be referred
to henceforth as just a 'constructor'), the constructor may query
whether it was called to construct an object (viz., using 'new Foo()')
with JS_IsConstructing. (N.B. JS_IsConstructing may *only* be called
from within a constructor and will assert if otherwise.) The most
significant change from slow native constructors is that a constructor
is not passed a newly-constructed object as 'this'; the constructor must
construct its own new object. (This avoids the general (and slow)
object-creation path in the JS engine and takes advantage of the
class-specific knowledge that constructors usually have.) Furthermore,
JS_THIS/JS_THIS_OBJECT may *not* be used from a constructor when
JS_IsConstructing is true, as there is no 'this' object. Finally, a
constructor must return a non-primitive value on success.

When converting an old slow native constructor to a new native (with or
without JSFUN_CONSTRUCTOR), take special care to check that the return
value is always set on success paths; slow native constructors could get
away with not setting *rval, so this is a likely gotcha

Because a constructor now longer has a stack frame which can be queried
simply from the JSContext, the signature for JS_IsConstructing takes an
additional 'vp' parameter which must be the 'vp' passed to the constructor:

JSBool foo_ctor(JSContext *cx, uintN argc, jsval *vp) {
if (JS_IsConstructing(cx, vp))
...
}

There is one additional issue which only effects embeddings that use
JSCLASS_CONSTRUCT_PROTOTYPE or JS_ConstructObject: both these APIs
currently allow non-typical objects to be passed as the 'this' parameter
to slow native constructors. Thus, there needs to be some way to
communicate an atypical 'this' object from callers to new constructors.
This is achieved by the new API JS_IsConstructingMaybeWithThis:

JSBool foo_ctor(JSContext *cx, uintN argc, jsval *vp) {
JSObject *thisobj;
if (JS_IsConstructingMaybeWithThis(cx, vp, &thisobj)) {
if (thisobj)
// atypical object passed as 'this'
else
// create typical object here
} else {
// native called as function
}
}

Constructors only need to handle this case if the embedding actually is
known to use the aforementioned APIs, and only for constructors actually
called by these APIs. E.g., it looks like there are only 5 in
SpiderMonkey/Firefox.

Feedback and questions welcome. Better identifier than
JS_ConstructingMaybeWithThis welcome. This is being actively
implemented and plans may change as new issues or feedback comes to light.

Brendan Eich

unread,
Aug 16, 2010, 2:29:35 PM8/16/10
to
> Feedback and questions welcome.

I endorse this plan!

Embedders, please read and comment. Almost all of you have slow
natives, i.e. functions with this kind of signature:

JSBool foo(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
jsval *rval)

The proposed changes require you to rewrite to

JSBool foo(JSContext *cx, uintN argc, jsval *vp)

and (as Luke noted) be careful to call JS_SET_RVAL before successful
returns. And make the other changes mentioned in the o.p. if you use
the implicated APIs, but this is the big one and I wanted to reiterate
it.


> Better identifier than JS_ConstructingMaybeWithThis welcome.

JS_IsConstructingPossiblyWithGivenThisObject? That is overlong but
clearer. It still wants a comma of some sort before Possibly. How
about

JS_IsConstructingThis

Is it shorter and no less clear?

/be

Luke Wagner

unread,
Aug 16, 2010, 2:46:44 PM8/16/10
to
>> Better identifier than JS_ConstructingMaybeWithThis welcome.
>
> JS_IsConstructingPossiblyWithGivenThisObject? That is overlong but
> clearer. It still wants a comma of some sort before Possibly.

We have tentatively decided on
JS_IsConstructing_PossiblyWithGivenThisObject (with _ as the comma, not
a general style-guideline-go-ahead to use _ willy nilly).

Wes Garland

unread,
Aug 16, 2010, 3:08:21 PM8/16/10
to Brendan Eich, dev-tech-...@lists.mozilla.org
Brendan;

On Mon, Aug 16, 2010 at 2:29 PM, Brendan Eich <bre...@mozilla.org> wrote:

> Embedders, please read and comment. Almost all of you have slow
> natives, i.e. functions with this kind of signature:
>

I have been expecting this day to come for a while, so I've been writing
mostly fast natives for the last year or so. I can't think of any way this
will cause embedders problems, except that they will have to do a bunch of
code-rewriting.

Which brings me to my gentle prodding point -- I'm encouraging you all at
Mozilla to keep up the API breakage, hopefully so that it mostly happens
this year, and we can settle back down to a stable API for the future. This
change, in particular, tells me that any more "but think of the embedders!"
comments in Bugzilla should be shot down, in favour of whatever better API
is being proposed for the long term. And that it should happen sooner,
rather than later.

Wes

--
Wesley W. Garland
Director, Product Development
PageMail, Inc.
+1 613 542 2787 x 102

Luke Wagner

unread,
Aug 16, 2010, 5:19:31 PM8/16/10
to
I forgot an additional subtle error to watch out for when converting
slow-native constructors:

A slow native is passed at least its declared number of arguments. If
the callee passes too few, the remainder is filled with undefined.
OTOH, a fast native receives only the actual arguments; anything past
argc is unitialized/invalid memory and must not be accessed without a
sufficient argc test.

Luke Wagner

unread,
Aug 20, 2010, 1:59:03 PM8/20/10
to
The patches for this are in review. Some minor adjustments were made
while working through mozilla.

First, regarding this initial statement:

> Setting JSCLASS_CONSTRUCTOR in JSClass::flags will cause the constructor
> function created by JS_InitClass to have the JSFUN_CONSTRUCTOR flag.
> JSFUN_CONSTRUCTOR may also be set for functions created directly by
> JS_DefineFunction, through the attrs argument, or by JS_DefineFunctions,
> through JSFunctionSpec::flags.

This flag was removed: every native passed as the constructor argument
to JS_InitClass is clearly a constructor, so JS_InitClass always sets
JSFUN_CONSTRUCTOR on the constructor function.

Next, a clarification: JSClass::construct has no associated function
flags, but, as you might expect, it will be called with the same
convention as JSFUN_CONSTRUCTOR.

Finally, a new API was added to simplify the process of converting a
slow-native constructor that really just wants the old this-object
passed to slow natives:

JSObject *
JS_NewObjectForConstructor(JSContext *cx, const jsval *vp);

With this API, a constructor can be written simply:

JSBool
foo_ctor(JSContext *cx, uintN argc, jsval *vp) {

JSObject *obj = JS_NewObjectFromConstructor(cx, vp);
if (!obj)
return JS_FALSE;
// initialize obj as before
}

Of course, this is less efficient than if foo_ctor already knew the
proper class and prototype to use to create the new object.

Luke Wagner

unread,
Aug 20, 2010, 2:06:26 PM8/20/10
to

> JSBool
> foo_ctor(JSContext *cx, uintN argc, jsval *vp) {
> JSObject *obj = JS_NewObjectFromConstructor(cx, vp);

Typo: s/From/For/

0 new messages