Skip to content
This repository has been archived by the owner on Jan 3, 2023. It is now read-only.

Call to Type.GetType for a missing type causes FSI on Mono to produce an erroneous error #483

Closed
daniel-chambers opened this issue Aug 31, 2015 · 10 comments

Comments

@daniel-chambers
Copy link

Not sure whether to post this here or over on the VisualFSharp repo, but it's to do with F# on Mono, so I'll try here first. Please let me know if you'd like me to move it.

Given the following Reflection.fsx script:

let log4netType = System.Type.GetType("log4net.Config.XmlConfigurator, log4net")
let exists = log4netType <> null
do printfn "%A" exists

on Mono if you run fsharpi Reflection.fsx you get the following output:

/var/app/stdin(0,1): error FS0078: Unable to find the file 'log4net.dll' in any of
  /usr/lib/mono/4.5
  /var/app
  /usr/lib/cli/fsharp/
false
Stopped due to error

whereas on Windows using .NET fsi (fsi Reflection.fsx) you get the expected behaviour and output:

false

Interestingly, on Mono the error was printed and FSI returns an error exit code, but the code in the script actually did continue to execute and finish just fine, even though theoretically there was a fatal error at Line 1.

This is causing issues for us because we're trying to do builds on Mono using FAKE, and the FAKE builds reference third party assemblies that attempt to load other assemblies at runtime using Type.GetType, which causes our builds to fail because FSI spits this error, even though the builds run just fine. When the builds are run on Windows with .NET FSI, they work as expected (no error).

I've tried this with F# 3.1 on Mono 4.0.1, F# 4 on Mono 4.0.3, and F# 4 on Mono 4.2.0 Alpha; same behaviour in all cases.

@daniel-chambers
Copy link
Author

It's worth mentioning if you compile the same code as an exe with fsc and run that on Mono, it works as expected with no errors.

[<EntryPoint>]
let main _ =
    let log4netType = System.Type.GetType("log4net.Config.XmlConfigurator, log4net")
    let exists = log4netType <> null
    do printfn "%A" exists
    0
> fsharpc Reflection.fs
F# Compiler for F# 4.0 (Open Source Edition)
Freely distributed under the Apache 2.0 Open Source License

> mono Reflection.exe
false

@7sharp9
Copy link
Member

7sharp9 commented Aug 31, 2015

Looks like your missing a #r reference in the script. Presumably in the fsproj you will have a dll reference.

@daniel-chambers
Copy link
Author

The point is that there is no #r, and there should not be. Type.GetType returns null if the type was not found, but in FSI on Mono it also causes an error, which isn't right.

We #r in a third party assembly, which uses Type.GetType internally to test for the presence of log4net; if it exists it uses it, if not, it doesn't. Their normally safe type test causes our code to crash in Mono FSI, which it shouldn't.

@7sharp9
Copy link
Member

7sharp9 commented Aug 31, 2015

Reference resolution differs between windows and mono in fsharpi, you need a #r or #i for mono.

@daniel-chambers
Copy link
Author

I'm afraid I don't understand what you mean. Type.GetType is supposed to return null if the type cannot be found or loaded. No error is supposed to be raised. (See MSDN spec)

If the behaviour was actually different between Mono and Windows I would expect the exe version of the same code to fail in the same way on Mono. It does not; indeed it runs as expected, returning null and not failing. To be crystal clear, we are not referencing log4net in the exe version either (see above example using fsc instead of fsi).

@7sharp9
Copy link
Member

7sharp9 commented Aug 31, 2015

The behaviour of reference resolution in fsi differs between windows and mono. In Windows fsi will use msbuild to try and figure out a reference path, in mono it will use a simple path.

@daniel-chambers
Copy link
Author

I think we might be talking cross purposes here; it's my fault, I backwards named the "exists" variable in the examples above (fixed, you might want to look at them again, sorry).

The reference is not loaded on Windows, nor on Mono as it (correctly) cannot be found on either platform. This is intentional; the library that tries to load log4net will simply not use it if it is not found (ie if GetType returns null). However, only on Mono FSI does it cause an error. It doesn't even seem to be a proper exception that causes the code to fail; the code runs just fine, but FSI prints an error and fails with an exit code after the code has successfully run just fine.

@rneatherway
Copy link
Member

I think you are both coming at the same issue from different angles. I agree with @daniel-chambers that a #r is not required here, as the resolution should be permitted to fail, but the difference in behaviour is almost certainly due to the fact that FSI reference resolution differs on Windows and Mono.

FSI installs a backup assembly resolution handler to be used if the standard resolution fails: https://github.com/fsharp/fsharp/blob/master/src/fsharp/fsi/fsi.fs#L1452

The exception is being thrown in there, because it does in fact fail. This explains the difference in behaviour between running via FSC and FSI. However, it's surprising that this isn't also seen on Windows. It may be down to a difference in the implementation of System.Type.GetType i.e. the backup handler is called on Mono, but not on Windows. That would surprise me as well though.

@dsyme
Copy link
Contributor

dsyme commented Sep 3, 2015

Labelling this as a behaviour divergence bug

@dsyme
Copy link
Contributor

dsyme commented Dec 1, 2015

Added a potential fix for this.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests

4 participants