rustwasm / wasm-bindgen

Facilitating high-level interactions between Wasm modules and JavaScript
https://rustwasm.github.io/docs/wasm-bindgen/
Apache License 2.0
7.83k stars 1.08k forks source link

Can not cast FileSystemEntry into FileSystemFileEntry when is_file() returns true #4243

Closed LittleSaya closed 1 week ago

LittleSaya commented 3 weeks ago

Describe the Bug

Can not cast FileSystemEntry into FileSystemFileEntry when is_file() returns true.

Steps to Reproduce

  1. clone https://github.com/LittleSaya/volatile/tree/76130a78fd1b3418febd14c85ac8cc0e6673fb8a (the initial commit)
  2. cd into 0.0.2\wasm (the folder where wasm project lives in)
  3. run build.cmd (which will build and move compiled things to parent folder)
  4. use a live server or nginx to host the project root
  5. access http://127.0.0.1: < live server port>/0.0.2/index.html
  6. drag a file into that black box
  7. an alert dialog pops up, shows "when traversing files dropped by user, can not convert FileSystemEntry at ... to FileSystemFileEntry"

location of code

where I invoke alert()

Expected Behavior

No dialog shows up in step 7, and the number under the black box should be the number of files.

Actual Behavior

An alert dialog pops up, and the number under the black box is not changed.

Additional Context

If this issue could be solved, I have some additional questions which are not directly related to this issue: ## At last, it's the ArrayBuffer passed to BYOB reader, not the WASM memory, get detached, I'm asking wrong question... The description in MDN is pretty unclear: > The original view will be detached and no longer usable. It says `view`, not the underlying ArrayBuffer, ah...... ~~That is, how to avoid the **"detached arraybuffer"** error?~~ ~~Or, how to get a **stable** `ArrayBuffer` that I can use in Rust side to interact with stream reader & writer?~~ ~~I need to do a stream style "read-compress-write" process in browser, and to interact with reader and writer, I have to use `ArrayBuffer`s and `Uint8Array`s, but I always get "detached arraybuffer" errors, no matter if the `ArrayBuffer`s are passed in from JS side or directly created in Rust side.~~ ~~I tried check the `byte_length()` just before using `ArrayBuffer`s, and recreate the `ArrayBuffer` if `byte_length()` is zero (google says a byte length of zero means it is detached), but this seems just delayed the position where the error pops up......~~ ~~I know the underlying memery of wasm is an `ArrayBuffer`, and when memory grows, that `ArrayBuffer` will be discarded and recreated, I guess this is why I encounter "detached arraybuffer" error, but I have no idea how to deal with it.~~ Below are some old contents, I have solved those two extra questions when I initially post this issue, I just leave it here in case someone encounter similiar code orginazing problems like me. ================================================================= Please allow me to give you more details, this is a brief of a small part of the behavior of my codes: - I registered a `drop` event handler on that black box in the page, with the help of a `Closure`. - Inside that handler, I iterate through the `DataTransferItemList` and convert each item to a `FileSystemEntry`. - A function named `dfs` will be called to traverse the list of `FileSystemEntry` (deep first search, not fully implemented yet). - In `dfs`, convert `FileSystemEntry` to `FileSystemFileEntry` or `FileSystemDirectoryEntry` <--- this issue - Then, I create two closures `success_callback` and `error_callback` and pass them to `file_with_callback_and_callback()` on the entry to get the `File` object. - Collect `File` objects, update DOM or throw error (some normal business logic). - Call `forget()` on these two closures. <--- the first extra question - Get notified when all entries have been traversed and recorded. <--- the second extra question The first extra question is: In each loop, two closures are created and leaked, what it the proper way to prevent memory leak when using closure as callback in a loop? I managed to get over the first question, my solution is: 1. You have a function named `main` or something else as the entry of your wasm program. 2. Create a global context wrapped inside an `Rc>` in `main`, e.g. `Rc>`. 3. Store everything required by every closures in that global context. 4. Initialize all closures in the `main` function before doing anything else, each closure will capture a clone of `Rc>`. 5. Pass a clone of the context to where it is needed, for example, where an event handler is needed. 6. Invoke `mem::forget()` on the `Rc>` at the end of `main`, which will prevent closures from being deallocated, and because each closore is created only once, no memory is leaked (`main` is called only once). The second extra question is: The `file()` method on `FileSystemEntry` uses callback to provide the `File` object, this might be async, when I'm iterating through multiple entries, how could I know when the last entry is been processed? By converting dfs to bfs, using a counter to count unresolved directories, breaking the process into several closures which will invoke each other (through an `Rc>` like above) and stop when the counter reaches zero, I managed to get myself a notification when the last entry is processed. About why I'm using "File and Directory Entries API" in wasm: I'm developing a stream style zip compressor in browser, and I have no idea how to store persistent state which will cross js side and rust side correctly, so I'm attempting to implement everything in wasm. I have a related question on SO: [SO question](https://stackoverflow.com/questions/79146845/how-to-reuse-buffers-between-different-calls-in-rust-wasm)

Update 1

Calling dyn_into() to cast FileSystemEntry to FileSystemDirectory when is_directory() returns true will result in an Err as well, though it seems that everything is ok if I just call unchecked_into() directly.

LittleSaya commented 3 weeks ago

It seems that this is just a surface problem, if I use unchecked_into() directly, it works, and I can get the file object from success callback. Though I still need to figure out how to avoid memory leak and how to know when the last entry is processed.

LittleSaya commented 2 weeks ago

Just found there is no FileSystem***Entry in my devtool, I'm using chromium, and I found an article in MDN:

https://developer.mozilla.org/en-US/docs/Web/API/File_and_Directory_Entries_API/Firefox_support#chrome_deviations_from_the_specification

I guess this is the cause of this problem? After looking a bit into the generated js file, I found something like instanceof FileSystemEntry, and because there is not FileSystemEntry exists in browser, it will throw an error.

Chromium and edge both lacking FileSystem***Entry in their devtool, but Firefox has.

daxpedda commented 1 week ago

It seems to me that you resolved the issue on your own, your conclusion looks correct to me. Let me know if you are still having issues related to wasm-bindgen.