Subj : Re: updated jsgen (maybe rc1?) - beta 8 To : netscape.public.mozilla.jseng From : Marcello Bastea-Forte Date : Thu Jan 15 2004 04:30 pm I hate to bump my own message, but has anyone looked at this? The last bit could possibly be a bug in spidermonkey, or hopefully just my doing something wrong. But if it's the latter, I'd like to know. Thanks, Marcello Marcello Bastea-Forte wrote: > ----------------------------------------------------------------- > > But onto my actual message. Before I go into details, I've released yet > another version of jsgen: > > http://www.cs.unm.edu/~cello/jsgen/ > > As I have mentioned in previous messages, the problem is around this > code (from beta 7): > > ----------------------------------------------------------------- > JSObject *ChildClass::getJSObject(JSContext *cx) { > if (!cx) return NULL; > if (!_JSinternal.o) { > _JSinternal.o = JS_NewObject(cx, &ChildClass::_jsClass, > ParentClass::JSInit(cx,NULL), NULL); > if (!JS_SetPrivate(cx, _JSinternal.o, this)) return NULL; > } > return _JSinternal.o; > } > ----------------------------------------------------------------- > In order to get the JSObject* from a C++ instance, I check if there > already is one stored, and if not, call JS_NewObject. > > The problem here is calling ParentClass::JSInit for a prototype. The > JSInit function calls JS_InitClass and returns the object, but requires > a JSObject* to init the class into. > > While trying to figure out the best way to deal with the JSObject*, I > came across a bigger problem. > > I first noticed it by putting print(new ChildClass); in my code, I would > get an error (instead of it printing out [object ChildClass]). > > On further testing I realized that if I called one of ParentClass' > functions through a ChildClass object (functions that aren't > overridden), it would crash or get an error. > > Even more curious was that static functions/variables from ParentClass > worked fine. > > > (Note: the following solution to the problem is entirely based on how I > /think/ SpiderMonkey/JavaScript works, so if the following is based > incorrect ideas, let me know.) > > Based on the fact that static functions worked and non-static didn't, I > guessed that JS_InitClass isn't returning a regular prototype that can > be used in JS_NewObject. > > My understanding is that JS_InitClass returns some type of > static/constructor class that doesn't have the member functions or > variables/fields, but can be used to construct new classes from > JavaScript. But it can't be used as a prototype for JS_NewObject. > > (In the docs for JS_InitClass, it says "JS_InitClass returns a pointer > to a JS object that is the prototype for the newly initialized class." > Does this need to be clarified or am I just missing something?) > > At that point I tried, for the hell of it, modifying getJSObject to use > JS_NewObject to build the prototype (instead of JSInit/JS_InitClass). I > came up with the following *new* code (in beta 8): > ----------------------------------------------------------------- > JSObject *ChildClass::newJSObject(JSContext *cx) { > return JS_NewObject(cx, &ChildClass::_jsClass, > ParentClass::newJSObject(cx), NULL); > } > JSObject *JSTestChild::getJSObject(JSContext *cx) { > if (!cx) return NULL; > if (!_JSinternal.o) { > _JSinternal.o = newJSObject(cx); > if (!JS_SetPrivate(cx, _JSinternal.o, this)) return NULL; > } > return _JSinternal.o; > } > ----------------------------------------------------------------- > Since JS_NewObject doesn't require a JSObject* in which to define the > object, this method has an added advantage of solving the original > problem (two birds with one stone!). > > In addition, I added the following line to the JSConstructor routine > after the C++ constructor had been called: > ----------------------------------------------------------------- > JS_SetPrototype(cx,obj,ParentClass::newJSObject(cx)); > ----------------------------------------------------------------- > The question is, is this method a bad idea? > > The immediate problem I see is calling newJSObject all the time to > generate prototypes. (That is, a prototype can theoretically be shared > among all subclasses, the way I understand it, and thus only one > prototype is needed.) > > Also, explicitly setting the prototype in the JSConstructor routine > seems like bad design. > > Since the JS_InitClass appears to deal with constructing of new objects, > I look back at my JS_InitClass call that is unchanged since beta 7: > ----------------------------------------------------------------- > JS_InitClass(cx, obj, ParentClass::JSInit(cx,obj), > &ChildClass::_jsClass, ChildClass::JSConstructor, ... ); > ----------------------------------------------------------------- > Should I be putting a call to ParentClass::newJSObject(cx) instead of > the JSInit here? It /seems/ to work the same. > > I guess I'm still a little confused over JS_InitClass and JS_NewObject. > I've been examining the code for both functions trying to figure out > how they work, but that tends to confuse me more. (JS_InitClass calling > JS_NewObject would imply that I could use newJSObject instead of JSInit.) > > So, I'm at a point where on the surface everything seems to be working. > There are a few bits I'm uncertain on, but I don't see any obvious > alternatives to what I'm doing now. > > I just noticed now that the child class prototype field is printing out > as undefined in the following test code: > ----------------------------------------------------------------- > print("start"); > var foo = new JSTest; > print("\tfoo.prototype="+foo.prototype); > print("\tfoo.test2()="+foo.test2()); > print("\tfoo.test5()="+foo.test5(1,2,3)); > print("\tfoo="+foo); > var bar = new JSTestChild(1) > print("\tbar.prototype="+bar.prototype); > print("\tbar.test2()="+bar.test2()); > print("\tbar.test5()="+bar.test5(1,2,3)); > print("\tbar="+bar); > ----------------------------------------------------------------- > Yet test5(), which is only defined in JSTest gets called fine. Output > (with the previous jsgen this code would crash when trying to call > test5, but prototype would be correct!?): > ----------------------------------------------------------------- > start > Vector2dChild(0x53ce84) > JSTest(0, argv)=0x53ce60 > foo.prototype=undefined > JSTest::test2() > foo.test2()=2 > JSTest::test5(1,2,3) > foo.test5()=5 > foo=[object JSTest] > Vector2dChild(0x53d0fc) > JSTest()=0x53d0d8 > JSTestChild(1)=0x53d0d8 > bar.prototype=undefined > JSTestChild::test2() > bar.test2()=2 > JSTest::test5(1,2,3) > bar.test5()=5 > bar=[object JSTestChild] > ----------------------------------------------------------------- > > Thanks, > > Marcello .