On the other hand, how easy it was to find the size of every function is amazing to me... kudos to the rust community.
On the other hand, how easy it was to find the size of every function is amazing to me... kudos to the rust community.
FWIW, ELF symbol tables record the size of all symbols, including function ones, so something like `objdump -tj .text EXECUTABLE` will print the size of every (presumably) function in the .text section of EXECUTABLE in objdump's somewhat annoying native format. More generically, try something like `objdump -t EXECUTABLE | awk 'pass && $3 ~ /F/ { print $5, $6 } /^SYMBOL TABLE:/ { pass=1 }' | sort` (I did say the format was annoying). To enable builtin name demangling in GNU objdump, also pass -C.
I think you can compile both C# and Java to native code entirely (without a self-contained runtime) but it's not the common way to deploy executables written in those languages.
That said, I'm sure there are ways to get these statistics out of a .dll/.jar as well. Reasoning about them may be a little tough as you have no idea if the code will be JIT'ed or evaluated as bytecode until you run the code, but raw bytes on disk shouldn't pose a problem.
Worst case scenario, you can just dump the disassembled bytecode (https://stackoverflow.com/questions/11345034/how-many-bytes-... or https://stackoverflow.com/a/6574882) and start counting. Provided you don't add any native calls, of course, then you'll need to add objdump's transformed output as well.
Analyzing the bytecode can give some insight into how the compiler is likely to treat the code (inlining, intrisics, etc.), of course, so it's not useless, just not the whole story.
And, as you mention, JVM (and .NET) languages can also be AOT compiled. In those cases you could use more standard analysis similar to that in the OP.
The only difference is that AFAIK the languages you cite all have enough runtime information to allow downcasting, which in Rust one has to introduce manually (it is one of the solutions mentioned in the post).