emscripten and qt5 for wasm reading files from the local file system

394 views
Skip to first unread message

Balázs Varga

unread,
Oct 13, 2022, 11:31:12 AM10/13/22
to emscripten-discuss
Hi!

I run into a problem with
QFileDialog::getOpenFileContent function, which are use to access to the local filesystem with emscripten:
https://doc.qt.io/qt-5/qfiledialog.html#getOpenFileContent

I tried to call it on a different thread then what on the wasm application is running:
std::thread th1 (QFileDialog::getOpenFileContent, "*.*", onFileReady);
th1.join();

But in that case, it froze at the emscripten part in the openFileDialog function and no file dialogue was opened.
emscripten::val document = emscripten::val::global("document");
emscripten::val input = document.call<emscripten::val>("createElement", std::string("input"));

I got the error message at creating the input value.

Running on the main thread there is no error and works fine.
Any idea what could be the problem/solution here?
I got this error message:

worker.js onmessage() captured an uncaught exception: TypeError: Cannot read properties of undefined (reading 'createElement')
self.onmessage @ soffice.worker.js:207
soffice.worker.js:208 TypeError: Cannot read properties of undefined (reading 'createElement')
    at methodCaller_emscripten$$val_$std$$string$ (eval at new_ (47933e67-c694-46fe-af70-91dc8b50859c:8694:27), <anonymous>:5:20)
    at __emval_call_method (47933e67-c694-46fe-af70-91dc8b50859c:9267:14)
    at emscripten::internal::MethodCaller<emscripten::val, std::__2::basic_string<char, std::__2::char_traits<char>, std::__2::allocator<char> > >::call(emscripten::internal::_EM_VAL*, char const*, std::__2::basic_string<char, std::__2::char_traits<char>, std::__2::allocator<char> emscripten::internal::MethodCaller<emscripten::val, std::__2::basic_string<char, std::__2::char_traits<char>, std::__2::allocator<char> > >::call(emscripten::internal::_EM_VAL*, char const*, std::__2::basic_string<char, std::__2::char_traits<char>, std::__2::allocator<char> >&&) (val.h:278)
    at emscripten::val emscripten::val::call<emscripten::val, std::__2::basic_string<char, std::__2::char_traits<char>, std::__2::allocator<char> > >(char const*, std::__2::basic_string<char, std::__2::char_traits<char>, std::__2::allocator<char> >&&) emscripten::val emscripten::val::call<emscripten::val, std::__2::basic_string<char, std::__2::char_traits<char>, std::__2::allocator<char> > >(char const*, std::__2::basic_string<char, std::__2::char_traits<char>, std::__2::allocator<char> >&&) const (val.h:498)
    at QWasmLocalFileAccess::openFileDialog(std::__2::basic_string<char, std::__2::char_traits<char>, std::__2::allocator<char> > const&, QWasmLocalFileAccess::FileSelectMode, std::__2::function<void (qstdweb::FileList const&)> QWasmLocalFileAccess::openFileDialog(std::__2::basic_string<char, std::__2::char_traits<char>, std::__2::allocator<char> > const&, QWasmLocalFileAccess::FileSelectMode, std::__2::function<void (qstdweb::FileList const&)> const&) (qwasmlocalfileaccess.cpp:131)
    at QWasmLocalFileAccess::openFiles(std::__2::basic_string<char, std::__2::char_traits<char>, std::__2::allocator<char> > const&, QWasmLocalFileAccess::FileSelectMode, std::__2::function<void (int)> const&, std::__2::function<char* (unsigned long long, std::__2::basic_string<char, std::__2::char_traits<char>, std::__2::allocator<char> >)> const&, std::__2::function<void ()> QWasmLocalFileAccess::openFiles(std::__2::basic_string<char, std::__2::char_traits<char>, std::__2::allocator<char> > const&, QWasmLocalFileAccess::FileSelectMode, std::__2::function<void (int)> const&, std::__2::function<char* (unsigned long long, std::__2::basic_string<char, std::__2::char_traits<char>, std::__2::allocator<char> >)> const&, std::__2::function<void ()> const&) (qwasmlocalfileaccess.cpp:156)
    at QWasmLocalFileAccess::openFile(std::__2::basic_string<char, std::__2::char_traits<char>, std::__2::allocator<char> > const&, std::__2::function<void (bool)> const&, std::__2::function<char* (unsigned long long, std::__2::basic_string<char, std::__2::char_traits<char>, std::__2::allocator<char> >)> const&, std::__2::function<void ()> QWasmLocalFileAccess::openFile(std::__2::basic_string<char, std::__2::char_traits<char>, std::__2::allocator<char> > const&, std::__2::function<void (bool)> const&, std::__2::function<char* (unsigned long long, std::__2::basic_string<char, std::__2::char_traits<char>, std::__2::allocator<char> >)> const&, std::__2::function<void ()> const&) (qwasmlocalfileaccess.cpp:169)
    at QFileDialog::getOpenFileContent(QString const&, std::__2::function<void (QString const&, QByteArray const&)> QFileDialog::getOpenFileContent(QString const&, std::__2::function<void (QString const&, QByteArray const&)> const&)::$_2::operator()() (qfiledialog.cpp:2444)
    at decltype(std::__2::forward<QFileDialog::getOpenFileContent(QString const&, std::__2::function<void (QString const&, QByteArray const&)> const&)::$_2&>(fp)()) std::__2::__invoke<QFileDialog::getOpenFileContent(QString const&, std::__2::function<void (QString const&, QByteArray const&)> const&)::$_2&>(QFileDialog::getOpenFileContent(QString const&, std::__2::function<void (QString const&, QByteArray const&)> decltype(std::__2::forward<QFileDialog::getOpenFileContent(QString const&, std::__2::function<void (QString const&, QByteArray const&)> const&)::$_2&>(fp)()) std::__2::__invoke<QFileDialog::getOpenFileContent(QString const&, std::__2::function<void (QString const&, QByteArray const&)> const&)::$_2&>(QFileDialog::getOpenFileContent(QString const&, std::__2::function<void (QString const&, QByteArray const&)> const&)::$_2&) (type_traits:3694)
    at void std::__2::__invoke_void_return_wrapper<void, true>::__call<QFileDialog::getOpenFileContent(QString const&, std::__2::function<void (QString const&, QByteArray const&)> const&)::$_2&>(QFileDialog::getOpenFileContent(QString const&, std::__2::function<void (QString const&, QByteArray const&)> void std::__2::__invoke_void_return_wrapper<void, true>::__call<QFileDialog::getOpenFileContent(QString const&, std::__2::function<void (QString const&, QByteArray const&)> const&)::$_2&>(QFileDialog::getOpenFileContent(QString const&, std::__2::function<void (QString const&, QByteArray const&)> const&)::$_2&) (__functional_base:348)worker.js onmessage() captured an uncaught exception: TypeError: Cannot read properties of undefined (reading 'createElement')
self.onmessage @ soffice.worker.js:207
soffice.worker.js:208 TypeError: Cannot read properties of undefined (reading 'createElement')
    at methodCaller_emscripten$$val_$std$$string$ (eval at new_ (47933e67-c694-46fe-af70-91dc8b50859c:8694:27), <anonymous>:5:20)
    at __emval_call_method (47933e67-c694-46fe-af70-91dc8b50859c:9267:14)
    at emscripten::internal::MethodCaller<emscripten::val, std::__2::basic_string<char, std::__2::char_traits<char>, std::__2::allocator<char> > >::call(emscripten::internal::_EM_VAL*, char const*, std::__2::basic_string<char, std::__2::char_traits<char>, std::__2::allocator<char> emscripten::internal::MethodCaller<emscripten::val, std::__2::basic_string<char, std::__2::char_traits<char>, std::__2::allocator<char> > >::call(emscripten::internal::_EM_VAL*, char const*, std::__2::basic_string<char, std::__2::char_traits<char>, std::__2::allocator<char> >&&) (val.h:278)
    at emscripten::val emscripten::val::call<emscripten::val, std::__2::basic_string<char, std::__2::char_traits<char>, std::__2::allocator<char> > >(char const*, std::__2::basic_string<char, std::__2::char_traits<char>, std::__2::allocator<char> >&&) emscripten::val emscripten::val::call<emscripten::val, std::__2::basic_string<char, std::__2::char_traits<char>, std::__2::allocator<char> > >(char const*, std::__2::basic_string<char, std::__2::char_traits<char>, std::__2::allocator<char> >&&) const (val.h:498)
    at QWasmLocalFileAccess::openFileDialog(std::__2::basic_string<char, std::__2::char_traits<char>, std::__2::allocator<char> > const&, QWasmLocalFileAccess::FileSelectMode, std::__2::function<void (qstdweb::FileList const&)> QWasmLocalFileAccess::openFileDialog(std::__2::basic_string<char, std::__2::char_traits<char>, std::__2::allocator<char> > const&, QWasmLocalFileAccess::FileSelectMode, std::__2::function<void (qstdweb::FileList const&)> const&) (qwasmlocalfileaccess.cpp:131)
    at QWasmLocalFileAccess::openFiles(std::__2::basic_string<char, std::__2::char_traits<char>, std::__2::allocator<char> > const&, QWasmLocalFileAccess::FileSelectMode, std::__2::function<void (int)> const&, std::__2::function<char* (unsigned long long, std::__2::basic_string<char, std::__2::char_traits<char>, std::__2::allocator<char> >)> const&, std::__2::function<void ()> QWasmLocalFileAccess::openFiles(std::__2::basic_string<char, std::__2::char_traits<char>, std::__2::allocator<char> > const&, QWasmLocalFileAccess::FileSelectMode, std::__2::function<void (int)> const&, std::__2::function<char* (unsigned long long, std::__2::basic_string<char, std::__2::char_traits<char>, std::__2::allocator<char> >)> const&, std::__2::function<void ()> const&) (qwasmlocalfileaccess.cpp:156)
    at QWasmLocalFileAccess::openFile(std::__2::basic_string<char, std::__2::char_traits<char>, std::__2::allocator<char> > const&, std::__2::function<void (bool)> const&, std::__2::function<char* (unsigned long long, std::__2::basic_string<char, std::__2::char_traits<char>, std::__2::allocator<char> >)> const&, std::__2::function<void ()> QWasmLocalFileAccess::openFile(std::__2::basic_string<char, std::__2::char_traits<char>, std::__2::allocator<char> > const&, std::__2::function<void (bool)> const&, std::__2::function<char* (unsigned long long, std::__2::basic_string<char, std::__2::char_traits<char>, std::__2::allocator<char> >)> const&, std::__2::function<void ()> const&) (qwasmlocalfileaccess.cpp:169)
    at QFileDialog::getOpenFileContent(QString const&, std::__2::function<void (QString const&, QByteArray const&)> QFileDialog::getOpenFileContent(QString const&, std::__2::function<void (QString const&, QByteArray const&)> const&)::$_2::operator()() (qfiledialog.cpp:2444)
    at decltype(std::__2::forward<QFileDialog::getOpenFileContent(QString const&, std::__2::function<void (QString const&, QByteArray const&)> const&)::$_2&>(fp)()) std::__2::__invoke<QFileDialog::getOpenFileContent(QString const&, std::__2::function<void (QString const&, QByteArray const&)> const&)::$_2&>(QFileDialog::getOpenFileContent(QString const&, std::__2::function<void (QString const&, QByteArray const&)> decltype(std::__2::forward<QFileDialog::getOpenFileContent(QString const&, std::__2::function<void (QString const&, QByteArray const&)> const&)::$_2&>(fp)()) std::__2::__invoke<QFileDialog::getOpenFileContent(QString const&, std::__2::function<void (QString const&, QByteArray const&)> const&)::$_2&>(QFileDialog::getOpenFileContent(QString const&, std::__2::function<void (QString const&, QByteArray const&)> const&)::$_2&) (type_traits:3694)
    at void std::__2::__invoke_void_return_wrapper<void, true>::__call<QFileDialog::getOpenFileContent(QString const&, std::__2::function<void (QString const&, QByteArray const&)> const&)::$_2&>(QFileDialog::getOpenFileContent(QString const&, std::__2::function<void (QString const&, QByteArray const&)> void std::__2::__invoke_void_return_wrapper<void, true>::__call<QFileDialog::getOpenFileContent(QString const&, std::__2::function<void (QString const&, QByteArray const&)> const&)::$_2&>(QFileDialog::getOpenFileContent(QString const&, std::__2::function<void (QString const&, QByteArray const&)> const&)::$_2&) (__functional_base:348)

Thanks in advance,
Balazs

Sam Clegg

unread,
Oct 13, 2022, 12:35:24 PM10/13/22
to emscripte...@googlegroups.com
It looks like that API only works on the main thread (since it uses the DOM).

If you really need to call such APIs on a background thread there are various ways to proxy the work back to the main thread.  See https://emscripten.org/docs/porting/pthreads.html#proxying.   If the code in question is not a JS library (e.g. if its written in EM_JS or EM_ASM) you can use `MAIN_THREAD_EM_ASM` and/or `emscripten_sync_run_in_main_thread` helpers.

cheers,
sam


--
You received this message because you are subscribed to the Google Groups "emscripten-discuss" group.
To unsubscribe from this group and stop receiving emails from it, send an email to emscripten-disc...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/emscripten-discuss/0b5c70fa-8cd2-4705-a64c-4816c1189dccn%40googlegroups.com.

Balázs Varga

unread,
Oct 14, 2022, 6:24:01 AM10/14/22
to emscripte...@googlegroups.com
Thanks for getting back to me. :) Yes, the code is a c++ and not a JS library, so MAIN_THREAD_EM_ASM can be useful. But I have no idea yet how to make it work with that.

Best,
Balázs

Balázs Varga

unread,
Oct 17, 2022, 5:29:13 AM10/17/22
to emscripte...@googlegroups.com
Hi!

I would have a question about the FS.writeFile function.

I tried to use is in c++ similar like this: https://github.com/emscripten-core/emscripten/issues/17230

But didn't worked for me with this:

void Test::read()
{
    auto onFileReady = [=](const QString &fileName, const QByteArray &fileContents) {
        if (fileName.isEmpty()) {
            // Report error1
        } else {
            emscripten::val fileContentView = emscripten::val(emscripten::typed_memory_view(fileContents.size(), fileContents.constData()));
            emscripten::val fileContentCopy = emscripten::val::global("ArrayBuffer").new_(fileContents.size());
            emscripten::val fileContentCopyView = emscripten::val::global("Uint8Array").new_(fileContentCopy);
            fileContentCopyView.call<void>("set", fileContentView);
            //emscripten::val contentArray = emscripten::val::array();
            //contentArray.call<void>("push", fileContentCopyView);
            std::string path = fileName.toStdString();
            EM_ASM({
                FS.writeFile(path, new Uint8Array([ 1, 2, 3, 4 ]));
            });
        }
    };
   
    QFileDialog::getOpenFileContent("*.*", onFileReady);
}

If I tried to use a variable like 'path' in the example, it didn't work.
EM_ASM({
          FS.writeFile(path, new Uint8Array([ 1, 2, 3, 4 ]));
});

But If I used like this, it works:
EM_ASM({
          FS.writeFile("/write_file.txt", new Uint8Array([ 1, 2, 3, 4 ]));
});

So my question is, how can I use a variable inside the EM_ASM({ }) ?
Furthermore, how can I convert a char* to Uint8Array for the FS.writeFile() function? I tried a lot of ways (like in the example with emscripten::val) but it didn't work.

Thanks for the help in advance.

All the best,
Balázs

Balázs Varga

unread,
Oct 17, 2022, 7:35:45 AM10/17/22
to emscripten-discuss
I think I figured it out.
It looked like the file content was copied to the wasm memory with this:

void Test::read()
{
    auto onFileReady = [=](const QString &fileName, const QByteArray &fileContents) {
        if (fileName.isEmpty()) {
            // Report error1
        } else {
            std::string aFileName = "/home/user/" + fileName.toStdString();
            EM_ASM({
                FS.writeFile(UTF8ToString($0), new Uint8Array(Module.HEAPU8.buffer, $1, $2));
            }, aFileName.c_str(), fileContents.data(), fileContents.size());
        }
    };
   
    QFileDialog::getOpenFileContent("*.*", onFileReady);
}

But I am still not sure, that it is correct, even if it is worked somehow:
EM_ASM({
                FS.writeFile(UTF8ToString($0), new Uint8Array(Module.HEAPU8.buffer, $1, $2));
            }, aFileName.c_str(), fileContents.data(), fileContents.size());

Best,
Balázs
Reply all
Reply to author
Forward
0 new messages