RR: JavaScriptObject

5 views
Skip to first unread message

Emily Crutcher

unread,
Dec 14, 2006, 7:21:37 PM12/14/06
to Google Web Toolkit Contributors
For those of you new to the GWT contributors community, we use Request for Review (RR): in our subject line when requesting design review and feedback.
 
Also, to introduce myself,  I'm Emily working in beautiful Atlanta GA as a Google developer on the Google Web Toolkit.    I am most interested in GWT areas such as I18N, Generators, Widget Design, JavaScript import/export, and validation.

 

Personally, I am married and have a very active two-year old, Kaitlyn, who is an honorary member of the GWT team by virtue of attending several GWT project meetings.   Restoring old houses (in specific my own), board games, and politics number highly amongst my hobbies.

 

It's nice to meet you all!

 
Now back to our regularly scheduled design discussion ...
 
When programming in GWT, it is frequently necessary to interact with native JavaScript objects. Examples of such interaction include manipulating DOM elements, accessing APIs such as Google Maps/Google Search, or creating more efficient implementations data structures such as HashMap.

GWT introduced the JavaScriptObject to deal with such cases. However, the semantics of JavaScriptObject are currently ambiguous and require that the underlying object itself be modifiable, a requirement that many JavaScript objects cannot support. We'd like  to upgrade JavaScriptObject such that the following criteria are met.
 
  • Immutable JavaScript objects
    • Support JavaScript objects where neither the Prototype nor the JavaScript object itself may be modified. 
  •  Identity is preserved
    • If the same JavaScriptObject is returned by two native calls, then the two references should be true under "=="
  • Hosted Mode/Web Mode compatibility
    • Except where impossible, hosted mode and web mode should behave the same in respect to JavaScriptObject.
  • Good Performance characteristics
    • Speed should be maximized, and, so long as it doesn't impact speed, code size and memory usage should be minimized
  • Conceptually simple
    • Should be easy to explain to people what the behaviors and limitations of JavaScriptObject.
  • Support for Interfaces
    •   A JavaScriptObjectshould be able to implement at least a limited set of interfaces.
There is no perfect solution that meets all the requirements above. Instead, three potential solutions are provided below, each with distinct advantages and disadvantages
 
Proposal 1 - Restricted JavaScriptObject 
Restrict the use of classes extending JavaScriptObject in the following ways:
  • No polymorphic dispatch. i.e. only can introduce final instance methods.
  • No casting to Object
  • No casting to any interface which does not implement the marking interface JavaScriptInterface
  • No instanceof support
  • Casting is not programmatically checked, which can lead to hard-to-debug user errors
Pros
  1. Fast
  2. Conceptually simple
  3. Preserves identity
 
Cons
  1. Requires significant java language restrictions.  
  2. Severely restricts the use of classes derived from JavaScriptObject, requiring manual wrapping of JavaScriptObject in many cases.
Proposal 2 - Autoboxing of JavaScriptObject
When JavaScriptObject is upcast or are assigned additional fields, they are wrapped in a proxy object. All normal Object operations, such as polymorphism and instance of are supported.

'==' is replaced by a method to first dereference proxies before calling "==" .

The overhead for creating JavaScript method calls is very low , so the expected overhead for replacing '==' is very low.

The overhead for  creating int auto boxing proxy objects is reasonably low  so the expected overhead for creating  JavaScriptObject proxy objects is also reasonably low.

Pros

  1. Usage is easy to understand
  2. No limitations on JavaScriptObject
  3. Hard for user to cause runtime crashes
  4. Proxies are only created where necessary, so reasonably efficient
  5. No language changes or restrictions needed 

Cons
  1. "==" becomes a small function rather than native operation
  2. Extra memory overhead for proxies where needed
Proposal 3 - Interface and Object static dispatching
Each class which extends JavaScriptObject must implement a static instanceOf(JavaScriptObject) method, which returns whether the passed in JavaScriptObject is an instance of the given class.

Gather the set of Interfaces any JavaScriptObject inherits. If a JavaScriptObject is ever cast as an Object, also include Object.

All dispatches, instanceofs and casts from that set must first check to see if the object extends JavaScriptObject. If it does, then use the instanceOf methods to check which one. 

Dynamic dispatch is supported. No fields can be added to the class.

Pros
  1. Identity is preserved
  2. No memory overhead for proxies
Cons
  1. Code size and speed overhead for every interface implemented by a JavaScriptObject 
  2. Code size and speed overhead for calls to  Object hashCode, toString, and equals.
  3. User error in instanceOf method can produce hard-to-debug crashes.
  4. The introduction of new JavaScriptObject classes can change behavior in working code by "stealing" the object's dispatch
  5. Complex to explain to user
  6. Requires slight java language restrictions


--
"There are only 10 types of people in the world: Those who understand binary, and those who don't"

Sandy McArthur

unread,
Dec 14, 2006, 9:16:44 PM12/14/06
to Google Web Toolkit Contributors
Could you give some examples where JavaScriptObject is current
insufficient? I've read your post a few times now and I don't get what
problems you are wanting to address.

John Tamplin

unread,
Dec 15, 2006, 12:32:30 AM12/15/06
to Google-Web-Tool...@googlegroups.com
To me, it seems like proposal 2 is the best combination of trade-offs.

--
John A. Tamplin
Software Engineer, Google

Emily Crutcher

unread,
Dec 15, 2006, 8:26:42 AM12/15/06
to Google-Web-Tool...@googlegroups.com
Many of the issues are around JavaScript API imports.  Right now, we require that all JavaScriptObjects be able to have the Object methods added to them if they are ever upcast to Object. A JavaScript API, such as Google search, may not appreciate its objects getting random extra methods assigned to it.
 
Also, some javascript objects, such as Element and the XML libraries simple cannot be modified, so they end up either all being accessed via static methods (see DOM) or a proxy pattern (see XML library), this is not the ideal way to right new APIS. For instance, with the XML library, a lot of work had to be done to avoid huge memory problems. A single DOM class is understandable, but it is not the ideal overall structure for Java code. 
 
Additionally, when trying to directly extend JavaScriptObject, it is very easy to get run time errors with little explanation, as the dynamic dispatching rules are not totally defined.  We would far prefer that any errors be caught at compile time instead.

Scott Blum

unread,
Dec 15, 2006, 10:01:51 AM12/15/06
to Google-Web-Tool...@googlegroups.com
Couple of variant ideas I'd like to mention.

4) Autoboxing JavaScriptObject without respecting "=="

Same pros as #2.

Cons:
1. "==" identity not preserved; you'd have to use .equals() to test identity.
2. Extra memory overhead for proxies where needed. (same as idea #2).

My opinion is that Con #1 is not actually so bad, for a couple of reasons.  First, identity is actually not preserved in hosted mode.  If you call a JSNI method that returns the same JavaScript object twice, you won't get the same wrapper object in hosted mode.  So far, I haven't seem complaints about this.  It doesn't really make sense to worry too much about JSO identity in web mode if we don't respect it in web mode.  Secondly, many JavaScript-level APIs (such as the DOM) already don't preserved identity.  On many browsers, document.getElementById("foo") !== document.getElementById("foo").  It almost seems safer just to get people used to the idea of using JSO.equals() if they care about identity.

Another thing that perhaps isn't crystal clear about idea #2 is that it would effectively turn "==" into a function call whenever the arguments are statically typed to be Object.  This could have code size and performance impact on the Collections classes, for example.

5) Tailored support for specific JavaScript object categories

The basic idea is that you break types of JSOs into two or three categories, say: modifiable prototype, unmodifiable prototype, unmodifiable object.  Then the compiler would generate code to do the most efficient thing given the constraints.  So "modifiable prototype" would be the lightest-weight construct, unmodifiable object the heaviest.  This follows our general "pay for what you use" philosophy.  Note: the existing JSO support is essentially "unmodifiable prototype".  This could be mixed in with any of the above ideas-- would would still need to pick one of those ideas to implement "unmodifiable object".

This could be implemented either through some sort of metadata, or perhaps through a class heirarchy.  It would also be backwards compatibile with our existing JSO properties.

Scott

kebernet

unread,
Dec 15, 2006, 11:54:36 AM12/15/06
to Google Web Toolkit Contributors
I really wished I could say I liked proposal 1, but

- No casting to Object

Seems like almost a deal breaker to me.

Just a quick question about Proposal #2:


>When JavaScriptObject is upcast or are assigned additional fields, they are
>wrapped in a proxy object. All normal Object operations, such as
>polymorphism and instance of are supported.

Forgive me for not knowing, but it seems like, while in many cases the
overhead of these proxies might be low, the XML DOM classes used in
com.google.gwt.xml.client would seem like they would begin generating
inordinate numbers of proxies, no?

My first response is Proposal #3 would seem to be the best here. While
the Con of:
>5. Complex to explain to user
is a pretty big one, I think the number of end users who are actually
going to be implementing something that directly extends
JavaScriptObject is going to be few.

Between #2 and #3 it seems like the big question is just how much the
speed overhead is for calls to .equals and .hashCode. If it is fairly
minimal, I tend to opt in that direction for the basic reason that a
good sized GWT application can really expand to take a lot of RAM right
now, and on a family computer with 512mb, big browser usage can really
make the performance seem horrible. While the proxies used in #2 might
individually be small, it is my experience that when you want to juggle
JSOs, you want to juggle a LOT of them.

Sandy McArthur

unread,
Dec 15, 2006, 12:02:08 PM12/15/06
to Google Web Toolkit Contributors
Scott Blum wrote:
> 4) Autoboxing JavaScriptObject without respecting "=="
[...]

> Cons:
> 1. "==" identity not preserved; you'd have to use .equals() to test
> identity.
[...]

> My opinion is that Con #1 is not actually so bad, for a couple of reasons.
> First, identity is actually not preserved in hosted mode.

I support not trying to maintain "==" behavior. The example of
auto-boxing an int and using == for auto-boxed int only works up to
127. After that you must use equals because Java only does a lookup for
small ints boxed to Integers.

Scott Blum

unread,
Dec 15, 2006, 1:33:07 PM12/15/06
to Google-Web-Tool...@googlegroups.com
On 12/15/06, kebernet <kebe...@gmail.com> wrote:
Just a quick question about Proposal #2:
>When JavaScriptObject is upcast or are assigned additional fields, they are
>wrapped in a proxy object. All normal Object operations, such as
>polymorphism and instance of are supported.

Forgive me for not knowing, but it seems like, while in many cases the
overhead of these proxies might be low, the XML DOM classes used in
com.google.gwt.xml.client would seem like they would begin generating
inordinate numbers of proxies, no?

Well, it depends.  One of the ideas regarding this approach that I'm not sure came through is that it might be possible to not actually perform the wrapping until/unless the user performs a cast (either an explicit cast, or an implicit upcast via assignment-to-weaker-type).  So the overhead would only be incurred if you actually change the static type.

In practice, this might be a pretty common use case since people may want to put JSOs into collection classes (implicit Object upcast).  At least, until we have generics.

My first response is Proposal #3 would seem to be the best here. While
the Con of:
>5. Complex to explain to user
is a pretty big one, I think the number of end users who are actually
going to be implementing something that directly extends
JavaScriptObject is going to be few.

We'd like to make it easier to do.  The ultimate goal of better JavaScriptObject support is to make it a lot easier to integrate 1) integrate with an existing JS lib or 2) build a reusable JS lib using GWT.  Part of this solution might involve generating classes that extend JavaScriptObject in order to efficiently interoperate.

Scott

kebernet

unread,
Dec 15, 2006, 2:31:16 PM12/15/06
to Google Web Toolkit Contributors

> Well, it depends. One of the ideas regarding this approach that I'm not
> sure came through is that it might be possible to not actually perform the
> wrapping until/unless the user performs a cast (either an explicit cast, or
> an implicit upcast via assignment-to-weaker-type). So the overhead would
> only be incurred if you actually change the static type.
>
> In practice, this might be a pretty common use case since people may want to
> put JSOs into collection classes (implicit Object upcast). At least, until
> we have generics.
>
FWIW, I can say in my code this is a very common case. I end up using
JSO's and core.Element in collections and hashmaps to keep a lookup to
associated UIObjects quite frequently. This is one of the reasons why
the performance of .hashCode stands out as a concern for Proposal #3,
though IIRC, Element has its own .hashCode implementation anyway.


> Scott

Reply all
Reply to author
Forward
0 new messages