Skip to content

Commit 6a7ac3e

Browse files
committed
Array-based List type
1 parent 4eb00a2 commit 6a7ac3e

File tree

14 files changed

+577
-440
lines changed

14 files changed

+577
-440
lines changed

.vscode/launch.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -95,9 +95,9 @@
9595
"type": "coreclr",
9696
"request": "launch",
9797
"name": "bench-compile (.NET)",
98-
"program": "${workspaceFolder}/src/fable-standalone/test/bench-compiler/bin/Debug/netcoreapp2.1/bench-compiler.dll",
98+
"program": "${workspaceFolder}/src/fable-standalone/test/bench-compiler/bin/Debug/netcoreapp3.1/bench-compiler.dll",
9999
// "args": ["${workspaceRoot}/tests/Main/Fable.Tests.fsproj", "out-tests", "--commonjs"],
100-
"args": ["${workspaceRoot}/../fable-test/fable-test.fsproj", "out-test", "--typescript"],
100+
"args": ["${workspaceRoot}/../fable-test/fable-test.fsproj", "out-test"],
101101
"cwd": "${workspaceFolder}/src/fable-standalone/test/bench-compiler"
102102
},
103103
{

src/Fable.Transforms/FSharp2Fable.Util.fs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1203,6 +1203,10 @@ module Util =
12031203
else
12041204
// Indexed properties keep the get_/set_ prefix and are compiled as methods
12051205
if indexedProp then memb.CompiledName, false, false
1206+
// performance optimization, compile get_Current as instance call instead of a getter
1207+
elif memb.FullName = "System.Collections.IEnumerator.get_Current" ||
1208+
memb.FullName = "System.Collections.Generic.IEnumerator.get_Current"
1209+
then getMemberDisplayName memb, false, false
12061210
else getMemberDisplayName memb, isGetter, isSetter
12071211
if isGetter then
12081212
let t = memb.ReturnParameter.Type |> makeType Map.empty

src/Fable.Transforms/FSharp2Fable.fs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -206,6 +206,11 @@ let private getAttachedMemberInfo com ctx r nonMangledNameConflicts
206206
let name, isGetter, isSetter =
207207
// For indexed properties, keep the get_/set_ prefix and compile as method
208208
if indexedProp then sign.Name, false, false
209+
// performance optimization, compile get_Current as instance call instead of a getter
210+
elif sign.Name = "get_Current" &&
211+
(fullName = Some "System.Collections.IEnumerator" ||
212+
fullName = Some "System.Collections.Generic.IEnumerator`1")
213+
then Naming.removeGetSetPrefix sign.Name, false, false
209214
else Naming.removeGetSetPrefix sign.Name, isGetter, isSetter
210215
// Setters can have same name as getters, assume there will always be a getter
211216
if not isSetter && nonMangledNameConflicts declaringEntityName name then

src/Fable.Transforms/Fable2Babel.fs

Lines changed: 18 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -439,7 +439,7 @@ module Annotation =
439439
makeNativeTypeAnnotation com ctx [genArg] "Array"
440440

441441
let makeListTypeAnnotation com ctx genArg =
442-
makeImportTypeAnnotation com ctx [genArg] "Types" "List"
442+
makeImportTypeAnnotation com ctx [genArg] "List" "List"
443443

444444
let makeUnionTypeAnnotation com ctx genArgs =
445445
List.map (typeAnnotation com ctx) genArgs
@@ -655,12 +655,6 @@ module Util =
655655
| [] -> expr
656656
| m::ms -> get None expr m |> getParts ms
657657

658-
let makeList com ctx headAndTail =
659-
match headAndTail with
660-
| None -> [||]
661-
| Some(TransformExpr com ctx head, TransformExpr com ctx tail) -> [|head; tail|]
662-
|> libConsCall com ctx "Types" "List"
663-
664658
let makeArray (com: IBabelCompiler) ctx exprs =
665659
List.mapToArray (fun e -> com.TransformAsExpr(ctx, e)) exprs
666660
|> ArrayExpression :> Expression
@@ -893,12 +887,17 @@ module Util =
893887
| Fable.NewTuple vals -> makeArray com ctx vals
894888
// Optimization for bundle size: compile list literals as List.ofArray
895889
| Replacements.ListLiteral(exprs, t) ->
896-
match exprs with
897-
| [] -> makeList com ctx None
898-
| [expr] -> Some(expr, Fable.Value(Fable.NewList (None,t), None)) |> makeList com ctx
899-
| exprs -> [|makeArray com ctx exprs|] |> libCall com ctx r "List" "ofArray"
890+
[|List.rev exprs |> makeArray com ctx|]
891+
|> libCall com ctx r "List" "newList"
892+
// match exprs with
893+
// | [] -> libCall com ctx r "List" "empty" [||]
894+
// | [TransformExpr com ctx expr] -> libCall com ctx r "List" "singleton" [|expr|]
895+
// | exprs -> [|makeArray com ctx exprs|] |> libCall com ctx r "List" "ofArray"
900896
| Fable.NewList (headAndTail, _) ->
901-
makeList com ctx headAndTail
897+
match headAndTail with
898+
| None -> libCall com ctx r "List" "empty" [||]
899+
| Some(TransformExpr com ctx head, TransformExpr com ctx tail) ->
900+
libCall com ctx r "List" "cons" [|head; tail|]
902901
| Fable.NewOption (value, t) ->
903902
match value with
904903
| Some (TransformExpr com ctx e) ->
@@ -1086,8 +1085,10 @@ module Util =
10861085
match getKind with
10871086
| Fable.ByKey(Fable.ExprKey(TransformExpr com ctx prop)) -> getExpr range expr prop
10881087
| Fable.ByKey(Fable.FieldKey field) -> get range expr field.Name
1089-
| Fable.ListHead -> get range expr "head"
1090-
| Fable.ListTail -> get range expr "tail"
1088+
// | Fable.ListHead -> get range expr "Head"
1089+
// | Fable.ListTail -> get range expr "Tail"
1090+
| Fable.ListHead -> libCall com ctx range "List" "head" [|expr|]
1091+
| Fable.ListTail -> libCall com ctx range "List" "tail" [|expr|]
10911092
| Fable.TupleIndex index -> getExpr range expr (ofInt index)
10921093
| Fable.OptionValue ->
10931094
if mustWrapOption typ || com.Options.Typescript
@@ -1143,9 +1144,9 @@ module Util =
11431144
let op = if nonEmpty then BinaryUnequal else BinaryEqual
11441145
upcast BinaryExpression(op, com.TransformAsExpr(ctx, expr), NullLiteral(), ?loc=range)
11451146
| Fable.ListTest nonEmpty ->
1146-
let expr = com.TransformAsExpr(ctx, expr)
1147-
let op = if nonEmpty then BinaryUnequal else BinaryEqual
1148-
upcast BinaryExpression(op, get None expr "tail", NullLiteral(), ?loc=range)
1147+
// let expr = get range (com.TransformAsExpr(ctx, expr)) "IsEmpty"
1148+
let expr = libCall com ctx range "List" "isEmpty" [|com.TransformAsExpr(ctx, expr)|]
1149+
if nonEmpty then upcast UnaryExpression(UnaryNot, expr, ?loc=range) else expr
11491150
| Fable.UnionCaseTest tag ->
11501151
let expected = ofInt tag
11511152
let actual = com.TransformAsExpr(ctx, expr) |> getUnionExprTag None

src/Fable.Transforms/Replacements.fs

Lines changed: 17 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1664,6 +1664,8 @@ let resizeArrays (com: ICompiler) (ctx: Context) r (t: Type) (i: CallInfo) (this
16641664
Helper.LibCall(com, "Seq", "filter", t, [arg; ar], ?loc=r) |> toArray com t |> Some
16651665
| "AddRange", Some ar, [arg] ->
16661666
Helper.LibCall(com, "Array", "addRangeInPlace", t, [arg; ar], ?loc=r) |> Some
1667+
| "GetRange", Some ar, [idx; cnt] ->
1668+
Helper.LibCall(com, "Array", "getSubArray", t, [ar; idx; cnt], ?loc=r) |> Some
16671669
| "Contains", Some (MaybeCasted(ar)), [arg] ->
16681670
match ar.Type with
16691671
| Array _ ->
@@ -1771,29 +1773,26 @@ let arrayModule (com: ICompiler) (ctx: Context) r (t: Type) (i: CallInfo) (_: Ex
17711773
Helper.LibCall(com, "Array", meth, t, args, i.SignatureArgTypes, ?loc=r) |> Some
17721774

17731775
let lists (com: ICompiler) (ctx: Context) r (t: Type) (i: CallInfo) (thisArg: Expr option) (args: Expr list) =
1776+
let meth = Naming.removeGetSetPrefix i.CompiledName |> Naming.lowerFirst
17741777
match i.CompiledName, thisArg, args with
1775-
// Use methods for Head and Tail (instead of Get(ListHead) for example) to check for empty lists
1776-
| ReplaceName
1777-
[ "get_Head", "head"
1778-
"get_Tail", "tail"
1779-
"get_Item", "item"
1780-
"get_Length", "length"
1781-
"GetSlice", "slice" ] methName, Some x, _ ->
1782-
let args = match args with [ExprType Unit] -> [x] | args -> args @ [x]
1783-
Helper.LibCall(com, "List", methName, t, args, i.SignatureArgTypes, ?loc=r) |> Some
1784-
| "get_IsEmpty", Some x, _ -> Test(x, ListTest false, r) |> Some
1785-
| "get_Empty", None, _ -> NewList(None, (genArg com ctx r 0 i.GenericArgs)) |> makeValue r |> Some
1786-
| "Cons", None, [h;t] -> NewList(Some(h,t), (genArg com ctx r 0 i.GenericArgs)) |> makeValue r |> Some
1778+
| ("get_Head" | "get_Tail" | "get_IsEmpty" | "get_Length"), Some x, _ ->
1779+
Helper.LibCall(com, "List", meth, t, [x], i.SignatureArgTypes, ?loc=r) |> Some
1780+
// get r t x meth |> Some
1781+
| ("get_Item" | "GetSlice"), Some x, _ ->
1782+
Helper.LibCall(com, "List", meth, t, args @ [x], i.SignatureArgTypes, ?loc=r) |> Some
1783+
| ("get_Empty" | "Cons"), None, _ ->
1784+
Helper.LibCall(com, "List", meth, t, args, i.SignatureArgTypes, ?loc=r) |> Some
17871785
| ("GetHashCode" | "Equals" | "CompareTo"), Some callee, _ ->
17881786
Helper.InstanceCall(callee, i.CompiledName, t, args, i.SignatureArgTypes, ?loc=r) |> Some
17891787
| _ -> None
17901788

17911789
let listModule (com: ICompiler) (ctx: Context) r (t: Type) (i: CallInfo) (_: Expr option) (args: Expr list) =
17921790
match i.CompiledName, args with
1793-
| "IsEmpty", [x] -> Test(x, ListTest false, r) |> Some
1794-
| "Empty", _ -> NewList(None, (genArg com ctx r 0 i.GenericArgs)) |> makeValue r |> Some
1795-
| "Singleton", [x] ->
1796-
NewList(Some(x, Value(NewList(None, t), None)), (genArg com ctx r 0 i.GenericArgs)) |> makeValue r |> Some
1791+
// | ("Head" | "Tail" | "IsEmpty") as meth, [x] -> get r t x (Naming.lowerFirst meth) |> Some
1792+
// | "IsEmpty", [x] -> Test(x, ListTest false, r) |> Some
1793+
// | "Empty", _ -> NewList(None, (genArg com ctx r 0 i.GenericArgs)) |> makeValue r |> Some
1794+
// | "Singleton", [x] ->
1795+
// NewList(Some(x, Value(NewList(None, t), None)), (genArg com ctx r 0 i.GenericArgs)) |> makeValue r |> Some
17971796
// Use a cast to give it better chances of optimization (e.g. converting list
17981797
// literals to arrays) after the beta reduction pass
17991798
| "ToSeq", [x] -> toSeq t x |> Some
@@ -2586,8 +2585,8 @@ let enumerables (com: ICompiler) (ctx: Context) r t (i: CallInfo) (thisArg: Expr
25862585

25872586
let enumerators (com: ICompiler) (ctx: Context) r (t: Type) (i: CallInfo) (thisArg: Expr option) (args: Expr list) =
25882587
match i.CompiledName, thisArg with
2589-
| "get_Current", Some x -> get r t x "Current" |> Some
2590-
| meth, Some x -> Helper.InstanceCall(x, meth, t, args, i.SignatureArgTypes, ?loc=r) |> Some
2588+
// | "get_Current", Some x -> get r t x "Current" |> Some
2589+
| meth, Some x -> Helper.InstanceCall(x, Naming.removeGetSetPrefix meth, t, args, i.SignatureArgTypes, ?loc=r) |> Some
25912590
| _ -> None
25922591

25932592
let events (com: ICompiler) (ctx: Context) r (t: Type) (i: CallInfo) (thisArg: Expr option) (args: Expr list) =

src/fable-library/Array.fs

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -101,8 +101,7 @@ module Helpers =
101101

102102
open Helpers
103103

104-
let private indexNotFound() =
105-
failwith "An index satisfying the predicate was not found in the collection."
104+
let private indexNotFoundMsg = "An index satisfying the predicate was not found in the collection."
106105

107106
// Pay attention when benchmarking to append and filter functions below
108107
// if implementing via native JS array .concat() and .filter() do not fall behind due to js-native transitions.
@@ -445,15 +444,15 @@ let partition (f: 'T -> bool) (source: 'T[]) ([<Inject>] cons: IArrayCons<'T>) =
445444
let find (predicate: 'T -> bool) (array: 'T[]): 'T =
446445
match findImpl predicate array with
447446
| Some res -> res
448-
| None -> indexNotFound()
447+
| None -> failwith indexNotFoundMsg
449448

450449
let tryFind (predicate: 'T -> bool) (array: 'T[]): 'T option =
451450
findImpl predicate array
452451

453452
let findIndex (predicate: 'T -> bool) (array: 'T[]): int =
454453
match findIndexImpl predicate array with
455454
| index when index > -1 -> index
456-
| _ -> indexNotFound()
455+
| _ -> failwith indexNotFoundMsg
457456

458457
let tryFindIndex (predicate: 'T -> bool) (array: 'T[]): int option =
459458
match findIndexImpl predicate array with
@@ -463,7 +462,7 @@ let tryFindIndex (predicate: 'T -> bool) (array: 'T[]): int option =
463462
let pick chooser (array: _[]) =
464463
let rec loop i =
465464
if i >= array.Length then
466-
indexNotFound()
465+
failwith indexNotFoundMsg
467466
else
468467
match chooser array.[i] with
469468
| None -> loop(i+1)
@@ -480,7 +479,7 @@ let tryPick chooser (array: _[]) =
480479

481480
let findBack predicate (array: _[]) =
482481
let rec loop i =
483-
if i < 0 then indexNotFound()
482+
if i < 0 then failwith indexNotFoundMsg
484483
elif predicate array.[i] then array.[i]
485484
else loop (i - 1)
486485
loop (array.Length - 1)
@@ -501,7 +500,7 @@ let findLastIndex predicate (array: _[]) =
501500

502501
let findIndexBack predicate (array: _[]) =
503502
let rec loop i =
504-
if i < 0 then indexNotFound()
503+
if i < 0 then failwith indexNotFoundMsg
505504
elif predicate array.[i] then i
506505
else loop (i - 1)
507506
loop (array.Length - 1)

0 commit comments

Comments
 (0)