.Net bug with <%= %> and option parameters?


  • Trolleybus Mechanic

    I just had the weirdest fucking bug I've ever seen, and still haven't technically "solved" it. Has anyone come across this? Can anyone repro it?

    Setup (don't know how many of these factors matter)

    • VS2012
    • VB.net
    • Website project
    • Publish compiled to single .dll (ie: don't deploy the .vb files)
    • IIS 6.1 (running on either Windows 7 or Windows Server 2012)
    • Project uses .Net 4.6.1

    Code:

    • a class in App_Code with a Shared function with the signature Public Shared SomeFunction(ByVal s as String, Optional ByVal o as Object = Nothing, Optional ByVal b as Boolean = False) as String
    • Let the function return any non-empty string, it doesn't matter. Say it just returns "Echo " & s
    • A user control (.ascx)-- maybe this also is repro in a straight .aspx page, don't know
    • On the control, emit the directive <%= TheClass.SomeFunction("_GiveMeMoney") %>

    If you run this code locally before compiling / deploying anything, you'll see "Echo _GiveMeMoney". All good.

    But if you "publish" the project-- compiling all the code into a .dll-- and then try to visit that page-- well, then the function is NEVER CALLED AT ALL.

    No "Ambiguous function" warning. Nothing in the Event log. Nothing. If you set a breakpoint on SomeFunction, it never gets hit. It's like .Net suddenly decides "Well, fuck that, I'm not calling that function".

    But if you fill in even the FIRST optional parameter, it works. <%= TheClass.SomeFunction("_GiveMeMoney", Nothing) %>, gets you "Echo _GiveMeMoney".

    At first I thought this was environmental-- it was only happening in one project, on one server. But then I copied the compiled website to my local server, and can reproduce.

    I'm fuckstumped on this one.


  • kills Dumbledore

    @lorne-kates said in .Net bug with <%= %> and option parameters?:

    a class in App_Code with a Shared function with the signature Public Shared SomeFunction(ByVal s as String, Optional ByVal o as Object, Optional ByVal b as Boolean) as String

    Optional arguments in VB need a default value. Are you missing that or did you just not mention it in the post?
    Public Shared SomeFunction(ByVal s as String, Optional ByVal o as Object = Nothing, Optional ByVal b as Boolean = false) as String

    In a more strongly typed language I'd assume you just missed it out, but VB can do funny things sometimes


  • Trolleybus Mechanic

    @jaloopa said in .Net bug with <%= %> and option parameters?:

    Are you missing that or did you just not mention it in the post?

    Edited the post to reflect...


  • kills Dumbledore

    @lorne-kates In that case I've got nothing


  • Trolleybus Mechanic

    Hey, got an actual update on this!

    So this bug reared it's head again. It was a bit of a crunch time, so I did what I did before-- instead of using an optional parameter, made 2 signatures with the less-parameter calling the more-parameter one.

    Afterwards, I had a bit of downtime at work, and wanted to fucking solve this.

    I could reliably reproduce the issue-- which, again, is any method with an optional parameter, and that method is called from <%= %> blocks-- once compiled and deployed, fails to call as if it was compiled away.

    Cool.

    So I put the method back the way it was, compiled as the project normally does (File system, combine all .dll into a single Website.dll), and there was the issue again. Cool, I can try to figure this out.

    I started with reading up on exactly how .Net handles methods with optional parameters, and how that ends up in compiled IL code. If I have Sub Blah(Optional ByVal x as String = ""), and I call both Blah() and Blah("FuckYouGiveMeMoney"), the compiled IL code basically looks something like this (pseudocode because it's late)

    set variable x = ""
    call method Blah(x)
    
    set variable x = "FuckYouGiveMeMoney"
    call method Blah(x)
    

    Basically, the "optional" parameter gets baked in during compile time, not dynamically evaluated at run time. Well, okay then, if that's the case, surely I can use some sort of tool to look at what the actual IL code that gets compiled is.

    I come across ILSpy, which is just what I want. I point it at my Website.dll, and there's every class and method. Time to see what that one class looks like compiled.

    Click on ThatClass and-- ILSpy throws a null reference exception.

    ... ok

    Now, one thing I should mention-- the only way I'm able to reproduce this thing is if the method signature contains an optional parameter that is of type Object.

    Optional ByVal o as Object = Nothing

    If, instead of Object, I use any concrete class-- the calls work fine, and ILSpy shows me the IL code expected.

    Just to be sure, I get the latest VS2017 and can reproduce it there also. This is a real actual goddamn bug!

    I filed a bug with ILSpy first, since I could now create a blank solution and reproduce it reliably. Their reply is:

    "Huh, that's weird. That bit of IL code should return something like UnsetObject-- but instead it's null. That isn't the expected value at all."

    And long story not as long: the reason the "oops it's null" IL code is created instead of the "just use default(object)" code is because aspnet_merge, which is compiler / dll creator / etc-- fucks it up.

    So if anyone ever comes across this bug themselves, know that .Net chokes to death on optional parameters of type Object after you run it through aspnet_merge.

    There's even a Visual Studio bug about it! I filed a bug like a big boy! And they will (tenatively with extremely low priority) look into it.


  • Notification Spam Recipient

    @Lorne-Kates said in .Net bug with <%= %> and option parameters?:

    if the method signature contains an optional parameter that is of type Object.

    Good to know. ๐Ÿ‘



  • @Lorne-Kates said in .Net bug with <%= %> and option parameters?:

    ILSpy

    dnspy
    dnspy
    dnspy
    dnspy
    dnspy