csharpfftfsharpintegrationinterpolationlinear-algebramathdifferentiationmatrixnumericsrandomregressionstatisticsmathnet
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
292 lines
14 KiB
292 lines
14 KiB
#nowarn "62" // This construct is for compatibility with OCaml.
|
|
|
|
//-------------------------------------------------------------------------------------------------
|
|
// Internal members
|
|
|
|
namespace Microsoft.FSharp.Compiler.CodeDom.Internal
|
|
|
|
open System
|
|
open System.IO
|
|
open System.Text
|
|
open System.Collections
|
|
open System.CodeDom
|
|
open System.CodeDom.Compiler
|
|
|
|
open Microsoft.FSharp.Compiler.CodeDom.Internal
|
|
module AspNetUtils =
|
|
|
|
/// Preprocessing of the CodeDom compile unit from ASP.NET
|
|
let aspNetPreProcessCompileUnit (c:CodeCompileUnit) =
|
|
|
|
// Remove partial calsses from ASP.NET generated code (we can leave empty namespaces)
|
|
// F# doesn't support partial classes, so we need to remove generated 'second' part of the
|
|
// handwritten class that contains fields for all controls - these have to be written
|
|
// manually in the handwritten code...
|
|
(* if false then *)
|
|
begin
|
|
for ns in c.Namespaces do
|
|
// Phase 1. Find classes to remove
|
|
let toRemove =
|
|
ns.Types
|
|
|> Seq.cast
|
|
|> Seq.filter (fun (t:CodeTypeDeclaration) -> t.IsPartial )
|
|
|> Seq.to_list
|
|
// Phase 2. remove (destructive).
|
|
toRemove |> List.iter ns.Types.Remove;
|
|
end;
|
|
|
|
|
|
// Fix one very specific bug in ASP.NET generated CodeDom tree.
|
|
// In one case it generates "<null>.SetStringResourcePointer" instead of
|
|
// "this.SetStringResourcePointer" (Reported here:
|
|
// http://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=164512)
|
|
for ns in c.Namespaces do
|
|
for t in ns.Types do
|
|
for m in t.Members do
|
|
match m with
|
|
| :? CodeMemberMethod as meth ->
|
|
if (meth.Name = "FrameworkInitialize") then
|
|
for smt in meth.Statements do
|
|
match smt with
|
|
| :? CodeExpressionStatement as stm ->
|
|
match stm.Expression with
|
|
| :? CodeMethodInvokeExpression as inv ->
|
|
if ((inv.Method.TargetObject = null) && (inv.Method.MethodName = "SetStringResourcePointer")) then
|
|
inv.Method.TargetObject <- new CodeThisReferenceExpression();
|
|
| _ -> ();
|
|
| _ -> ();
|
|
else ();
|
|
| _ -> ();
|
|
|
|
|
|
// Sorts ASP.NET source files
|
|
// ASP.NET compiler sends three kinds of files
|
|
// - CodeDOM generated file from ASPX file
|
|
// - handwritten file (codebehind for the previous)
|
|
// - fast factory for creating the first one
|
|
//
|
|
// We need to send them to compiler in following order:
|
|
// - handwritten, aspx, handwritten, aspx, ..., factory
|
|
//
|
|
// File name is: "Something.num.fs" where num is an index, but the array isn't
|
|
// sorted so we sort them using the index. Then we find all ASPX and swap
|
|
// ASPX and handwritten file. Factory should be the last file.
|
|
|
|
|
|
/// Does the file contain the given text?
|
|
let fileContains (text:string) (filename:string) =
|
|
try
|
|
use fs = new IO.StreamReader(filename)
|
|
let rec testLine () =
|
|
let line = fs.ReadLine()
|
|
(line <> null) && (line.IndexOf(text) <> -1 or testLine())
|
|
testLine()
|
|
with _ -> false
|
|
|
|
/// Swap two elements of an array
|
|
let swap (arr:'a[]) i j =
|
|
let tmp = arr.[j]
|
|
arr.[j] <- arr.[i]
|
|
arr.[i] <- tmp
|
|
|
|
|
|
/// Compiled forms of ASPX source files always contain "(__initialized:bool)".
|
|
/// The factory file contains "static member Create_ASP_"
|
|
let isFactoryFile filename =
|
|
fileContains "static member Create_ASP_" filename &&
|
|
fileContains "FastObjectFactory" filename
|
|
|
|
let isGeneratedFromAspxFile filename =
|
|
fileContains "static val mutable __initialized:bool" filename &&
|
|
fileContains "<autogenerated>" filename
|
|
|
|
let isUserFile filename =
|
|
not (isGeneratedFromAspxFile filename) &&
|
|
not (isFactoryFile filename)
|
|
|
|
// REVIEW: will "Temporary ASP.NET Files" survive internationalization????
|
|
let indexOfAspNetFile(el:string) =
|
|
let n =
|
|
try
|
|
let bnum = el.LastIndexOf(".", el.Length - 4)
|
|
Int32.Parse(el.Substring(bnum + 1, el.Length - bnum - 4))
|
|
with _ -> -1
|
|
if (el.IndexOf("Temporary ASP.NET Files") <> -1)
|
|
then n
|
|
else -1
|
|
|
|
|
|
let aspNetSortFiles (files:string array) =
|
|
|
|
// Verify that files are from ASP.NET & sort them.
|
|
let aspFiles = files |> Array.map (fun el -> indexOfAspNetFile el, el)
|
|
let allAspNet = aspFiles |> Array.forall (fun (n, a) -> n <> -1)
|
|
|
|
let sortedFiles =
|
|
if (allAspNet) then
|
|
Array.sortInPlaceWith (fun (n1, _) (n2, _) -> n1 - n2) aspFiles
|
|
let filesMod = aspFiles |> Array.map snd
|
|
|
|
//System.Windows.Forms.MessageBox.Show(sprintf "filesMod = %A" filesMod) |> ignore
|
|
// Rearrange files
|
|
// NOTE: ack! surely there's a nicer way to do this!
|
|
let mutable i = 0
|
|
while (i < filesMod.Length) do
|
|
if (isGeneratedFromAspxFile filesMod.[i]
|
|
&& not (isFactoryFile filesMod.[i])
|
|
&& isUserFile filesMod.[i+1])
|
|
then
|
|
swap filesMod i (i+1)
|
|
i <- i + 1
|
|
i <- i + 1
|
|
filesMod
|
|
else
|
|
files
|
|
//System.Windows.Forms.MessageBox.Show(sprintf "sortedFiles = %A" sortedFiles) |> ignore
|
|
sortedFiles
|
|
|
|
// Generic code generator that can be specialized with preprocessing function and additional
|
|
// CodeGenerator configuration options (this is used by ASP.NET generator)
|
|
// preprocCu:
|
|
module FSharpCodeGenerator =
|
|
let Create(preprocCu, addop) =
|
|
|
|
let usingStringWriter f =
|
|
let sb = new StringBuilder()
|
|
use sw = new StringWriter(sb)
|
|
f (sw :> TextWriter);
|
|
let res = sb.ToString();
|
|
res
|
|
|
|
{ new ICodeGenerator with
|
|
// Identifier related functions
|
|
member this.CreateEscapedIdentifier (value:string) : string =
|
|
Generator.makeEscapedIdentifier value
|
|
member this.CreateValidIdentifier (value:string) : string =
|
|
Generator.makeValidIdentifier value
|
|
member this.IsValidIdentifier (value:string) : bool =
|
|
Generator.isValidIdentifier value;
|
|
member this.ValidateIdentifier (value:string) : unit =
|
|
if (not (Generator.isValidIdentifier value)) then
|
|
raise (ArgumentException(sprintf "'%s' is not a valid F# identifier!" value))
|
|
|
|
// Implementations of code generation related functions
|
|
member this.GenerateCodeFromCompileUnit(compileUnit, textWriter, options) =
|
|
(Generator.createContext textWriter options addop)
|
|
|> (Generator.generateCompileUnit compileUnit preprocCu)
|
|
|> ignore
|
|
|
|
member this.GenerateCodeFromExpression(codeExpr, textWriter, options) : unit =
|
|
(Generator.createContext textWriter options addop)
|
|
|> (Generator.generateExpression codeExpr)
|
|
|> ignore
|
|
|
|
member this.GenerateCodeFromNamespace(codeNamespace, textWriter, options) : unit =
|
|
(Generator.createContext textWriter options addop)
|
|
|> (Generator.generateNamespace codeNamespace)
|
|
|> ignore
|
|
|
|
member this.GenerateCodeFromStatement(codeStatement, textWriter, options) : unit =
|
|
(Generator.createContext textWriter options addop)
|
|
|> (Generator.generateStatement codeStatement)
|
|
|> ignore
|
|
|
|
member this.GenerateCodeFromType(codeTypeDecl, textWriter, options) : unit =
|
|
(Generator.createContext textWriter options addop)
|
|
|> (Generator.generateTypeDeclOnly codeTypeDecl)
|
|
|> ignore
|
|
member this.GetTypeOutput (t:CodeTypeReference) : string =
|
|
usingStringWriter (fun sw ->
|
|
(Generator.createContext sw (CodeGeneratorOptions()) addop)
|
|
|> (Generator.generateTypeRef t) |> ignore)
|
|
|
|
member this.Supports (supports:GeneratorSupport) : bool =
|
|
(supports &&& (GeneratorSupport.ReturnTypeAttributes ||| GeneratorSupport.ParameterAttributes |||
|
|
GeneratorSupport.AssemblyAttributes ||| GeneratorSupport.StaticConstructors |||
|
|
GeneratorSupport.NestedTypes ||| GeneratorSupport.EntryPointMethod |||
|
|
GeneratorSupport.GotoStatements ||| GeneratorSupport.MultipleInterfaceMembers |||
|
|
GeneratorSupport.ChainedConstructorArguments
|
|
) = enum 0) }
|
|
|
|
// Generic code compiler - the argument is a function for sorting files that can be used by
|
|
// tool-specific providers if needed (like in case of ASP.NET)
|
|
module FSharpCodeCompiler =
|
|
let Create(sortFiles) =
|
|
{ new ICodeCompiler with
|
|
member this.CompileAssemblyFromDom ((options:CompilerParameters),(compileUnit:CodeCompileUnit)) : CompilerResults =
|
|
this.CompileAssemblyFromDomBatch (options, [|compileUnit|]);
|
|
member this.CompileAssemblyFromSource ((options:CompilerParameters),(source:string)) : CompilerResults =
|
|
this.CompileAssemblyFromSourceBatch (options, [|source|]);
|
|
member this.CompileAssemblyFromFile ((options:CompilerParameters),(fileName:string)) : CompilerResults =
|
|
this.CompileAssemblyFromFileBatch (options, [|fileName|]);
|
|
|
|
member this.CompileAssemblyFromDomBatch ((options:CompilerParameters),(compilationUnits:CodeCompileUnit array)) : CompilerResults =
|
|
let res = new CompilerResults(options.TempFiles);
|
|
let files =
|
|
compilationUnits
|
|
|> Array.map ( fun cu ->
|
|
let fn = res.TempFiles.AddExtension("fs", false)
|
|
use wr = new StreamWriter(fn, false, Encoding.UTF8)
|
|
Generator.createContext wr (CodeGeneratorOptions()) Generator.AdditionalOptions.None
|
|
|> (Generator.generateCompileUnit cu (fun _ -> ())) |> ignore
|
|
fn)
|
|
Compiler.compileAssemblyFromFileBatch options files res sortFiles
|
|
|
|
member this.CompileAssemblyFromSourceBatch ((options:CompilerParameters),(sources:string array)) : CompilerResults =
|
|
let res = new CompilerResults(options.TempFiles);
|
|
let files = sources |> Array.map ( fun src ->
|
|
let fn = res.TempFiles.AddExtension("fs", false)
|
|
use wr = new StreamWriter(fn)
|
|
wr.Write(src)
|
|
fn)
|
|
Compiler.compileAssemblyFromFileBatch options files res sortFiles
|
|
|
|
member this.CompileAssemblyFromFileBatch ((options:CompilerParameters),(fileNames:string array)) : CompilerResults =
|
|
Compiler.compileAssemblyFromFileBatch options fileNames (new CompilerResults(options.TempFiles)) sortFiles
|
|
}
|
|
|
|
|
|
//-------------------------------------------------------------------------------------------------
|
|
// Public types
|
|
|
|
namespace Microsoft.FSharp.Compiler.CodeDom
|
|
|
|
open System
|
|
open System.IO
|
|
open System.Text
|
|
open System.Collections
|
|
open System.CodeDom
|
|
open System.CodeDom.Compiler
|
|
|
|
open Microsoft.FSharp.Compiler.CodeDom.Internal
|
|
|
|
type FSharpCodeProvider() =
|
|
inherit CodeDomProvider()
|
|
override this.FileExtension = "fs";
|
|
|
|
override this.CreateCompiler() =
|
|
FSharpCodeCompiler.Create(AspNetUtils.aspNetSortFiles)
|
|
|
|
override this.CreateGenerator() =
|
|
FSharpCodeGenerator.Create((fun _ -> ()), Generator.AdditionalOptions.None)
|
|
|
|
type FSharpAspNetCodeProvider() =
|
|
inherit CodeDomProvider()
|
|
override this.FileExtension = "fs";
|
|
|
|
override this.CreateCompiler() =
|
|
FSharpCodeCompiler.Create(AspNetUtils.aspNetSortFiles)
|
|
|
|
override this.CreateGenerator() =
|
|
|
|
// If you set the options to "Generator.AdditionalOptions.UnknonwFieldsAsLocals"
|
|
// than the generator will treat all fields that are not declared in the class as locals
|
|
// (and will generated "fld" instead of "this.fld")
|
|
// This will make it possible to use implicit class syntax in ASP.NET, but only when
|
|
// the fields will have non-private visibility (because we need to access them from inherited class
|
|
|
|
// AspNetArrays is workaround for ASP.NET which generates wrong type in array initializers
|
|
// (according to the CodeDOM test suite it should be "int[]" for [| 1; 2 |], but ASP.NET gives us "int" !!
|
|
let opts = Generator.AdditionalOptions.AspNetArrays
|
|
FSharpCodeGenerator.Create(AspNetUtils.aspNetPreProcessCompileUnit, opts)
|
|
|
|
|