JScript exceptions not handled/thrown across frames if thrown from a expando method.

Hope all of you are doing great!

Today I am going to discuss sort of limitation of Jscript engine which I had hard time debugging. I hope through this post i can save you guys from running into same issue.

Scenario -
Let’s say Foo() is defined in one IE frame (Let’s say FR1) and this method just throws an exception. The same frame also has one object (let’s say myObj1) whose one of the properties points to Foo (let’s say myObj1.callFoo = Foo; ).

Now user calls this method twice from another frame (Let’s say FR2) …

  1. In first case the function Foo is called directly -
    window.top.FR1.Foo();
  1. In the second case object expando is invoked –
    window.top.FR1. myObj1.callFoo();

Issue Reported -
The issue customer has reported is that when method is called in the second frame directly (window.top.FR1.Foo(); ), exception is thrown in first frame and caught in the second frame but when it is called through the expando (window.top.FR1. myObj1.callFoo(); ), exception is thrown in first frame but doesn't reach the second frame. Wow!!!

Reason -

For every frame IE creates a seperate instance of JScript engine. Let's fay for Frame FR1 it creates engine JS1 and for frame2 engine creates JS2.

  1. For the first case (window.top.FR1.Foo(); ) - Call from JS2 is delegated to IE which call the function object in JS1. Now here is the catch. When any method in JS engine is called by IE, engine sets the caller property to IE. If some exception is thrown during execution and is there is no try-catch handler, JS engine checks this caller property. If property value is set to the IE (or other host) exception is propogated down to IE (host). IE then propogates it further down in the caller chain. Eventually the exception reaches JS2.
  2. Second case (window.top.FR1. myObj1.callFoo(); ) - IE creates a new dispatch object for the object myObj1 (of the first engine), and returns to the second engine. Since second engine has got a dispatch object, it direclty invokes the method. Neither IE is there in the call chain, nor is the caller set to second engine in the first engine. So when exception is thrown, there is no handler available (as parent can't be traced back) and exception is reported unhandled.

This behavior is “by design" as once the diispatch object is available, script need not delegate the call to IE. It can direclty call that object's methods.

Workaround

If you are running into the scenario 2 and can't avoid this then here is the simple workaround. Create a wrapper method in FR1 for every such expando and call that method from FR2.

For example in FR1 add following ...

function WrapperCallFoo()
{

myObj1.callFoo();
}

Now in FR2 replace all window.top.FR1. myObj1.callFoo(); with window.top.FR1. WrapperCallFoo();

 

I also took a look at Eric's blog. Here one of the comments by Dan Shappir points out that when function is directly called, it is actually called in the context of IE and the second time in the context of the global object. What I have written here is just a detailed explanation of what is going on behind the scene.