Skip to content

Commit c9f7643

Browse files
committed
[WIP] Fable JS support
1 parent aa69bc0 commit c9f7643

26 files changed

+1385
-183
lines changed

src/fable/Fable.Client.Node/Main.fs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -304,7 +304,7 @@ type FileResolver() =
304304
kv.Value |> Seq.map (fun (srcFile, trgFile) ->
305305
// Use GetFullPath to prevent things like "parentDir/./childDir"
306306
// which can cause problems when calculating relative paths
307-
srcFile, Path.GetFullPath <| Path.Combine(outDir, projDir, Path.ChangeExtension(trgFile, ".js"))))
307+
srcFile, Path.GetFullPath <| Path.Combine3(outDir, projDir, Path.ChangeExtension(trgFile, ".js"))))
308308
|> Map
309309

310310
let mergeProjectOpts (opts1: FSharpProjectOptions option, resolver: FileResolver)

src/fable/Fable.Compiler/FSharp2Fable.Util.fs

Lines changed: 18 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,9 @@ namespace Fable.FSharp2Fable
22

33
open System
44
open System.Collections.Generic
5+
#if !FABLE_COMPILER
56
open System.Reflection
7+
#endif
68
open System.Text.RegularExpressions
79
open Microsoft.FSharp.Compiler
810
open Microsoft.FSharp.Compiler.Ast
@@ -11,7 +13,7 @@ open Fable
1113
open Fable.AST
1214
open Fable.AST.Fable.Util
1315

14-
#if DOTNETCORE
16+
#if DOTNETCORE && !FABLE_COMPILER
1517
[<AutoOpen>]
1618
module ReflectionAdapters =
1719
type System.Reflection.Assembly with
@@ -210,7 +212,7 @@ module Helpers =
210212
|> function
211213
| Some name -> name.ConstructorArguments.[0] |> snd |> string
212214
| None -> Naming.lowerFirst unionCase.DisplayName
213-
|> makeConst
215+
|> makeStrConst
214216

215217
let getArgCount (meth: FSharpMemberOrFunctionOrValue) =
216218
let args = meth.CurriedParameterGroups
@@ -519,7 +521,7 @@ module Patterns =
519521
Call(None,_op_Equality,[],[_typeInt],
520522
[ILAsm ("[I_ldlen; AI_conv DT_I4]",[],[_matchValue2])
521523
Const (length,_typeInt2)]),
522-
Const (_falseConst,_typeBool)) -> Some (matchValue, length)
524+
Const (_falseConst,_typeBool)) -> Some (matchValue, length, _typeInt2)
523525
| _ -> None
524526

525527
let (|NumberKind|_|) = function
@@ -553,10 +555,10 @@ module Patterns =
553555
| _ when typ.TypeDefinition.IsEnum -> true
554556
| _ -> false
555557
let rec makeSwitch map matchValue e =
556-
let addCase map (idx: int) (case: obj) =
557-
match Map.tryFind idx map with
558-
| Some cases -> Map.add idx (case::cases) map
559-
| None -> Map.add idx [case] map
558+
// let addCase map (idx: int) (case: obj) =
559+
// match Map.tryFind idx map with
560+
// | Some cases -> Map.add idx (case::cases) map
561+
// | None -> Map.add idx [case] map
560562
match e with
561563
| IfThenElse(Call(None,op_Equality,[],_,[Value var; Const(case,_)]),
562564
DecisionTreeSuccess(idx, []), elseExpr)
@@ -1102,6 +1104,7 @@ module Util =
11021104
|||> Seq.fold2 (fun acc genPar (ResolveGeneric ctx t) -> (genPar.Name, t)::acc)
11031105
|> List.rev
11041106

1107+
#if !FABLE_COMPILER
11051108
let getEmitter =
11061109
// Prevent ReflectionTypeLoadException
11071110
// From http://stackoverflow.com/a/7889272
@@ -1125,6 +1128,7 @@ module Util =
11251128
let typ = getTypes assembly |> Seq.find (fun x ->
11261129
x.AssemblyQualifiedName = tdef.QualifiedName)
11271130
System.Activator.CreateInstance(typ))
1131+
#endif
11281132

11291133
let emittedGenericArguments com (ctx: Context) r meth (typArgs, methTypArgs)
11301134
macro (args: Fable.Expr list) =
@@ -1162,6 +1166,7 @@ module Util =
11621166
let macro, args =
11631167
emittedGenericArguments com ctx r meth (typArgs, methTypArgs) macro args
11641168
Fable.Apply(Fable.Emit(macro) |> Fable.Value, args, Fable.ApplyMeth, typ, r) |> Some
1169+
#if !FABLE_COMPILER
11651170
| (:? FSharpType as emitFsType)::(:? string as emitMethName)::extraArg
11661171
when emitFsType.HasTypeDefinition ->
11671172
try
@@ -1179,6 +1184,7 @@ module Util =
11791184
sprintf "Error when invoking %s.%s"
11801185
emitFsType.TypeDefinition.DisplayName emitMethName
11811186
|> attachRange r |> fun msg -> Exception(msg + ": " + exMsg, ex) |> raise
1187+
#endif
11821188
| _ -> "EmitAttribute must receive a string or Type argument" |> attachRange r |> failwith
11831189
| _ -> None
11841190

@@ -1267,7 +1273,7 @@ module Util =
12671273
let loc = if meth.IsInstanceMember then Fable.InstanceLoc else Fable.StaticLoc
12681274
match ent.TryGetMember(methName, getMemberKind meth, loc, argTypes) with
12691275
| Some m -> m.OverloadName | None -> methName
1270-
let ext = makeGet r Fable.Any typRef (makeConst methName)
1276+
let ext = makeGet r Fable.Any typRef (makeStrConst methName)
12711277
let bind = Fable.Emit("$0.bind($1)($2...)") |> Fable.Value
12721278
Fable.Apply (bind, ext::callee::args, Fable.ApplyMeth, typ, r) |> Some
12731279
| _ -> None
@@ -1324,17 +1330,17 @@ module Util =
13241330
(** *Check if this a getter or setter *)
13251331
match getMemberKind meth with
13261332
| Fable.Getter | Fable.Field ->
1327-
makeGetFrom com ctx r typ callee (makeConst methName)
1333+
makeGetFrom com ctx r typ callee (makeStrConst methName)
13281334
| Fable.Setter ->
1329-
Fable.Set (callee, Some (makeConst methName), args.Head, r)
1335+
Fable.Set (callee, Some (makeStrConst methName), args.Head, r)
13301336
(** *Check if this is an implicit constructor *)
13311337
| Fable.Constructor ->
13321338
Fable.Apply (callee, args, Fable.ApplyCons, typ, r)
13331339
(** *If nothing of the above applies, call the method normally *)
13341340
| Fable.Method as kind ->
13351341
let applyMeth methName =
13361342
// let calleeType = Fable.Function(Some argTypes, typ)
1337-
let m = makeGet r Fable.Any callee (makeConst methName)
1343+
let m = makeGet r Fable.Any callee (makeStrConst methName)
13381344
Fable.Apply(m, args, Fable.ApplyMeth, typ, r)
13391345
if belongsToInterfaceOrImportedEntity meth
13401346
then
@@ -1393,7 +1399,7 @@ module Util =
13931399
// Cases when tryEnclosingEntity returns None are rare (see #237)
13941400
// Let's assume the value belongs to the current enclosing module
13951401
| None -> Fable.DeclaredType(ctx.enclosingEntity, []) |> makeNonGenTypeRef com
1396-
Fable.Apply (typeRef, [makeConst v.CompiledName], Fable.ApplyGet, typ, r)
1402+
Fable.Apply (typeRef, [makeStrConst v.CompiledName], Fable.ApplyGet, typ, r)
13971403

13981404
let makeDelegateFrom (com: IFableCompiler) ctx delegateType fsExpr =
13991405
let ctx = { ctx with isDelegate = true}

src/fable/Fable.Compiler/FSharp2Fable.fs

Lines changed: 36 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
module Fable.FSharp2Fable.Compiler
22

3+
#if !FABLE_COMPILER
34
open System.IO
5+
#endif
46
open System.Collections.Generic
57
open System.Text.RegularExpressions
68

@@ -22,10 +24,10 @@ open Util
2224
let private (|SpecialValue|_|) com ctx = function
2325
| BasicPatterns.ILFieldGet (None, typ, fieldName) as fsExpr when typ.HasTypeDefinition ->
2426
match typ.TypeDefinition.TryFullName, fieldName with
25-
| Some "System.String", "Empty" -> Some (makeConst "")
26-
| Some "System.Guid", "Empty" -> Some (makeConst "00000000-0000-0000-0000-000000000000")
27+
| Some "System.String", "Empty" -> Some (makeStrConst "")
28+
| Some "System.Guid", "Empty" -> Some (makeStrConst "00000000-0000-0000-0000-000000000000")
2729
| Some "System.TimeSpan", "Zero" ->
28-
Fable.Wrapped(makeConst 0, makeType com ctx.typeArgs fsExpr.Type) |> Some
30+
Fable.Wrapped(makeIntConst 0, makeType com ctx.typeArgs fsExpr.Type) |> Some
2931
| Some "System.DateTime", "MaxValue"
3032
| Some "System.DateTime", "MinValue" ->
3133
CoreLibCall("Date", Some (Naming.lowerFirst fieldName), false, [])
@@ -132,7 +134,7 @@ and private transformNonListNewUnionCase com ctx (fsExpr: FSharpExpr) fsType uni
132134
| KeyValueUnion ->
133135
let key, value =
134136
match argExprs with
135-
| [] -> lowerCaseName unionCase, makeConst true
137+
| [] -> lowerCaseName unionCase, makeBoolConst true
136138
| [expr] -> lowerCaseName unionCase, expr
137139
| [key; expr] when hasAtt Atts.erase unionCase.Attributes -> key, expr
138140
| _ -> FableError("KeyValue Union Cases must have one or zero fields: " + unionType.FullName, range) |> raise
@@ -144,13 +146,13 @@ and private transformNonListNewUnionCase com ctx (fsExpr: FSharpExpr) fsType uni
144146
| PojoUnion ->
145147
List.zip (Seq.toList unionCase.UnionCaseFields) argExprs
146148
|> List.map (fun (fi, e) -> fi.Name, e)
147-
|> List.append ["type", makeConst unionCase.Name]
149+
|> List.append ["type", makeStrConst unionCase.Name]
148150
|> makeJsObject (Some range)
149151
| ListUnion ->
150152
failwithf "transformNonListNewUnionCase must not be used with List %O" range
151153
| OtherType ->
152154
let argExprs = [
153-
makeConst unionCase.Name // Include Tag name in args
155+
makeStrConst unionCase.Name // Include Tag name in args
154156
Fable.Value(Fable.ArrayConst(Fable.ArrayValues argExprs, Fable.Any))
155157
]
156158
buildApplyInfo com ctx (Some range) unionType unionType (unionType.FullName)
@@ -225,14 +227,14 @@ and private transformExprWithRole (role: Role) (com: IFableCompiler) ctx fsExpr
225227

226228
| CreateEvent (callee, eventName, meth, typArgs, methTypArgs, methArgs) ->
227229
let callee, args = com.Transform ctx callee, List.map (com.Transform ctx) methArgs
228-
let callee = Fable.Apply(callee, [makeConst eventName], Fable.ApplyGet, Fable.Any, None)
230+
let callee = Fable.Apply(callee, [makeStrConst eventName], Fable.ApplyGet, Fable.Any, None)
229231
let r, typ = makeRangeFrom fsExpr, makeType com ctx.typeArgs fsExpr.Type
230232
makeCallFrom com ctx r typ meth (typArgs, methTypArgs) (Some callee) args
231233

232-
| CheckArrayLength (Transform com ctx arr, length) ->
234+
| CheckArrayLength (Transform com ctx arr, length, FableType com ctx typ) ->
233235
let r = makeRangeFrom fsExpr
234-
let lengthExpr = Fable.Apply(arr, [makeConst "length"], Fable.ApplyGet, Fable.Number Int32, r)
235-
makeEqOp r [lengthExpr; makeConst length] BinaryEqualStrict
236+
let lengthExpr = Fable.Apply(arr, [makeStrConst "length"], Fable.ApplyGet, Fable.Number Int32, r)
237+
makeEqOp r [lengthExpr; makeTypeConst typ length] BinaryEqualStrict
236238

237239
| PrintFormat (Transform com ctx expr) -> expr
238240

@@ -250,7 +252,7 @@ and private transformExprWithRole (role: Role) (com: IFableCompiler) ctx fsExpr
250252
([record], updatedFields)
251253
||> List.fold (fun acc (FieldName fieldName, e) ->
252254
let r, value = makeRangeFrom e, com.Transform ctx e
253-
let e = Fable.Set(record, Some(makeConst fieldName), value, r)
255+
let e = Fable.Set(record, Some(makeStrConst fieldName), value, r)
254256
e::acc)
255257
Fable.Sequential(assignments, r)
256258

@@ -280,20 +282,8 @@ and private transformExprWithRole (role: Role) (com: IFableCompiler) ctx fsExpr
280282
|> makeLoop (makeRangeFrom fsExpr)
281283

282284
(** Values *)
283-
// Arrays with small data (ushort, byte) won't fit the NewArray pattern
284-
// as they would require too much memory
285-
| BasicPatterns.Const(:? System.Array as arr, typ) ->
286-
let arrExprs = [
287-
for i in 0 .. (arr.GetLength(0) - 1) ->
288-
arr.GetValue(i) |> makeConst
289-
]
290-
match arr.GetType().GetElementType().FullName with
291-
| NumberKind kind -> Fable.Number kind
292-
| _ -> Fable.Any
293-
|> makeArray <| arrExprs
294-
295285
| BasicPatterns.Const(value, FableType com ctx typ) ->
296-
let e = makeConst value
286+
let e = makeTypeConst typ value
297287
if e.Type = typ then e
298288
// Enumerations are compiled as const but they have a different type
299289
else Fable.Wrapped (e, typ)
@@ -457,29 +447,29 @@ and private transformExprWithRole (role: Role) (com: IFableCompiler) ctx fsExpr
457447
| None -> makeType com ctx.typeArgs calleeType
458448
|> makeNonGenTypeRef com
459449
let r, typ = makeRangeFrom fsExpr, makeType com ctx.typeArgs fsExpr.Type
460-
makeGetFrom com ctx r typ callee (makeConst fieldName)
450+
makeGetFrom com ctx r typ callee (makeStrConst fieldName)
461451

462452
| BasicPatterns.TupleGet (_tupleType, tupleElemIndex, Transform com ctx tupleExpr) ->
463453
let r, typ = makeRangeFrom fsExpr, makeType com ctx.typeArgs fsExpr.Type
464-
makeGetFrom com ctx r typ tupleExpr (makeConst tupleElemIndex)
454+
makeGetFrom com ctx r typ tupleExpr (makeIntConst tupleElemIndex)
465455

466456
| BasicPatterns.UnionCaseGet (Transform com ctx unionExpr, fsType, unionCase, FieldName fieldName) ->
467457
let typ, range = makeType com ctx.typeArgs fsExpr.Type, makeRangeFrom fsExpr
468458
match fsType with
469459
| ErasedUnion | OptionUnion ->
470460
Fable.Wrapped(unionExpr, typ)
471461
| ListUnion ->
472-
makeGet range typ unionExpr (Naming.lowerFirst fieldName |> makeConst)
462+
makeGet range typ unionExpr (Naming.lowerFirst fieldName |> makeStrConst)
473463
| PojoUnion ->
474-
makeConst fieldName |> makeGet range typ unionExpr
464+
makeStrConst fieldName |> makeGet range typ unionExpr
475465
| KeyValueUnion ->
476466
FableError("KeyValueUnion types cannot be used in pattern matching", ?range=range) |> raise
477467
| StringEnum ->
478468
FableError("StringEnum types cannot have fields", ?range=range) |> raise
479469
| OtherType ->
480470
let i = unionCase.UnionCaseFields |> Seq.findIndex (fun x -> x.Name = fieldName)
481-
let fields = makeGet range typ unionExpr ("Fields" |> makeConst)
482-
makeGet range typ fields (i |> makeConst)
471+
let fields = makeGet range typ unionExpr ("Fields" |> makeStrConst)
472+
makeGet range typ fields (i |> makeIntConst)
483473

484474
| BasicPatterns.ILFieldSet (callee, typ, fieldName, value) ->
485475
failwithf "Unsupported ILField reference %O: %A" (makeRange fsExpr.Range) fsExpr
@@ -489,11 +479,11 @@ and private transformExprWithRole (role: Role) (com: IFableCompiler) ctx fsExpr
489479
match callee with
490480
| Some (Transform com ctx callee) -> callee
491481
| None -> makeNonGenTypeRef com calleeType
492-
Fable.Set (callee, Some (makeConst fieldName), value, makeRangeFrom fsExpr)
482+
Fable.Set (callee, Some (makeStrConst fieldName), value, makeRangeFrom fsExpr)
493483

494484
| BasicPatterns.UnionCaseTag (Transform com ctx unionExpr, _unionType) ->
495485
let r, typ = makeRangeFrom fsExpr, makeType com ctx.typeArgs fsExpr.Type
496-
makeGetFrom com ctx r typ unionExpr (makeConst "tag")
486+
makeGetFrom com ctx r typ unionExpr (makeStrConst "tag")
497487

498488
| BasicPatterns.UnionCaseSet (Transform com ctx unionExpr, _type, _case, _caseField, _valueExpr) ->
499489
makeRange fsExpr.Range |> failwithf "Unexpected UnionCaseSet %O"
@@ -640,8 +630,8 @@ and private transformExprWithRole (role: Role) (com: IFableCompiler) ctx fsExpr
640630

641631
| BasicPatterns.UnionCaseTest(Transform com ctx unionExpr, fsType, unionCase) ->
642632
let checkCase name =
643-
let left = makeGet None Fable.String unionExpr (makeConst name)
644-
let right = makeConst unionCase.Name
633+
let left = makeGet None Fable.String unionExpr (makeStrConst name)
634+
let right = makeStrConst unionCase.Name
645635
makeBinOp (makeRangeFrom fsExpr) Fable.Boolean [left; right] BinaryEqualStrict
646636
match fsType with
647637
| ErasedUnion ->
@@ -665,7 +655,7 @@ and private transformExprWithRole (role: Role) (com: IFableCompiler) ctx fsExpr
665655
makeBinOp (makeRangeFrom fsExpr) Fable.Boolean [unionExpr; Fable.Value Fable.Null] opKind
666656
| ListUnion ->
667657
let opKind = if unionCase.CompiledName = "Empty" then BinaryEqual else BinaryUnequal
668-
let expr = makeGet None Fable.Any unionExpr (makeConst "tail")
658+
let expr = makeGet None Fable.Any unionExpr (makeStrConst "tail")
669659
makeBinOp (makeRangeFrom fsExpr) Fable.Boolean [expr; Fable.Value Fable.Null] opKind
670660
| StringEnum ->
671661
makeBinOp (makeRangeFrom fsExpr) Fable.Boolean [unionExpr; lowerCaseName unionCase] BinaryEqualStrict
@@ -678,30 +668,31 @@ and private transformExprWithRole (role: Role) (com: IFableCompiler) ctx fsExpr
678668

679669
(** Pattern Matching *)
680670
| Switch(matchValue, cases, defaultCase, decisionTargets) ->
681-
let transformCases assignVar =
671+
let transformCases caseType assignVar =
682672
let transformBody idx =
683673
let body = transformExpr com ctx (snd decisionTargets.[idx])
684674
match assignVar with
685675
| Some assignVar -> Fable.Set(assignVar, None, body, body.Range)
686676
| None -> body
687677
let cases =
688678
cases |> Seq.map (fun kv ->
689-
List.map makeConst kv.Value, transformBody kv.Key)
679+
List.map (makeTypeConst caseType) kv.Value, transformBody kv.Key)
690680
|> Seq.toList
691681
let defaultCase = transformBody defaultCase
692682
cases, defaultCase
693683
let matchValue =
694684
let t = makeType com ctx.typeArgs matchValue.FullType
695685
makeValueFrom com ctx None t UnknownRole matchValue
696686
let r, typ = makeRangeFrom fsExpr, makeType com ctx.typeArgs fsExpr.Type
687+
let caseType = matchValue.Type
697688
match typ with
698689
| Fable.Unit ->
699-
let cases, defaultCase = transformCases None
690+
let cases, defaultCase = transformCases caseType None
700691
Fable.Switch(matchValue, cases, Some defaultCase, typ, r)
701692
| _ ->
702693
let assignVar = com.GetUniqueVar() |> makeIdent
703694
let cases, defaultCase =
704-
Fable.IdentValue assignVar |> Fable.Value |> Some |> transformCases
695+
Fable.IdentValue assignVar |> Fable.Value |> Some |> transformCases caseType
705696
makeSequential r [
706697
Fable.VarDeclaration(assignVar, Fable.Value Fable.Null, true)
707698
Fable.Switch(matchValue, cases, Some defaultCase, typ, r)
@@ -1189,6 +1180,9 @@ let private getProjectMaps (com: ICompiler) (parsedProj: FSharpCheckProjectResul
11891180
// This dictionary must be mutable so `dict` cannot be used
11901181
let dic = Dictionary()
11911182
dic.Add(Naming.current, Map.empty)
1183+
1184+
#if FABLE_COMPILER
1185+
#else
11921186
parsedProj.ProjectContext.GetReferencedAssemblies()
11931187
|> Seq.choose (fun assembly ->
11941188
assembly.FileName |> Option.bind (fun asmPath ->
@@ -1231,6 +1225,7 @@ let private getProjectMaps (com: ICompiler) (parsedProj: FSharpCheckProjectResul
12311225
dic.Add(asmPath, fableMap)
12321226
with _ -> ()
12331227
)
1228+
#endif
12341229
dic
12351230

12361231
let transformFiles (com: ICompiler) (parsedProj: FSharpCheckProjectResults) (projInfo: FSProjectInfo) =
@@ -1239,8 +1234,8 @@ let transformFiles (com: ICompiler) (parsedProj: FSharpCheckProjectResults) (pro
12391234
||> Map.findOrRun (fun () -> getProjectMaps com parsedProj projInfo)
12401235
// Cache for entities and inline expressions
12411236
let entitiesCache = Dictionary<string, Fable.Entity>()
1242-
let inlineExprsCache: Dictionary<string, Dictionary<FSharpMemberOrFunctionOrValue,int> * FSharpExpr> =
1243-
Map.findOrNew "inline" projInfo.Extra
1237+
let newCache = fun () -> Dictionary<string, Dictionary<FSharpMemberOrFunctionOrValue,int> * FSharpExpr>()
1238+
let inlineExprsCache = Map.findOrRun newCache "inline" projInfo.Extra
12441239
// Start transforming files
12451240
let entryFile =
12461241
parsedProj.AssemblyContents.ImplementationFiles

src/fable/Fable.Compiler/Fable.Compiler.fsproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@
4545
</ItemGroup>
4646
<ItemGroup>
4747
<Compile Include="AssemblyInfo.fs" />
48-
<Compile Include="Util.fs" />
48+
<Compile Include="Utils.fs" />
4949
<Compile Include="Replacements.fs" />
5050
<Compile Include="FSharp2Fable.Util.fs" />
5151
<Compile Include="FSharp2Fable.fs" />

0 commit comments

Comments
 (0)