From 478ec47546726bcb5d638dfd540dfe60b2ff9bb0 Mon Sep 17 00:00:00 2001 From: Eugene Auduchinok Date: Thu, 17 May 2018 17:00:51 +0300 Subject: [PATCH 1/2] Add IFileSystem.SourceFileReadShim returning char[] --- src/absil/illib.fs | 19 ++++++++++++++++++- src/fsharp/CompileOps.fs | 17 ++++++----------- src/fsharp/UnicodeLexing.fs | 16 +++++----------- 3 files changed, 29 insertions(+), 23 deletions(-) diff --git a/src/absil/illib.fs b/src/absil/illib.fs index a1adb372e98..e90c7d41b69 100644 --- a/src/absil/illib.fs +++ b/src/absil/illib.fs @@ -1192,6 +1192,9 @@ module Shim = /// A shim over FileStream with FileMode.Open,FileAccess.Read,FileShare.ReadWrite abstract FileStreamReadShim: fileName:string -> Stream + /// A shim used for lexing F# source files + abstract SourceFileReadShim: fileName: string * codepage: int option -> char[] + /// A shim over FileStream with FileMode.Create,FileAccess.Write,FileShare.Read abstract FileStreamCreateShim: fileName:string -> Stream @@ -1232,6 +1235,10 @@ module Shim = type DefaultFileSystem() = + + let fileStremReadShim fileName = + new FileStream(fileName,FileMode.Open,FileAccess.Read,FileShare.ReadWrite) :> Stream + interface IFileSystem with member __.AssemblyLoadFrom(fileName: string) = @@ -1242,7 +1249,17 @@ module Shim = member __.ReadAllBytesShim (fileName: string) = File.ReadAllBytes fileName - member __.FileStreamReadShim (fileName: string) = new FileStream(fileName,FileMode.Open,FileAccess.Read,FileShare.ReadWrite) :> Stream + member __.FileStreamReadShim (fileName: string) = + fileStremReadShim fileName + + member __.SourceFileReadShim(fileName: string, codepage: int option) = + // Use the .NET functionality to auto-detect the unicode encoding + use stream = fileStremReadShim fileName + use reader = + match codepage with + | None -> new StreamReader(stream,true) + | Some n -> new StreamReader(stream,System.Text.Encoding.GetEncoding(n)) + reader.ReadToEnd().ToCharArray() member __.FileStreamCreateShim (fileName: string) = new FileStream(fileName,FileMode.Create,FileAccess.Write,FileShare.Read ,0x1000,false) :> Stream diff --git a/src/fsharp/CompileOps.fs b/src/fsharp/CompileOps.fs index 094f07e73f2..30c07691f39 100644 --- a/src/fsharp/CompileOps.fs +++ b/src/fsharp/CompileOps.fs @@ -5020,7 +5020,7 @@ module private ScriptPreprocessClosure = open Internal.Utilities.Text.Lexing /// Represents an input to the closure finding process - type ClosureSource = ClosureSource of filename: string * referenceRange: range * sourceText: string * parseRequired: bool + type ClosureSource = ClosureSource of filename: string * referenceRange: range * source: char[] * parseRequired: bool /// Represents an output of the closure finding process type ClosureFile = ClosureFile of string * range * ParsedInput option * (PhasedDiagnostic * bool) list * (PhasedDiagnostic * bool) list * (string * range) list // filename, range, errors, warnings, nowarns @@ -5035,7 +5035,7 @@ module private ScriptPreprocessClosure = seen.ContainsKey(check) /// Parse a script from source. - let ParseScriptText(filename:string, source:string, tcConfig:TcConfig, codeContext, lexResourceManager:Lexhelp.LexResourceManager, errorLogger:ErrorLogger) = + let ParseScriptText(filename:string, source: char[], tcConfig:TcConfig, codeContext, lexResourceManager:Lexhelp.LexResourceManager, errorLogger:ErrorLogger) = // fsc.exe -- COMPILED\!INTERACTIVE // fsi.exe -- !COMPILED\INTERACTIVE @@ -5047,7 +5047,7 @@ module private ScriptPreprocessClosure = | CodeContext.CompilationAndEvaluation -> ["INTERACTIVE"] | CodeContext.Compilation -> ["COMPILED"] | CodeContext.Editing -> "EDITING" :: (if IsScript filename then ["INTERACTIVE"] else ["COMPILED"]) - let lexbuf = UnicodeLexing.StringAsLexbuf source + let lexbuf = LexBuffer<_>.FromChars(source) let isLastCompiland = (IsScript filename), tcConfig.target.IsExe // The root compiland is last in the list of compilands. ParseOneInputLexbuf (tcConfig, lexResourceManager, defines, lexbuf, filename, isLastCompiland, errorLogger) @@ -5078,12 +5078,7 @@ module private ScriptPreprocessClosure = let ClosureSourceOfFilename(filename, m, inputCodePage, parseRequired) = try let filename = FileSystem.GetFullPathShim(filename) - use stream = FileSystem.FileStreamReadShim filename - use reader = - match inputCodePage with - | None -> new StreamReader(stream, true) - | Some (n: int) -> new StreamReader(stream, Encoding.GetEncoding(n)) - let source = reader.ReadToEnd() + let source = FileSystem.SourceFileReadShim(filename, inputCodePage) [ClosureSource(filename, m, source, parseRequired)] with e -> errorRecovery e m @@ -5251,9 +5246,9 @@ type LoadClosure with // /// A temporary TcConfig is created along the way, is why this routine takes so many arguments. We want to be sure to use exactly the /// same arguments as the rest of the application. - static member ComputeClosureOfScriptText(ctok, legacyReferenceResolver, defaultFSharpBinariesDir, filename:string, source:string, codeContext, useSimpleResolution:bool, useFsiAuxLib, lexResourceManager:Lexhelp.LexResourceManager, applyCommmandLineArgs, assumeDotNetFramework, tryGetMetadataSnapshot, reduceMemoryUsage) : LoadClosure = + static member ComputeClosureOfScriptText(ctok, legacyReferenceResolver, defaultFSharpBinariesDir, filename:string, source: string, codeContext, useSimpleResolution:bool, useFsiAuxLib, lexResourceManager:Lexhelp.LexResourceManager, applyCommmandLineArgs, assumeDotNetFramework, tryGetMetadataSnapshot, reduceMemoryUsage) : LoadClosure = use unwindBuildPhase = PushThreadBuildPhaseUntilUnwind BuildPhase.Parse - ScriptPreprocessClosure.GetFullClosureOfScriptText(ctok, legacyReferenceResolver, defaultFSharpBinariesDir, filename, source, codeContext, useSimpleResolution, useFsiAuxLib, lexResourceManager, applyCommmandLineArgs, assumeDotNetFramework, tryGetMetadataSnapshot, reduceMemoryUsage) + ScriptPreprocessClosure.GetFullClosureOfScriptText(ctok, legacyReferenceResolver, defaultFSharpBinariesDir, filename, source.ToCharArray(), codeContext, useSimpleResolution, useFsiAuxLib, lexResourceManager, applyCommmandLineArgs, assumeDotNetFramework, tryGetMetadataSnapshot, reduceMemoryUsage) /// Analyze a set of script files and find the closure of their references. The resulting references are then added to the given TcConfig. /// Used from fsi.fs and fsc.fs, for #load and command line. diff --git a/src/fsharp/UnicodeLexing.fs b/src/fsharp/UnicodeLexing.fs index b6013c0342d..92455f47f75 100644 --- a/src/fsharp/UnicodeLexing.fs +++ b/src/fsharp/UnicodeLexing.fs @@ -38,18 +38,12 @@ let numRetries = 60 /// we can't just return the LexBuffer object, since the file it wraps wouldn't /// get closed when we're finished with the LexBuffer. Hence we return the stream, /// the reader and the LexBuffer. The caller should dispose the first two when done. -let UnicodeFileAsLexbuf (filename,codePage : int option, retryLocked:bool) : Lexbuf = +let UnicodeFileAsLexbuf (filename, codepage: int option, retryLocked: bool): Lexbuf = // Retry multiple times since other processes may be writing to this file. let rec getSource retryNumber = - try - // Use the .NET functionality to auto-detect the unicode encoding - use stream = FileSystem.FileStreamReadShim(filename) - use reader = - match codePage with - | None -> new StreamReader(stream,true) - | Some n -> new StreamReader(stream,System.Text.Encoding.GetEncoding(n)) - reader.ReadToEnd() - with + try + FileSystem.SourceFileReadShim(filename, codepage) + with // We can get here if the file is locked--like when VS is saving a file--we don't have direct // access to the HRESULT to see that this is EONOACCESS. | :? System.IO.IOException as err when retryLocked && err.GetType() = typeof -> @@ -65,5 +59,5 @@ let UnicodeFileAsLexbuf (filename,codePage : int option, retryLocked:bool) : Le else reraise() let source = getSource 0 - let lexbuf = LexBuffer<_>.FromChars(source.ToCharArray()) + let lexbuf = LexBuffer<_>.FromChars(source) lexbuf From a84226c8189be93a2a7260af43a0b687e9aa321e Mon Sep 17 00:00:00 2001 From: Eugene Auduchinok Date: Fri, 18 May 2018 19:05:29 +0300 Subject: [PATCH 2/2] Fix test --- tests/service/FileSystemTests.fs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/service/FileSystemTests.fs b/tests/service/FileSystemTests.fs index e7442e5298a..0a927584b85 100644 --- a/tests/service/FileSystemTests.fs +++ b/tests/service/FileSystemTests.fs @@ -69,6 +69,8 @@ let B = File1.A + File1.A""" member __.AssemblyLoadFrom(fileName) = defaultFileSystem.AssemblyLoadFrom fileName member __.AssemblyLoad(assemblyName) = defaultFileSystem.AssemblyLoad assemblyName + member __.SourceFileReadShim(fileName, codepage) = defaultFileSystem.SourceFileReadShim(fileName, codepage) + let UseMyFileSystem() = let myFileSystem = MyFileSystem(Shim.FileSystem) Shim.FileSystem <- myFileSystem