Skip to content

Commit 2637859

Browse files
authored
Merge pull request #639 from 7sharp9/compiler-api-harmonise
Compiler api harmonise
2 parents aafe852 + 571485f commit 2637859

File tree

3 files changed

+215
-115
lines changed

3 files changed

+215
-115
lines changed

src/fsharp/vs/SimpleServices.fs

Lines changed: 8 additions & 115 deletions
Original file line numberDiff line numberDiff line change
@@ -113,113 +113,6 @@ namespace Microsoft.FSharp.Compiler.SimpleSourceCodeServices
113113
let checker = InteractiveChecker.Create()
114114
let fileversion = 0
115115
let loadTime = DateTime.Now
116-
117-
let mkCompilationErorHandlers() =
118-
let errors = ResizeArray<_>()
119-
120-
let errorSink warn exn =
121-
let mainError,relatedErrors = SplitRelatedErrors exn
122-
let oneError trim e = errors.Add(ErrorInfo.CreateFromException (e, warn, trim, Range.range0))
123-
oneError false mainError
124-
List.iter (oneError true) relatedErrors
125-
126-
let errorLogger =
127-
{ new ErrorLogger("CompileAPI") with
128-
member x.WarnSinkImpl(exn) = errorSink true exn
129-
member x.ErrorSinkImpl(exn) = errorSink false exn
130-
member x.ErrorCount = errors |> Seq.filter (fun e -> e.Severity = FSharpErrorSeverity.Error) |> Seq.length }
131-
132-
let loggerProvider =
133-
{ new ErrorLoggerProvider() with
134-
member x.CreateErrorLoggerThatQuitsAfterMaxErrors(_tcConfigBuilder, _exiter) = errorLogger }
135-
errors, errorLogger, loggerProvider
136-
137-
let tryCompile errorLogger f =
138-
use unwindParsePhase = PushThreadBuildPhaseUntilUnwind (BuildPhase.Parse)
139-
use unwindEL_2 = PushErrorLoggerPhaseUntilUnwind (fun _ -> errorLogger)
140-
let exiter = { new Exiter with member x.Exit n = raise StopProcessing }
141-
try
142-
f exiter
143-
0
144-
with e ->
145-
stopProcessingRecovery e Range.range0
146-
1
147-
148-
/// Compile using the given flags. Source files names are resolved via the FileSystem API. The output file must be given by a -o flag.
149-
let compileFromArgs (argv: string[], tcImportsCapture, dynamicAssemblyCreator) =
150-
151-
let errors, errorLogger, loggerProvider = mkCompilationErorHandlers()
152-
let result =
153-
tryCompile errorLogger (fun exiter ->
154-
mainCompile (argv, (*bannerAlreadyPrinted*)true, (*openBinariesInMemory*)true, exiter, loggerProvider, tcImportsCapture, dynamicAssemblyCreator) )
155-
156-
errors.ToArray(), result
157-
158-
let compileFromAsts (asts, assemblyName, outFile, dependencies, noframework, pdbFile, executable, tcImportsCapture, dynamicAssemblyCreator) =
159-
160-
let errors, errorLogger, loggerProvider = mkCompilationErorHandlers()
161-
162-
let executable = defaultArg executable true
163-
let target = if executable then CompilerTarget.ConsoleExe else CompilerTarget.Dll
164-
165-
let result =
166-
tryCompile errorLogger (fun exiter ->
167-
compileOfAst ((*openBinariesInMemory=*)true, assemblyName, target, outFile, pdbFile, dependencies, noframework, exiter, loggerProvider, asts, tcImportsCapture, dynamicAssemblyCreator))
168-
169-
errors.ToArray(), result
170-
171-
let dynamicAssemblyCreator (debugInfo: bool, tcImportsRef: TcImports option ref, execute: _ option, assemblyBuilderRef: _ option ref) (_tcConfig,ilGlobals,_errorLogger,outfile,_pdbfile,ilxMainModule,_signingInfo) =
172-
173-
// Create an assembly builder
174-
let assemblyName = System.Reflection.AssemblyName(System.IO.Path.GetFileNameWithoutExtension outfile)
175-
let flags = System.Reflection.Emit.AssemblyBuilderAccess.Run
176-
#if FX_NO_APP_DOMAINS
177-
let assemblyBuilder = AssemblyBuilder.DefineDynamicAssembly(assemblyName, flags)
178-
let moduleBuilder = assemblyBuilder.DefineDynamicModule("IncrementalModule")
179-
#else
180-
let assemblyBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(assemblyName, flags)
181-
let moduleBuilder = assemblyBuilder.DefineDynamicModule("IncrementalModule", debugInfo)
182-
#endif
183-
// Omit resources in dynamic assemblies, because the module builder is constructed without a filename the module
184-
// is tagged as transient and as such DefineManifestResource will throw an invalid operation if resources are present.
185-
//
186-
// Also, the dynamic assembly creator can't currently handle types called "<Module>" from statically linked assemblies.
187-
let ilxMainModule =
188-
{ ilxMainModule with
189-
TypeDefs = ilxMainModule.TypeDefs.AsList |> List.filter (fun td -> not (isTypeNameForGlobalFunctions td.Name)) |> mkILTypeDefs
190-
Resources=mkILResources [] }
191-
192-
// The function used to resolve typees while emitting the code
193-
let assemblyResolver s =
194-
match tcImportsRef.Value.Value.TryFindExistingFullyQualifiedPathFromAssemblyRef s with
195-
| Some res -> Some (Choice1Of2 res)
196-
| None -> None
197-
198-
// Emit the code
199-
let _emEnv,execs = ILRuntimeWriter.emitModuleFragment(ilGlobals, ILRuntimeWriter.emEnv0, assemblyBuilder, moduleBuilder, ilxMainModule, debugInfo, assemblyResolver)
200-
201-
// Execute the top-level initialization, if requested
202-
if execute.IsSome then
203-
for exec in execs do
204-
match exec() with
205-
| None -> ()
206-
| Some exn -> raise exn
207-
208-
// Register the reflected definitions for the dynamically generated assembly
209-
for resource in ilxMainModule.Resources.AsList do
210-
if IsReflectedDefinitionsResource resource then
211-
Quotations.Expr.RegisterReflectedDefinitions(assemblyBuilder, moduleBuilder.Name, resource.Bytes)
212-
213-
// Save the result
214-
assemblyBuilderRef := Some assemblyBuilder
215-
216-
let setOutputStreams execute =
217-
// Set the output streams, if requested
218-
match execute with
219-
| Some (writer,error) ->
220-
System.Console.SetOut writer
221-
System.Console.SetError error
222-
| None -> ()
223116

224117

225118
/// Tokenize a single line, returning token information and a tokenization state represented by an integer
@@ -278,14 +171,14 @@ namespace Microsoft.FSharp.Compiler.SimpleSourceCodeServices
278171
checker.ParseAndCheckProject(options)
279172

280173
member x.Compile (argv: string[]) =
281-
compileFromArgs (argv, None, None)
174+
CompileHelpers.compileFromArgs (argv, None, None)
282175

283176
member x.Compile (ast:ParsedInput list, assemblyName:string, outFile:string, dependencies:string list, ?pdbFile:string, ?executable:bool, ?noframework:bool) =
284177
let noframework = defaultArg noframework false
285-
compileFromAsts (ast, assemblyName, outFile, dependencies, noframework, pdbFile, executable, None, None)
178+
CompileHelpers.compileFromAsts (ast, assemblyName, outFile, dependencies, noframework, pdbFile, executable, None, None)
286179

287180
member x.CompileToDynamicAssembly (otherFlags: string[], execute: (TextWriter * TextWriter) option) =
288-
setOutputStreams execute
181+
CompileHelpers.setOutputStreams execute
289182

290183
// References used to capture the results of compilation
291184
let tcImportsRef = ref (None: TcImports option)
@@ -294,10 +187,10 @@ namespace Microsoft.FSharp.Compiler.SimpleSourceCodeServices
294187

295188
// Function to generate and store the results of compilation
296189
let debugInfo = otherFlags |> Array.exists (fun arg -> arg = "-g" || arg = "--debug:+" || arg = "/debug:+")
297-
let dynamicAssemblyCreator = Some (dynamicAssemblyCreator (debugInfo, tcImportsRef, execute, assemblyBuilderRef))
190+
let dynamicAssemblyCreator = Some (CompileHelpers.dynamicAssemblyCreator (debugInfo, tcImportsRef, execute, assemblyBuilderRef))
298191

299192
// Perform the compilation, given the above capturing function.
300-
let errorsAndWarnings, result = compileFromArgs (otherFlags, tcImportsCapture, dynamicAssemblyCreator)
193+
let errorsAndWarnings, result = CompileHelpers.compileFromArgs (otherFlags, tcImportsCapture, dynamicAssemblyCreator)
301194

302195
// Retrieve and return the results
303196
let assemblyOpt =
@@ -308,7 +201,7 @@ namespace Microsoft.FSharp.Compiler.SimpleSourceCodeServices
308201
errorsAndWarnings, result, assemblyOpt
309202

310203
member x.CompileToDynamicAssembly (asts:ParsedInput list, assemblyName:string, dependencies:string list, execute: (TextWriter * TextWriter) option, ?debug:bool, ?noframework:bool) =
311-
setOutputStreams execute
204+
CompileHelpers.setOutputStreams execute
312205

313206
// References used to capture the results of compilation
314207
let tcImportsRef = ref (None: TcImports option)
@@ -323,11 +216,11 @@ namespace Microsoft.FSharp.Compiler.SimpleSourceCodeServices
323216
let outFile = Path.Combine(location, assemblyName + ".dll")
324217

325218
// Function to generate and store the results of compilation
326-
let dynamicAssemblyCreator = Some (dynamicAssemblyCreator (debugInfo, tcImportsRef, execute, assemblyBuilderRef))
219+
let dynamicAssemblyCreator = Some (CompileHelpers.dynamicAssemblyCreator (debugInfo, tcImportsRef, execute, assemblyBuilderRef))
327220

328221
// Perform the compilation, given the above capturing function.
329222
let errorsAndWarnings, result =
330-
compileFromAsts (asts, assemblyName, outFile, dependencies, noframework, None, Some execute.IsSome, tcImportsCapture, dynamicAssemblyCreator)
223+
CompileHelpers.compileFromAsts (asts, assemblyName, outFile, dependencies, noframework, None, Some execute.IsSome, tcImportsCapture, dynamicAssemblyCreator)
331224

332225
// Retrieve and return the results
333226
let assemblyOpt =

src/fsharp/vs/service.fs

Lines changed: 171 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ open System
99
open System.IO
1010
open System.Text
1111
open System.Threading
12+
open System.Reflection.Emit
1213
open System.Runtime
1314
open System.Collections.Generic
1415

@@ -23,6 +24,7 @@ open Microsoft.FSharp.Compiler.AbstractIL.Internal.Library
2324
open Microsoft.FSharp.Compiler.AccessibilityLogic
2425
open Microsoft.FSharp.Compiler.Ast
2526
open Microsoft.FSharp.Compiler.CompileOps
27+
open Microsoft.FSharp.Compiler.Driver
2628
open Microsoft.FSharp.Compiler.ErrorLogger
2729
open Microsoft.FSharp.Compiler.Lib
2830
open Microsoft.FSharp.Compiler.MSBuildResolver
@@ -2123,6 +2125,115 @@ module Helpers =
21232125
let AreSubsumable3((fileName1:string,_,o1:FSharpProjectOptions),(fileName2:string,_,o2:FSharpProjectOptions)) =
21242126
(fileName1 = fileName2)
21252127
&& FSharpProjectOptions.AreSubsumable(o1,o2)
2128+
2129+
module CompileHelpers =
2130+
let mkCompilationErorHandlers() =
2131+
let errors = ResizeArray<_>()
2132+
2133+
let errorSink warn exn =
2134+
let mainError,relatedErrors = SplitRelatedErrors exn
2135+
let oneError trim e = errors.Add(ErrorInfo.CreateFromException (e, warn, trim, Range.range0))
2136+
oneError false mainError
2137+
List.iter (oneError true) relatedErrors
2138+
2139+
let errorLogger =
2140+
{ new ErrorLogger("CompileAPI") with
2141+
member x.WarnSinkImpl(exn) = errorSink true exn
2142+
member x.ErrorSinkImpl(exn) = errorSink false exn
2143+
member x.ErrorCount = errors |> Seq.filter (fun e -> e.Severity = FSharpErrorSeverity.Error) |> Seq.length }
2144+
2145+
let loggerProvider =
2146+
{ new ErrorLoggerProvider() with
2147+
member x.CreateErrorLoggerThatQuitsAfterMaxErrors(_tcConfigBuilder, _exiter) = errorLogger }
2148+
errors, errorLogger, loggerProvider
2149+
2150+
let tryCompile errorLogger f =
2151+
use unwindParsePhase = PushThreadBuildPhaseUntilUnwind (BuildPhase.Parse)
2152+
use unwindEL_2 = PushErrorLoggerPhaseUntilUnwind (fun _ -> errorLogger)
2153+
let exiter = { new Exiter with member x.Exit n = raise StopProcessing }
2154+
try
2155+
f exiter
2156+
0
2157+
with e ->
2158+
stopProcessingRecovery e Range.range0
2159+
1
2160+
2161+
/// Compile using the given flags. Source files names are resolved via the FileSystem API. The output file must be given by a -o flag.
2162+
let compileFromArgs (argv: string[], tcImportsCapture, dynamicAssemblyCreator) =
2163+
2164+
let errors, errorLogger, loggerProvider = mkCompilationErorHandlers()
2165+
let result =
2166+
tryCompile errorLogger (fun exiter ->
2167+
mainCompile (argv, (*bannerAlreadyPrinted*)true, (*openBinariesInMemory*)true, exiter, loggerProvider, tcImportsCapture, dynamicAssemblyCreator) )
2168+
2169+
errors.ToArray(), result
2170+
2171+
let compileFromAsts (asts, assemblyName, outFile, dependencies, noframework, pdbFile, executable, tcImportsCapture, dynamicAssemblyCreator) =
2172+
2173+
let errors, errorLogger, loggerProvider = mkCompilationErorHandlers()
2174+
2175+
let executable = defaultArg executable true
2176+
let target = if executable then CompilerTarget.ConsoleExe else CompilerTarget.Dll
2177+
2178+
let result =
2179+
tryCompile errorLogger (fun exiter ->
2180+
compileOfAst ((*openBinariesInMemory=*)true, assemblyName, target, outFile, pdbFile, dependencies, noframework, exiter, loggerProvider, asts, tcImportsCapture, dynamicAssemblyCreator))
2181+
2182+
errors.ToArray(), result
2183+
2184+
let dynamicAssemblyCreator (debugInfo: bool, tcImportsRef: TcImports option ref, execute: _ option, assemblyBuilderRef: _ option ref) (_tcConfig,ilGlobals,_errorLogger,outfile,_pdbfile,ilxMainModule,_signingInfo) =
2185+
2186+
// Create an assembly builder
2187+
let assemblyName = System.Reflection.AssemblyName(System.IO.Path.GetFileNameWithoutExtension outfile)
2188+
let flags = System.Reflection.Emit.AssemblyBuilderAccess.Run
2189+
#if FX_NO_APP_DOMAINS
2190+
let assemblyBuilder = AssemblyBuilder.DefineDynamicAssembly(assemblyName, flags)
2191+
let moduleBuilder = assemblyBuilder.DefineDynamicModule("IncrementalModule")
2192+
#else
2193+
let assemblyBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(assemblyName, flags)
2194+
let moduleBuilder = assemblyBuilder.DefineDynamicModule("IncrementalModule", debugInfo)
2195+
#endif
2196+
// Omit resources in dynamic assemblies, because the module builder is constructed without a filename the module
2197+
// is tagged as transient and as such DefineManifestResource will throw an invalid operation if resources are present.
2198+
//
2199+
// Also, the dynamic assembly creator can't currently handle types called "<Module>" from statically linked assemblies.
2200+
let ilxMainModule =
2201+
{ ilxMainModule with
2202+
TypeDefs = ilxMainModule.TypeDefs.AsList |> List.filter (fun td -> not (isTypeNameForGlobalFunctions td.Name)) |> mkILTypeDefs
2203+
Resources=mkILResources [] }
2204+
2205+
// The function used to resolve typees while emitting the code
2206+
let assemblyResolver s =
2207+
match tcImportsRef.Value.Value.TryFindExistingFullyQualifiedPathFromAssemblyRef s with
2208+
| Some res -> Some (Choice1Of2 res)
2209+
| None -> None
2210+
2211+
// Emit the code
2212+
let _emEnv,execs = ILRuntimeWriter.emitModuleFragment(ilGlobals, ILRuntimeWriter.emEnv0, assemblyBuilder, moduleBuilder, ilxMainModule, debugInfo, assemblyResolver)
2213+
2214+
// Execute the top-level initialization, if requested
2215+
if execute.IsSome then
2216+
for exec in execs do
2217+
match exec() with
2218+
| None -> ()
2219+
| Some exn -> raise exn
2220+
2221+
// Register the reflected definitions for the dynamically generated assembly
2222+
for resource in ilxMainModule.Resources.AsList do
2223+
if IsReflectedDefinitionsResource resource then
2224+
Quotations.Expr.RegisterReflectedDefinitions(assemblyBuilder, moduleBuilder.Name, resource.Bytes)
2225+
2226+
// Save the result
2227+
assemblyBuilderRef := Some assemblyBuilder
2228+
2229+
let setOutputStreams execute =
2230+
// Set the output streams, if requested
2231+
match execute with
2232+
| Some (writer,error) ->
2233+
System.Console.SetOut writer
2234+
System.Console.SetError error
2235+
| None -> ()
2236+
21262237

21272238
// There is only one instance of this type, held in FSharpChecker
21282239
type BackgroundCompiler(projectCacheSize, keepAssemblyContents, keepAllBackgroundResolutions) as self =
@@ -2697,6 +2808,66 @@ type FSharpChecker(projectCacheSize, keepAssemblyContents, keepAllBackgroundReso
26972808

26982809
member ic.TryGetRecentTypeCheckResultsForFile(filename, options, ?source) = ic.TryGetRecentCheckResultsForFile(filename,options,?source=source)
26992810

2811+
member ic.Compile(argv: string[]) =
2812+
CompileHelpers.compileFromArgs (argv, None, None)
2813+
2814+
member ic.Compile (ast:ParsedInput list, assemblyName:string, outFile:string, dependencies:string list, ?pdbFile:string, ?executable:bool, ?noframework:bool) =
2815+
let noframework = defaultArg noframework false
2816+
CompileHelpers.compileFromAsts (ast, assemblyName, outFile, dependencies, noframework, pdbFile, executable, None, None)
2817+
2818+
member ic.CompileToDynamicAssembly (otherFlags: string[], execute: (TextWriter * TextWriter) option) =
2819+
CompileHelpers.setOutputStreams execute
2820+
2821+
// References used to capture the results of compilation
2822+
let tcImportsRef = ref (None: TcImports option)
2823+
let assemblyBuilderRef = ref None
2824+
let tcImportsCapture = Some (fun tcImports -> tcImportsRef := Some tcImports)
2825+
2826+
// Function to generate and store the results of compilation
2827+
let debugInfo = otherFlags |> Array.exists (fun arg -> arg = "-g" || arg = "--debug:+" || arg = "/debug:+")
2828+
let dynamicAssemblyCreator = Some (CompileHelpers.dynamicAssemblyCreator (debugInfo, tcImportsRef, execute, assemblyBuilderRef))
2829+
2830+
// Perform the compilation, given the above capturing function.
2831+
let errorsAndWarnings, result = CompileHelpers.compileFromArgs (otherFlags, tcImportsCapture, dynamicAssemblyCreator)
2832+
2833+
// Retrieve and return the results
2834+
let assemblyOpt =
2835+
match assemblyBuilderRef.Value with
2836+
| None -> None
2837+
| Some a -> Some (a :> System.Reflection.Assembly)
2838+
2839+
errorsAndWarnings, result, assemblyOpt
2840+
2841+
member ic.CompileToDynamicAssembly (asts:ParsedInput list, assemblyName:string, dependencies:string list, execute: (TextWriter * TextWriter) option, ?debug:bool, ?noframework:bool) =
2842+
CompileHelpers.setOutputStreams execute
2843+
2844+
// References used to capture the results of compilation
2845+
let tcImportsRef = ref (None: TcImports option)
2846+
let assemblyBuilderRef = ref None
2847+
let tcImportsCapture = Some (fun tcImports -> tcImportsRef := Some tcImports)
2848+
2849+
let debugInfo = defaultArg debug false
2850+
let noframework = defaultArg noframework false
2851+
let location = Path.Combine(Path.GetTempPath(),"test"+string(hash assemblyName))
2852+
try Directory.CreateDirectory(location) |> ignore with _ -> ()
2853+
2854+
let outFile = Path.Combine(location, assemblyName + ".dll")
2855+
2856+
// Function to generate and store the results of compilation
2857+
let dynamicAssemblyCreator = Some (CompileHelpers.dynamicAssemblyCreator (debugInfo, tcImportsRef, execute, assemblyBuilderRef))
2858+
2859+
// Perform the compilation, given the above capturing function.
2860+
let errorsAndWarnings, result =
2861+
CompileHelpers.compileFromAsts (asts, assemblyName, outFile, dependencies, noframework, None, Some execute.IsSome, tcImportsCapture, dynamicAssemblyCreator)
2862+
2863+
// Retrieve and return the results
2864+
let assemblyOpt =
2865+
match assemblyBuilderRef.Value with
2866+
| None -> None
2867+
| Some a -> Some (a :> System.Reflection.Assembly)
2868+
2869+
errorsAndWarnings, result, assemblyOpt
2870+
27002871
/// This function is called when the entire environment is known to have changed for reasons not encoded in the ProjectOptions of any project/compilation.
27012872
/// For example, the type provider approvals file may have changed.
27022873
member ic.InvalidateAll() =

0 commit comments

Comments
 (0)