-
Notifications
You must be signed in to change notification settings - Fork 323
Expand file tree
/
Copy pathCompiler.fs
More file actions
252 lines (208 loc) · 9.21 KB
/
Compiler.fs
File metadata and controls
252 lines (208 loc) · 9.21 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
namespace Fable
module Literals =
[<Literal>]
let VERSION = "5.1.0"
[<Literal>]
let JS_LIBRARY_VERSION = "2.1.0"
type CompilerOptionsHelper =
static member Make
(
?language,
?typedArrays,
?define,
?debugMode,
?optimizeFSharpAst,
?verbosity,
?fileExtension,
?clampByteArrays,
?noReflection
)
=
{
CompilerOptions.Define = defaultArg define []
DebugMode = defaultArg debugMode true
Language = defaultArg language JavaScript
FileExtension = defaultArg fileExtension ".fs.js"
TypedArrays = defaultArg typedArrays true
OptimizeFSharpAst = defaultArg optimizeFSharpAst false
Verbosity = defaultArg verbosity Verbosity.Normal
ClampByteArrays = defaultArg clampByteArrays false
NoReflection = defaultArg noReflection false
TriggeredByDependency = false
}
[<RequireQualifiedAccess>]
type Severity =
| Warning
| Error
| Info
[<RequireQualifiedAccess>]
type OutputType =
| Library
| Exe
open FSharp.Compiler.CodeAnalysis
open FSharp.Compiler.Symbols
open Fable.AST
type InlineExpr =
{
Args: Fable.Ident list
Body: Fable.Expr
FileName: string
GenericArgs: string list
ScopeIdents: Set<string>
}
type CompilerPlugins = { MemberDeclarationPlugins: Map<Fable.EntityRef, System.Type> }
type SourceReader = string -> int * Lazy<string>
type Compiler =
abstract LibraryDir: string
abstract CurrentFile: string
abstract OutputDir: string option
abstract OutputType: OutputType
abstract ProjectFile: string
abstract ProjectOptions: FSharpProjectOptions
abstract SourceFiles: string[]
abstract Options: CompilerOptions
abstract Plugins: CompilerPlugins
abstract IncrementCounter: unit -> int
abstract IsPrecompilingInlineFunction: bool
abstract WillPrecompileInlineFunction: file: string -> Compiler
abstract GetImplementationFile: fileName: string -> FSharpImplementationFileDeclaration list
abstract GetRootModule: fileName: string -> string * FSharpXmlDoc option
abstract TryGetEntity: Fable.EntityRef -> Fable.Entity option
abstract GetInlineExpr: string -> InlineExpr
abstract AddWatchDependency: file: string -> unit
abstract AddLog:
msg: string * severity: Severity * ?range: SourceLocation * ?fileName: string * ?tag: string -> unit
type InlineExprLazy(f: Compiler -> InlineExpr) =
let mutable value: InlineExpr voption = ValueNone
member this.Calculate(com: Compiler) =
lock this
<| fun () ->
match value with
| ValueSome v -> v
| ValueNone ->
let v = f com
value <- ValueSome v
v
[<AutoOpen>]
module CompilerExt =
let expectedVersionMatchesActual (expected: string) (actual: string) =
try
let r = System.Text.RegularExpressions.Regex(@"^(\d+)\.(\d+)(?:\.(\d+))?")
let parse v =
let m = r.Match(v)
int m.Groups[1].Value,
int m.Groups[2].Value,
if m.Groups[3].Success then
int m.Groups[3].Value
else
0
let actualMajor, actualMinor, actualPatch = parse actual
let expectedMajor, expectedMinor, expectedPatch = parse expected
actualMajor > expectedMajor
|| (actualMajor = expectedMajor
&& (actualMinor > expectedMinor
|| (actualMinor = expectedMinor && actualPatch >= expectedPatch)))
with _ ->
false
let private coreAssemblyNames = set Metadata.coreAssemblies
let mutable private _language = JavaScript
let mutable private _checkNulls = false
type Compiler with
static member CoreAssemblyNames = coreAssemblyNames
static member Language = _language
static member CheckNulls = _checkNulls
/// Set these only once at the start of the program
static member SetLanguageUnsafe language = _language <- language
static member SetCheckNullsUnsafe enabled = _checkNulls <- enabled
member com.GetEntity(entityRef: Fable.EntityRef) =
match com.TryGetEntity(entityRef) with
| Some e -> e
| None ->
let category =
match entityRef.Path with
| Fable.CoreAssemblyName _ -> "core"
| Fable.AssemblyPath _ -> "external"
| Fable.PrecompiledLib _ -> "precompiled"
| Fable.SourcePath _ -> "user"
failwith $"Cannot find %s{category} entity %s{entityRef.FullName}"
member com.TryGetMember(memberRef: Fable.MemberRef) : Fable.MemberFunctionOrValue option =
match memberRef with
| Fable.GeneratedMemberRef gen -> Some(gen :> _)
| Fable.MemberRef(declaringEntity, memberInfo) ->
com.TryGetEntity(declaringEntity)
|> Option.bind (fun ent -> ent.TryFindMember(memberInfo))
member com.GetMember(memberRef: Fable.MemberRef) : Fable.MemberFunctionOrValue =
match com.TryGetMember(memberRef) with
| Some e -> e
| None -> failwith $"Cannot find member ref: %A{memberRef}"
member com.ToPluginHelper() =
{ new PluginHelper with
member _.LibraryDir = com.LibraryDir
member _.CurrentFile = com.CurrentFile
member _.OutputDir = com.OutputDir
member _.ProjectFile = com.ProjectFile
member _.SourceFiles = com.SourceFiles
member _.Options = com.Options
member _.GetRootModule(fileName) = com.GetRootModule(fileName) |> fst
member _.GetEntity(ref) = com.GetEntity(ref)
member _.GetMember(ref) = com.GetMember(ref)
member _.LogWarning(msg, r) =
com.AddLog(msg, Severity.Warning, ?range = r, fileName = com.CurrentFile)
member _.LogError(msg, r) =
com.AddLog(msg, Severity.Error, ?range = r, fileName = com.CurrentFile)
member _.GetOutputPath(file) =
let file = Path.ChangeExtension(file, com.Options.FileExtension)
match com.OutputDir with
| None -> file
| Some outDir ->
// TODO: This is a simplified version of the actual mechanism and will not work with deduplicated paths
let projDir = Path.GetDirectoryName(com.ProjectFile)
let relPath = Path.getRelativeFileOrDirPath true projDir false file
let relPath =
if relPath.StartsWith("./", System.StringComparison.Ordinal) then
relPath[2..]
else
relPath
Path.Combine(outDir, relPath)
member this.GetOutputPath() = this.GetOutputPath(com.CurrentFile)
}
member com.ApplyPlugin<'Plugin, 'Input when 'Plugin :> PluginAttribute>
(plugins: Map<_, _>, atts: Fable.Attribute seq, input: 'Input, transform)
=
if Map.isEmpty plugins then
input
else
// Reverse attributes so plugins closer to member/type are applied first
(input, Seq.rev atts)
||> Seq.fold (fun input att ->
match Map.tryFind att.Entity plugins with
| None -> input
| Some plugin ->
let pluginInstance =
System.Activator.CreateInstance(plugin, List.toArray att.ConstructorArgs) :?> 'Plugin
if not (expectedVersionMatchesActual pluginInstance.FableMinimumVersion Literals.VERSION) then
failwithf
"Plugin %s expects v%s but currently running Fable v%s"
plugin.FullName
pluginInstance.FableMinimumVersion
Literals.VERSION
let helper = com.ToPluginHelper()
transform pluginInstance helper input
)
member com.ApplyMemberDeclarationPlugin(file: Fable.File, decl: Fable.MemberDecl) =
match com.TryGetMember(decl.MemberRef) with
| None -> decl
| Some memb ->
com.ApplyPlugin<MemberDeclarationPluginAttribute, _>(
com.Plugins.MemberDeclarationPlugins,
memb.Attributes,
decl,
fun p h i -> p.Transform(h, file, i)
)
member com.ApplyMemberCallPlugin(memb: Fable.MemberFunctionOrValue, expr: Fable.Expr) =
com.ApplyPlugin<MemberDeclarationPluginAttribute, _>(
com.Plugins.MemberDeclarationPlugins,
memb.Attributes,
expr,
fun p h e -> p.TransformCall(h, memb, e)
)