Skip to content

Commit

Permalink
[WIP] Fixes
Browse files Browse the repository at this point in the history
  • Loading branch information
vzarytovskii committed Apr 27, 2021
1 parent 0279acf commit 2c962f8
Show file tree
Hide file tree
Showing 5 changed files with 88 additions and 69 deletions.
47 changes: 31 additions & 16 deletions src/fsharp/absil/ilread.fs
Original file line number Diff line number Diff line change
Expand Up @@ -116,13 +116,31 @@ type BinaryFile =
/// Gives views over a raw chunk of memory, for example those returned to us by the memory manager in Roslyn's
/// Visual Studio integration. 'obj' must keep the memory alive. The object will capture it and thus also keep the memory alive for
/// the lifetime of this object.
type RawMemoryFile(fileName: string, obj: obj, addr: nativeint, length: int) =
do stats.rawMemoryFileCount <- stats.rawMemoryFileCount + 1
let view = ByteMemory.FromUnsafePointer(addr, length, obj).AsReadOnly()
member _.HoldObj() = obj // make sure we capture 'obj'
member _.FileName = fileName
type RawMemoryFile =
val mutable private holder: obj
val mutable private fileName: string
val mutable private view: ReadOnlyByteMemory

new (fileName: string, obj: obj, addr: nativeint, length: int) =
stats.rawMemoryFileCount <- stats.rawMemoryFileCount + 1
{
holder = obj
fileName = fileName
view = ByteMemory.FromUnsafePointer(addr, length, obj).AsReadOnly()
}

new (fileName: string, holder: obj, bmem: ByteMemory) =
{
holder = holder // gonna be finalized due to how we pass the holder when create RawByteMemory
fileName = fileName
view = bmem.AsReadOnly()
}

member r.HoldObj() = r.holder // make sure we capture the holder.
member r.FileName = r.fileName

interface BinaryFile with
override _.GetView() = view
override r.GetView() = r.view

/// Gives a view over any ByteMemory, can be stream-based, mmap-ed, or just byte array.
type ByteMemoryFile(fileName: string, view: ByteMemory) =
Expand Down Expand Up @@ -3893,25 +3911,22 @@ let createByteFileChunk opts fileName chunk =

ByteFile(fileName, bytes) :> BinaryFile

let _createMemoryMapFile fileName =
let mmf, accessor, length =
// TODO: Need to integrate with FileSystem
let fileStream = File.Open(fileName, FileMode.Open, FileAccess.Read, FileShare.Read)
let length = fileStream.Length
let mmf = MemoryMappedFile.CreateFromFile(fileStream, null, length, MemoryMappedFileAccess.Read, HandleInheritability.None, leaveOpen=false)
mmf, mmf.CreateViewAccessor(0L, fileStream.Length, MemoryMappedFileAccess.Read), length
let createMemoryMapFile fileName =
let byteMem = FileSystem.OpenFileForReadShim(fileName)

let safeHolder =
{ new obj() with
override x.Finalize() =
(x :?> IDisposable).Dispose()
interface IDisposable with
member x.Dispose() =
GC.SuppressFinalize x
accessor.Dispose()
mmf.Dispose()
byteMem.AsStream().Dispose()
stats.memoryMapFileClosedCount <- stats.memoryMapFileClosedCount + 1 }

stats.memoryMapFileOpenedCount <- stats.memoryMapFileOpenedCount + 1
safeHolder, RawMemoryFile(fileName, safeHolder, accessor.SafeMemoryMappedViewHandle.DangerousGetHandle(), int length) :> BinaryFile

safeHolder, RawMemoryFile(fileName, safeHolder, byteMem) :> BinaryFile

let OpenILModuleReaderFromBytes fileName assemblyContents options =
let pefile = ByteFile(fileName, assemblyContents) :> BinaryFile
Expand Down
3 changes: 1 addition & 2 deletions src/fsharp/service/ItemKey.fs
Original file line number Diff line number Diff line change
Expand Up @@ -418,8 +418,7 @@ and [<Sealed>] ItemKeyStoreBuilder() =

fixup.WriteInt32(postCount - preCount)

// TODO: Needs to use FileSystem APIs?
member _._TryBuildAndReset() =
member _.TryBuildAndReset() =
if b.Count > 0 then
let length = int64 b.Count
let mmf =
Expand Down
3 changes: 1 addition & 2 deletions src/fsharp/service/SemanticClassificationKey.fs
Original file line number Diff line number Diff line change
Expand Up @@ -63,8 +63,7 @@ type SemanticClassificationKeyStoreBuilder() =
use ptr = fixed semanticClassification
b.WriteBytes(NativePtr.ofNativeInt (NativePtr.toNativeInt ptr), semanticClassification.Length * sizeof<SemanticClassificationItem>)

// TODO: Needs to use FileSystem APIs?
member _._TryBuildAndReset() =
member _.TryBuildAndReset() =
if b.Count > 0 then
let length = int64 b.Count
let mmf =
Expand Down
7 changes: 3 additions & 4 deletions src/fsharp/utils/FileSystem.fs
Original file line number Diff line number Diff line change
Expand Up @@ -285,11 +285,8 @@ type ReadOnlyByteMemory(bytes: ByteMemory) =

[<AutoOpen>]
module MemoryMappedFileExtensions =

type MemoryMappedFile with

// TODO: Needs to use FileSystem APIs?
static member _TryFromByteMemory(bytes: ReadOnlyByteMemory) =
static member TryFromByteMemory(bytes: ReadOnlyByteMemory) =
let length = int64 bytes.Length
if length = 0L then
None
Expand Down Expand Up @@ -396,8 +393,10 @@ type DefaultAssemblyLoader() =

type IFileSystem =
abstract AssemblyLoader: IAssemblyLoader

abstract OpenFileForReadShim: filePath: string * ?shouldShadowCopy: bool -> ByteMemory
abstract OpenFileForWriteShim: filePath: string * ?fileMode: FileMode * ?fileAccess: FileAccess * ?fileShare: FileShare -> Stream

abstract GetFullPathShim: fileName: string -> string
abstract GetFullFilePathInDirectoryShim: dir: string -> fileName: string -> string
abstract IsPathRootedShim: path: string -> bool
Expand Down
97 changes: 52 additions & 45 deletions tests/service/FileSystemTests.fs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ open FSharp.Compiler.Service.Tests.Common
let fileName1 = @"c:\mycode\test1.fs" // note, the path doesn' exist
let fileName2 = @"c:\mycode\test2.fs" // note, the path doesn' exist

type internal MyFileSystem(defaultFileSystem:IFileSystem) =
type internal MyFileSystem(defaultFileSystem:IFileSystem) =
let file1 = """
module File1
Expand All @@ -31,25 +31,26 @@ let B = File1.A + File1.A"""
let files = dict [(fileName1, file1); (fileName2, file2)]

interface IFileSystem with

member _.AssemblyLoader = DefaultAssemblyLoader() :> IAssemblyLoader
// Implement the service to open files for reading and writing
member _.FileStreamReadShim(fileName) =
match files.TryGetValue fileName with
| true, text -> new MemoryStream(Encoding.UTF8.GetBytes(text)) :> Stream
| _ -> defaultFileSystem.FileStreamReadShim(fileName)

member _.FileStreamCreateShim(fileName) =
defaultFileSystem.FileStreamCreateShim(fileName)

member _.IsStableFileHeuristic(fileName) =
member _.OpenFileForReadShim(filePath, ?shouldShadowCopy: bool) =
let shouldShadowCopy = defaultArg shouldShadowCopy false
match files.TryGetValue filePath with
| true, text ->
let bytes = Encoding.UTF8.GetBytes(text)
ByteArrayMemory(bytes, 0, bytes.Length) :> ByteMemory
| _ -> defaultFileSystem.OpenFileForReadShim(filePath, shouldShadowCopy)

member _.OpenFileForWriteShim(filePath, ?fileMode: FileMode, ?fileAccess: FileAccess, ?fileShare: FileShare) =
let fileMode = defaultArg fileMode FileMode.OpenOrCreate
let fileAccess = defaultArg fileAccess FileAccess.ReadWrite
let fileShare = defaultArg fileShare FileShare.Read
defaultFileSystem.OpenFileForWriteShim(filePath, fileMode, fileAccess, fileShare)

member _.IsStableFileHeuristic(fileName) =
defaultFileSystem.IsStableFileHeuristic(fileName)

member _.FileStreamWriteExistingShim(fileName) =
defaultFileSystem.FileStreamWriteExistingShim(fileName)

member _.ReadAllBytesShim(fileName) =
match files.TryGetValue fileName with
| true, text -> Encoding.UTF8.GetBytes(text)
| _ -> defaultFileSystem.ReadAllBytesShim(fileName)

// Implement the service related to temporary paths and file time stamps
member _.GetTempPathShim() = defaultFileSystem.GetTempPathShim()
Expand All @@ -58,16 +59,23 @@ let B = File1.A + File1.A"""
member _.IsInvalidPathShim(fileName) = defaultFileSystem.IsInvalidPathShim(fileName)
member _.IsPathRootedShim(fileName) = defaultFileSystem.IsPathRootedShim(fileName)

// Implement the service related to file existence and deletion
member _.SafeExists(fileName) = files.ContainsKey(fileName) || defaultFileSystem.SafeExists(fileName)
member _.FileDelete(fileName) = defaultFileSystem.FileDelete(fileName)
member _.CopyShim(src, dest, overwrite) = defaultFileSystem.CopyShim(src, dest, overwrite)
member _.DirectoryCreateShim(path) = defaultFileSystem.DirectoryCreateShim(path)
member _.DirectoryDeleteShim(path) = defaultFileSystem.DirectoryDeleteShim(path)
member _.DirectoryExistsShim(path) = defaultFileSystem.DirectoryExistsShim(path)
member _.EnumerateDirectoriesShim(path) = defaultFileSystem.EnumerateDirectoriesShim(path)
member _.EnumerateFilesShim(path, pattern) = defaultFileSystem.EnumerateFilesShim(path, pattern)
member _.FileDeleteShim(fileName) = defaultFileSystem.FileDeleteShim(fileName)
member _.FileExistsShim(fileName) = defaultFileSystem.FileExistsShim(fileName)
member _.GetCreationTimeShim(path) = defaultFileSystem.GetCreationTimeShim(path)
member _.GetDirectoryNameShim(path) = defaultFileSystem.GetDirectoryNameShim(path)
member _.GetFullFilePathInDirectoryShim(dir) (fileName) = defaultFileSystem.GetFullFilePathInDirectoryShim dir fileName
member _.NormalizePathShim(path) = defaultFileSystem.NormalizePathShim(path)


// Implement the service related to assembly loading, used to load type providers
// and for F# interactive.
member _.AssemblyLoadFrom(fileName) = defaultFileSystem.AssemblyLoadFrom fileName
member _.AssemblyLoad(assemblyName) = defaultFileSystem.AssemblyLoad assemblyName

let UseMyFileSystem() =

let UseMyFileSystem() =
let myFileSystem = MyFileSystem(FileSystemAutoOpens.FileSystem)
FileSystemAutoOpens.FileSystem <- myFileSystem
{ new IDisposable with member x.Dispose() = FileSystemAutoOpens.FileSystem <- myFileSystem }
Expand All @@ -77,35 +85,35 @@ let UseMyFileSystem() =
#if NETCOREAPP
[<Ignore("SKIPPED: need to check if these tests can be enabled for .NET Core testing of FSharp.Compiler.Service")>]
#endif
let ``FileSystem compilation test``() =
if System.Environment.OSVersion.Platform = System.PlatformID.Win32NT then // file references only valid on Windows
let ``FileSystem compilation test``() =
if System.Environment.OSVersion.Platform = System.PlatformID.Win32NT then // file references only valid on Windows
use myFileSystem = UseMyFileSystem()
let programFilesx86Folder = System.Environment.GetEnvironmentVariable("PROGRAMFILES(X86)")

let projectOptions =
let allFlags =
[| yield "--simpleresolution";
yield "--noframework";
yield "--debug:full";
yield "--define:DEBUG";
yield "--optimize-";
yield "--doc:test.xml";
yield "--warn:3";
yield "--fullpaths";
yield "--flaterrors";
yield "--target:library";
for r in [ sysLib "mscorlib"; sysLib "System"; sysLib "System.Core"; fsCoreDefaultReference() ] do
let projectOptions =
let allFlags =
[| yield "--simpleresolution";
yield "--noframework";
yield "--debug:full";
yield "--define:DEBUG";
yield "--optimize-";
yield "--doc:test.xml";
yield "--warn:3";
yield "--fullpaths";
yield "--flaterrors";
yield "--target:library";
for r in [ sysLib "mscorlib"; sysLib "System"; sysLib "System.Core"; fsCoreDefaultReference() ] do
yield "-r:" + r |]

{ ProjectFileName = @"c:\mycode\compilation.fsproj" // Make a name that is unique in this directory.
ProjectId = None
SourceFiles = [| fileName1; fileName2 |]
OtherOptions = allFlags
OtherOptions = allFlags
ReferencedProjects = [| |];
IsIncompleteTypeCheckEnvironment = false
UseScriptResolutionRules = true
UseScriptResolutionRules = true
LoadTime = System.DateTime.Now // Not 'now', we don't want to force reloading
UnresolvedReferences = None
UnresolvedReferences = None
OriginalLoadReferences = []
Stamp = None }

Expand All @@ -115,4 +123,3 @@ let ``FileSystem compilation test``() =
results.AssemblySignature.Entities.Count |> shouldEqual 2
results.AssemblySignature.Entities.[0].MembersFunctionsAndValues.Count |> shouldEqual 1
results.AssemblySignature.Entities.[0].MembersFunctionsAndValues.[0].DisplayName |> shouldEqual "B"

0 comments on commit 2c962f8

Please sign in to comment.