Tuesday, March 4th, 2008

Category: FlashCategory: Flex

Proxy/XML + [] = broken

There’s an issue with the Proxy and XML classes when you use the [] syntax to execute methods.

For example, this is the working behaviour:

var g:Function = myProxy.go; // calls getProperty on the Proxy subclass
myProxy.go(); // calls callProperty on the Proxy subclass

This is the non-working behaviour:

var g:Function = myProxy['go']; // calls getProperty on the Proxy subclass
myProxy['go'](); // calls _getProperty_ on the Proxy subclass

In the second example the second line calls getProperty. So why is that a big deal? Two things: first, it’s not consistent with non-[] syntax, and second, it calls getProperty so you don’t know that the user is actually calling a method.

This was reported to Adobe on JIRA (Calling a method on a subclass of proxy dynamically invokes getProperty rather than callProperty) and after months of inactivity it was finally concluded that this is not a bug. Why? Here’s the example that was given:

“Not an issue, the following code chunks are equivalent: ”

var foo:* = mp['go']; // this calls getProperty()
foo();

mp['go']() // calls getProperty

However, if you take the above example and don’t use [] syntax the result is different:

var foo:* = mp.go; // calls getProperty
foo();

mp.go(); // calls callProperty

So why would the first example be correct?

Why then is there a callProperty at all? Why doesn’t the second example also call getProperty so that you must return a function? Because a method is being called. In the first example a method is also being called, but, because of how things are handled by the player (I can only assume, it’s quite possible it’s a compiler issue), getProperty is called because it is seen as a dynamic call.

The thing is the Proxy class is supposed to let you handle dynamic calls - that’s it’s purpose. The Proxy class is there to give you the power to manage how an object behaves. In my case the object I’m creating belongs to a very dynamic API. It’s quite possible for a developer to use the [] syntax and for my class to have no idea if they meant to call a function or not.

For example, in the code below my Proxy subclass knows exactly what to do:

myProxy.obj.obj2.method();

myProxy.obj // calls getProperty - return an object
myProxy.obj.obj2 // calls getProperty - return an object
myProxy.obj.obj2.method() // calls callProperty - return a function

In the following code my Proxy subclass has no idea that you’ve called a function:

myProxy['obj']['obj2']['method']();

myProxy[’obj’] // calls getProperty - return an object
myProxy[’obj’][’obj2′] // calls getProperty - return an object
myProxy[’obj’][’obj2′][’method’]() // calls getProperty - return an object

You’ll then get a runtime error because a function was expected.

Proxy is supposed to handle everything that isn’t hardwired dynamically. There should be no difference with the [] syntax.

The thing is this problem isn’t limited to subclasses of Proxy. It’s a problem for the XML class as well.

For example, the following code will generate a runtime error:

myXML['toString']();

cheap Rodney Atkins song get Nine Inch Nails track DVD music John Mayer CD melodies Sting free Duran Duran melodies cheap Madonna music get The Cranberries track get Era track free track Sorry buy Sorry track download track MOBY CD music Marcus Johnston free mp3 Duran Duran cheap mp3 Nine Inch Nails free The Cranberries melodies cheap song Duran Duran CD music Era buy Rodney Atkins song free Sting melodies buy album John Mayer buy track Enigma free Marcus Johnston track get Duran Duran melodies buy Jennifer Lopez melodies get Marcus Johnston music free Madonna mp3 download melodies Enigma buy MOBY music free Era album download Jennifer Lopez melodies free song Contemplacion cheap mp3 Marcus Johnston CD mp3 Rodney Atkins download Marcus Johnston song get album Sorry CD song Rodney Atkins buy track Era get Nine Inch Nails melodies buy The Cranberries song buy MOBY track download Sorry track cheap mp3 Sorry buy Jennifer Lopez track buy album Jennifer Lopez download Era mp3 get song John Mayer buy album Madonna download album Sorry download Sting music get John Mayer track CD melodies Marcus Johnston cheap album Nine Inch Nails buy music Jennifer Lopez cheap Contemplacion song free Enigma music get album John Mayer free Rodney Atkins music DVD track Sorry DVD album Jennifer Lopez download Madonna mp3

All of the XML class public members are methods. Why? So that you can execute E4X calls to make working with XML easier. For example:

trace(myXML.somenode.anothernode.@name);

Things would break if Adobe would have left public members as properties and you happened to have a node with the same name:

var node:XML = myXML.length;

The above code returns an XML node because the length “property” of XML is actually a method:

var l:Number = myXML.length();

But now take the above example and use the [] syntax:

var l:Number = myXML['length']();

Oops, runtime error: function expected. Why? Well, it looks as though it’s the same issue as with Proxy. When you execute the above example the length method isn’t called. Instead the XML class looks up “length” in the XML data and returns an XMLList instance.

Btw, this bug is the reason that you shouldn’t Proxy an XML object.

Now, why would anyone dynamically call methods on an XML object? Doesn’t really matter. There’s nothing wrong with using the [] syntax. The problem is a deficiency in the Flash Player or the compiler.

Why should there be a difference between this:

myProxy.go();

and this?:

myProxy['go']();

.
.
.

Categories: Flash and Flex.

3 Responses to 'Proxy/XML + [] = broken'

Subscribe to comments with RSS or TrackBack to 'Proxy/XML + [] = broken'.

  1. Theo | Thursday, January 3rd, 2008 | 6:37 pm

    I share your grief. Proxy isn’t as nice as it could be. I’ve run into a similar problem: calling methods that are in namespaces. For example proxiedObject.myNs::method() will call getProperty on the proxy object, not callProperty.

    Even though Proxy gives us more possibilities (like supporting for … in and for each … in), it has some major drawbacks compared to the old __resolve. The major issue in my view is that you have to inherit Proxy to be able to do proxying. This means that you can’t inherit from anything else, and it gets really difficult doing things that could be useful, as you can’t inherit the base class you need to.

    It would be so much better if there was another way to mark a class as being a proxy, something like the “dynamic” keyword, for example.

  2. David R | Wednesday, May 21st, 2008 | 11:47 am

    I see your problem, but unfortunately I agree that it’s not a bug. It’s just the way the language works, and there’s no way around it without some fundamental changes to the compiler and the player, which would actually break away from the EcmaScript standard.

    I think a solution for your particular case, is in your “getProperty” method, if they are accessing property “method”, you should return a function. This way, when they try to call it later, it will execute correctly.

  3. Derek Vadneau | Wednesday, May 21st, 2008 | 12:15 pm

    “I think a solution for your particular case, is in your “getProperty” method, if they are accessing property “method”, you should return a function. This way, when they try to call it later, it will execute correctly.”

    You’re missing the case. I do NOT know it’s a method. How would I know? I _know_ it’s a method when callProperty is called, but I _don’t_ know it’s a method if getProperty is called.

    callProperty is supposed to be called when a method is being invoked. getPropety is supposed to be called when the value of a property is being retrieved.

    This is a method call:

    obj.go();

    This is a method call:

    obj[”go”]();

    This is a property get:

    var a = obj.go;

    This is a property get:

    var a = obj[”go”];

    This is also a property get on “go”:

    obj.go.fn();

    This is also a property get on “go”:

    obj[”go”].fn();

    The fact that the compiler/player doesn’t know obj[”go”]() is a method call is the problem. It’s NOT a property get.

    “I think a solution for your particular case, is in your “getProperty” method, if they are accessing property “method”, you should return a function.”

    Again, I’ve thought of this, and can do this when I know it’s a method, but in a dynamic situation I don’t always.

    Take a look at my example for how this breaks using E4X in AS3. How does the XML object know that myXML[”length”] is referring to a method rather than a property as per the E4X specs? It doesn’t. And when you try to call the returned value (actually an XMLList object) you get a runtime error.

    How could this be remedied in the XML object? It can’t, unless Adobe states that “length” and all the other methods for XML are “special” and can’t be used in E4X this way. Or, they fix the player/compiler.

    “there’s no way around it without some fundamental changes to the compiler and the player”

    It was done wrong. Not my responsibility to fix. I’ll report the bugs as I find them. Player 10 is still in the works. The new compiler is as well. Will it be fixed? Not if we pretend it’s not a bug.

Leave a Reply

If this is your first comment it will need to be approved before it will appear.