@@ -247,61 +247,66 @@ module Reflection =
247247 let private ofString s = StringLiteral s :> Expression
248248 let private ofArray babelExprs = ArrayExpression( List.toArray babelExprs) :> Expression
249249
250- let rec private toTypeTester com ctx r = function
251- | Fable.Regex -> Identifier " RegExp" :> Expression
252- | Fable.MetaType -> libValue com ctx " Reflection" " TypeInfo"
253- | Fable.LambdaType _ | Fable.DelegateType _ -> ofString " function"
254- | Fable.AnonymousRecordType _ -> ofString " unknown" // Recognize shape? (it's possible in F#)
255- | Fable.Any -> ofString " any"
256- | Fable.Unit -> ofString " undefined"
257- | Fable.Boolean -> ofString " boolean"
258- | Fable.Char
259- | Fable.String -> ofString " string"
260- | Fable.Number _ -> ofString " number"
261- | Fable.Enum _ -> ofString " number"
262- | Fable.Option t -> ofArray [ ofString " option" ; toTypeTester com ctx r t]
263- | Fable.Array t -> ofArray [ ofString " array" ; toTypeTester com ctx r t]
264- | Fable.List t -> ofArray [ ofString " list" ; toTypeTester com ctx r t]
265- | Fable.Tuple genArgs ->
266- let genArgs = List.map ( toTypeTester com ctx r) genArgs
267- ofArray [ ofString " tuple" ; ofArray genArgs]
268- | Fable.GenericParam name ->
269- sprintf " Cannot resolve generic param %s for type testing, evals to true" name |> addWarning com [] r
270- ofString " any"
271- | Fable.DeclaredType( ent, _) when ent.IsInterface ->
272- " Cannot type test interfaces, evals to false" |> addWarning com [] r
273- ofString " unknown"
274- | Fable.DeclaredType( ent, genArgs) ->
275- match tryJsConstructor com ctx ent with
276- | Some cons ->
277- if not ( List.isEmpty genArgs) then
278- " Generic args are ignored in type testing" |> addWarning com [] r
279- cons
280- | None ->
281- sprintf " Cannot type test %s , evals to false" ent.FullName |> addWarning com [] r
282- ofString " unknown"
250+ let transformTypeTest ( com : IBabelCompiler ) ctx range expr ( typ : Fable.Type ): Expression =
251+ let warnAndEvalToFalse msg =
252+ " Cannot type test (evals to false): " + msg
253+ |> addWarning com [] range
254+ BooleanLiteral false :> Expression
283255
284- let transformTypeTest ( com : IBabelCompiler ) ctx range ( expr' : Fable.Expr ) ( typ : Fable.Type ): Expression =
285- let (| EntityFullName |) ( e : Fable.Entity ) = e.FullName
256+ let jsTypeof ( primitiveType : string ) ( Util.TransformExpr com ctx expr ): Expression =
257+ let typeof = UnaryExpression( UnaryTypeof, expr)
258+ upcast BinaryExpression( BinaryEqualStrict, typeof, StringLiteral primitiveType, ?loc= range)
286259
287- let expr = com.TransformAsExpr( ctx, expr')
288- match typ with
289- // Special cases
290- | Fable.DeclaredType( EntityFullName Types.idisposable, _) ->
291- match expr' with
292- | MaybeCasted( ExprType( Fable.DeclaredType( ent2, _))) when FSharp2Fable.Util.hasInterface Types.idisposable ent2 ->
293- upcast BooleanLiteral true
294- | _ -> libCall com ctx None " Util" " isDisposable" [| expr|]
295- | Fable.DeclaredType( EntityFullName Types.ienumerable, _) ->
296- libCall com ctx None " Util" " isIterable" [| expr|]
297- | Fable.DeclaredType( EntityFullName Types.array, _) -> // Untyped array
298- libCall com ctx None " Util" " isArrayLike" [| expr|]
299- | Fable.DeclaredType( EntityFullName Types.exception_, _) ->
300- libCall com ctx None " Types" " isException" [| expr|]
301- | _ ->
302- let typeTester = toTypeTester com ctx range typ
303- libCall com ctx range " Reflection" " typeTest" [| expr; typeTester|]
260+ let jsInstanceof consExpr ( Util.TransformExpr com ctx expr ): Expression =
261+ upcast BinaryExpression( BinaryInstanceOf, expr, consExpr, ?loc= range)
304262
263+ match typ with
264+ | Fable.Any -> upcast BooleanLiteral true
265+ | Fable.Unit -> upcast BinaryExpression( BinaryEqual, com.TransformAsExpr( ctx, expr), Util.undefined None, ?loc= range)
266+ | Fable.Boolean -> jsTypeof " boolean" expr
267+ | Fable.Char | Fable.String _ -> jsTypeof " string" expr
268+ | Fable.Number _ | Fable.Enum _ -> jsTypeof " number" expr
269+ | Fable.Regex -> jsInstanceof ( Identifier " RegExp" ) expr
270+ | Fable.LambdaType _ | Fable.DelegateType _ -> jsTypeof " function" expr
271+ | Fable.Array _ | Fable.Tuple _ ->
272+ libCall com ctx None " Util" " isArrayLike" [| com.TransformAsExpr( ctx, expr)|]
273+ | Fable.List _ ->
274+ jsInstanceof ( libValue com ctx " Types" " List" ) expr
275+ | Fable.AnonymousRecordType _ ->
276+ warnAndEvalToFalse " anonymous records"
277+ | Fable.MetaType ->
278+ jsInstanceof ( libValue com ctx " Reflection" " TypeInfo" ) expr
279+ | Fable.Option _ -> warnAndEvalToFalse " options" // TODO
280+ | Fable.GenericParam _ -> warnAndEvalToFalse " generic parameters"
281+ | Fable.DeclaredType ( ent, genArgs) ->
282+ match ent.FullName with
283+ | Types.idisposable ->
284+ match expr with
285+ | MaybeCasted( ExprType( Fable.DeclaredType ( ent2, _)))
286+ when FSharp2Fable.Util.hasInterface Types.idisposable ent2 ->
287+ upcast BooleanLiteral true
288+ | _ -> libCall com ctx None " Util" " isDisposable" [| com.TransformAsExpr( ctx, expr)|]
289+ | Types.ienumerable ->
290+ [| com.TransformAsExpr( ctx, expr)|]
291+ |> libCall com ctx None " Util" " isIterable"
292+ | Types.array ->
293+ [| com.TransformAsExpr( ctx, expr)|]
294+ |> libCall com ctx None " Util" " isArrayLike"
295+ | Types.exception_ ->
296+ [| com.TransformAsExpr( ctx, expr)|]
297+ |> libCall com ctx None " Types" " isException"
298+ | _ when ent.IsInterface ->
299+ warnAndEvalToFalse " interfaces"
300+ | _ ->
301+ match tryJsConstructor com ctx ent with
302+ | Some cons ->
303+ // TODO: Emit warning only once per file?
304+ if not ( List.isEmpty genArgs) then
305+ " Generic args are ignored in type testing"
306+ |> addWarning com [] range
307+ jsInstanceof cons expr
308+ | None ->
309+ warnAndEvalToFalse ent.FullName
305310
306311// TODO: I'm trying to tell apart the code to generate annotations, but it's not a very clear distinction
307312// as there are many dependencies from/to the Util module below
@@ -1119,20 +1124,19 @@ module Util =
11191124 [| TryStatement( transformBlock com ctx returnStrategy body,
11201125 ?handler= handler, ?finalizer= finalizer, ?loc= r) :> Statement|]
11211126
1122- // Even if IfStatement doesn't enforce it, compile both branches as blocks
1123- // to prevent conflict (e.g. `then` doesn't become a block while `else` does)
1124- let rec transformIfStatement ( com : IBabelCompiler ) ctx r ret ( guardExpr : Expression ) thenStmnt elseStmnt =
1125- let thenStmnt = transformBlock com ctx ret thenStmnt
1126- match elseStmnt: Fable.Expr with
1127- | Fable.IfThenElse( TransformExpr com ctx guardExpr', thenStmnt', elseStmnt', r2) ->
1128- let elseStmnt = transformIfStatement com ctx r2 ret guardExpr' thenStmnt' elseStmnt'
1129- IfStatement( guardExpr, thenStmnt, elseStmnt, ?loc= r)
1130- | expr ->
1131- match com.TransformAsStatements( ctx, ret, expr) with
1132- | [||] -> IfStatement( guardExpr, thenStmnt, ?loc= r)
1133- | [|:? ExpressionStatement as e|] when ( e.Expression :? NullLiteral) ->
1134- IfStatement( guardExpr, thenStmnt, ?loc= r)
1135- | statements -> IfStatement( guardExpr, thenStmnt, BlockStatement statements, ?loc= r)
1127+ let rec transformIfStatement ( com : IBabelCompiler ) ctx r ret guardExpr thenStmnt elseStmnt =
1128+ match com.TransformAsExpr( ctx, guardExpr) with
1129+ | :? BooleanLiteral as b when b.Value ->
1130+ com.TransformAsStatements( ctx, ret, thenStmnt)
1131+ | :? BooleanLiteral as b when not b.Value ->
1132+ com.TransformAsStatements( ctx, ret, elseStmnt)
1133+ | guardExpr ->
1134+ let thenStmnt = transformBlock com ctx ret thenStmnt
1135+ match com.TransformAsStatements( ctx, ret, elseStmnt) with
1136+ | [||] -> IfStatement( guardExpr, thenStmnt, ?loc= r) :> Statement
1137+ | [| elseStmnt|] -> IfStatement( guardExpr, thenStmnt, elseStmnt, ?loc= r) :> Statement
1138+ | statements -> IfStatement( guardExpr, thenStmnt, BlockStatement statements, ?loc= r) :> Statement
1139+ |> Array.singleton
11361140
11371141 let transformGet ( com : IBabelCompiler ) ctx range typ fableExpr ( getKind : Fable.GetKind ) =
11381142 match getKind with
@@ -1579,8 +1583,6 @@ module Util =
15791583 | Some( Fable.FieldKey fi) -> get None expr fi.Name |> Assign
15801584 com.TransformAsStatements( ctx, Some ret, value)
15811585
1582- // Even if IfStatement doesn't enforce it, compile both branches as blocks
1583- // to prevent conflicts (e.g. `then` doesn't become a block while `else` does)
15841586 | Fable.IfThenElse( guardExpr, thenExpr, elseExpr, r) ->
15851587 let asStatement =
15861588 match returnStrategy with
@@ -1591,10 +1593,7 @@ module Util =
15911593 Option.isSome ctx.TailCallOpportunity
15921594 || ( isJsStatement ctx false thenExpr) || ( isJsStatement ctx false elseExpr)
15931595 if asStatement then
1594- match com.TransformAsExpr( ctx, guardExpr) with
1595- // In some situations (like some type tests) the condition may be always true
1596- | :? BooleanLiteral as e when e.Value -> com.TransformAsStatements( ctx, returnStrategy, thenExpr)
1597- | guardExpr -> [| transformIfStatement com ctx r returnStrategy guardExpr thenExpr elseExpr :> Statement|]
1596+ transformIfStatement com ctx r returnStrategy guardExpr thenExpr elseExpr
15981597 else
15991598 let guardExpr ' = transformAsExpr com ctx guardExpr
16001599 let thenExpr ' = transformAsExpr com ctx thenExpr
0 commit comments