Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 19 additions & 14 deletions src/Compiler/AbstractIL/il.fs
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,14 @@ type PrimaryAssembly =
| System_Runtime -> "System.Runtime"
| NetStandard -> "netstandard"

static member IsPossiblePrimaryAssembly(fileName: string) =
let name = System.IO.Path.GetFileNameWithoutExtension(fileName)

String.Compare(name, "mscorlib", true) <> 0
|| String.Compare(name, "System.Runtime", true) <> 0
|| String.Compare(name, "netstandard", true) <> 0
|| String.Compare(name, "System.Private.CoreLib", true) <> 0

// --------------------------------------------------------------------
// Utilities: type names
// --------------------------------------------------------------------
Expand Down Expand Up @@ -2803,12 +2811,16 @@ and [<Sealed>] ILTypeDefs(f: unit -> ILPreTypeDef[]) =
member x.GetEnumerator() =
(seq { for pre in array.Value -> pre.GetTypeDef() }).GetEnumerator()

member x.AsArrayOfPreTypeDefs() = array.Value
member _.AsArrayOfPreTypeDefs() = array.Value

member x.FindByName nm =
member _.FindByName nm =
let ns, n = splitILTypeName nm
dict.Value[ (ns, n) ].GetTypeDef()

member _.ExistsByName nm =
let ns, n = splitILTypeName nm
dict.Value.ContainsKey((ns, n))

and [<NoEquality; NoComparison>] ILPreTypeDef =
abstract Namespace: string list
abstract Name: string
Expand Down Expand Up @@ -3331,15 +3343,9 @@ let tname_UIntPtr = "System.UIntPtr"
let tname_TypedReference = "System.TypedReference"

[<NoEquality; NoComparison; StructuredFormatDisplay("{DebugText}")>]
type ILGlobals
(
primaryScopeRef: ILScopeRef,
assembliesThatForwardToPrimaryAssembly: ILAssemblyRef list,
fsharpCoreAssemblyScopeRef: ILScopeRef
) =
type ILGlobals(primaryScopeRef: ILScopeRef, equivPrimaryAssemblyRefs: ILAssemblyRef list, fsharpCoreAssemblyScopeRef: ILScopeRef) =

let assembliesThatForwardToPrimaryAssembly =
Array.ofList assembliesThatForwardToPrimaryAssembly
let equivPrimaryAssemblyRefs = Array.ofList equivPrimaryAssemblyRefs

let mkSysILTypeRef nm = mkILTyRef (primaryScopeRef, nm)

Expand Down Expand Up @@ -3394,17 +3400,16 @@ type ILGlobals

member x.IsPossiblePrimaryAssemblyRef(aref: ILAssemblyRef) =
aref.EqualsIgnoringVersion x.primaryAssemblyRef
|| assembliesThatForwardToPrimaryAssembly
|> Array.exists aref.EqualsIgnoringVersion
|| equivPrimaryAssemblyRefs |> Array.exists aref.EqualsIgnoringVersion

/// For debugging
[<DebuggerBrowsable(DebuggerBrowsableState.Never)>]
member x.DebugText = x.ToString()

override x.ToString() = "<ILGlobals>"

let mkILGlobals (primaryScopeRef, assembliesThatForwardToPrimaryAssembly, fsharpCoreAssemblyScopeRef) =
ILGlobals(primaryScopeRef, assembliesThatForwardToPrimaryAssembly, fsharpCoreAssemblyScopeRef)
let mkILGlobals (primaryScopeRef, equivPrimaryAssemblyRefs, fsharpCoreAssemblyScopeRef) =
ILGlobals(primaryScopeRef, equivPrimaryAssemblyRefs, fsharpCoreAssemblyScopeRef)

let mkNormalCall mspec = I_call(Normalcall, mspec, None)

Expand Down
21 changes: 14 additions & 7 deletions src/Compiler/AbstractIL/il.fsi
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ open FSharp.Compiler.IO
open System.Collections.Generic
open System.Reflection

/// Represents the target primary assembly
[<RequireQualifiedAccess>]
type internal PrimaryAssembly =
| Mscorlib
Expand All @@ -16,6 +17,11 @@ type internal PrimaryAssembly =

member Name: string

/// Checks if an assembly resolution may represent a primary assembly that actually contains the
/// definition of Sytem.Object. Note that the chosen target primary assembly may not actually be the one
/// that contains the definition of System.Object - it is just the one we are choosing to emit for.
static member IsPossiblePrimaryAssembly: fileName: string -> bool

/// Represents guids
type ILGuid = byte[]

Expand Down Expand Up @@ -1407,12 +1413,12 @@ type ILTypeDefs =
/// Get some information about the type defs, but do not force the read of the type defs themselves.
member internal AsArrayOfPreTypeDefs: unit -> ILPreTypeDef[]

/// Calls to <c>FindByName</c> will result in any laziness in the overall
/// set of ILTypeDefs being read in in addition
/// to the details for the type found, but the remaining individual
/// type definitions will not be read.
/// Calls to <c>FindByName</c> will result in all the ILPreTypeDefs being read.
member internal FindByName: string -> ILTypeDef

/// Calls to <c>ExistsByName</c> will result in all the ILPreTypeDefs being read.
member internal ExistsByName: string -> bool

/// Represents IL Type Definitions.
[<NoComparison; NoEquality>]
type ILTypeDef =
Expand Down Expand Up @@ -1841,10 +1847,11 @@ type internal ILGlobals =
member IsPossiblePrimaryAssemblyRef: ILAssemblyRef -> bool

/// Build the table of commonly used references given functions to find types in system assemblies
///
/// primaryScopeRef is the primary assembly we are emitting
/// equivPrimaryAssemblyRefs are ones regarded as equivalent
val internal mkILGlobals:
primaryScopeRef: ILScopeRef *
assembliesThatForwardToPrimaryAssembly: ILAssemblyRef list *
fsharpCoreAssemblyScopeRef: ILScopeRef ->
primaryScopeRef: ILScopeRef * equivPrimaryAssemblyRefs: ILAssemblyRef list * fsharpCoreAssemblyScopeRef: ILScopeRef ->
ILGlobals

val internal PrimaryAssemblyILGlobals: ILGlobals
Expand Down
50 changes: 25 additions & 25 deletions src/Compiler/Driver/CompilerImports.fs
Original file line number Diff line number Diff line change
Expand Up @@ -2321,13 +2321,13 @@ and [<Sealed>] TcImports
| _, [ ResolvedImportedAssembly ccu ] -> ccu.FSharpViewOfMetadata.ILScopeRef
| _ -> failwith "primaryScopeRef - unexpected"

let resolvedAssemblies = tcResolutions.GetAssemblyResolutions()

let primaryAssemblyResolvedPath =
match primaryAssemblyResolution with
| [ primaryAssemblyResolution ] -> primaryAssemblyResolution.resolvedPath
| _ -> failwith "primaryAssemblyResolvedPath - unexpected"

let resolvedAssemblies = tcResolutions.GetAssemblyResolutions()

let readerSettings: ILReaderOptions =
{
pdbDirPath = None
Expand All @@ -2336,28 +2336,28 @@ and [<Sealed>] TcImports
tryGetMetadataSnapshot = tcConfig.tryGetMetadataSnapshot
}

let tryFindAssemblyByExportedType manifest (exportedType: ILExportedTypeOrForwarder) =
match exportedType.ScopeRef, primaryScopeRef with
| ILScopeRef.Assembly aref1, ILScopeRef.Assembly aref2 when aref1.EqualsIgnoringVersion aref2 ->
mkRefToILAssembly manifest |> Some
| _ -> None

let tryFindAssemblyThatForwardsToPrimaryAssembly manifest =
manifest.ExportedTypes.TryFindByName "System.Object"
|> Option.bind (tryFindAssemblyByExportedType manifest)

// Determine what other assemblies could have been the primary assembly
// by checking to see if "System.Object" is an exported type.
let assembliesThatForwardToPrimaryAssembly =
resolvedAssemblies
|> List.choose (fun resolvedAssembly ->
if primaryAssemblyResolvedPath <> resolvedAssembly.resolvedPath then
let reader = OpenILModuleReader resolvedAssembly.resolvedPath readerSettings

reader.ILModuleDef.Manifest
|> Option.bind tryFindAssemblyThatForwardsToPrimaryAssembly
else
None)
let tryFindEquivPrimaryAssembly (resolvedAssembly: AssemblyResolution) =
if primaryAssemblyResolvedPath = resolvedAssembly.resolvedPath then
None
else
let reader = OpenILModuleReader resolvedAssembly.resolvedPath readerSettings
let mdef = reader.ILModuleDef

// We check the exported types of all assemblies, since many may forward System.Object,
// but only check the actual type definitions for specific assemblies that we know
// might actually declare System.Object.
match mdef.Manifest with
| Some manifest when
manifest.ExportedTypes.TryFindByName "System.Object" |> Option.isSome
|| PrimaryAssembly.IsPossiblePrimaryAssembly resolvedAssembly.resolvedPath
&& mdef.TypeDefs.ExistsByName "System.Object"
->
mkRefToILAssembly manifest |> Some
| _ -> None

// Find assemblies which also declare System.Object
let equivPrimaryAssemblyRefs =
resolvedAssemblies |> List.choose tryFindEquivPrimaryAssembly

let! fslibCcu, fsharpCoreAssemblyScopeRef =
node {
Expand Down Expand Up @@ -2406,7 +2406,7 @@ and [<Sealed>] TcImports
sysCcus |> Array.tryFind (fun ccu -> ccuHasType ccu path typeName)

let ilGlobals =
mkILGlobals (primaryScopeRef, assembliesThatForwardToPrimaryAssembly, fsharpCoreAssemblyScopeRef)
mkILGlobals (primaryScopeRef, equivPrimaryAssemblyRefs, fsharpCoreAssemblyScopeRef)

// OK, now we have both mscorlib.dll and FSharp.Core.dll we can create TcGlobals
let tcGlobals =
Expand Down
42 changes: 42 additions & 0 deletions tests/FSharp.Compiler.UnitTests/FsiTests.fs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,13 @@ open FluentAssertions
open FSharp.Compiler.Interactive.Shell
open FSharp.Test
open Xunit
open System.Threading

type Sentinel () =
let x = ()

module MyModule =
let test(x: int) = ()

[<Collection("SingleThreaded")>]
module FsiTests =
Expand Down Expand Up @@ -636,3 +643,38 @@ module FsiTests =
let boundValue = fsiSession.GetBoundValues() |> List.exactlyOne
Assert.shouldBe typeof<CustomType2[,]> boundValue.Value.ReflectionType
boundValue.Value.ReflectionValue.Should().Be(mdArr, "") |> ignore

#if NETCOREAPP
[<Fact>]
let ``Evaluating simple reference and code succeeds under permutations``() =

for useSdkRefsFlag in ["/usesdkrefs"; "/usesdkrefs-"] do
for multiemitFlag in ["/multiemit"; "/multiemit-"] do
let config = FsiEvaluationSession.GetDefaultConfiguration()
let argv = [|
typeof<Sentinel>.Assembly.Location
"--noninteractive"
"--targetprofile:netcore"
"--langversion:preview"
multiemitFlag
useSdkRefsFlag
|]
let fsi = FsiEvaluationSession.Create(config, argv, TextReader.Null, TextWriter.Null, TextWriter.Null)
let assemblyPath = typeof<Sentinel>.Assembly.Location.Replace("\\", "/")
let code = $@"
#r ""{assemblyPath}""
FSharp.Compiler.UnitTests.MyModule.test(3)"
let ch, errors = fsi.EvalInteractionNonThrowing(code, CancellationToken.None)
errors
|> Array.iter (fun e -> printfn "error: %A" e)
match ch with
| Choice1Of2 v ->
let v =
match v with
| Some v -> sprintf "%A" v.ReflectionValue
| None -> "(none)"
printfn "value: %A" v
| Choice2Of2 e ->
printfn "exception: %A" e
raise e
#endif