A JScript .NET Design Donnybrook

A JScript .NET Design Donnybrook

  • Comments 22

When you're designing a new programming language, the "main line" cases are easy.  It's the "corner" cases that bedevil language designers -- the little fiddly bits where two language features that make perfect sense on their own interact in some weird way.  Unfortunately, I suspect that any language sufficiently feature-rich to be interesting will have weird corner cases.  Getting them right is one of the more interesting and difficult aspects of language design.

Want to give it a try yourself?  Now's your chance.  Here is a question that sparked a protracted and hilarious debate amongst the JScript .NET design team back in 2000.  I'd be interested to see what your opinions are.  So as to not spoil the fun, figure out what you think this code should do before you run it or decompile it into IL.

class foo {
  function get abc() {
    return 5;
  }
}
class foo2 extends foo {
  function bar(ob) {
    print(ob.abc);
  }
}
class foo3 extends foo2 { 
   hide function get abc()
{
    return 50;
  }
}
var f = new foo3();

Clearly print(f.abc); must print out 50.  But should f.bar(f); print out 50 or 5?  

Justify your answer.

HINT: While you're thinking it over, keep in mind that nowhere in this code are there any type annotations.  The question was originally raised in the context of speculative compiler optimizations which we might perform in the absence of type annotations.  Are there codegen optimizations which we could perform that make your desired behaviour more efficient in common cases?

  • I stayed out of this one since I already knew the answer and mostly why it became that way. But I enjoy the quirkyness. Java also lives in this shadow world between the Static and Dynamic models. You can write a very similar piece of code to this and get the same behavior.
  • Yuck. My mind just isn't capable of understanding scripting languages.

    BTW, why on earth does every type have a .cctor? I know script is supposed to be inefficient, but isn't this going over the top? ;-)
  • Jeroen: I'm pretty sure that every type has a .cctor (even if it is empty) because it made the code generator much easier. When the compiler / runtime can both compile any valid program into IL *OR* dynamically interpret it, this becomes very important.

    Philip: The reason it checks for foo2 before foo3 is that we _assume_ (ha ha ha) that the developer of foo2.bar only knew about objects of type foo2 when [s]he wrote the code; they were not expecting someone else to come along, subclass their type, and add some other random method to it.

    If they expected this kind of tomfoolery, they would have used a type annotation (or so the thinking went).

    Basically performance of minimally-tweaked legacy JScript code was REALLY REALLY important for us in ASP .NET (who would upgrade if their pages were slower?) so trade offs like this were made.

    Interestingly, this chain of things-JScript-looks-for on a late bound call can get arbitrarily long, and it's *still* going to be faster than the latebound call in all reasonable cases. isinst is fast compared to InvokeMember ;-)
  • Also, an empty cctor is not inefficient. The jitter will optimize it away.

    I suspect that there really was no good reason to always generate the cctor even when it was empty -- perhaps it just worked out easier to write the codegen that way.
  • >>Also, an empty cctor is not inefficient. The jitter will optimize it away.<<

    I didn't know that! Thanks for setting me straight and I apologise for the snide remark.
  • Here's some back-and-forth from an email conversation I had with a user a while back. Why should one

Page 2 of 2 (22 items) 12