Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[JS/TS] Added missing IReadOnlyCollection helpers #3953

Merged
merged 1 commit into from
Nov 9, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/Fable.Cli/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
* [Rust] Updated string comparisons (by @ncave)
* [Rust] Fixed derived traits mapping (by @ncave)
* [JS/TS] Added missing ICollection helpers (#3914) (by @ncave)
* [JS/TS] Added missing IReadOnlyCollection helpers (by @ncave)

## 4.23.0 - 2024-10-28

Expand Down
6 changes: 4 additions & 2 deletions src/Fable.Transforms/Replacements.fs
Original file line number Diff line number Diff line change
Expand Up @@ -2697,6 +2697,7 @@ let collections (com: ICompiler) (ctx: Context) r (t: Type) (i: CallInfo) (thisA
| ("get_Count" | "get_IsReadOnly" | "Add" | "Remove" | "Clear" | "Contains" | "CopyTo") as meth, Some ar ->
let meth = Naming.removeGetSetPrefix meth |> Naming.lowerFirst
Helper.LibCall(com, "CollectionUtil", meth, t, ar :: args, ?loc = r) |> Some
| "GetEnumerator", Some callee -> getEnumerator com r t callee |> Some
| _ -> None

let conditionalWeakTable (com: ICompiler) (ctx: Context) r t (i: CallInfo) (thisArg: Expr option) (args: Expr list) =
Expand Down Expand Up @@ -3983,8 +3984,6 @@ let private replacedModules =
Types.conditionalWeakTable, conditionalWeakTable
Types.ienumerableGeneric, enumerables
Types.ienumerable, enumerables
Types.valueCollection, enumerables
Types.keyCollection, enumerables
"System.Collections.Generic.Dictionary`2.Enumerator", enumerators
"System.Collections.Generic.Dictionary`2.ValueCollection.Enumerator", enumerators
"System.Collections.Generic.Dictionary`2.KeyCollection.Enumerator", enumerators
Expand All @@ -3994,6 +3993,9 @@ let private replacedModules =
Types.resizeArray, resizeArrays
"System.Collections.Generic.IList`1", resizeArrays
"System.Collections.IList", resizeArrays
Types.valueCollection, collections
Types.keyCollection, collections
Types.ireadonlycollection, collections
Types.icollectionGeneric, collections
Types.icollection, collections
"System.Collections.Generic.CollectionExtensions", collectionExtensions
Expand Down
4 changes: 2 additions & 2 deletions src/Fable.Transforms/Rust/Fable2Rust.fs
Original file line number Diff line number Diff line change
Expand Up @@ -362,7 +362,7 @@ module TypeInfo =
[ ty ] |> makeImportType com ctx "Native" "Arc"

let makeBoxTy com ctx (ty: Rust.Ty) : Rust.Ty =
[ ty ] |> makeImportType com ctx "Native" "Box"
[ ty ] |> makeImportType com ctx "Native" (rawIdent "Box")

let makeMutTy com ctx (ty: Rust.Ty) : Rust.Ty =
[ ty ] |> makeImportType com ctx "Native" "MutCell"
Expand Down Expand Up @@ -1511,7 +1511,7 @@ module Util =
[ value ] |> makeNew com ctx "Native" "Arc"

let makeBoxValue com ctx (value: Rust.Expr) =
[ value ] |> makeNew com ctx "Native" "Box"
[ value ] |> makeNew com ctx "Native" (rawIdent "Box")

let makeMutValue com ctx (value: Rust.Expr) =
[ value ] |> makeNew com ctx "Native" "MutCell"
Expand Down
3 changes: 3 additions & 0 deletions src/Fable.Transforms/Transforms.Util.fs
Original file line number Diff line number Diff line change
Expand Up @@ -327,6 +327,9 @@ module Types =
[<Literal>]
let idictionary = "System.Collections.Generic.IDictionary`2"

[<Literal>]
let ireadonlycollection = "System.Collections.Generic.IReadOnlyCollection`1"

[<Literal>]
let ireadonlydictionary = "System.Collections.Generic.IReadOnlyDictionary`2"

Expand Down
2 changes: 1 addition & 1 deletion src/fable-library-rust/src/Exception.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
pub mod Exception_ {
use crate::Native_::{Any, Box_, Func0, LrcPtr};
use crate::Native_::{Any, Box, Func0, LrcPtr};
use crate::String_::{fromSlice, string};
use crate::System::Exception;
use crate::Util_::new_Exception;
Expand Down
11 changes: 10 additions & 1 deletion src/fable-library-rust/src/Native.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ pub mod Native_ {

// re-export at module level
// pub use alloc::borrow::Cow;
pub use alloc::boxed::Box as Box_;
pub use alloc::boxed::Box;
pub use alloc::rc::Rc;
pub use alloc::string::{String, ToString};
pub use alloc::sync::Arc;
Expand Down Expand Up @@ -163,6 +163,15 @@ pub mod Native_ {
)
}

#[cfg(feature = "no_std")]
pub fn get_args(argc: isize, argv: *const *const u8) -> impl Iterator<Item = &'static str> {
(0..argc as usize).map(move |i| unsafe {
let curr_argv = argv.add(i).read_volatile();
let c_str = core::ffi::CStr::from_ptr(curr_argv as *const _);
c_str.to_str().unwrap()
})
}

// -----------------------------------------------------------
// IEqualityComparer key wrapper
// -----------------------------------------------------------
Expand Down
69 changes: 48 additions & 21 deletions src/fable-library-ts/CollectionUtil.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,21 @@ export function count<T>(col: Iterable<T>): number {
if (typeof (col as any)["System.Collections.Generic.ICollection`1.get_Count"] === "function") {
return (col as any)["System.Collections.Generic.ICollection`1.get_Count"](); // collection
} else {
if (isArrayLike(col)) {
return col.length; // resize array
if (typeof (col as any)["System.Collections.Generic.IReadOnlyCollection`1.get_Count"] === "function") {
return (col as any)["System.Collections.Generic.IReadOnlyCollection`1.get_Count"](); // collection
} else {
if (typeof (col as any).size === "number") {
return (col as any).size; // map, set
if (isArrayLike(col)) {
return col.length; // array, resize array
} else {
let count = 0;
for (const _ of col) {
count++;
if (typeof (col as any).size === "number") {
return (col as any).size; // map, set
} else {
let count = 0;
for (const _ of col) {
count++;
}
return count; // other collections
}
return count;
}
}
}
Expand All @@ -24,7 +28,16 @@ export function isReadOnly<T>(col: Iterable<T>): boolean {
if (typeof (col as any)["System.Collections.Generic.ICollection`1.get_IsReadOnly"] === "function") {
return (col as any)["System.Collections.Generic.ICollection`1.get_IsReadOnly"](); // collection
} else {
return false;
if (isArrayLike(col)) {
return ArrayBuffer.isView(col); // true for typed arrays, false for other arrays
} else {
if (typeof (col as any).size === "number") {
return false; // map, set
} else {
return true; // other collections
}
}

}
}

Expand All @@ -45,7 +58,7 @@ export function contains<T>(col: Iterable<T>, item: T): boolean {
return (col as any)["System.Collections.Generic.ICollection`1.Contains2B595"](item); // collection
} else {
if (isArrayLike(col)) {
let i = col.findIndex(x => equals(x, item)); // resize array
let i = col.findIndex(x => equals(x, item)); // array, resize array
return i >= 0;
} else {
if (typeof (col as any).has === "function") {
Expand All @@ -55,7 +68,7 @@ export function contains<T>(col: Iterable<T>, item: T): boolean {
return (col as any).has(item); // set
}
} else {
return false; // unknown collection
return false; // other collections
}
}
}
Expand All @@ -66,7 +79,11 @@ export function add<T>(col: Iterable<T>, item: T): void {
return (col as any)["System.Collections.Generic.ICollection`1.Add2B595"](item); // collection
} else {
if (isArrayLike(col)) {
col.push(item); // resize array
if (ArrayBuffer.isView(col)) {
// TODO: throw for typed arrays?
} else {
col.push(item); // array, resize array
}
} else {
if (typeof (col as any).add === "function") {
return (col as any).add(item); // set
Expand All @@ -80,7 +97,7 @@ export function add<T>(col: Iterable<T>, item: T): void {
throw new Error("An item with the same key has already been added. Key: " + item[0]);
}
} else {
// unknown collection
// TODO: throw for other collections?
}
}
}
Expand All @@ -92,12 +109,17 @@ export function remove<T>(col: Iterable<T>, item: T): boolean {
return (col as any)["System.Collections.Generic.ICollection`1.Remove2B595"](item); // collection
} else {
if (isArrayLike(col)) {
let i = col.findIndex(x => equals(x, item));
if (i >= 0) {
col.splice(i, 1); // resize array
return true;
} else {
if (ArrayBuffer.isView(col)) {
// TODO: throw for typed arrays
return false;
} else {
let i = col.findIndex(x => equals(x, item));
if (i >= 0) {
col.splice(i, 1); // array, resize array
return true;
} else {
return false;
}
}
} else {
if (typeof (col as any).delete === "function") {
Expand All @@ -111,7 +133,8 @@ export function remove<T>(col: Iterable<T>, item: T): boolean {
return (col as any).delete(item); // set
}
} else {
return false; // unknown collection
// TODO: throw for other collections?
return false; // other collections
}
}
}
Expand All @@ -122,12 +145,16 @@ export function clear<T>(col: Iterable<T>): void {
return (col as any)["System.Collections.Generic.ICollection`1.Clear"](); // collection
} else {
if (isArrayLike(col)) {
col.splice(0); // resize array
if (ArrayBuffer.isView(col)) {
// TODO: throw for typed arrays?
} else {
col.splice(0); // array, resize array
}
} else {
if (typeof (col as any).clear === "function") {
(col as any).clear(); // map, set
} else {
// unknown collection
// TODO: throw for other collections?
}
}
}
Expand Down
51 changes: 46 additions & 5 deletions tests/Js/Main/ArrayTests.fs
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ let tests =
ParamArrayTest.Add(let ar = [|1;2;3|] in sideEffect ar; ar)
|> equal 9

#if FABLE_COMPILER && FABLE_TYPED_ARRAYS
#if FABLE_COMPILER_JAVASCRIPT
testCase "Typed Arrays work" <| fun () ->
let xs = [| 1; 2; 3; |]
let ys = [| 1.; 2.; 3.; |]
Expand Down Expand Up @@ -1236,10 +1236,19 @@ let tests =
System.Array.Resize(&xs, 0)
xs |> equal [||]

// testCase "Array ICollection.IsReadOnly works" <| fun _ ->
// let xs = [| ("A", 1); ("B", 2); ("C", 3) |]
// let coll = xs :> ICollection<_>
// coll.IsReadOnly |> equal false
testCase "Array IReadOnlyCollection.Count works" <| fun _ ->
let xs = [| ("A", 1); ("B", 2); ("C", 3) |]
let coll = xs :> IReadOnlyCollection<_>
coll.Count |> equal 3

testCase "Array ICollection.IsReadOnly works" <| fun _ ->
let xs = [| ("A", 1); ("B", 2); ("C", 3) |]
let coll = xs :> ICollection<_>
#if FABLE_COMPILER_JAVASCRIPT || FABLE_COMPILER_TYPESCRIPT
coll.IsReadOnly |> equal false // Arrays are the same as ResizeArrays
#else
coll.IsReadOnly |> equal true
#endif

testCase "Array ICollection.Count works" <| fun _ ->
let xs = [| ("A", 1); ("B", 2); ("C", 3) |]
Expand All @@ -1259,4 +1268,36 @@ let tests =
let ys = [| ("D", 4); ("E", 5); ("F", 6) |]
coll.CopyTo(ys, 0)
ys = xs |> equal true

testCase "Array IReadOnlyCollection.Count with typed arrays works" <| fun _ ->
let xs = [| 1; 2; 3 |]
let coll = xs :> IReadOnlyCollection<_>
coll.Count |> equal 3

testCase "Array ICollection.IsReadOnly with typed arrays works" <| fun _ ->
let xs = [| 1; 2; 3 |]
let coll = xs :> ICollection<_>
#if FABLE_COMPILER_TYPESCRIPT
coll.IsReadOnly |> equal false // Arrays are the same as ResizeArrays
#else
coll.IsReadOnly |> equal true
#endif

testCase "Array ICollection.Count with typed arrays works" <| fun _ ->
let xs = [| 1; 2; 3 |]
let coll = xs :> ICollection<_>
coll.Count |> equal 3

testCase "Array ICollection.Contains with typed arrays works" <| fun _ ->
let xs = [| 1; 2; 3 |]
let coll = xs :> ICollection<_>
coll.Contains(4) |> equal false
coll.Contains(2) |> equal true

testCase "Array ICollection.CopyTo with typed arrays works" <| fun _ ->
let xs = [| 1; 2; 3 |]
let coll = xs :> ICollection<_>
let ys = [| 4; 5; 6 |]
coll.CopyTo(ys, 0)
ys = xs |> equal true
]
5 changes: 5 additions & 0 deletions tests/Js/Main/DictionaryTests.fs
Original file line number Diff line number Diff line change
Expand Up @@ -271,6 +271,11 @@ let tests =
table.add "C" 3
table.Dic.Count |> equal 3

testCase "Dictionary IReadOnlyCollection.Count works" <| fun _ ->
let xs = [| ("A", 1); ("B", 2); ("C", 3) |] |> Array.map KeyValuePair
let coll = (Dictionary xs) :> IReadOnlyCollection<_>
coll.Count |> equal 3

testCase "Dictionary ICollection.IsReadOnly works" <| fun _ ->
let xs = [| ("A", 1); ("B", 2); ("C", 3) |] |> Array.map KeyValuePair
let coll = (Dictionary xs) :> ICollection<KeyValuePair<_,_>>
Expand Down
5 changes: 5 additions & 0 deletions tests/Js/Main/HashSetTests.fs
Original file line number Diff line number Diff line change
Expand Up @@ -222,6 +222,11 @@ let tests =
apa.Contains ({ i = 5; s = "foo"}) |> equal true
apa.Contains ({ i = 5; s = "fo"}) |> equal false

testCase "HashSet IReadOnlyCollection.Count works" <| fun _ ->
let xs = [| ("A", 1); ("B", 2); ("C", 3) |]
let coll = (HashSet xs) :> IReadOnlyCollection<_>
coll.Count |> equal 3

testCase "HashSet ICollection.IsReadOnly works" <| fun _ ->
let xs = [| ("A", 1); ("B", 2); ("C", 3) |]
let coll = (HashSet xs) :> ICollection<_>
Expand Down
5 changes: 5 additions & 0 deletions tests/Js/Main/MapTests.fs
Original file line number Diff line number Diff line change
Expand Up @@ -292,6 +292,11 @@ let tests =
|> Map.count
|> equal 1

testCase "Map IReadOnlyCollection.Count works" <| fun _ ->
let xs = [| ("A", 1); ("B", 2); ("C", 3) |]
let coll = (Map xs) :> IReadOnlyCollection<_>
coll.Count |> equal 3

testCase "Map ICollection.IsReadOnly works" <| fun _ ->
let xs = [| ("A", 1); ("B", 2); ("C", 3) |]
let coll = (Map xs) :> ICollection<_>
Expand Down
5 changes: 5 additions & 0 deletions tests/Js/Main/ResizeArrayTests.fs
Original file line number Diff line number Diff line change
Expand Up @@ -319,6 +319,11 @@ let tests =
xs.CopyTo(2, ys, 1, 2)
ys |> equal [|5;3;4;8;9|]

testCase "ResizeArray IReadOnlyCollection.Count works" <| fun _ ->
let xs = [| ("A", 1); ("B", 2); ("C", 3) |]
let coll = (ResizeArray xs) :> IReadOnlyCollection<_>
coll.Count |> equal 3

testCase "ResizeArray ICollection.IsReadOnly works" <| fun _ ->
let xs = [| ("A", 1); ("B", 2); ("C", 3) |]
let coll = (ResizeArray xs) :> ICollection<_>
Expand Down
5 changes: 5 additions & 0 deletions tests/Js/Main/SetTests.fs
Original file line number Diff line number Diff line change
Expand Up @@ -292,6 +292,11 @@ let tests =
largeSetA.IsProperSubsetOf(largeSetB) |> equal false
largeSetA.IsSubsetOf(largeSetB) |> equal true

testCase "Set IReadOnlyCollection.Count works" <| fun _ ->
let xs = [| ("A", 1); ("B", 2); ("C", 3) |]
let coll = (Set xs) :> IReadOnlyCollection<_>
coll.Count |> equal 3

testCase "Set ICollection.IsReadOnly works" <| fun _ ->
let xs = [| ("A", 1); ("B", 2); ("C", 3) |]
let coll = (Set xs) :> ICollection<_>
Expand Down
3 changes: 2 additions & 1 deletion tests/Rust/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@ edition = "2021"
no_std = ["fable_library_rust/no_std"]
static_do_bindings = ["fable_library_rust/static_do_bindings"]
threaded = ["fable_library_rust/threaded"]
# default = ["threaded"] # Uncomment when attempting to debug/use rust analyzer to switch to threaded mode
# default = ["no_std"] # Uncomment to default to "no_std" mode
# default = ["threaded"] # Uncomment to default to "threaded" mode

[dependencies]
fable_library_rust = { path = "./fable_modules/fable-library-rust" }
Loading