Closed Bug 224649 Opened 21 years ago Closed 20 years ago

javascript tries to set property of java object fails

Categories

(Core :: DOM: Core & HTML, defect)

x86
All
defect
Not set
major

Tracking

()

RESOLVED FIXED
mozilla1.7final

People

(Reporter: xiaobin.lu, Assigned: jst)

References

()

Details

Attachments

(2 files, 1 obsolete file)

User-Agent:       Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; .NET CLR 1.1.4322)
Build Identifier: Windows; U; Windows NT 5.1; en-US; rv:1.5) Gecko/20031007

Javascript can't set public field of java object.

I attached the java file and a html page. After further debugging into the 
issue, I think this bug should belong to javascript engine instead of 
liveconnect.

Here is my analysis:
 If I write something like below in javascript:
       docuement.appletObject.TEST_MESSAGE = 20;

  Javascript engine will call js_SetProperty (See js\src\jsobj.c), here is the 
code:
        if (!js_LookupProperty(cx, obj, id, &pobj, (JSProperty **)&sprop))
        return JS_FALSE;

    if (sprop && !OBJ_IS_NATIVE(pobj)) {
        OBJ_DROP_PROPERTY(cx, pobj, (JSProperty *)sprop);
        sprop = NULL;
    }

    The js_LookupProperty will succeed, however, since OBJ_IS_NATIVE returns 
false, it will execute OBJ_DROP_PROPERTY. OBJ_IS_NATIVE is a macro like below:
      #define MAP_IS_NATIVE
(map)                                                    \
    ((map)->ops == &js_ObjectOps ||                                           \
      ((map)->ops && (map)->ops->newObjectMap == js_ObjectOps.newObjectMap))  
#define OBJ_IS_NATIVE(obj)  MAP_IS_NATIVE((obj)->map)

 Since obj is a really a JavaObject(if you are familiar with liveconnect), so 
the map->ops won't be &js_ObjectOps or the newObjectMap won't be 
js_ObjectOps.newObjectMap. So the OBJ_IS_NATIVE will return false. This case 
the property be dropped from the object, I guess.

   I want to know whether javascript here does the right thing. Could some 
javascript guru evaluate this and tell us is this a liveconnect bug or 
javascript problem?

    






Reproducible: Always

Steps to Reproduce:
1. Open Mozilla
2. Visit the Test.html

Actual Results:  
The second dialog box shows "Value of MyApplet.getTestMessage():Test"

Expected Results:  
The second dialog box shows "Value of MyApplet.getTestMessage():20"

This works for Netscape 4.7 and IE.
Attached file Testcase
The testcase contains a Java class/source file & a html page.
cc'ing Brendan, Mike on this question -
OBJ_DROP_PROPERTY doesn't remove properties from objects, it simply drops a
reference used for controlling the lifetime of the JSProperty in question.  Do
you see the liveconnect property-setter being called, for your test in question?
Sorry, the JS engine code is correct.  Can you say what is going wrong in more
detail?

/be
Component: JavaScript Engine → Java: Live Connect
The setter of the liveconnect object(jsj_JavaObject::setPropertybyId) is not 
getting called.

Let me try to clarify.
 
In Javascript, I wrote something like: "document.myApplet.TEST_MESSAGE = 20", 
this will call into jsobj.c:js_SetProperty. Here is the code from lxr:
          
     if (!js_LookupProperty(cx, obj, id, &pobj, (JSProperty **)&sprop))
          return JS_FALSE;
 
     if (sprop && !OBJ_IS_NATIVE(pobj)) {
         OBJ_DROP_PROPERTY(cx, pobj, (JSProperty *)sprop);
         sprop = NULL;
     }
     
     attrs = JSPROP_ENUMERATE;
     flags = 0;
     shortid = 0;
     clasp = OBJ_GET_CLASS(cx, obj);
     getter = clasp->getProperty;
-->  setter = clasp->setProperty;
    
  js_LookupProperty will call the liveconnect lookupProperty correctly. I 
noticed that the setter has been set to "XPC_WN_Helper_SetProperty" during my 
debugging. And I think something maybe wrong here and finally cause the wrong 
method to get called. (Just my thought, should we try to get "proto" object 
and if proto is not native object, call "OBJ_SET_PROPERTY"? This is just my 
thought got from the "js_LookupProperty".) 



ECMA-262 Edition 3 section 8.6.2.2 says that js_SetProperty has to work that
way.  If the property does not exist in the object itself (only on a prototype,
whether native or host object -- step 3 in 8.6.2.2), then a new property is
created which "shadows" the proto-property (step 6).

Why was js_SetProperty even called here?  Shouldn't JavaObject_setPropertyById
have been called instead, if you were setting the property of a JavaObject?

Note that js_SetProperty assumes OBJ_IS_NATIVE(obj).  If you were to implement a
non-native JSObjectOps, and point its setProperty hook at js_SetProperty, you
would create an invalid situation.

/be
Assignee: general → xiaobin.lu
Is the applet object a native (JSObjectOps) object, whose prototype is a Java
(JavaObject_ops) object?  That can't work, given how ECMA specifies [[Put]],
also known as JSObjectOps.setProperty, must work.

/be
Cc'ing DOM guys in case there's some bogus prototyping going on (see comment 7).

/be
When the browser tries to get the Java object, it creates a JSObject ( I think 
it is a xpc wrapped native embed node) whose prototype is the JSObject wrapped 
around the real applet object. The code is in nsDOMClassInfo.cpp:line 5727 (
nsHTMLExternalObjSH::PostCreate). That is why js_SetProperty is called instead 
of "JavaObject_setPropertyById". 

The problem is if we delegate the call to js_SetProperty of that xpc embed node, 
should we consider how to set the property in its prototype object? As Brendan 
commented (comment #6), if the logic inside js_SetProperty is right, then there 
must be something wrong in the xpconnect code. Since when we put the JavaObject 
as a prototype object in a xpc wrapped native embed node, we should consider how 
to set the property of the its prototype.
 
When did this regress?  The patch checked in for bug 12367 was different from
the only attachment in that bug, but I don't know why.  I know why the patch
checked in for that bug (rev 3.98 of js/src/jsobj.c) does what it does -- it
does that for ECMA conformance, per comment 6.  But the patch in 12367 does what
the code in nsHTMLExternalObjSH::PostCreate expects.

jst, do you remember any of this?

/be
Rev 3.98 of jsobj.c was checked in over two years ago, on 03-Sep-2001.

/be
Component: Java: Live Connect → DOM HTML
Reassign to DOM guy.
Assignee: xiaobin.lu → jst
And the QA, too -
QA Contact: PhilSchwartau → ian
Brendan, I'm curious why the patch for bug 12367
(http://bugzilla.mozilla.org/attachment.cgi?id=47921&action=view) is different
from the real code
(http://lxr.mozilla.org/seamonkey/source/js/src/jsobj.c#2704)? Using the
original patch, setting the field value of java object will be successful.
Kyle, I know -- that's what I said in comment 10, second sentence.  But I went
on to say that I don't know why the patch wasn't what was checked in.  And that
I do know that the checked-in change was necessary to preserve ECMA conformance,
per comment 6 first paragraph.

The patch attached to bug 12367 is not valid per ECMA-262.

The issue I raised in comment 11 (and comment 10) is this: why wasn't this
broken for the last two years?  Or was it broken, and no one noticed?

I'll try to find jst on IRC and figure out the real fix.

/be
Brendan, first, sorry for not being aware that you already mentioned that;
second, I still think the original patch does not break ECMA conformance. 
  if (sprop && !OBJ_IS_NATIVE(pobj)) 
means the property was found, and it's not a native object, so we should go to
ECMA-262 Edition 3 section 8.6.2.2 step 4 - call the object's setProperty
method, rather than pretending the property was not found (set sprop = NULL).
I think the reason, that no one noticed this issue before, is partly because of
the poor performance of js->java call. Even with the simplest test case, it will
take 3+ minutes to complete the first js->java call on my P4 1.8GHz windows box.
Re: comment 16: you are misreading the code and/or the ECMA spec.  The code in
js_SetProperty has a non-null sprop, but it comes from a non-native object pobj,
returned from the call to js_LookupProperty.  That means it comes from a
prototype object of obj, the input parameter to js_SetProperty that is a native
object (by definition -- otherwise you wouldn't have called js_SetProperty on obj).

Since pobj is a prototype, sprop is not "in" obj in the sense of the spec, and
ECMA-262 Edition 3 8.6.2.2 step 3 applies.

Re: comment 17: I cannot believe it takes 3 minutes to call from JS into Java on
such a fast machine.  What is going on?  Are a ton of classes being loaded? 
Whatever the problem, that performance problem is fatal to LiveConnect.  Is
there a bug on file to track that performance problem?

/be
The reason of liveconnect preformace lies in its agrresive reflection to the 
javaobject/javaclass. One of the main goals of JEA design is to resolve the 
performance problem of liveconnect. It utilizes the similar idea as Microsoft 
IDispatch mechanism to do the scripting.
Xiaobin, thanks for confirming that.  Isn't it the case that LiveConnect used to
perform better?  What changed?  I'm in favor of JEA (I hope you're looking at
the IDispatch support added to xpconnect by dbradley), but if we used to have
better LiveConnect performance with OJI, can't we do something in the short run
to recover that better performance?

/be
The reason of the bad performance of liveconnect is due to the current 
implementation of liveconnect module itself. We never have a better performance 
since then.

There is a way to get a good performance with the current implementation which 
will involves a lot change. I would rather persue the change in JEA. We are now 
busy with the implementation/prototyping. And I got it to work with 
JavaObject already and the performace is comparable to Netscape 4.x liveconnect 
implementation. 

The JEA implementation of liveconnect still uses the current hook between 
javascript engine and liveconnect (JSObjectOps), but the internal 
implementation uses the similar idea of IDispatch (e.g, getPropertybyID will 
call getIDOfName etc). I haven't got a chance to talk to dbradley to see how it 
can utilize the hook of xpconnect IDispatch engine. But I would rather to make 
the current thing work better and later on we can think how to improve it to 
utilize the xpconnect IDispatch stuff.
I have some time to look into this problem again. 

As shown in the picture at the end of nsHTMLExternalObjSH::PostCreate(), every
applet object is wrapped by a xpc native node:

  // this.__proto__.__proto__.__proto__
  //   ^      ^         ^         ^
  //   |      |__ pi (plugin instance) wrapper, most likely wrapped
  //   |          by LiveConnect
  //   |__ xpc wrapped native embed node

Whenever we call the lookupProperty/getProperty/setProperty methods of an
applet, it always goes to js_LookupProperty/js_GetProperty/js_SetProperty
(through XPC_WN_NoCall_JSOps). 

An applet object not only has java properties, but also has DOM properties. In
js_LookupProperty, we try to find the native property at first, then over to
obj.proto if failed (http://lxr.mozilla.org/mozilla/source/js/src/jsobj.c#2481).
Same thing we did in js_GetProperty
(http://lxr.mozilla.org/mozilla/source/js/src/jsobj.c#2652). So we should *not*
assume that every obj goes into js_SetProperty is native.
I noticed that the setProperty of java applet was already broken in mozilla 1.0
that is the earliest mozilla I can find.
> So we should *not* assume that every obj goes into js_SetProperty is native.

Yes, we should and do -- you mean "we should *not* assume that every native
object has a native prototype" I think -- but js_SetProperty handles that case
too, per ECMA-262 (it creates a shadowing property directly in the native
object, which hides or "shadows" the prototype's property of the same id).

So the way plugins are prototyped is in conflict with ECMA-262.  We need a way
to merge the DOM and applet objects without using prototyping.  One way to do it
is with a DOM object whose class hooks know how to get and set properties in the
applet, too.  jst, how hard would that be?

/be
Modifying the way plugins are prototyped is a big change. Can we just take this
1-line patch as a short term workaround until we rewrite the whole plugins
prototyping things? This bug is a blocker of liveconnect funtionality.

RCS file: /cvsroot/mozilla/js/src/jsobj.c,v
retrieving revision 3.155
diff -p -u -r3.155 jsobj.c
--- jsobj.c     11 Feb 2004 07:21:59 -0000      3.155
+++ jsobj.c     30 Mar 2004 09:08:24 -0000
@@ -2707,7 +2707,7 @@ js_SetProperty(JSContext *cx, JSObject *
  
     if (sprop && !OBJ_IS_NATIVE(pobj)) {
         OBJ_DROP_PROPERTY(cx, pobj, (JSProperty *)sprop);
-        sprop = NULL;
+        return OBJ_SET_PROPERTY(cx, pobj, id, vp);
     }
  
     /*
OS: Windows XP → All
Why should we violate the ECMA spec to fix LiveConnect?  That patch will have
bad effects for the usual case, where there is a N:1, not a 1:1, relationship
between the direct object (the plugin DOM object) and its prototype (the applet
object).  Then any setting of a property in a non-native prototype shared among
N direct objects will collide in the prototype, rather than shadow the
proto-property with a native, direct property of the same id.

Kyle, you make a good point that redoing the prototype chain now is a bigger fix
than needed.  A minimal fix in nsDOMClassInfo.cpp is not that hard; it should
avoid hacking directly into the JS engine in a code path not specific to this
LiveConnect special case.  I can't stress enough how important it is to make
hacks and workarounds (and even ultimate fixes, if they require special case
code) be as local to the situation they address as possible -- until you have
enough instances of the special case to generalize into a lower layer.

Attaching a first cut patch.  I'm not going to have time to push it through
testing and review, but if others can help do those tasks, I can approve it for 1.7.

/be
Kyle, can you remove your jsobj.c patch and test this patch?  Thanks.

/be
Attachment #145120 - Flags: superreview?(jst)
Attachment #145120 - Flags: review?(jst)
Note that that patch needs an '\' at the end of the line:

+#define EXTERNAL_OBJ_SCRIPTABLE_FLAGS
Attached patch Add missing '\'.Splinter Review
Attachment #145120 - Attachment is obsolete: true
Attachment #145120 - Flags: superreview?(jst)
Attachment #145120 - Flags: review?(jst)
Comment on attachment 145128 [details] [diff] [review]
Add missing '\'.

r+sr=jst
Attachment #145128 - Flags: superreview+
Attachment #145128 - Flags: review+
Let's get this tested and I'll get another driver to approve the patch for 1.7.

/be
Flags: blocking1.7+
Comment on attachment 145128 [details] [diff] [review]
Add missing '\'.

I tested it. It works like a charm. Thanks for fixing this. Should we do the
equivalent to the GetProperty?
Comment on attachment 145128 [details] [diff] [review]
Add missing '\'.

Kyle: there's no need for extra work in the getProperty case, because the
shadowing problem doesn't exist for get -- only for set ([[Put]] in ECMA
terms).  You can get an id from an object o, and if it has a prototype p in
which id is defined, you'll find that property without creating a property in o
that hides the one in p.  Only if you are setting o[id] will you potentially
make a shadowing property, per ECMA.

Going for 1.7 approval so jst can do the checkin.

/be
Attachment #145128 - Flags: approval1.7?
okay, thanks for the clear and throughout explanation.
Target Milestone: --- → mozilla1.7final
Comment on attachment 145128 [details] [diff] [review]
Add missing '\'.

a=chofmann for 1.7
Attachment #145128 - Flags: approval1.7? → approval1.7+
Fix checked in. Thanks for the patch, brendan!
Status: NEW → RESOLVED
Closed: 20 years ago
Resolution: --- → FIXED
Component: DOM: HTML → DOM: Core & HTML
QA Contact: ian → general
You need to log in before you can comment on or make changes to this bug.

Attachment

General

Creator:
Created:
Updated:
Size: