@@ -21,7 +21,6 @@ type Import =
2121type ITailCallOpportunity =
2222 abstract Label: string
2323 abstract Args: string list
24- abstract ReplaceArgs: bool
2524 abstract IsRecursiveRef: Fable.Expr -> bool
2625
2726type Context =
@@ -57,27 +56,14 @@ module Util =
5756 | args -> args
5857
5958 type NamedTailCallOpportunity ( com : ICompiler , name , args : Fable.Ident list ) =
60- let getTailCallArgIds ( com : ICompiler ) ( args : Fable.Ident list ) =
61- // If some arguments are functions we need to capture the current values to
62- // prevent delayed references from getting corrupted, for that we use block-scoped
63- // ES2015 variable declarations. See #681
64- let replaceArgs =
65- args |> List.exists ( fun arg ->
66- match arg.Type with
67- | Fable.FunctionType _ -> true
68- | _ -> false )
69- replaceArgs, args |> List.map ( fun arg ->
70- if replaceArgs
71- then com.GetUniqueVar( " arg" )
72- else arg.Name)
73- let replaceArgs , argIds =
74- discardUnitArg args |> getTailCallArgIds com
59+ // Capture the current argument values to prevent delayed references from getting corrupted,
60+ // for that we use block-scoped ES2015 variable declarations. See #681, #1859
61+ let argIds = discardUnitArg args |> List.map ( fun arg -> com.GetUniqueVar( arg.Name))
7562 interface ITailCallOpportunity with
7663 member __.Label = name
7764 member __.Args = argIds
78- member __.ReplaceArgs = replaceArgs
7965 member __.IsRecursiveRef ( e ) =
80- match e with Fable.IdentExpr id -> name = id.Name | _ -> false
66+ match e with Fable.IdentExpr id -> name = id.Name | _ -> false
8167
8268 let prepareBoundThis ( boundThis : string ) ( args : Fable.Ident list ) =
8369 match args with
@@ -963,19 +949,24 @@ module Util =
963949 elif List.sameLength idents values then List.zip idents values
964950 else failwith " Target idents/values lengths differ"
965951
966- let getDecisionTargetAndBindValues ( ctx : Context ) targetIndex boundValues =
952+ let getDecisionTargetAndBindValues ( com : IBabelCompiler ) ( ctx : Context ) targetIndex boundValues =
967953 let idents , target = getDecisionTarget ctx targetIndex
968- let bindings , replacements =
969- (([], Map.empty), matchTargetIdentAndValues idents boundValues)
970- ||> List.fold ( fun ( bindings , replacements ) ( ident , expr ) ->
971- if hasDoubleEvalRisk expr <> DoubleEvalRisk.No // && isReferencedMoreThan 1 ident.Name body
972- then ( ident, expr):: bindings, replacements
973- else bindings, Map.add ident.Name expr replacements)
974- let target = FableTransforms.replaceValues replacements target
975- bindings, target
954+ let identsAndValues = matchTargetIdentAndValues idents boundValues
955+ if not com.Options.debugMode then
956+ let bindings , replacements =
957+ (([], Map.empty), identsAndValues)
958+ ||> List.fold ( fun ( bindings , replacements ) ( ident , expr ) ->
959+ if canHaveSideEffects expr then
960+ ( ident, expr):: bindings, replacements
961+ else
962+ bindings, Map.add ident.Name expr replacements)
963+ let target = FableTransforms.replaceValues replacements target
964+ List.rev bindings, target
965+ else
966+ identsAndValues, target
976967
977968 let transformDecisionTreeSuccessAsExpr ( com : IBabelCompiler ) ( ctx : Context ) targetIndex boundValues =
978- let bindings , target = getDecisionTargetAndBindValues ctx targetIndex boundValues
969+ let bindings , target = getDecisionTargetAndBindValues com ctx targetIndex boundValues
979970 match bindings with
980971 | [] -> com.TransformAsExpr( ctx, target)
981972 | bindings -> com.TransformAsExpr( ctx, Fable.Let( bindings, target))
@@ -991,8 +982,8 @@ module Util =
991982 let targetAssignment = assign None targetId ( ofInt targetIndex) |> ExpressionStatement :> Statement
992983 Array.append [| targetAssignment|] assignments
993984 | ret ->
994- let bindings , target = getDecisionTargetAndBindValues ctx targetIndex boundValues
995- let bindings = bindings |> List.rev |> Seq.collect ( fun ( i , v ) -> transformBindingAsStatements com ctx i v) |> Seq.toArray
985+ let bindings , target = getDecisionTargetAndBindValues com ctx targetIndex boundValues
986+ let bindings = bindings |> Seq.collect ( fun ( i , v ) -> transformBindingAsStatements com ctx i v) |> Seq.toArray
996987 Array.append bindings ( com.TransformAsStatements( ctx, ret, target))
997988
998989 let transformDecisionTreeAsSwitch expr =
@@ -1101,8 +1092,19 @@ module Util =
11011092 ( targets : ( Fable.Ident list * Fable.Expr ) list ) ( treeExpr : Fable.Expr ): Statement [] =
11021093 // If some targets are referenced multiple times, host bound idents,
11031094 // resolve the decision index and compile the targets as a switch
1104- let targetsWithMultiRefs = getTargetsWithMultipleReferences treeExpr
1105- if not ( List.isEmpty targetsWithMultiRefs) then
1095+ match getTargetsWithMultipleReferences treeExpr with
1096+ | [] ->
1097+ let ctx = { ctx with DecisionTargets = targets }
1098+ match transformDecisionTreeAsSwitch treeExpr with
1099+ | Some( evalExpr, cases, ( defaultIndex, defaultBoundValues)) ->
1100+ let t = treeExpr.Type
1101+ let cases = cases |> List.map ( fun ( caseExpr , targetIndex , boundValues ) ->
1102+ [ caseExpr], Fable.DecisionTreeSuccess( targetIndex, boundValues, t))
1103+ let defaultCase = Fable.DecisionTreeSuccess( defaultIndex, defaultBoundValues, t)
1104+ [| transformSwitch com ctx true returnStrategy evalExpr cases ( Some defaultCase)|]
1105+ | None ->
1106+ com.TransformAsStatements( ctx, returnStrategy, treeExpr)
1107+ | targetsWithMultiRefs ->
11061108 // If the bound idents are not referenced in the target, remove them
11071109 let targets =
11081110 targets |> List.map ( fun ( idents , expr ) ->
@@ -1126,17 +1128,6 @@ module Util =
11261128 transformDecisionTreeWithTwoSwitches com ctx returnStrategy targets treeExpr
11271129 else
11281130 transformDecisionTreeWithTwoSwitches com ctx returnStrategy targets treeExpr
1129- else
1130- let ctx = { ctx with DecisionTargets = targets }
1131- match transformDecisionTreeAsSwitch treeExpr with
1132- | Some( evalExpr, cases, ( defaultIndex, defaultBoundValues)) ->
1133- let t = treeExpr.Type
1134- let cases = cases |> List.map ( fun ( caseExpr , targetIndex , boundValues ) ->
1135- [ caseExpr], Fable.DecisionTreeSuccess( targetIndex, boundValues, t))
1136- let defaultCase = Fable.DecisionTreeSuccess( defaultIndex, defaultBoundValues, t)
1137- [| transformSwitch com ctx true returnStrategy evalExpr cases ( Some defaultCase)|]
1138- | None ->
1139- com.TransformAsStatements( ctx, returnStrategy, treeExpr)
11401131
11411132 let rec transformAsExpr ( com : IBabelCompiler ) ctx ( expr : Fable.Expr ): Expression =
11421133 match expr with
@@ -1317,14 +1308,13 @@ module Util =
13171308 let args , body =
13181309 match isTailCallOptimized, tailcallChance, body with
13191310 | true , Some tc, U2.Case1 body ->
1311+ // Replace args, see NamedTailCallOpportunity constructor
13201312 let args , body =
1321- if tc.ReplaceArgs then
1322- let varDeclaration =
1323- List.zip args tc.Args |> List.map ( fun ( arg , tempVar ) ->
1324- arg.Name, Some( Identifier tempVar :> Expression))
1325- |> multiVarDeclaration Const
1326- tc.Args |> List.map Identifier, BlockStatement( Array.append [| varDeclaration|] body.Body)
1327- else args, body
1313+ let varDeclaration =
1314+ List.zip args tc.Args |> List.map ( fun ( arg , tempVar ) ->
1315+ arg.Name, Some( Identifier tempVar :> Expression))
1316+ |> multiVarDeclaration Const
1317+ tc.Args |> List.map Identifier, BlockStatement( Array.append [| varDeclaration|] body.Body)
13281318 // Make sure we don't get trapped in an infinite loop, see #1624
13291319 let body = BlockStatement( Array.append body.Body [| BreakStatement()|])
13301320 args, LabeledStatement( Identifier tc.Label, WhileStatement( BooleanLiteral true , body))
0 commit comments