Closed
Bug 293972
Opened 19 years ago
Closed 19 years ago
npruntime API does not support ambiguous members
Categories
(Core Graveyard :: Plug-ins, defect)
Core Graveyard
Plug-ins
Tracking
(Not tracked)
RESOLVED
FIXED
People
(Reporter: zhayupeng, Assigned: zhayupeng)
References
Details
Attachments
(1 file, 6 obsolete files)
8.51 KB,
patch
|
brendan
:
review+
brendan
:
superreview+
asa
:
approval1.8b4+
|
Details | Diff | Splinter Review |
In some cases, we need to support ambiguous members, a property and a methos has same name. For example, java suppport this kind of thing. You can define a property and a function with same name. But currently NPAPI+ does not support it. The implementation in nsJSNPRuntime will not handle this case. It will throw exception. To resolve this problem, we need to implement a Member class when the object has ambiguous members. I will attach a patch soon.
Attachment #183472 -
Flags: review?(jst)
Comment 2•19 years ago
|
||
Comment on attachment 183472 [details] [diff] [review] patch v1 I don't understand why you can't just return a callable proxy from your npobj's hasProperty, which does the right things for convert and call. Can you explain to my why that wouldn't work?
(In reply to comment #2) > (From update of attachment 183472 [details] [diff] [review] [edit]) > I don't understand why you can't just return a callable proxy from your npobj's > hasProperty, which does the right things for convert and call. Can you explain > to my why that wouldn't work? > nsJSNPRuntime will throw exception if I return true in both haaMethod and hasProperty. I don't know how to return a callable proxy through NPObject... If you have idea, please let me know. Thanks.
small fix for last patch
Attachment #183472 -
Attachment is obsolete: true
Attachment #185657 -
Flags: review?(jst)
Attachment #183472 -
Flags: review?(jst)
Comment on attachment 185657 [details] [diff] [review] patch v2 >Index: nsJSNPRuntime.cpp >+JS_STATIC_DLL_CALLBACK(JSBool) >+NPObjectMember_convert(JSContext *cx, JSObject *obj, >+ JSType type, jsval *vp); ^ doesn't line up >@@ -1357,4 +1402,129 @@ >+CreateNPObjectMember(NPP npp, JSContext *cx, JSObject *obj, >+ NPObject* npobj, jsval id, jsval *vp) >+{ if !vp you're not going to throw an exception: >+ NS_ENSURE_TRUE(vp, false); is that ok? >+ if (!npobj || !npobj->_class || !npobj->_class->getProperty) { >+ ThrowJSException(cx, "Bad NPObject"); >+ >+ return false; >+ } >+ >+ jsval fieldValue, methodValue; >+ >+ NPVariant npv; >+ VOID_TO_NPVARIANT(npv); >+ if (!npobj->_class->getProperty(npobj, (NPIdentifier)id, &npv)) >+ return false; >+ >+ fieldValue = NPVariantToJSVal(npp, cx, &npv); >+ >+ JSObject *methodObject, *funObject; >+ methodObject = >+ JS_GetFunctionObject(JS_NewFunction(cx, CallNPMethod, 0, JSFUN_BOUND_METHOD, >+ NULL, JS_GetStringBytes(JSVAL_TO_STRING(id)))); JS_NewFunction can fail, you just crashed. Don't do that. >+ funObject = JS_CloneFunctionObject(cx, methodObject, obj); JS_CloneFunctionObject can fail, i'm not quite sure where/when you'll crash, but i'm sure you will. deal with it. afaik JS_CloneFunction will not preserve its parameter such that it doesn't get destroyed by a last ditch gc in the process of constructing the function. Either establish a localrootscope or somehow make sure methodObject is rooted before you call this creature. >+ methodValue = OBJECT_TO_JSVAL(funObject); >+ >+ NPObjectMethodOrFieldValue *memberValue = >+ (NPObjectMethodOrFieldValue *)JS_malloc(cx, sizeof(*memberValue)); >+ if (!memberValue) >+ return false; >+ >+ JSObject *objp = JS_NewObject(cx, &sNPObjectMemberClass, nsnull, nsnull); JS_NewObject performed a last ditch GC and just destroyed your nice memberValue because it wasn't rooted. see above. >+ if (!objp) { >+ JS_free(cx, memberValue); >+ return false; >+ } >+ >+ JS_SetPrivate(cx, objp, (void *)memberValue); >+ >+ memberValue->methodValue = methodValue; >+ memberValue->fieldValue = fieldValue; >+ memberValue->npp = npp; >+ >+ *vp = OBJECT_TO_JSVAL(objp); >+ return true; >+} >+JS_STATIC_DLL_CALLBACK(JSBool) >+NPObjectMember_convert(JSContext *cx, JSObject *obj, JSType type, jsval *vp) >+{ >+ NPObjectMethodOrFieldValue *memberValue; >+ memberValue = (NPObjectMethodOrFieldValue *)JS_GetPrivate(cx, obj); >+ >+ NPVariant npVar; >+ NPObject* npobj; >+ if (!memberValue) { >+ if (type == JSTYPE_OBJECT) { >+ *vp = OBJECT_TO_JSVAL(obj); v this doesn't line up. >+ return JS_TRUE; >+ } >+ if (!JSValToNPVariant(memberValue->npp, cx, >+ memberValue->fieldValue, &npVar)) why the block: >+ { >+ return JS_FALSE; >+ } you don't use a block here: >+ if (!npobj) >+ return JS_FALSE; >+ default: >+ NS_ASSERTION(0, "illegal operation on JSObject prototype object"); NS_ERROR. But what does this mean? if js code can trigger this case then you should not use NS_ERROR/NS_ASSERTION. If this means someone writing c++/c code screwed up, then this is ok. >+ return JS_FALSE; >+ } >+} >+// JS engine should not call it anyway >+JS_STATIC_DLL_CALLBACK(JSBool) >+NPObjectMember_Call(JSContext *cx, JSObject *obj, >+ uintN argc, jsval *argv, jsval *rval) >+{ >+ NS_ASSERTION(0, "Should not call NPObjectMember"); NS_ERROR. >+ return JS_TRUE; > }
Attachment #185657 -
Flags: review?(jst) → review-
Comment on attachment 185657 [details] [diff] [review] patch v2 Thanks, timeless. I still want jst to look at it before I made a new patch.
Attachment #185657 -
Flags: review- → review?(jst)
Comment on attachment 185657 [details] [diff] [review] patch v2 Thanks, timeless. I still want jst to look at it before I made a new patch.
> >+ JSObject *methodObject, *funObject; > >+ methodObject = > >+ JS_GetFunctionObject(JS_NewFunction(cx, CallNPMethod, 0, JSFUN_BOUND_METHOD, > >+ NULL, JS_GetStringBytes(JSVAL_TO_STRING(id)))); > JS_NewFunction can fail, you just crashed. Don't do that. > > >+ funObject = JS_CloneFunctionObject(cx, methodObject, obj); > JS_CloneFunctionObject can fail, i'm not quite sure where/when you'll crash, > but i'm sure you will. deal with it. > afaik JS_CloneFunction will not preserve its parameter such that it doesn't get > destroyed by a last ditch gc in the process of constructing the function. > Either establish a localrootscope or somehow make sure methodObject is rooted > before you call this creature. What do you mean? I should add the function obj to root before clone it? > >+ JSObject *objp = JS_NewObject(cx, &sNPObjectMemberClass, nsnull, nsnull); > JS_NewObject performed a last ditch GC and just destroyed your nice memberValue > because it wasn't rooted. see above. Can you explain whay above code works for me if it can be destroyed?
build w/ TOO_MUCH_GC and WAY_TOO_MUCH_GC, you should crash. http://www.mozilla.org/js/spidermonkey/gctips.html
Assignee | ||
Comment 10•19 years ago
|
||
update dependency
Blocks: 251151
Summary: NPAPI+ does not support ambiguous members → npruntime API does not support ambiguous members
Comment 11•19 years ago
|
||
Comment on attachment 185657 [details] [diff] [review] patch v2 + // To support ambiguous members, we should return a Member class remove "should", and use the full class name here (NPObject Member class". + if (npobj->_class->hasProperty(npobj, (NPIdentifier)id) && + npobj->_class->hasMethod(npobj, (NPIdentifier)id)) ... + if (npobj->_class->hasProperty(npobj, (NPIdentifier)id)) { That's two hasProperty() calls in a row (and potentially two hasMethod() calls too. Make only one of each calls and use PRBool locals to hold the result of those calls and check those... - In CreateNPObjectMember(): + fieldValue = NPVariantToJSVal(npp, cx, &npv); + + JSObject *methodObject, *funObject; + methodObject = + JS_GetFunctionObject(JS_NewFunction(cx, CallNPMethod, 0, JSFUN_BOUND_METHOD, + NULL, JS_GetStringBytes(JSVAL_TO_STRING(id)))); + funObject = JS_CloneFunctionObject(cx, methodObject, obj); + methodValue = OBJECT_TO_JSVAL(funObject); + + NPObjectMethodOrFieldValue *memberValue = + (NPObjectMethodOrFieldValue *)JS_malloc(cx, sizeof(*memberValue)); + if (!memberValue) + return false; + + JSObject *objp = JS_NewObject(cx, &sNPObjectMemberClass, nsnull, nsnull); + if (!objp) { + JS_free(cx, memberValue); + return false; + } + + JS_SetPrivate(cx, objp, (void *)memberValue); + + memberValue->methodValue = methodValue; + memberValue->fieldValue = fieldValue; + memberValue->npp = npp; All of that is likely to crash if GC runs in the middle of this code (last-ditch GC if we're low on memory), as timeless already mentioned. And the new JS member class also needs to ensure that the value/function values don't get GC'd and deleted before the member object does. I'm not sure this general approach of exposing callable members is what we'd want (see comment below re JSTYPE_FUNCTION), but if it is, I'd suggest rewriting the above like this (note the use of PR_Malloc() instead of JS_Malloc(), JS_Free() needs to be replaced with PR_Free() in all places where the memberValue is freed too): NPObjectMethodOrFieldValue *memberValue = (NPObjectMethodOrFieldValue *)PR_malloc(cx, sizeof(*memberValue)); if (!memberValue) { return false; } memberValue->npp = npp; memberValue->fieldValue = JSVAL_VOID; memberValue->methodValue = JSVAL_VOID; JSObject *objp = ::JS_NewObject(cx, &sNPObjectMemberClass, nsnull, nsnull); if (!objp) { PR_free(cx, memberValue); return false; } // This will pass ownership of memberValue to obj ::JS_SetPrivate(cx, objp, memberValue); ::JS_LockGCThing(cx, obj); memberValue->fieldValue = NPVariantToJSVal(npp, cx, &npv); JSObject *methodObject, *funObject; JSFunction *fun = ::JS_NewFunction(cx, CallNPMethod, 0, JSFUN_BOUND_METHOD, NULL, ::JS_GetStringBytes(JSVAL_TO_STRING(id))); if (!fun) { ::JS_UnlockGCThing(cx, obj); return false; } funObject = ::JS_CloneFunctionObject(cx, ::JS_GetFunctionObject(fun), obj); if (!funObject) { ::JS_UnlockGCThing(cx, obj); return false; } memberValue->methodValue = OBJECT_TO_JSVAL(funObject); ::JS_UnlockGCThing(cx, obj); And in addition to that, you'll need a mark hook that looks something like this: JS_STATIC_DLL_CALLBACK(uint32) NPObjectMember_Mark(JSContext *cx, JSObject *obj, void *arg) { NPObjectMethodOrFieldValue *memberValue; memberValue = (NPObjectMethodOrFieldValue *)JS_GetPrivate(cx, obj); if (!memberValue) return; if (!JSVAL_IS_PRIMITIVE(memberValue->fieldValue)) { ::JS_MarkGCThing(cx, JSVAL_TO_OBJECT(memberValue->fieldValue), "NPObject Member => fieldValue", arg); } if (!JSVAL_IS_PRIMITIVE(memberValue->methodValue)) { ::JS_MarkGCThing(cx, JSVAL_TO_OBJECT(memberValue->methodValue), "NPObject Member => methodValue", arg); } return 0; } - In NPObjectMember_convert() + switch (type) { + case JSTYPE_VOID: + case JSTYPE_STRING: + case JSTYPE_NUMBER: + case JSTYPE_BOOLEAN: + if (JSVAL_IS_PRIMITIVE(memberValue->fieldValue)) { + *vp = memberValue->fieldValue; Why do this only for primitive values? [...] + npobj->_class->invoke(npobj, _getstringidentifier("toString"), + nsnull, 0, &retVar); + *vp = NPVariantToJSVal(memberValue->npp, cx, &retVar); And what's the purpose of invoking toString() on the NPObject here? + case JSTYPE_OBJECT: + *vp = memberValue->fieldValue; + return JS_TRUE; Shouldn't object be grouped in with the above types (related the the comment earlier about primitive values). + case JSTYPE_FUNCTION: + *vp = memberValue->methodValue; + return JS_TRUE; Couldn't you use the NPObjectMember_Call() hook to make this member object callable instead of hooking into the convert hook? If you did that, you wouldn't need to create and clone a function in CreateNPObjectMember(), you'd simply create a JSObject of your new class that carries the NPObject, field value, and method name as private data (much like you now carry the field value and the new function object), then in the call() hook you'd simply call the method on the NPObject. r- until we figure all that out.
Attachment #185657 -
Flags: review?(jst) → review-
Assignee | ||
Comment 12•19 years ago
|
||
>
> Couldn't you use the NPObjectMember_Call() hook to make this member object
> callable instead of hooking into the convert hook? If you did that, you
> wouldn't need to create and clone a function in CreateNPObjectMember(), you'd
> simply create a JSObject of your new class that carries the NPObject, field
> value, and method name as private data (much like you now carry the field value
> and the new function object), then in the call() hook you'd simply call the
> method on the NPObject.
>
I tried this way, but the JSObject passed in Call() method is strange. It has no
class and I can not get private data from it. Actually it has no slots...
Johnny, do you have any idea? Thanks!
Assignee | ||
Comment 13•19 years ago
|
||
OK, I have resolved last comments. As usual, I need to get it from argv[-2]. I will try to address other comments soon.
Assignee | ||
Comment 14•19 years ago
|
||
Johnny, This is the modified patch. Please review.
Attachment #188530 -
Flags: review?(jst)
Attachment #188530 -
Attachment is obsolete: true
Attachment #188530 -
Flags: review?(jst)
Attachment #185657 -
Attachment is obsolete: true
Assignee | ||
Comment 15•19 years ago
|
||
Attachment #188531 -
Flags: review?(jst)
Comment 16•19 years ago
|
||
Comment on attachment 188531 [details] [diff] [review] patch v3 Yeah, that's a lot better! But you left you the mark() hook to ensure the member object's private data's members doen't get collected, see my earlier comments. Add that, and I think this is good to go.
Attachment #188531 -
Flags: review?(jst) → review-
Assignee | ||
Comment 17•19 years ago
|
||
new patch with jst's comments
Attachment #188531 -
Attachment is obsolete: true
Attachment #188622 -
Flags: review?(jst)
Comment 18•19 years ago
|
||
Comment on attachment 188622 [details] [diff] [review] patch v4 +JS_STATIC_DLL_CALLBACK(JSBool) +NPObjectMember_convert(JSContext *cx, JSObject *obj, + JSType type, jsval *vp); Fix indentation. +static JSClass sNPObjectMemberClass = + { + "NPObject Member class", JSCLASS_HAS_PRIVATE, Maybe name this "NPObject Ambigous Member class"? + // To support ambiguous members, we return NPObject Member class here. + if (hasProperty && hasMethod) { + NPP npp = LookupNPP(npobj); + + if (!npp) { + ThrowJSException(cx, "No NPP found for NPObject!"); + + return JS_FALSE; + } This npp lookup and check also exists further down this method, you could simply pull that code up above this check and reduce the code size a bit. - In CreateNPObjectMember(): + if (!npobj || !npobj->_class || !npobj->_class->getProperty) { You should check that npobj->_class->invoke() is non-null here too, or in NPObjectMember_Call() if you prefer to do it there. - In NPObjectMember_convert(): + JS_ReportError(cx, "illegal operation on JSObject prototype object"); Prefix JS_Report... with '::'. - In NPObjectMember_Call(): + if (!JS_InstanceOf(cx, objp, &sNPObjectMemberClass, nsnull)) Add :: here too. + NPObjectMemberPrivate *memberPrivate = + (NPObjectMemberPrivate *)::JS_GetPrivate(cx, objp); Actually, you could replace the JS_InstanceOf() and JS_GetPrivate() calls with one call to JS_GetInstancePrivate(), like this: memberPrivate = ::JS_GetInstancePrivate(cx, obj, &sNPObjectMemberClass, argv); - In NPObjectMember_Mark(): + memberPrivate = (NPObjectMemberPrivate *)JS_GetPrivate(cx, obj); Add :: r=jst with those changes. Thanks for doing all this work!
Attachment #188622 -
Flags: review?(jst) → review+
Assignee | ||
Comment 19•19 years ago
|
||
Johnny, thanks for review. Please check this patch and give r if it is OK for you.
Attachment #188622 -
Attachment is obsolete: true
Attachment #189009 -
Flags: review?(jst)
Comment 20•19 years ago
|
||
Comment on attachment 189009 [details] [diff] [review] patch v5 r=jst
Attachment #189009 -
Flags: review?(jst) → review+
Assignee | ||
Comment 21•19 years ago
|
||
Comment on attachment 189009 [details] [diff] [review] patch v5 Brendan, please review this patch. Thanks!
Attachment #189009 -
Flags: superreview?(brendan)
Comment 22•19 years ago
|
||
Comment on attachment 189009 [details] [diff] [review] patch v5 CreateNPObjectMember: - Nit: call the sNPObjectMemberClass instance "memobj" instead of "objp". - If a last-ditch GC nests under JS_NewObject while trying to allocate the slots vector for the already-allocated new memobj (whose allocation just overwrote the cx->newborn[GCX_OBJECT] root), and if the fieldValue was of type object, requiring a new JSObject from nsNPObjWrapper::GetNewOrUsed, then that new object would be GC'd. The best way to handle this is to create memobj (objp in the patch) first, then set *vp = OBJECT_TO_JSVAL(memobj), call JS_AddRoot(cx, vp), then set memobj's private, then call npobj->_class->getProperty and NPVariantToJSVal to initialize the marked memberPrivate->fieldValue. Don't return after making a successful JS_AddRoot call without calling JS_RemoveRoot, when you no longer need to protect *vp from being GC'd away. NPObjectMember_convert: - Nit: combine NPObjectMemberPrivate *memberPrivate and next line, which sets that variable, using an initializer (wrapped to the next line, of course). Same nit later on, in a different method. - There's no need to handle the !memberPrivate case here, you can assert that memberPrivate is non-null. Nothing calls convert on an improperly constructed JSObject. NPObjectMember_Call: - Early Throw/return JS_FALSE in loop to "// Convert arguments" leaks npargs if npargs_buf wasn't big enough. - Nit: NPVariant npv; instead of NPVariant v; (the latter name is used for jsvals). How about another patch addressing these, then I'll sr= -- I'm assuming you have tests working and can retest. Thanks, /be
Attachment #189009 -
Flags: superreview?(brendan) → superreview-
Assignee | ||
Comment 23•19 years ago
|
||
Thanks a lot, Brendan. This is a new patch addressing your comments, please review.
Attachment #189009 -
Attachment is obsolete: true
Attachment #189024 -
Flags: superreview?(brendan)
Comment 24•19 years ago
|
||
Comment on attachment 189024 [details] [diff] [review] patch v6 Noting jst's r=, stamping my sr=, requesting 1.8b4 approval. /be
Attachment #189024 -
Flags: superreview?(brendan)
Attachment #189024 -
Flags: superreview+
Attachment #189024 -
Flags: review+
Attachment #189024 -
Flags: approval1.8b4?
Updated•19 years ago
|
Flags: blocking1.8b4?
Updated•19 years ago
|
Attachment #189024 -
Flags: approval1.8b4? → approval1.8b4+
Assignee | ||
Comment 25•19 years ago
|
||
Just checked in http://tinderbox.mozilla.org/bonsai/cvsview2.cgi?diff_mode=context&whitespace_mode=show&subdir=mozilla/modules/plugin/base/src&command=DIFF_FRAMESET&file=nsJSNPRuntime.h&rev1=1.5&rev2=1.6&root=/cvsroot I will mark FIXD once the tree is OK with this patch.
Assignee | ||
Comment 26•19 years ago
|
||
->FIXED
Status: NEW → RESOLVED
Closed: 19 years ago
Resolution: --- → FIXED
Updated•19 years ago
|
Flags: blocking1.8b4?
Updated•2 years ago
|
Product: Core → Core Graveyard
You need to log in
before you can comment on or make changes to this bug.
Description
•