Showing posts with label JScript. Show all posts
Showing posts with label JScript. Show all posts

Friday, February 01, 2008

Why dynamic languages suck

I had a little bit of JScript that wasn't working as expected. I stared and stared at it... For a very long time... Here it is.

var splitDetails = details.split("\t");
for (var i=0; i<splitDetails.Length; i++)
{
  // do something
} 

The problem was that the //do something was never getting hit. Life would have been easier with a debugger but this was server-side JScript in Metastorm which doesn't support debugging so I was forced to just stare at it to work out what was wrong.

No doubt you're now shouting at your computer "you're a fecking idiot! The problem is obvious!" and perhaps you'd be right. Perhaps I was just having a senior moment, which I think I'm entitled to have at my age. For those of you who haven't spotted the error I'd typed length with an upper-case L.

So what happens here? JScript doesn't throw an error because I've tried to access a property that doesn't exist, instead it returns null. Then I presume it converts that to 0 for the loop test so never drops into the code inside the loop.

This kind of thing is all too easy in a dynamic language. If I was using a compiled statically-typed language the compiler would have picked up the problem immediately. Perhaps the advantages of dynamic languages outweigh these kind of problems but personally I'd rather have the crutch of a compiler.

Tuesday, January 08, 2008

JScript.NET performance

JScript.NET doesn't really seem to have caught on too much. The reasons are clear, there is no IDE support for it in Visual Studio.NET and if the company that developed the language doesn't want to provide an IDE, who else is likely to?

But since Metastorm BPM uses JScript.NET as its primary interface to the world of .NET I have to use it on a fairly regular basis. Many moons ago I had a look at the IL code produced from compiling JScript.NET and was horrified to see it was all late bound calls. Funnily enough the primary reason somebody had decided to use JScript.NET rather than JScript was to try to improve the performance of 8000 lines of server-side JScript code (why he had ever thought that 8000 lines of JScript code would not have performance problems I don't know). Anyway, compiling it did improve the performance somewhat but probably not so much as if the IL code produced had not been late bound.

So now several years later, whilst preparing some course notes, I thought I'd take a look at what the IL code looked like these days in .NET 2. And the results are worth noting I think.

Say we have two methods as follows

        public static function DoSomethingFast() : Object
        {
            var test = new StringBuilder();
            test.Append("a");
            test.Append("a");

            return test.ToString();
        }   

        public static function DoSomethingSlow() : Object
        {
            var test = "Hello world";

            var test = new StringBuilder();
            test.Append("a");
            test.Append("a");

            return test.ToString();
        }

After compiling that with jsc.exe, the IL produced for the first method looks like this

.method public static object DoSomethingFast() cil managed
{
    .maxstack 3
    .locals init (
        [0] class [mscorlib]System.Text.StringBuilder builder,
        [1] object obj2)
    L_0000: newobj instance void [mscorlib]System.Text.StringBuilder::.ctor()
    L_0005: stloc.0
    L_0006: ldloc.0
    L_0007: ldstr "a"
    L_000c: call instance class [mscorlib]System.Text.StringBuilder [mscorlib]System.Text.StringBuilder::Append(string)
    L_0011: pop
    L_0012: ldloc.0
    L_0013: ldstr "a"
    L_0018: call instance class [mscorlib]System.Text.StringBuilder [mscorlib]System.Text.StringBuilder::Append(string)
    L_001d: pop
    L_001e: ldloc.0
    L_001f: callvirt instance string [mscorlib]System.Text.StringBuilder::ToString()
    L_0024: stloc.1
    L_0025: br L_002a
    L_002a: ldloc.1
    L_002b: ret
}

Looking at that lifted my spirits since it looks like the kind of code you'd get from a compiled C# method, no late-binding at all. Now on to the second method, which has only one difference from the first one.

.method public static object DoSomethingSlow() cil managed
{
    .maxstack 11
    .locals init (
        [0] object obj2,
        [1] object obj3,
        [2] class [Microsoft.JScript]Microsoft.JScript.LateBinding binding,
        [3] class [Microsoft.JScript]Microsoft.JScript.LateBinding binding2,
        [4] class [Microsoft.JScript]Microsoft.JScript.LateBinding binding3,
        [5] object obj4,
        [6] object obj5,
        [7] object obj6)
    L_0000: ldstr "Append"
    L_0005: newobj instance void [Microsoft.JScript]Microsoft.JScript.LateBinding::.ctor(string)
    L_000a: stloc.2
    L_000b: ldstr "Append"
    L_0010: newobj instance void [Microsoft.JScript]Microsoft.JScript.LateBinding::.ctor(string)
    L_0015: stloc.3
    L_0016: ldstr "ToString"
    L_001b: newobj instance void [Microsoft.JScript]Microsoft.JScript.LateBinding::.ctor(string)
    L_0020: stloc.s binding3
    L_0022: ldstr "Hello world"
    L_0027: stloc.0
    L_0028: newobj instance void [mscorlib]System.Text.StringBuilder::.ctor()
    L_002d: stloc.0
    L_002e: ldloc.2
    L_002f: dup
    L_0030: ldloc.0
    L_0031: ldtoken TestSpeed.TestClass
    L_0036: call class [Microsoft.JScript]Microsoft.JScript.Vsa.VsaEngine [Microsoft.JScript]Microsoft.JScript.Vsa.VsaEngine::CreateEngineWithType(valuetype [mscorlib]System.RuntimeTypeHandle)
    L_003b: call object [Microsoft.JScript]Microsoft.JScript.Convert::ToObject(object, class [Microsoft.JScript]Microsoft.JScript.Vsa.VsaEngine)
    L_0040: stfld object [Microsoft.JScript]Microsoft.JScript.LateBinding::obj
    L_0045: ldc.i4.1
    L_0046: newarr object
    L_004b: dup
    L_004c: ldc.i4.0
    L_004d: ldstr "a"
    L_0052: stelem.ref
    L_0053: ldc.i4.0
    L_0054: ldc.i4.0
    L_0055: ldtoken TestSpeed.TestClass
    L_005a: call class [Microsoft.JScript]Microsoft.JScript.Vsa.VsaEngine [Microsoft.JScript]Microsoft.JScript.Vsa.VsaEngine::CreateEngineWithType(valuetype [mscorlib]System.RuntimeTypeHandle)
    L_005f: call instance object [Microsoft.JScript]Microsoft.JScript.LateBinding::Call(object[], bool, bool, class [Microsoft.JScript]Microsoft.JScript.Vsa.VsaEngine)
    L_0064: pop
    L_0065: ldloc.3
    L_0066: dup
    L_0067: ldloc.0
    L_0068: ldtoken TestSpeed.TestClass
    L_006d: call class [Microsoft.JScript]Microsoft.JScript.Vsa.VsaEngine [Microsoft.JScript]Microsoft.JScript.Vsa.VsaEngine::CreateEngineWithType(valuetype [mscorlib]System.RuntimeTypeHandle)
    L_0072: call object [Microsoft.JScript]Microsoft.JScript.Convert::ToObject(object, class [Microsoft.JScript]Microsoft.JScript.Vsa.VsaEngine)
    L_0077: stfld object [Microsoft.JScript]Microsoft.JScript.LateBinding::obj
    L_007c: ldc.i4.1
    L_007d: newarr object
    L_0082: dup
    L_0083: ldc.i4.0
    L_0084: ldstr "a"
    L_0089: stelem.ref
    L_008a: ldc.i4.0
    L_008b: ldc.i4.0
    L_008c: ldtoken TestSpeed.TestClass
    L_0091: call class [Microsoft.JScript]Microsoft.JScript.Vsa.VsaEngine [Microsoft.JScript]Microsoft.JScript.Vsa.VsaEngine::CreateEngineWithType(valuetype [mscorlib]System.RuntimeTypeHandle)
    L_0096: call instance object [Microsoft.JScript]Microsoft.JScript.LateBinding::Call(object[], bool, bool, class [Microsoft.JScript]Microsoft.JScript.Vsa.VsaEngine)
    L_009b: pop
    L_009c: ldloc.0
    L_009d: stloc.s obj4
    L_009f: ldloc.s obj4
    L_00a1: isinst object
    L_00a6: dup
    L_00a7: stloc.s obj5
    L_00a9: brfalse L_00ba
    L_00ae: ldloc.s obj5
    L_00b0: callvirt instance string [mscorlib]System.Object::ToString()
    L_00b5: br L_00ee
    L_00ba: ldloc.s obj4
    L_00bc: stloc.s obj6
    L_00be: ldloc.s binding3
    L_00c0: dup
    L_00c1: ldloc.s obj6
    L_00c3: ldtoken TestSpeed.TestClass
    L_00c8: call class [Microsoft.JScript]Microsoft.JScript.Vsa.VsaEngine [Microsoft.JScript]Microsoft.JScript.Vsa.VsaEngine::CreateEngineWithType(valuetype [mscorlib]System.RuntimeTypeHandle)
    L_00cd: call object [Microsoft.JScript]Microsoft.JScript.Convert::ToObject(object, class [Microsoft.JScript]Microsoft.JScript.Vsa.VsaEngine)
    L_00d2: stfld object [Microsoft.JScript]Microsoft.JScript.LateBinding::obj
    L_00d7: ldc.i4.0
    L_00d8: newarr object
    L_00dd: ldc.i4.0
    L_00de: ldc.i4.0
    L_00df: ldtoken TestSpeed.TestClass
    L_00e4: call class [Microsoft.JScript]Microsoft.JScript.Vsa.VsaEngine [Microsoft.JScript]Microsoft.JScript.Vsa.VsaEngine::CreateEngineWithType(valuetype [mscorlib]System.RuntimeTypeHandle)
    L_00e9: call instance object [Microsoft.JScript]Microsoft.JScript.LateBinding::Call(object[], bool, bool, class [Microsoft.JScript]Microsoft.JScript.Vsa.VsaEngine)
    L_00ee: stloc.1
    L_00ef: br L_00f4
    L_00f4: ldloc.1
    L_00f5: ret
}

Suddenly things look terribly messy again, with lots of late-binding goo all over the place. The reason is pretty straight forward I think. In the first case, the compiler can infer the type of the test variable, whereas in the second case it can't because the variable is used to hold two different types. It does however mean that minor changes to code can lead to it being compiled completely differently without any warning, which is a bit of a worry.

Wednesday, May 23, 2007

More on JScript exceptions

I don't know much about JScript, so when I need to find out anything I head off to Google. So that's what I did when I wanted to know about throwing exceptions. Almost every example I've seen shows something like - 

throw "something went wrong";

Initially I thought this would be wrapped up as an exception by the JScript runtime, but it turns out it is actually a string that gets thrown. Which may be fine in some cases. The problem comes when you want to catch exceptions. If you want to catch all exceptions thrown in a bit of code (yeh I know, bad idea, but this is JScript not software engineering...) then things will get complicated. The runtime will throw exceptions but your own code is throwing strings, so you have to deal with them differently. Turns out the solution is pretty straighforward -

throw new Error("something went wrong");

Now everything is an exception and life is good.

As a side note, I've not considered throwing anything other than an exception before. C# doesn't allow it, although I guess IL probably does since it needs to support JScript.NET. Delphi partially allows it, you can throw any object you like but not strings since they aren't proper objects in the Delphi world. But it's not clear to me why you'd ever want/need to do it...

Wednesday, May 02, 2007

JScript try catch syntax

I alway forget the syntax for JScript exception handling and searching on the web always seems to bring up the wrong thing, so here is an example as my own personal reminder.

    try
    {
        // do stuff
        return "blah";    
    }
    catch(e)
    {
        return e.message;
    }

Yeh, I know, I should be able to remember that... I blame it on the crap JScript editors, none of which provide the mental crutch of code completion.

Remember too, JScript requires the e, you can't use catch().