Skip to content

Commit f0bcb6c

Browse files
authored
[JS/TS] Added missing ICollection helpers (#3949)
1 parent ce9be9c commit f0bcb6c

File tree

14 files changed

+1162
-720
lines changed

14 files changed

+1162
-720
lines changed

src/Fable.Cli/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1010
### Fixed
1111

1212
* [Rust] Fixed derived traits mapping (by @ncave)
13+
* [JS/TS] Added missing ICollection helpers (#3914) (by @ncave)
1314

1415
## 4.23.0 - 2024-10-28
1516

src/Fable.Transforms/Replacements.fs

Lines changed: 25 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1790,6 +1790,10 @@ let injectIndexOfArgs com ctx r genArgs args =
17901790

17911791
injectArg com ctx r "Array" "indexOf" genArgs args
17921792

1793+
let copyToArray (com: ICompiler) r t (i: CallInfo) args =
1794+
Helper.LibCall(com, "Util", "copyToArray", t, args, i.SignatureArgTypes, genArgs = i.GenericArgs, ?loc = r)
1795+
|> Some
1796+
17931797
let resizeArrays (com: ICompiler) (ctx: Context) r (t: Type) (i: CallInfo) (thisArg: Expr option) (args: Expr list) =
17941798
match i.CompiledName, thisArg, args with
17951799
| ".ctor", _, [] -> makeResizeArray (getElementType t) [] |> Some
@@ -1803,6 +1807,13 @@ let resizeArrays (com: ICompiler) (ctx: Context) r (t: Type) (i: CallInfo) (this
18031807
|> Some
18041808
| "get_Item", Some ar, [ idx ] -> getExpr r t ar idx |> Some
18051809
| "set_Item", Some ar, [ idx; value ] -> setExpr r ar idx value |> Some
1810+
| "CopyTo", Some ar, [ target ] ->
1811+
let count = getFieldWith r t ar "length"
1812+
copyToArray com r t i [ ar; makeIntConst 0; target; makeIntConst 0; count ]
1813+
| "CopyTo", Some ar, [ target; targetIndex ] ->
1814+
let count = getFieldWith r t ar "length"
1815+
copyToArray com r t i [ ar; makeIntConst 0; target; targetIndex; count ]
1816+
| "CopyTo", Some ar, [ _sourceIndex; _target; _targetIndex; _count ] -> copyToArray com r t i (ar :: args)
18061817
| "Add", Some ar, [ arg ] ->
18071818
"void ($0)"
18081819
|> emitExpr r t [ Helper.InstanceCall(ar, "push", t, [ arg ]) ]
@@ -1942,10 +1953,6 @@ let tuples (com: ICompiler) (ctx: Context) r (t: Type) (i: CallInfo) (thisArg: E
19421953
| "ToTuple", _ -> changeKind false args
19431954
| _ -> None
19441955

1945-
let copyToArray (com: ICompiler) r t (i: CallInfo) args =
1946-
Helper.LibCall(com, "Util", "copyToArray", t, args, i.SignatureArgTypes, genArgs = i.GenericArgs, ?loc = r)
1947-
|> Some
1948-
19491956
let arrays (com: ICompiler) (ctx: Context) r (t: Type) (i: CallInfo) (thisArg: Expr option) (args: Expr list) =
19501957
match i.CompiledName, thisArg, args with
19511958
| "get_Length", Some arg, _ -> getFieldWith r t arg "length" |> Some
@@ -2649,6 +2656,11 @@ let dictionaries (com: ICompiler) (ctx: Context) r t (i: CallInfo) (thisArg: Exp
26492656
makeComparerFromEqualityComparer eqComp
26502657
|> makeDictionaryWithComparer com r t arg
26512658
|> Some
2659+
| [ IEnumerable ], [ arg ] -> makeDictionary com ctx r t arg |> Some
2660+
| [ IEnumerable; IEqualityComparer ], [ arg; eqComp ] ->
2661+
makeComparerFromEqualityComparer eqComp
2662+
|> makeDictionaryWithComparer com r t arg
2663+
|> Some
26522664
| [ IEqualityComparer ], [ eqComp ]
26532665
| [ Number _; IEqualityComparer ], [ _; eqComp ] ->
26542666
makeComparerFromEqualityComparer eqComp
@@ -2680,6 +2692,13 @@ let dictionaries (com: ICompiler) (ctx: Context) r t (i: CallInfo) (thisArg: Exp
26802692
Some c -> Helper.InstanceCall(c, methName, t, args, i.SignatureArgTypes, ?loc = r) |> Some
26812693
| _ -> None
26822694

2695+
let collections (com: ICompiler) (ctx: Context) r (t: Type) (i: CallInfo) (thisArg: Expr option) (args: Expr list) =
2696+
match i.CompiledName, thisArg with
2697+
| ("get_Count" | "get_IsReadOnly" | "Add" | "Remove" | "Clear" | "Contains" | "CopyTo") as meth, Some ar ->
2698+
let meth = Naming.removeGetSetPrefix meth |> Naming.lowerFirst
2699+
Helper.LibCall(com, "CollectionUtil", meth, t, ar :: args, ?loc = r) |> Some
2700+
| _ -> None
2701+
26832702
let conditionalWeakTable (com: ICompiler) (ctx: Context) r t (i: CallInfo) (thisArg: Expr option) (args: Expr list) =
26842703
match i.CompiledName, thisArg with
26852704
| ".ctor", _ ->
@@ -3975,8 +3994,8 @@ let private replacedModules =
39753994
Types.resizeArray, resizeArrays
39763995
"System.Collections.Generic.IList`1", resizeArrays
39773996
"System.Collections.IList", resizeArrays
3978-
Types.icollectionGeneric, resizeArrays
3979-
Types.icollection, resizeArrays
3997+
Types.icollectionGeneric, collections
3998+
Types.icollection, collections
39803999
"System.Collections.Generic.CollectionExtensions", collectionExtensions
39814000
"System.ReadOnlySpan`1", readOnlySpans
39824001
Types.hashset, hashSets
Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
import { equals, isArrayLike } from "./Util.js";
2+
3+
export function count<T>(col: Iterable<T>): number {
4+
if (typeof (col as any)["System.Collections.Generic.ICollection`1.get_Count"] === "function") {
5+
return (col as any)["System.Collections.Generic.ICollection`1.get_Count"](); // collection
6+
} else {
7+
if (isArrayLike(col)) {
8+
return col.length; // resize array
9+
} else {
10+
if (typeof (col as any).size === "number") {
11+
return (col as any).size; // map, set
12+
} else {
13+
let count = 0;
14+
for (const _ of col) {
15+
count++;
16+
}
17+
return count;
18+
}
19+
}
20+
}
21+
}
22+
23+
export function isReadOnly<T>(col: Iterable<T>): boolean {
24+
if (typeof (col as any)["System.Collections.Generic.ICollection`1.get_IsReadOnly"] === "function") {
25+
return (col as any)["System.Collections.Generic.ICollection`1.get_IsReadOnly"](); // collection
26+
} else {
27+
return false;
28+
}
29+
}
30+
31+
export function copyTo<T>(col: Iterable<T>, array: T[], arrayIndex: number) {
32+
if (typeof (col as any)["System.Collections.Generic.ICollection`1.CopyToZ3B4C077E"] === "function") {
33+
(col as any)["System.Collections.Generic.ICollection`1.CopyToZ3B4C077E"](array, arrayIndex); // collection
34+
} else {
35+
let i = arrayIndex;
36+
for (const v of col) {
37+
array[i] = v;
38+
i++;
39+
}
40+
}
41+
}
42+
43+
export function contains<T>(col: Iterable<T>, item: T): boolean {
44+
if (typeof (col as any)["System.Collections.Generic.ICollection`1.Contains2B595"] === "function") {
45+
return (col as any)["System.Collections.Generic.ICollection`1.Contains2B595"](item); // collection
46+
} else {
47+
if (isArrayLike(col)) {
48+
let i = col.findIndex(x => equals(x, item)); // resize array
49+
return i >= 0;
50+
} else {
51+
if (typeof (col as any).has === "function") {
52+
if (typeof (col as any).set === "function" && isArrayLike(item)) {
53+
return (col as any).has(item[0]) && equals((col as any).get(item[0]), item[1]); // map
54+
} else {
55+
return (col as any).has(item); // set
56+
}
57+
} else {
58+
return false; // unknown collection
59+
}
60+
}
61+
}
62+
}
63+
64+
export function add<T>(col: Iterable<T>, item: T): void {
65+
if (typeof (col as any)["System.Collections.Generic.ICollection`1.Add2B595"] === "function") {
66+
return (col as any)["System.Collections.Generic.ICollection`1.Add2B595"](item); // collection
67+
} else {
68+
if (isArrayLike(col)) {
69+
col.push(item); // resize array
70+
} else {
71+
if (typeof (col as any).add === "function") {
72+
return (col as any).add(item); // set
73+
} else {
74+
if (typeof (col as any).has === "function"
75+
&& typeof (col as any).set === "function"
76+
&& isArrayLike(item)) {
77+
if ((col as any).has(item[0]) === false) {
78+
(col as any).set(item[0], item[1]); // map
79+
} else {
80+
throw new Error("An item with the same key has already been added. Key: " + item[0]);
81+
}
82+
} else {
83+
// unknown collection
84+
}
85+
}
86+
}
87+
}
88+
}
89+
90+
export function remove<T>(col: Iterable<T>, item: T): boolean {
91+
if (typeof (col as any)["System.Collections.Generic.ICollection`1.Remove2B595"] === "function") {
92+
return (col as any)["System.Collections.Generic.ICollection`1.Remove2B595"](item); // collection
93+
} else {
94+
if (isArrayLike(col)) {
95+
let i = col.findIndex(x => equals(x, item));
96+
if (i >= 0) {
97+
col.splice(i, 1); // resize array
98+
return true;
99+
} else {
100+
return false;
101+
}
102+
} else {
103+
if (typeof (col as any).delete === "function") {
104+
if (typeof (col as any).set === "function" && isArrayLike(item)) {
105+
if ((col as any).has(item[0]) && equals((col as any).get(item[0]), item[1])) {
106+
return (col as any).delete(item[0]); // map
107+
} else {
108+
return false;
109+
}
110+
} else {
111+
return (col as any).delete(item); // set
112+
}
113+
} else {
114+
return false; // unknown collection
115+
}
116+
}
117+
}
118+
}
119+
120+
export function clear<T>(col: Iterable<T>): void {
121+
if (typeof (col as any)["System.Collections.Generic.ICollection`1.Clear"] === "function") {
122+
return (col as any)["System.Collections.Generic.ICollection`1.Clear"](); // collection
123+
} else {
124+
if (isArrayLike(col)) {
125+
col.splice(0); // resize array
126+
} else {
127+
if (typeof (col as any).clear === "function") {
128+
(col as any).clear(); // map, set
129+
} else {
130+
// unknown collection
131+
}
132+
}
133+
}
134+
}

tests/Dart/src/ArrayTests.fs

Lines changed: 33 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,9 @@
11
module Fable.Tests.Dart.Array
22

3-
open System
43
open Util
54

65
type ParamArrayTest =
7-
static member Add([<ParamArray>] xs: int[]) = Array.sum xs
6+
static member Add([<System.ParamArray>] xs: int[]) = Array.sum xs
87

98
let add (xs: int[]) = ParamArrayTest.Add(xs)
109

@@ -312,7 +311,7 @@ let tests () =
312311
// TODO: Char.IsLetter
313312
// testCase "Array.filter with chars works" <| fun () ->
314313
// let xs = [|'a'; '2'; 'b'; 'c'|]
315-
// let ys = xs |> Array.filter Char.IsLetter
314+
// let ys = xs |> Array.filter System.Char.IsLetter
316315
// ys.Length |> equal 3
317316

318317
testCase "Array.find works" <| fun () ->
@@ -960,16 +959,16 @@ let tests () =
960959
ys :? System.Array |> equal true
961960
zs :? System.Array |> equal false
962961

963-
testCase "Array.Copy works with numeric arrays" <| fun () ->
962+
testCase "System.Array.Copy works with numeric arrays" <| fun () ->
964963
let source = [| 99 |]
965964
let destination = [| 1; 2; 3 |]
966-
Array.Copy(source, 0, destination, 0, 1)
965+
System.Array.Copy(source, 0, destination, 0, 1)
967966
equal [| 99; 2; 3 |] destination
968967

969-
testCase "Array.Copy works with non-numeric arrays" <| fun () ->
968+
testCase "System.Array.Copy works with non-numeric arrays" <| fun () ->
970969
let source = [| "xy"; "xx"; "xyz" |]
971970
let destination = [| "a"; "b"; "c" |]
972-
Array.Copy(source, 1, destination, 1, 2)
971+
System.Array.Copy(source, 1, destination, 1, 2)
973972
equal [| "a"; "xx"; "xyz" |] destination
974973

975974
testCase "Array.splitInto works" <| fun () ->
@@ -1102,3 +1101,30 @@ let tests () =
11021101
throwsAnyError (fun () -> Array.removeManyAt 0 2 [||] |> ignore)
11031102
throwsAnyError (fun () -> Array.removeManyAt -1 2 [|1|] |> ignore)
11041103
throwsAnyError (fun () -> Array.removeManyAt 2 2 [|1|] |> ignore)
1104+
1105+
// testCase "Array.compareWith works" <| fun () -> // See #2961
1106+
// let a = [|1;3|]
1107+
// let b = [|1;2;3|]
1108+
// // compares lengths first, then elements
1109+
// let c1 = a < b
1110+
// let c2 = compare a b
1111+
// // should compare elements first, then lengths
1112+
// let c3 = Array.compareWith compare a b
1113+
// equal c1 true
1114+
// equal c2 -1
1115+
// equal c3 1
1116+
1117+
// testCase "System.Array.Resize works" <| fun () ->
1118+
// let mutable xs = [|1; 2; 3; 4; 5|]
1119+
// System.Array.Resize(&xs, 3)
1120+
// xs |> equal [|1; 2; 3|]
1121+
// System.Array.Resize(&xs, 7)
1122+
// xs |> equal [|1; 2; 3; 0; 0; 0; 0|]
1123+
// System.Array.Resize(&xs, 0)
1124+
// xs |> equal [||]
1125+
// xs <- null
1126+
// System.Array.Resize(&xs, 3)
1127+
// xs |> equal [|0; 0; 0|]
1128+
// xs <- null
1129+
// System.Array.Resize(&xs, 0)
1130+
// xs |> equal [||]

0 commit comments

Comments
 (0)