PSA: dnSpy is a terrifyingly good .NET decompiler
-
Since this came up multiple times yesterday in different areas of my life:
dnSpy is a better C# IDE than Visual Studio even though it doesn't read .cs files. This terrifies me.
-
@ben_lubar Does it have the mythical feature we've talked about where you can point to a method and it'll tell you what possible exceptions could occur at that point in the code?
Still shocked Visual Studio has all this bullshit but not that.
-
@blakeyrat said in PSA: dnSpy is a terrifyingly good .NET decompiler:
Still shocked Visual Studio has all this bullshit but not that.
How is it supposed to know which important exceptions occur? Some things that come out of the runtime are able to occur at pretty much any point since they represent things going really quite nastily wrong, which is good (
NullPointerException
is the classic example of this, and it's a heck of a lot more benign than the crash you get from unmanaged code) but that does mean that they pollute the results for “what could be thrown from this code?” something rotten.
-
@dkf Depends - mistakenly dereferencing null is very different from intentionally constructing a NRE.
-
@dkf said in PSA: dnSpy is a terrifyingly good .NET decompiler:
How is it supposed to know which important exceptions occur?
You're moving the goal posts. I only said ALL possible exceptions. "important" is your invention from your head, do not cram your words in to my mouth.
-
@blakeyrat said in PSA: dnSpy is a terrifyingly good .NET decompiler:
@ben_lubar Does it have the mythical feature we've talked about where you can point to a method and it'll tell you what possible exceptions could occur at that point in the code?
Still shocked Visual Studio has all this bullshit but not that.
As far as I know, it can't do that, sadly.
-
@ben_lubar said in PSA: dnSpy is a terrifyingly good .NET decompiler:
dnSpy is a better C# IDE than Visual Studio even though it doesn't read .cs files. This terrifies me.
Great.
Now I'm going to have to clone an empty Console application exe every time I want to start a new project.
-
@blakeyrat Can that possibly work?
I thought unlike Java C# doesn't have checked exceptions, by design.
-
@topspin said in PSA: dnSpy is a terrifyingly good .NET decompiler:
@blakeyrat Can that possibly work?
I'm sure all the data needed to show me that is present in the Intellisense database.
C# doesn't have checked exceptions. That doesn't mean me, as the software, doesn't want to see what exceptions could POSSIBLY occur.
-
@blakeyrat said in PSA: dnSpy is a terrifyingly good .NET decompiler:
C# doesn't have checked exceptions. That doesn't mean me, as the software, doesn't want to see what exceptions could POSSIBLY occur.
I think one of the problems with implementing such a feature would be that C# doesn't separate checked (stuff you should catch) from unchecked (stuff you shouldn't catch) exceptions. For the result to be useful, you'd somehow have to be able to separate "useful" exception types from "useless" exception types, otherwise the list of possible exception types would possibly be incredibly huge.
-
@dfdub said in PSA: dnSpy is a terrifyingly good .NET decompiler:
@blakeyrat said in PSA: dnSpy is a terrifyingly good .NET decompiler:
C# doesn't have checked exceptions. That doesn't mean me, as the software, doesn't want to see what exceptions could POSSIBLY occur.
I think one of the problems with implementing such a feature would be that C# doesn't separate checked (stuff you should catch) from unchecked (stuff you shouldn't catch) exceptions. For the result to be useful, you'd somehow have to be able to separate "useful" exception types from "useless" exception types, otherwise the list of possible exception types would possibly be incredibly huge.
Where do you get the idea that unchecked exceptions are things that shouldn't be caught?
-
@blakeyrat Oh, it would definitely be useful, no question about that.
I'm just wondering how it would be implemented. You could be calling a function on an interface for which you don't know all the different implementations and what exceptions they throw.
But I guess it would already be pretty useful if it showed you all the exceptions that could potentially be generated by the code it knows about.
-
@blakeyrat said in PSA: dnSpy is a terrifyingly good .NET decompiler:
@topspin said in PSA: dnSpy is a terrifyingly good .NET decompiler:
@blakeyrat Can that possibly work?
I'm sure all the data needed to show me that is present in the Intellisense database.
C# doesn't have checked exceptions. That doesn't mean me, as the software, doesn't want to see what exceptions could POSSIBLY occur.
Without checked exceptions this is not possible in the general case, because you could call a delegate which might theoretically throw any arbitrary exception.
-
@topspin said in PSA: dnSpy is a terrifyingly good .NET decompiler:
You could be calling a function on an interface for which you don't know all the different implementations and what exceptions they throw.
But assuming Visual Studio has access to all the code or DLLs your program will run, it can know what types implement that interface, or even better, run Rapid Type Analysis or something like that to figure out exactly what types the interface variable can be holding at that point in the program.
-
@pie_flavor said in PSA: dnSpy is a terrifyingly good .NET decompiler:
Where do you get the idea that unchecked exceptions are things that shouldn't be caught?
Have you seen Joe Duffy's blog post series about Midori?
They made exceptions work a lot better by making three basic changes:
- Things that are definitely the result of a programming error (NRE, bounds check errors, stack overflow, div by 0, etc) do not throw an exception; they terminate the process, because hitting one means that the process is in an inconsistent state and can't be trusted to not be corrupt anymore. (This is less of a horrifying action than it sounds like at first, due to architectural raisins too involved to get into here.) Only actual recoverable errors get thrown as exceptions.
- Because programming errors are the vast majority of all exceptions, rule #1 whittles them down far enough that what's left is small enough that checked exceptions are no longer particularly burdensome. So they added checked exceptions, made them part of the type system, (included in a method's formal signature,) and made them mandatory.
- In order to aid code readability by making it immediately apparent when you're dealing with code that can throw exceptions, they added a syntactic rule that any call to a method with a
throws
clause in its signature must be prefixed by thetry
keyword. (Much like the you prefix calls to async methods withawait
.)
-
@masonwheeler Ok.
... That didn't really answer my question.
-
@pie_flavor It did. He's saying that unchecked exceptions (in the Java RuntimeError sense, not in the sense of all C# exceptions) shouldn't be caught because they're the result of something gone horribly wrong.
Or maybe that you should only catch them somewhere in main to say "oops, better save and close".
-
@topspin said in PSA: dnSpy is a terrifyingly good .NET decompiler:
Or maybe that you should only catch them somewhere in main to say "oops, better save and close".
Even that is a bad idea, because if your process is in an undefined state, that means you have no good way of knowing that the internal representation of your project, which you're about to save, is not corrupt.
-
@masonwheeler said in PSA: dnSpy is a terrifyingly good .NET decompiler:
Things that are definitely the result of a programming error (NRE, bounds check errors, stack overflow, div by 0, etc)
This category is precisely what I want my IDE to tell me might happen.
"Hey, this value might be null here but you dereference it without checking."
-
The point of this idea that of blakeys isn't to add checked exceptions.
It's to make it so I don't need to memorize all 304748483 classes I work with on a daily basis and keep the entire stack in my head. It's so:
- The xmldocs can be automatically generated accurately.
- I can look at a list and go "okay so I have a null check so that can't happen, wait, what, a filenotfound can get thrown out of that module 8 layers down the stack? Better display a good error message!"
It's not a language feature, it's a development aid.
-
@masonwheeler said in PSA: dnSpy is a terrifyingly good .NET decompiler:
if your process is in an undefined state
One of the key points of throwing exceptions from standard operations (dereferencing, division, etc.) when their preconditions fail is that the program is still in a defined state. The places where things really go haywire are when more subtle problems happen, such as running out of memory or having weird problems when supposedly-validated loading code from disk. Most code never hits those, thankfully.
-
@ben_lubar said in PSA: dnSpy is a terrifyingly good .NET decompiler:
This category is precisely what I want my IDE to tell me might happen.
"Hey, this value might be null here but you dereference it without checking."That is definitely nice; I've had it in a previous project, where it caught several annoying bugs. However, I think that without it being built into the standard library API, it gets really annoying very quickly due to the number of false alarms (and the problem is far worse with third-party APIs, which are often not as well characterised in the first place; at least with the standard library you usually have a human-readable statement about nullability of arguments and results). In short, it's a great place to be, but I wouldn't want to start to go there from here.
-
@topspin said in PSA: dnSpy is a terrifyingly good .NET decompiler:
@pie_flavor It did. He's saying that unchecked exceptions (in the Java RuntimeError sense, not in the sense of all C# exceptions) shouldn't be caught because they're the result of something gone horribly wrong.
Or maybe that you should only catch them somewhere in main to say "oops, better save and close".Yeah, except that's not what unchecked exceptions mean at all. Consider for example NumberFormatException.
-
@ben_lubar said in PSA: dnSpy is a terrifyingly good .NET decompiler:
This category is precisely what I want my IDE to tell me might happen.
"Hey, this value might be null here but you dereference it without checking."Exactly.
And, again: I'm shocked that nobody has made this tool yet.
-
@blakeyrat Did you know that you could make it?
-
@blakeyrat I'm trying to figure out if you can do it with reflection.
-
@Weng I'm pretty sure you can't. The only way is to analyze bytecode. Of course that will only give you explicit throws, and not runtime-generated ones like NRE.
-
@ben_lubar said in PSA: dnSpy is a terrifyingly good .NET decompiler:
"Hey, this value might be null here but you dereference it without checking."
Make assigning
null
to not-explicitly-nullable references invalid code.Rust's
Option
GeNeRiC MakEs iT iMPoSSibLe To RefEReNCeNone
WitHoUt eXPlicITLy unWrAPpiNg oR MaTChinG BeCAusEOption<T>
Is NoT tHE SaMe TypE AsT
.
-
@pie_flavor Well, that's what they should mean. Why else would you make that distinction otherwise.
-
@Zecc Not sure if Sponge Bob or Gribnit.
-
@topspin I'll admit, mentioning @pie_flavor after my post confused me there for a bit.
Anyway, just to clarify, I'm not saying Rust's Option / C# nullables / whatever it's called in Kotlin again / etc. are a bad idea. On the contrary. If not for backwards compatibility C# should mandate use of Nullable<T> where appropriate.
I was just mocking @pie_flavor's incessant mentions of Rust.
-
@topspin said in PSA: dnSpy is a terrifyingly good .NET decompiler:
@Zecc Not sure if Sponge Bob or Gribnit.
I can read and understand it. Definitely Spongebob.
-
@Zecc said in PSA: dnSpy is a terrifyingly good .NET decompiler:
@topspin I'll admit, mentioning @pie_flavor after my post confused me there for a bit.
Still does, apparently. @topspin was talking about checked/unchecked exceptions.
-
@masonwheeler said in PSA: dnSpy is a terrifyingly good .NET decompiler:
@pie_flavor said in PSA: dnSpy is a terrifyingly good .NET decompiler:
Where do you get the idea that unchecked exceptions are things that shouldn't be caught?
Have you seen Joe Duffy's blog post series about Midori?
I never heard of that, but I already hate it from what you wrote.
They made exceptions work a lot better by making three basic changes:
- Things that are definitely the result of a programming error (NRE, bounds check errors, stack overflow, div by 0, etc) do not throw an exception; they terminate the process, (...)
It is easier to check exceptions at the end of a long calculation than check all my division operators. I'll already threw it in the garbage when you said this part.
- Because programming errors are the vast majority of all exceptions, rule #1 whittles them down far enough that what's left is small enough that checked exceptions are no longer particularly burdensome. So they added checked exceptions, made them part of the type system, (included in a method's formal signature,) and made them mandatory.
I hate this for the same reason. For many things I want to do something when things get wrong, but it doesn't matter (other than for logging) what exactly gone wrong. Checked exceptions just get in the way, and I hate it.
- In order to aid code readability by making it immediately apparent when you're dealing with code that can throw exceptions, they added a syntactic rule that any call to a method with a
throws
clause in its signature must be prefixed by thetry
keyword. (Much like the you prefix calls to async methods withawait
.)
Hate it for the same reasons as the others. I just work with the assumption that all code can throw exceptions, and I catch them where it makes sense to catch them.
-
@Gąska said in PSA: dnSpy is a terrifyingly good .NET decompiler:
@Zecc said in PSA: dnSpy is a terrifyingly good .NET decompiler:
@topspin I'll admit, mentioning @pie_flavor after my post confused me there for a bit.
Still does, apparently. @topspin was talking about checked/unchecked exceptions.
I know, I clicked the unreadable† link to the post being replied.
† ††
†† Why haven't I fixed this?†††
†††
-
@Zecc said in PSA: dnSpy is a terrifyingly good .NET decompiler:
@Gąska said in PSA: dnSpy is a terrifyingly good .NET decompiler:
@Zecc said in PSA: dnSpy is a terrifyingly good .NET decompiler:
@topspin I'll admit, mentioning @pie_flavor after my post confused me there for a bit.
Still does, apparently. @topspin was talking about checked/unchecked exceptions.
I know, I clicked the unreadable† link to the post being replied.
† ††
†† Why haven't I fixed this?†††
†††
-
@ben_lubar said in PSA: dnSpy is a terrifyingly good .NET decompiler:
@topspin said in PSA: dnSpy is a terrifyingly good .NET decompiler:
You could be calling a function on an interface for which you don't know all the different implementations and what exceptions they throw.
But assuming Visual Studio has access to all the code or DLLs your program will run, it can know what types implement that interface, or even better, run Rapid Type Analysis or something like that to figure out exactly what types the interface variable can be holding at that point in the program.
I don't know how NHibernate works, but I work with Hibernate (the java version) every day. At runtime, it replaces the objects that it manages with proxies that do stuff like lazy loading for you in the background. As you might expect this can throw its own raft of exceptions. I suppose the IDE could see the annotations on the class and figure out that it needs to look at Hibernate for exceptions.
-
@masonwheeler said in PSA: dnSpy is a terrifyingly good .NET decompiler:
They made exceptions work a lot better by making three basic changes:
Things that are definitely the result of a programming error (NRE, bounds check errors, stack overflow, div by 0, etc) do not throw an exception; they terminate the process, because hitting one means that the process is in an inconsistent state and can't be trusted to not be corrupt anymore. (This is less of a horrifying action than it sounds like at first, due to architectural raisins too involved to get into here.) Only actual recoverable errors get thrown as exceptions.
I get what they're saying but OTOH, that's totally unacceptable for something that's servicing multiple clients, which in fairness you only need to worry about in tiny niche applications like you might serve over the web.
-
@boomzilla said in PSA: dnSpy is a terrifyingly good .NET decompiler:
I get what they're saying but OTOH, that's totally unacceptable for something that's servicing multiple clients, which in fairness you only need to worry about in tiny niche applications like you might serve over the web.
Yeah, this is where the "this is less of a horrifying action than it sounds like at first, due to architectural raisins too involved to get into here" part comes in. He explains elsewhere that Midori was designed with a "lightweight process" model, similar to the sort of stuff you see in Erlang, where it would be no big deal to spin off a process to deal with each web request, and having one of them crash would not involve crashing the server.
-
@sockpuppet7 said in PSA: dnSpy is a terrifyingly good .NET decompiler:
@masonwheeler said in PSA: dnSpy is a terrifyingly good .NET decompiler:
@pie_flavor said in PSA: dnSpy is a terrifyingly good .NET decompiler:
Where do you get the idea that unchecked exceptions are things that shouldn't be caught?
Have you seen Joe Duffy's blog post series about Midori?
I never heard of that, but I already hate it from what you wrote.
They made exceptions work a lot better by making three basic changes:
- Things that are definitely the result of a programming error (NRE, bounds check errors, stack overflow, div by 0, etc) do not throw an exception; they terminate the process, (...)
It is easier to check exceptions at the end of a long calculation than check all my division operators. I'll already threw it in the garbage when you said this part.
- Because programming errors are the vast majority of all exceptions, rule #1 whittles them down far enough that what's left is small enough that checked exceptions are no longer particularly burdensome. So they added checked exceptions, made them part of the type system, (included in a method's formal signature,) and made them mandatory.
I hate this for the same reason. For many things I want to do something when things get wrong, but it doesn't matter (other than for logging) what exactly gone wrong. Checked exceptions just get in the way, and I hate it.
- In order to aid code readability by making it immediately apparent when you're dealing with code that can throw exceptions, they added a syntactic rule that any call to a method with a
throws
clause in its signature must be prefixed by thetry
keyword. (Much like the you prefix calls to async methods withawait
.)
Hate it for the same reasons as the others. I just work with the assumption that all code can throw exceptions, and I catch them where it makes sense to catch them.
There's a joke about stuff frequently getting deleted and vanishing without a trace just waiting to be made here...
-
@pie_flavor said in PSA: dnSpy is a terrifyingly good .NET decompiler:
Where do you get the idea that unchecked exceptions are things that shouldn't be caught?
If your library throws unchecked exceptions that I'm supposed to catch, then I don't want to use your library.
Seriously, this is the whole point of checked exceptions: Separating unresolvable error situations from resolvable error situations. If that's not always the case, then you've spotted a misuse of unchecked exception types.
-
@dfdub Looks like you'll never be using Integer.parseInt then.
-
@pie_flavor said in PSA: dnSpy is a terrifyingly good .NET decompiler:
@dfdub Looks like you'll never be using Integer.parseInt then.
Is "the user entered something that is not an integer" an unrecoverable condition?
-
@ben_lubar said in PSA: dnSpy is a terrifyingly good .NET decompiler:
@pie_flavor said in PSA: dnSpy is a terrifyingly good .NET decompiler:
@dfdub Looks like you'll never be using Integer.parseInt then.
Is "the user entered something that is not an integer" an unrecoverable condition?
Depends on the context.
-
@pie_flavor There's arguments for not having checked exceptions (that's why C# decided against them). But if you do have checked exceptions and you expect the user of a function like parseInt to catch a certain exception, then that should better be a checked one.
In other words, sounds like a questionable design.
-
@topspin So in other words, every single Integer.parseInt call should be wrapped in a try block?
-
@pie_flavor No. You could wrap a whole bunch of them together. Or explicitly declare your function to propagate the exception to a higher level. But not make it propagate silently, since it's an error condition you should be prepared to handle.
Also, I googled it and the function signature declares that it
throws NumberFormatException
, so I'm confused about your point.
-
@topspin said in PSA: dnSpy is a terrifyingly good .NET decompiler:
@pie_flavor There's arguments for not having checked exceptions (that's why C# decided against them).
Laziness.
-
This post is deleted!
-
@topspin NumberFormatException is still unchecked.