Skip to content

Commit d6127fc

Browse files
committed
[WIP] Fable JS support
1 parent 1a794d2 commit d6127fc

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
@@ -212,7 +214,7 @@ module Helpers =
212214
|> function
213215
| Some name -> name.ConstructorArguments.[0] |> snd |> string
214216
| None -> Naming.lowerFirst unionCase.DisplayName
215-
|> makeConst
217+
|> makeStrConst
216218

217219
let getArgCount (meth: FSharpMemberOrFunctionOrValue) =
218220
let args = meth.CurriedParameterGroups
@@ -521,7 +523,7 @@ module Patterns =
521523
Call(None,_op_Equality,[],[_typeInt],
522524
[ILAsm ("[I_ldlen; AI_conv DT_I4]",[],[_matchValue2])
523525
Const (length,_typeInt2)]),
524-
Const (_falseConst,_typeBool)) -> Some (matchValue, length)
526+
Const (_falseConst,_typeBool)) -> Some (matchValue, length, _typeInt2)
525527
| _ -> None
526528

527529
let (|NumberKind|_|) = function
@@ -555,10 +557,10 @@ module Patterns =
555557
| _ when typ.TypeDefinition.IsEnum -> true
556558
| _ -> false
557559
let rec makeSwitch map matchValue e =
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
560+
// let addCase map (idx: int) (case: obj) =
561+
// match Map.tryFind idx map with
562+
// | Some cases -> Map.add idx (case::cases) map
563+
// | None -> Map.add idx [case] map
562564
match e with
563565
| IfThenElse(Call(None,op_Equality,[],_,[Value var; Const(case,_)]),
564566
DecisionTreeSuccess(idx, []), elseExpr)
@@ -1104,6 +1106,7 @@ module Util =
11041106
|||> Seq.fold2 (fun acc genPar (ResolveGeneric ctx t) -> (genPar.Name, t)::acc)
11051107
|> List.rev
11061108

1109+
#if !FABLE_COMPILER
11071110
let getEmitter =
11081111
// Prevent ReflectionTypeLoadException
11091112
// From http://stackoverflow.com/a/7889272
@@ -1127,6 +1130,7 @@ module Util =
11271130
let typ = getTypes assembly |> Seq.find (fun x ->
11281131
x.AssemblyQualifiedName = tdef.QualifiedName)
11291132
System.Activator.CreateInstance(typ))
1133+
#endif
11301134

11311135
let emittedGenericArguments com (ctx: Context) r meth (typArgs, methTypArgs)
11321136
macro (args: Fable.Expr list) =
@@ -1164,6 +1168,7 @@ module Util =
11641168
let macro, args =
11651169
emittedGenericArguments com ctx r meth (typArgs, methTypArgs) macro args
11661170
Fable.Apply(Fable.Emit(macro) |> Fable.Value, args, Fable.ApplyMeth, typ, r) |> Some
1171+
#if !FABLE_COMPILER
11671172
| (:? FSharpType as emitFsType)::(:? string as emitMethName)::extraArg
11681173
when emitFsType.HasTypeDefinition ->
11691174
try
@@ -1181,6 +1186,7 @@ module Util =
11811186
sprintf "Error when invoking %s.%s"
11821187
emitFsType.TypeDefinition.DisplayName emitMethName
11831188
|> attachRange r |> fun msg -> Exception(msg + ": " + exMsg, ex) |> raise
1189+
#endif
11841190
| _ -> "EmitAttribute must receive a string or Type argument" |> attachRange r |> failwith
11851191
| _ -> None
11861192

@@ -1269,7 +1275,7 @@ module Util =
12691275
let loc = if meth.IsInstanceMember then Fable.InstanceLoc else Fable.StaticLoc
12701276
match ent.TryGetMember(methName, getMemberKind meth, loc, argTypes) with
12711277
| Some m -> m.OverloadName | None -> methName
1272-
let ext = makeGet r Fable.Any typRef (makeConst methName)
1278+
let ext = makeGet r Fable.Any typRef (makeStrConst methName)
12731279
let bind = Fable.Emit("$0.bind($1)($2...)") |> Fable.Value
12741280
Fable.Apply (bind, ext::callee::args, Fable.ApplyMeth, typ, r) |> Some
12751281
| _ -> None
@@ -1326,17 +1332,17 @@ module Util =
13261332
(** *Check if this a getter or setter *)
13271333
match getMemberKind meth with
13281334
| Fable.Getter | Fable.Field ->
1329-
makeGetFrom com ctx r typ callee (makeConst methName)
1335+
makeGetFrom com ctx r typ callee (makeStrConst methName)
13301336
| Fable.Setter ->
1331-
Fable.Set (callee, Some (makeConst methName), args.Head, r)
1337+
Fable.Set (callee, Some (makeStrConst methName), args.Head, r)
13321338
(** *Check if this is an implicit constructor *)
13331339
| Fable.Constructor ->
13341340
Fable.Apply (callee, args, Fable.ApplyCons, typ, r)
13351341
(** *If nothing of the above applies, call the method normally *)
13361342
| Fable.Method as kind ->
13371343
let applyMeth methName =
13381344
// let calleeType = Fable.Function(Some argTypes, typ)
1339-
let m = makeGet r Fable.Any callee (makeConst methName)
1345+
let m = makeGet r Fable.Any callee (makeStrConst methName)
13401346
Fable.Apply(m, args, Fable.ApplyMeth, typ, r)
13411347
if belongsToInterfaceOrImportedEntity meth
13421348
then
@@ -1395,7 +1401,7 @@ module Util =
13951401
// Cases when tryEnclosingEntity returns None are rare (see #237)
13961402
// Let's assume the value belongs to the current enclosing module
13971403
| None -> Fable.DeclaredType(ctx.enclosingEntity, []) |> makeNonGenTypeRef com
1398-
Fable.Apply (typeRef, [makeConst v.CompiledName], Fable.ApplyGet, typ, r)
1404+
Fable.Apply (typeRef, [makeStrConst v.CompiledName], Fable.ApplyGet, typ, r)
13991405

14001406
let makeDelegateFrom (com: IFableCompiler) ctx delegateType fsExpr =
14011407
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)