Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Toggle main menu visibility
Loading...
Searching...
No Matches
qstdweb.cpp
Go to the documentation of this file.
1
// Copyright (C) 2025 The Qt Company Ltd.
2
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
// Qt-Security score:critical reason:data-parser
4
5
#
include
"qstdweb_p.h"
6
#
include
"qwasmsuspendresumecontrol_p.h"
7
#
include
"qwasmglobal_p.h"
8
9
#
include
<
QtCore
/
qcoreapplication
.
h
>
10
#
include
<
QtCore
/
qfile
.
h
>
11
#
include
<
QtCore
/
qmimedata
.
h
>
12
13
#
include
<
emscripten
/
emscripten
.
h
>
14
#
include
<
emscripten
/
bind
.
h
>
15
#
include
<
emscripten
/
val
.
h
>
16
#
include
<
emscripten
/
html5
.
h
>
17
#
include
<
emscripten
/
threading
.
h
>
18
19
#
include
<
cstdint
>
20
#
include
<
iostream
>
21
22
#
include
<
unordered_map
>
23
24
QT_BEGIN_NAMESPACE
25
26
using
namespace
Qt::StringLiterals;
27
using
emscripten::val;
28
29
namespace
qstdweb
{
30
31
static
void
usePotentialyUnusedSymbols
()
32
{
33
// Using this adds a reference on JSEvents and specialHTMLTargets which are always exported.
34
// This hack is needed as it is currently impossible to specify a dollar sign in
35
// target_link_options. The following is impossible:
36
// DEFAULT_LIBRARY_FUNCS_TO_INCLUDE=$JSEvents
37
// TODO(mikolajboc): QTBUG-108444, review this when cmake gets fixed.
38
// Volatile is to make this unoptimizable, so that the function is referenced, but is not
39
// called at runtime.
40
volatile
bool
doIt =
false
;
41
if
(doIt)
42
emscripten_set_wheel_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, 0, 0, NULL);
43
}
44
45
Q_CONSTRUCTOR_FUNCTION(usePotentialyUnusedSymbols)
46
typedef
double
uint53_t
;
// see Number.MAX_SAFE_INTEGER
47
namespace
{
48
// Reads file in chunks in order to avoid holding two copies in memory at the same time
49
struct
ChunkedFileReader
50
{
51
public
:
52
static
void
read(File file,
char
*buffer, uint32_t offset, uint32_t end,
53
std
::function<
void
()> onCompleted)
54
{
55
(
new
ChunkedFileReader(end,
std
::move(onCompleted),
std
::move(file)))
56
->readNextChunk(offset, buffer);
57
}
58
59
private
:
60
ChunkedFileReader(uint32_t end,
std
::function<
void
()> onCompleted, File file)
61
: end(end), onCompleted(std::move(onCompleted)), file(std::move(file))
62
{
63
}
64
65
void
readNextChunk(uint32_t chunkBegin,
char
*chunkBuffer)
66
{
67
// Copy current chunk from JS memory to Wasm memory
68
qstdweb
::ArrayBuffer result = fileReader.result();
69
qstdweb
::Uint8Array(result).copyTo(chunkBuffer);
70
71
// Read next chunk if not at buffer end
72
const
uint32_t nextChunkBegin =
std
::min(chunkBegin + result.byteLength(), end);
73
if
(nextChunkBegin == end) {
74
onCompleted();
75
delete
this
;
76
return
;
77
}
78
char
*nextChunkBuffer = chunkBuffer + result.byteLength();
79
fileReader.onLoad([
this
, nextChunkBegin, nextChunkBuffer](emscripten::val) {
80
readNextChunk(nextChunkBegin, nextChunkBuffer);
81
});
82
const
uint32_t nextChunkEnd =
std
::min(nextChunkBegin + chunkSize, end);
83
qstdweb
::Blob blob = file.slice(nextChunkBegin, nextChunkEnd);
84
fileReader.readAsArrayBuffer(blob);
85
}
86
87
static
constexpr
uint32_t chunkSize = 256 * 1024;
88
89
qstdweb
::FileReader fileReader;
90
uint32_t end;
91
std
::function<
void
()> onCompleted;
92
File file;
93
};
94
95
EM_JS(
bool
, jsHaveAsyncify, (), {
return
typeof
Asyncify !==
"undefined"
; });
96
EM_JS(
bool
, jsHaveJspi, (),
97
{
return
typeof
Asyncify !==
"undefined"
&& !!Asyncify.makeAsyncFunction && (!!WebAssembly.Function || !!WebAssembly.Suspending); });
98
99
}
// namespace
100
101
ArrayBuffer
::
ArrayBuffer
(
uint32_t
size
)
102
{
103
m_arrayBuffer
=
emscripten
::
val
::
global
(
"ArrayBuffer"
).
new_
(
size
);
104
}
105
106
ArrayBuffer
::
ArrayBuffer
(
const
emscripten
::
val
&
arrayBuffer
)
107
:
m_arrayBuffer
(
arrayBuffer
)
108
{
109
110
}
111
112
uint32_t
ArrayBuffer
::
byteLength
()
const
113
{
114
if
(
m_arrayBuffer
.
isUndefined
() ||
m_arrayBuffer
.
isNull
())
115
return
0;
116
117
return
m_arrayBuffer
[
"byteLength"
].
as
<
uint32_t
>();
118
}
119
120
ArrayBuffer
ArrayBuffer
::
slice
(
uint32_t
begin
,
uint32_t
end
)
const
121
{
122
return
ArrayBuffer
(
m_arrayBuffer
.
call
<
emscripten
::
val
>(
"slice"
,
begin
,
end
));
123
}
124
125
emscripten
::
val
ArrayBuffer
::
val
()
const
126
{
127
return
m_arrayBuffer
;
128
}
129
130
Blob
::
Blob
(
const
emscripten
::
val
&
blob
)
131
:
m_blob
(
blob
)
132
{
133
134
}
135
136
Blob
Blob
::
fromArrayBuffer
(
const
ArrayBuffer
&
arrayBuffer
)
137
{
138
auto
array
=
emscripten
::
val
::
array
();
139
array
.
call
<
void
>(
"push"
,
arrayBuffer
.
val
());
140
return
Blob
(
emscripten
::
val
::
global
(
"Blob"
).
new_
(
array
));
141
}
142
143
uint32_t
Blob
::
size
()
const
144
{
145
return
m_blob
[
"size"
].
as
<
uint32_t
>();
146
}
147
148
Blob
Blob
::
copyFrom
(
const
char
*
buffer
,
uint32_t
size
,
std
::
string
mimeType
)
149
{
150
Uint8Array
contentCopy
=
Uint8Array
::
copyFrom
(
buffer
,
size
);
151
152
emscripten
::
val
contentArray
=
emscripten
::
val
::
array
();
153
contentArray
.
call
<
void
>(
"push"
,
contentCopy
.
val
());
154
emscripten
::
val
type
=
emscripten
::
val
::
object
();
155
type
.
set
(
"type"
,
std
::
move
(
mimeType
));
156
return
Blob
(
emscripten
::
val
::
global
(
"Blob"
).
new_
(
contentArray
,
type
));
157
}
158
159
// Copies content from the given buffer into a Blob object
160
Blob
Blob
::
copyFrom
(
const
char
*
buffer
,
uint32_t
size
)
161
{
162
return
copyFrom
(
buffer
,
size
,
"application/octet-stream"
);
163
}
164
165
Blob
Blob
::
slice
(
uint32_t
begin
,
uint32_t
end
)
const
166
{
167
return
Blob
(
m_blob
.
call
<
emscripten
::
val
>(
"slice"
,
begin
,
end
));
168
}
169
170
ArrayBuffer
Blob
::
arrayBuffer_sync
()
const
171
{
172
emscripten
::
val
buffer
;
173
QList
<
uint32_t
>
handlers
;
174
qstdweb
::
Promise
::
make
(
175
handlers
,
176
m_blob
,
177
QStringLiteral
(
"arrayBuffer"
),
178
{
179
.
thenFunc
= [&
buffer
](
emscripten
::
val
arrayBuffer
) {
180
buffer
=
arrayBuffer
;
181
}
182
});
183
Promise
::
suspendExclusive
(
handlers
);
184
return
ArrayBuffer
(
buffer
);
185
}
186
187
emscripten
::
val
Blob
::
val
()
const
188
{
189
return
m_blob
;
190
}
191
192
File
::
File
(
const
emscripten
::
val
&
file
)
193
:
m_file
(
file
)
194
{
195
196
}
197
198
File
::~
File
() =
default
;
199
200
File
::
File
(
const
File
&
other
) =
default
;
201
202
File
::
File
(
File
&&
other
) =
default
;
203
204
File
&
File
::
operator
=(
const
File
&
other
) =
default
;
205
206
File
&
File
::
operator
=(
File
&&
other
) =
default
;
207
208
209
Blob
File
::
slice
(
uint64_t
begin
,
uint64_t
end
)
const
210
{
211
return
Blob
(
m_file
.
call
<
emscripten
::
val
>(
"slice"
,
uint53_t
(
begin
),
uint53_t
(
end
)));
212
}
213
214
std
::
string
File
::
name
()
const
215
{
216
return
m_file
[
"name"
].
as
<
std
::
string
>();
217
}
218
219
uint64_t
File
::
size
()
const
220
{
221
return
uint64_t
(
m_file
[
"size"
].
as
<
uint53_t
>());
222
}
223
224
std
::
string
Blob
::
type
()
const
225
{
226
return
m_blob
[
"type"
].
as
<
std
::
string
>();
227
}
228
229
// Streams partial file content into the given buffer asynchronously. The completed
230
// callback is called on completion.
231
void
File
::
stream
(
uint32_t
offset
,
uint32_t
length
,
char
*
buffer
,
232
std
::
function
<
void
()>
completed
)
const
233
{
234
ChunkedFileReader
::
read
(*
this
,
buffer
,
offset
,
offset
+
length
,
std
::
move
(
completed
));
235
}
236
237
// Streams file content into the given buffer asynchronously. The completed
238
// callback is called on completion.
239
void
File
::
stream
(
char
*
buffer
,
std
::
function
<
void
()>
completed
)
const
240
{
241
stream
(0,
size
(),
buffer
,
std
::
move
(
completed
));
242
}
243
244
std
::
string
File
::
type
()
const
245
{
246
return
m_file
[
"type"
].
as
<
std
::
string
>();
247
}
248
249
emscripten
::
val
File
::
val
()
const
250
{
251
return
m_file
;
252
}
253
254
FileUrlRegistration
::
FileUrlRegistration
(
File
file
)
255
{
256
m_path
=
QString
::
fromStdString
(
emscripten
::
val
::
global
(
"window"
)[
"URL"
].
call
<
std
::
string
>(
257
"createObjectURL"
,
file
.
file
()));
258
}
259
260
FileUrlRegistration
::~
FileUrlRegistration
()
261
{
262
emscripten
::
val
::
global
(
"window"
)[
"URL"
].
call
<
void
>(
"revokeObjectURL"
,
263
emscripten
::
val
(
m_path
.
toStdString
()));
264
}
265
266
FileUrlRegistration
::
FileUrlRegistration
(
FileUrlRegistration
&&
other
) =
default
;
267
268
FileUrlRegistration
&
FileUrlRegistration
::
operator
=(
FileUrlRegistration
&&
other
) =
default
;
269
270
FileList
::
FileList
(
const
emscripten
::
val
&
fileList
)
271
:
m_fileList
(
fileList
)
272
{
273
274
}
275
276
int
FileList
::
length
()
const
277
{
278
return
m_fileList
[
"length"
].
as
<
int
>();
279
}
280
281
File
FileList
::
item
(
int
index
)
const
282
{
283
return
File
(
m_fileList
[
index
]);
284
}
285
286
File
FileList
::
operator
[](
int
index
)
const
287
{
288
return
item
(
index
);
289
}
290
291
emscripten
::
val
FileList
::
val
()
const
292
{
293
return
m_fileList
;
294
}
295
296
ArrayBuffer
FileReader
::
result
()
const
297
{
298
return
ArrayBuffer
(
m_fileReader
[
"result"
]);
299
}
300
301
void
FileReader
::
readAsArrayBuffer
(
const
Blob
&
blob
)
const
302
{
303
m_fileReader
.
call
<
void
>(
"readAsArrayBuffer"
,
blob
.
m_blob
);
304
}
305
306
void
FileReader
::
onLoad
(
const
std
::
function
<
void
(
emscripten
::
val
)> &
onLoad
)
307
{
308
m_onLoad
.
reset
();
309
m_onLoad
=
std
::
make_unique
<
EventCallback
>(
m_fileReader
,
"load"
,
onLoad
);
310
}
311
312
void
FileReader
::
onError
(
const
std
::
function
<
void
(
emscripten
::
val
)> &
onError
)
313
{
314
m_onError
.
reset
();
315
m_onError
=
std
::
make_unique
<
EventCallback
>(
m_fileReader
,
"error"
,
onError
);
316
}
317
318
void
FileReader
::
onAbort
(
const
std
::
function
<
void
(
emscripten
::
val
)> &
onAbort
)
319
{
320
m_onAbort
.
reset
();
321
m_onAbort
=
std
::
make_unique
<
EventCallback
>(
m_fileReader
,
"abort"
,
onAbort
);
322
}
323
324
emscripten
::
val
FileReader
::
val
()
const
325
{
326
return
m_fileReader
;
327
}
328
329
// Constructs a Uint8Array which references the given emscripten::val, which must contain a JS Unit8Array
330
Uint8Array
::
Uint8Array
(
const
emscripten
::
val
&
uint8Array
)
331
:
m_uint8Array
(
uint8Array
)
332
{
333
334
}
335
336
// Constructs a Uint8Array which references an ArrayBuffer
337
Uint8Array
::
Uint8Array
(
const
ArrayBuffer
&
buffer
)
338
:
m_uint8Array
(
Uint8Array
::
constructor_
().
new_
(
buffer
.
m_arrayBuffer
))
339
{
340
341
}
342
343
// Constructs a Uint8Array which references a view into an ArrayBuffer
344
Uint8Array
::
Uint8Array
(
const
ArrayBuffer
&
buffer
,
uint32_t
offset
,
uint32_t
length
)
345
:
m_uint8Array
(
Uint8Array
::
constructor_
().
new_
(
buffer
.
m_arrayBuffer
,
offset
,
length
))
346
{
347
348
}
349
350
// Constructs a Uint8Array which references an area on the heap.
351
Uint8Array
::
Uint8Array
(
const
char
*
buffer
,
uint32_t
size
)
352
:
m_uint8Array
(
emscripten
::
typed_memory_view
(
size
,
buffer
))
353
{
354
355
}
356
357
// Constructs a Uint8Array which allocates and references a new ArrayBuffer with the given size.
358
Uint8Array
::
Uint8Array
(
uint32_t
size
)
359
:
m_uint8Array
(
Uint8Array
::
constructor_
().
new_
(
size
))
360
{
361
362
}
363
364
ArrayBuffer
Uint8Array
::
buffer
()
const
365
{
366
return
ArrayBuffer
(
m_uint8Array
[
"buffer"
]);
367
}
368
369
uint32_t
Uint8Array
::
length
()
const
370
{
371
return
m_uint8Array
[
"length"
].
as
<
uint32_t
>();
372
}
373
374
void
Uint8Array
::
set
(
const
Uint8Array
&
source
)
375
{
376
m_uint8Array
.
call
<
void
>(
"set"
,
source
.
m_uint8Array
);
// copies source content
377
}
378
379
Uint8Array
Uint8Array
::
subarray
(
uint32_t
begin
,
uint32_t
end
)
380
{
381
// Note: using uint64_t here errors with "Cannot convert a BigInt value to a number"
382
// (see JS BigInt and Number types). Use uint32_t for now.
383
return
Uint8Array
(
m_uint8Array
.
call
<
emscripten
::
val
>(
"subarray"
,
begin
,
end
));
384
}
385
386
// Copies the Uint8Array content to a destination on the heap
387
void
Uint8Array
::
copyTo
(
char
*
destination
)
const
388
{
389
Uint8Array
(
destination
,
length
()).
set
(*
this
);
390
}
391
392
// Copies the Uint8Array content to a destination QByteArray
393
QByteArray
Uint8Array
::
copyToQByteArray
()
const
394
{
395
if
(
length
() >
std
::
numeric_limits
<
qsizetype
>::
max
())
396
return
QByteArray
();
397
398
QByteArray
destinationArray
;
399
destinationArray
.
resize
(
length
());
400
copyTo
(
destinationArray
.
data
());
401
return
destinationArray
;
402
}
403
404
// Copies the Uint8Array content to a destination on the heap
405
void
Uint8Array
::
copy
(
char
*
destination
,
const
Uint8Array
&
source
)
406
{
407
Uint8Array
(
destination
,
source
.
length
()).
set
(
source
);
408
}
409
410
// Copies content from a source on the heap to a new Uint8Array object
411
Uint8Array
Uint8Array
::
copyFrom
(
const
char
*
buffer
,
uint32_t
size
)
412
{
413
Uint8Array
contentCopy
(
size
);
414
contentCopy
.
set
(
Uint8Array
(
buffer
,
size
));
415
return
contentCopy
;
416
}
417
418
// Copies content from a QByteArray to a new Uint8Array object
419
Uint8Array
Uint8Array
::
copyFrom
(
const
QByteArray
&
buffer
)
420
{
421
return
copyFrom
(
buffer
.
constData
(),
buffer
.
size
());
422
}
423
424
emscripten
::
val
Uint8Array
::
val
()
const
425
{
426
return
m_uint8Array
;
427
}
428
429
emscripten
::
val
Uint8Array
::
constructor_
()
430
{
431
return
emscripten
::
val
::
global
(
"Uint8Array"
);
432
}
433
434
EventCallback
::
EventCallback
(
emscripten
::
val
element
,
const
std
::
string
&
name
,
435
const
std
::
function
<
void
(
emscripten
::
val
)> &
fn
)
436
:
QWasmEventHandler
(
element
,
name
,
fn
)
437
{
438
439
}
440
441
size_t
qstdweb
::
Promise
::
State
::
s_numInstances
= 0;
442
443
//
444
// When a promise settles, all attached handlers will be called in
445
// the order they where added.
446
//
447
// In particular a finally handler will be called according to its
448
// position in the call chain. Which is not necessarily at the end,
449
//
450
// This makes cleanup difficult. If we cleanup to early, we will remove
451
// handlers before they have a chance to be called. This would be the
452
// case if we add a finally handler in the Promise constructor.
453
//
454
// For correct cleanup it is necessary that it happens after the
455
// last handler has been called.
456
//
457
// We choose to implement this by making sure the last handler
458
// is always a finally handler.
459
//
460
// In this case we have multiple finally handlers, so any called
461
// handler checks if it is the last handler to be called.
462
// If it is, the cleanup is performed, otherwise cleanup
463
// is delayed to the last handler.
464
//
465
// We could try to let the handlers cleanup themselves, but this
466
// only works for finally handlers. A then or catch handler is not
467
// necessarily called, and would not cleanup itself.
468
//
469
// We could try to let a (then,catch) pair cleanup both handlers,
470
// under the assumption that one of them will always be called.
471
// This does not work in the case that we do not have both handlers,
472
// or if the then handler throws (both should be called in this case).
473
//
474
Promise
&
Promise
::
addThenFunction
(
std
::
function
<
void
(
emscripten
::
val
)>
thenFunc
)
475
{
476
QWasmSuspendResumeControl
*
suspendResume
=
QWasmSuspendResumeControl
::
get
();
477
478
m_state
->
m_handlers
.
push_back
(
suspendResume
->
registerEventHandler
(
thenFunc
));
479
m_state
->
m_promise
=
480
m_state
->
m_promise
.
call
<
emscripten
::
val
>(
481
"then"
,
482
suspendResume
->
jsEventHandlerAt
(
483
m_state
->
m_handlers
.
back
()));
484
485
addFinallyFunction
([](){});
// Add a potential cleanup handler
486
return
(*
this
);
487
}
488
489
Promise
&
Promise
::
addCatchFunction
(
std
::
function
<
void
(
emscripten
::
val
)>
catchFunc
)
490
{
491
QWasmSuspendResumeControl
*
suspendResume
=
QWasmSuspendResumeControl
::
get
();
492
493
m_state
->
m_handlers
.
push_back
(
suspendResume
->
registerEventHandler
(
catchFunc
));
494
m_state
->
m_promise
=
495
m_state
->
m_promise
.
call
<
emscripten
::
val
>(
496
"catch"
,
497
suspendResume
->
jsEventHandlerAt
(
498
m_state
->
m_handlers
.
back
()));
499
500
addFinallyFunction
([](){});
// Add a potential cleanup handler
501
return
(*
this
);
502
}
503
504
Promise
&
Promise
::
addFinallyFunction
(
std
::
function
<
void
()>
finallyFunc
)
505
{
506
QWasmSuspendResumeControl
*
suspendResume
=
QWasmSuspendResumeControl
::
get
();
507
508
auto
thisHandler
=
std
::
make_shared
<
uint32_t
>((
uint32_t
)(-1));
509
auto
state
=
m_state
;
510
511
std
::
function
<
void
(
emscripten
::
val
)>
func
=
512
[
state
,
thisHandler
,
finallyFunc
](
emscripten
::
val
element
) {
513
Q_UNUSED
(
element
);
514
515
finallyFunc
();
516
517
// See comment at top, we can only do the cleanup
518
// if we are the last handler in the handler chain
519
if
(
state
->
m_handlers
.
back
() == *
thisHandler
) {
520
auto
guard
=
state
;
// removeEventHandler will remove also this function
521
QWasmSuspendResumeControl
*
suspendResume
=
QWasmSuspendResumeControl
::
get
();
522
for
(
int
i
= 0;
i
<
guard
->
m_handlers
.
size
(); ++
i
) {
523
suspendResume
->
removeEventHandler
(
guard
->
m_handlers
[
i
]);
524
guard
->
m_handlers
[
i
] = (
uint32_t
)(-1);
525
}
526
}
527
};
528
529
*
thisHandler
=
suspendResume
->
registerEventHandler
(
func
);
530
m_state
->
m_handlers
.
push_back
(*
thisHandler
);
531
m_state
->
m_promise
=
532
m_state
->
m_promise
.
call
<
emscripten
::
val
>(
533
"finally"
,
534
suspendResume
->
jsEventHandlerAt
(
535
m_state
->
m_handlers
.
back
()));
536
537
return
(*
this
);
538
}
539
540
void
Promise
::
suspendExclusive
()
541
{
542
Promise
::
suspendExclusive
(
m_state
->
m_handlers
);
543
}
544
545
// Blocks/suspends until the promise settles. The returned AwaitResult
546
// carries either the value (on resolve) or the error (on reject); the
547
// other field is left as emscripten::val::undefined().
548
Promise
::
AwaitResult
Promise
::
awaitExclusive
()
549
{
550
AwaitResult
r
;
551
addThenFunction
([&
r
](
emscripten
::
val
v
) {
r
.
value
=
v
; });
552
addCatchFunction
([&
r
](
emscripten
::
val
e
) {
r
.
error
=
e
; });
553
suspendExclusive
();
554
return
r
;
555
}
556
557
emscripten
::
val
Promise
::
getPromise
()
const
558
{
559
return
m_state
->
m_promise
;
560
}
561
562
uint32_t
Promise
::
adoptPromise
(
emscripten
::
val
promise
,
PromiseCallbacks
callbacks
,
QList
<
uint32_t
> *
handlers
)
563
{
564
Q_ASSERT_X
(!!
callbacks
.
catchFunc
|| !!
callbacks
.
finallyFunc
|| !!
callbacks
.
thenFunc
,
565
"Promise::adoptPromise"
,
"must provide at least one callback function"
);
566
567
QWasmSuspendResumeControl
*
suspendResume
=
QWasmSuspendResumeControl
::
get
();
568
569
// Registers a possibly-empty callback with suspendresumecontrol. Returns
570
// the the handler index if there was a valid callback, or nullopt.
571
auto
registerCallback
= [
suspendResume
](
std
::
function
<
void
(
emscripten
::
val
)>
cb
) ->
std
::
optional
<
uint32_t
>{
572
if
(!
cb
)
573
return
std
::
nullopt
;
574
return
std
::
optional
<
uint32_t
>{
suspendResume
->
registerEventHandler
(
std
::
move
(
cb
))};
575
};
576
577
// Register callbacks with suspendresumecontrol, so that it can
578
// resume the wasm instance when the promise resolves. The finally
579
// callback is sepecial, since we remove the event handlers there
580
// as cleanup, including the event handler for the cleanup function
581
// itself.
582
std
::
optional
<
uint32_t
>
thenIndex
=
registerCallback
(
std
::
move
(
callbacks
.
thenFunc
));
583
std
::
optional
<
uint32_t
>
catchIndex
=
registerCallback
(
std
::
move
(
callbacks
.
catchFunc
));
584
std
::
shared_ptr
<
uint32_t
>
finallyIndex
=
std
::
make_shared
<
uint32_t
>();;
585
auto
finallyFunc
=
callbacks
.
finallyFunc
;
586
587
// 'Finally' callback which performs clean-up and calls the user-provided finally.
588
auto
finally
= [
thenIndex
,
catchIndex
,
finallyIndex
,
finallyFunc
](
emscripten
::
val
){
589
QWasmSuspendResumeControl
*
suspendResume
=
QWasmSuspendResumeControl
::
get
();
590
591
// Clean up event handlers
592
if
(
thenIndex
)
593
suspendResume
->
removeEventHandler
(*
thenIndex
);
594
if
(
catchIndex
)
595
suspendResume
->
removeEventHandler
(*
catchIndex
);
596
597
// Call user finally
598
if
(
finallyFunc
)
599
finallyFunc
();
600
601
// Remove the finally (this) event handler last
602
suspendResume
->
removeEventHandler
(*
finallyIndex
);
603
};
604
605
*
finallyIndex
=
suspendResume
->
registerEventHandler
(
std
::
move
(
finally
));
606
607
// Set handlers on the promise
608
if
(
thenIndex
)
609
promise
=
610
promise
.
call
<
emscripten
::
val
>(
"then"
,
suspendResume
->
jsEventHandlerAt
(*
thenIndex
));
611
612
if
(
catchIndex
)
613
promise
=
promise
.
call
<
emscripten
::
val
>(
"catch"
,
614
suspendResume
->
jsEventHandlerAt
(*
catchIndex
));
615
616
promise
=
promise
.
call
<
emscripten
::
val
>(
"finally"
,
617
suspendResume
->
jsEventHandlerAt
(*
finallyIndex
));
618
619
if
(
handlers
) {
620
if
(
thenIndex
)
621
handlers
->
push_back
(*
thenIndex
);
622
if
(
catchIndex
)
623
handlers
->
push_back
(*
catchIndex
);
624
handlers
->
push_back
(*
finallyIndex
);
625
}
626
return
*
finallyIndex
;
627
}
628
629
void
Promise
::
suspendExclusive
(
QList
<
uint32_t
>
handlerIndices
)
630
{
631
QWasmSuspendResumeControl
*
suspendResume
=
QWasmSuspendResumeControl
::
get
();
632
suspendResume
->
suspendExclusive
(
handlerIndices
);
633
suspendResume
->
sendPendingEvents
();
634
}
635
636
void
Promise
::
all
(
std
::
vector
<
emscripten
::
val
>
promises
,
PromiseCallbacks
callbacks
)
637
{
638
auto
arr
=
emscripten
::
val
::
array
(
promises
);
639
auto
all
=
val
::
global
(
"Promise"
).
call
<
emscripten
::
val
>(
"all"
,
arr
);
640
adoptPromise
(
all
,
callbacks
);
641
}
642
643
// Asyncify and thread blocking: Normally, it's not possible to block the main
644
// thread, except if asyncify is enabled. Secondary threads can always block.
645
//
646
// haveAsyncify(): returns true if the main thread can block on QEventLoop::exec(),
647
// if either asyncify 1 or 2 (JSPI) is available.
648
//
649
// haveJspi(): returns true if asyncify 2 (JSPI) is available.
650
//
651
// canBlockCallingThread(): returns true if the calling thread can block on
652
// QEventLoop::exec(), using either asyncify or as a seconarday thread.
653
bool
haveJspi
()
654
{
655
static
bool
HaveJspi = jsHaveJspi();
656
return
HaveJspi;
657
}
658
659
bool
haveAsyncify
()
660
{
661
static
bool
HaveAsyncify = jsHaveAsyncify() ||
haveJspi
(
)
;
662
return
HaveAsyncify;
663
}
664
665
bool
canBlockCallingThread
()
666
{
667
return
haveAsyncify
(
)
|| !emscripten_is_main_runtime_thread();
668
}
669
670
BlobIODevice
::
BlobIODevice
(
Blob
blob
)
671
:
m_blob
(
blob
)
672
{
673
674
}
675
676
bool
BlobIODevice
::
open
(
QIODevice
::
OpenMode
mode
)
677
{
678
if
(
mode
.
testFlag
(
QIODevice
::
WriteOnly
))
679
return
false
;
680
return
QIODevice
::
open
(
mode
);
681
}
682
683
bool
BlobIODevice
::
isSequential
()
const
684
{
685
return
false
;
686
}
687
688
qint64
BlobIODevice
::
size
()
const
689
{
690
return
m_blob
.
size
();
691
}
692
693
bool
BlobIODevice
::
seek
(
qint64
pos
)
694
{
695
if
(
pos
>=
size
())
696
return
false
;
697
return
QIODevice
::
seek
(
pos
);
698
}
699
700
qint64
BlobIODevice
::
readData
(
char
*
data
,
qint64
maxSize
)
701
{
702
uint64_t
begin
=
QIODevice
::
pos
();
703
uint64_t
end
=
std
::
min
<
uint64_t
>(
begin
+
maxSize
,
size
());
704
uint64_t
size
=
end
-
begin
;
705
if
(
size
> 0) {
706
qstdweb
::
ArrayBuffer
buffer
=
m_blob
.
slice
(
begin
,
end
).
arrayBuffer_sync
();
707
qstdweb
::
Uint8Array
(
buffer
).
copyTo
(
data
);
708
}
709
return
size
;
710
}
711
712
qint64
BlobIODevice
::
writeData
(
const
char
*,
qint64
)
713
{
714
Q_UNREACHABLE
();
715
}
716
717
Uint8ArrayIODevice
::
Uint8ArrayIODevice
(Uint8Array array)
718
:
m_array
(
array
)
719
{
720
721
}
722
723
bool
Uint8ArrayIODevice
::
open
(QIODevice::OpenMode mode)
724
{
725
return
QIODevice::open(mode);
726
}
727
728
bool
Uint8ArrayIODevice
::
isSequential
()
const
729
{
730
return
false
;
731
}
732
733
qint64
Uint8ArrayIODevice
::
size
()
const
734
{
735
return
m_array.length();
736
}
737
738
bool
Uint8ArrayIODevice
::
seek
(qint64 pos)
739
{
740
if
(pos >= size())
741
return
false
;
742
return
QIODevice::seek(pos);
743
}
744
745
qint64
Uint8ArrayIODevice
::
readData
(
char
*data, qint64 maxSize)
746
{
747
uint64_t begin = QIODevice::pos();
748
uint64_t end =
std
::min<uint64_t>(begin + maxSize, size());
749
uint64_t size = end - begin;
750
if
(size > 0)
751
m_array.subarray(begin, end).copyTo(data);
752
return
size;
753
}
754
755
qint64
Uint8ArrayIODevice
::
writeData
(
const
char
*data, qint64 maxSize)
756
{
757
uint64_t begin = QIODevice::pos();
758
uint64_t end =
std
::min<uint64_t>(begin + maxSize, size());
759
uint64_t size = end - begin;
760
if
(size > 0)
761
m_array.subarray(begin, end).set(Uint8Array(data, size));
762
return
size;
763
}
764
765
FileSystemWritableFileStreamIODevice
::
FileSystemWritableFileStreamIODevice
(
FileSystemWritableFileStream
stream
)
766
:
m_stream
(
std
::
move
(
stream
))
767
{
768
}
769
770
bool
FileSystemWritableFileStreamIODevice
::
open
(
QIODevice
::
OpenMode
mode
)
771
{
772
if
(
mode
.
testFlag
(
QIODevice
::
ReadOnly
))
773
return
false
;
774
return
QIODevice
::
open
(
mode
);
775
}
776
777
void
FileSystemWritableFileStreamIODevice
::
close
()
778
{
779
if
(!
isOpen
()) {
780
QIODevice
::
close
();
781
return
;
782
}
783
784
QList
<
uint32_t
>
handlers
;
785
Promise
::
make
(
handlers
,
m_stream
.
val
(),
QStringLiteral
(
"close"
), {
786
.
thenFunc
= [](
emscripten
::
val
) {
787
}
788
});
789
Promise
::
suspendExclusive
(
handlers
);
790
791
QIODevice
::
close
();
792
}
793
794
bool
FileSystemWritableFileStreamIODevice
::
isSequential
()
const
795
{
796
return
false
;
797
}
798
799
qint64
FileSystemWritableFileStreamIODevice
::
size
()
const
800
{
801
return
m_size
;
802
}
803
804
bool
FileSystemWritableFileStreamIODevice
::
seek
(
qint64
pos
)
805
{
806
bool
success
=
false
;
807
808
emscripten
::
val
seekParams
=
emscripten
::
val
::
object
();
809
seekParams
.
set
(
"type"
,
std
::
string
(
"seek"
));
810
seekParams
.
set
(
"position"
,
static_cast
<
double
>(
pos
));
811
QList
<
uint32_t
>
handlers
;
812
Promise
::
make
(
handlers
,
m_stream
.
val
(),
QStringLiteral
(
"write"
), {
813
.
thenFunc
= [&
success
](
emscripten
::
val
) {
814
success
=
true
;
815
},
816
.
catchFunc
= [](
emscripten
::
val
) {
817
}
818
},
seekParams
);
819
Promise
::
suspendExclusive
(
handlers
);
820
821
if
(!
success
)
822
return
false
;
823
824
return
QIODevice
::
seek
(
pos
);
825
}
826
827
qint64
FileSystemWritableFileStreamIODevice
::
readData
(
char
*,
qint64
)
828
{
829
Q_UNREACHABLE
();
830
}
831
832
qint64
FileSystemWritableFileStreamIODevice
::
writeData
(
const
char
*
data
,
qint64
size
)
833
{
834
bool
success
=
false
;
835
836
Uint8Array
array
=
Uint8Array
::
copyFrom
(
data
,
size
);
837
QList
<
uint32_t
>
handlers
;
838
Promise
::
make
(
handlers
,
m_stream
.
val
(),
QStringLiteral
(
"write"
), {
839
.
thenFunc
= [&
success
](
emscripten
::
val
) {
840
success
=
true
;
841
},
842
.
catchFunc
= [](
emscripten
::
val
) {
843
}
844
},
array
.
val
());
845
Promise
::
suspendExclusive
(
handlers
);
846
847
if
(
success
) {
848
qint64
newPos
=
pos
() +
size
;
849
m_size
=
std
::
max
(
m_size
,
newPos
);
850
return
size
;
851
}
852
return
-1;
853
}
854
855
FileSystemWritableFileStream
::
FileSystemWritableFileStream
(
const
emscripten
::
val
&
writableStream
)
856
:
m_writableStream
(
writableStream
)
857
{
858
}
859
860
emscripten
::
val
FileSystemWritableFileStream
::
val
()
const
861
{
862
return
m_writableStream
;
863
}
864
865
FileSystemFileHandle
::
FileSystemFileHandle
(
const
emscripten
::
val
&
fileHandle
)
866
:
m_fileHandle
(
fileHandle
)
867
{
868
}
869
870
std
::
string
FileSystemFileHandle
::
name
()
const
871
{
872
return
m_fileHandle
[
"name"
].
as
<
std
::
string
>();
873
}
874
875
std
::
string
FileSystemFileHandle
::
kind
()
const
876
{
877
return
m_fileHandle
[
"kind"
].
as
<
std
::
string
>();
878
}
879
880
emscripten
::
val
FileSystemFileHandle
::
val
()
const
881
{
882
return
m_fileHandle
;
883
}
884
885
FileSystemFileIODevice
::
FileSystemFileIODevice
(
FileSystemFileHandle
fileHandle
)
886
:
m_fileHandle
(
fileHandle
)
887
{
888
}
889
890
bool
FileSystemFileIODevice
::
open
(
QIODevice
::
OpenMode
mode
)
891
{
892
if
(
isOpen
())
893
return
false
;
894
895
// Read mode: get the File and create a BlobIODevice
896
if
(
mode
&
QIODevice
::
ReadOnly
) {
897
File
file
;
898
bool
success
=
false
;
899
900
QList
<
uint32_t
>
handlers
;
901
Promise
::
make
(
handlers
,
m_fileHandle
.
val
(),
QStringLiteral
(
"getFile"
), {
902
.
thenFunc
= [&
file
, &
success
](
emscripten
::
val
fileVal
) {
903
file
=
File
(
fileVal
);
904
success
=
true
;
905
},
906
.
catchFunc
= [](
emscripten
::
val
) {
907
}
908
});
909
Promise
::
suspendExclusive
(
handlers
);
910
911
if
(
success
) {
912
m_blobDevice
=
std
::
make_unique
<
BlobIODevice
>(
file
.
slice
(0,
file
.
size
()));
913
m_size
=
file
.
size
();
914
915
if
(!
m_blobDevice
->
open
(
mode
))
916
return
false
;
917
}
else
{
918
return
false
;
919
}
920
}
921
922
// Write mode: create a writable stream
923
if
(
mode
&
QIODevice
::
WriteOnly
) {
924
FileSystemWritableFileStream
writableStream
;
925
bool
success
=
false
;
926
927
QList
<
uint32_t
>
handlers
;
928
Promise
::
make
(
handlers
,
m_fileHandle
.
val
(),
QStringLiteral
(
"createWritable"
), {
929
.
thenFunc
= [&
writableStream
, &
success
](
emscripten
::
val
writable
) {
930
writableStream
=
FileSystemWritableFileStream
(
writable
);
931
success
=
true
;
932
},
933
.
catchFunc
= [](
emscripten
::
val
) {
934
}
935
});
936
Promise
::
suspendExclusive
(
handlers
);
937
938
if
(
success
) {
939
m_writableDevice
=
std
::
make_unique
<
FileSystemWritableFileStreamIODevice
>(
writableStream
);
940
if
(!
m_writableDevice
->
open
(
mode
))
941
return
false
;
942
}
else
{
943
return
false
;
944
}
945
}
946
947
return
QIODevice
::
open
(
mode
);
948
}
949
950
void
FileSystemFileIODevice
::
close
()
951
{
952
if
(!
isOpen
()) {
953
QIODevice
::
close
();
954
return
;
955
}
956
957
if
(
m_writableDevice
) {
958
m_writableDevice
->
close
();
959
m_writableDevice
.
reset
();
960
}
961
if
(
m_blobDevice
) {
962
m_blobDevice
->
close
();
963
m_blobDevice
.
reset
();
964
}
965
966
QIODevice
::
close
();
967
}
968
969
bool
FileSystemFileIODevice
::
isSequential
()
const
970
{
971
return
false
;
972
}
973
974
qint64
FileSystemFileIODevice
::
size
()
const
975
{
976
return
m_size
;
977
}
978
979
bool
FileSystemFileIODevice
::
seek
(
qint64
pos
)
980
{
981
if
(
m_blobDevice
) {
982
if
(!
m_blobDevice
->
seek
(
pos
))
983
return
false
;
984
}
985
if
(
m_writableDevice
) {
986
if
(!
m_writableDevice
->
seek
(
pos
))
987
return
false
;
988
}
989
return
QIODevice
::
seek
(
pos
);
990
}
991
992
qint64
FileSystemFileIODevice
::
readData
(
char
*
data
,
qint64
maxSize
)
993
{
994
if
(!
m_blobDevice
)
995
return
-1;
996
997
return
m_blobDevice
->
read
(
data
,
maxSize
);
998
}
999
1000
qint64
FileSystemFileIODevice
::
writeData
(
const
char
*
data
,
qint64
size
)
1001
{
1002
if
(!
m_writableDevice
)
1003
return
-1;
1004
1005
qint64
written
=
m_writableDevice
->
write
(
data
,
size
);
1006
if
(
written
> 0) {
1007
qint64
newPos
=
pos
() +
written
;
1008
m_size
=
std
::
max
(
m_size
,
newPos
);
1009
}
1010
return
written
;
1011
}
1012
1013
}
// namespace qstdweb
1014
1015
QT_END_NAMESPACE
qstdweb::Uint8ArrayIODevice
Definition
qstdweb_p.h:390
qstdweb::Uint8ArrayIODevice::size
qint64 size() const override
For open random-access devices, this function returns the size of the device.
Definition
qstdweb.cpp:733
qstdweb::Uint8ArrayIODevice::seek
bool seek(qint64 pos) override
For random-access devices, this function sets the current position to pos, returning true on success,...
Definition
qstdweb.cpp:738
qstdweb::Uint8ArrayIODevice::Uint8ArrayIODevice
Uint8ArrayIODevice(Uint8Array array)
Definition
qstdweb.cpp:717
qstdweb::Uint8ArrayIODevice::isSequential
bool isSequential() const override
Returns true if this device is sequential; otherwise returns false.
Definition
qstdweb.cpp:728
qstdweb::Uint8ArrayIODevice::writeData
qint64 writeData(const char *data, qint64 size) override
Writes up to maxSize bytes from data to the device.
Definition
qstdweb.cpp:755
qstdweb::Uint8ArrayIODevice::open
bool open(QIODevice::OpenMode mode) override
Definition
qstdweb.cpp:723
qstdweb::Uint8ArrayIODevice::readData
qint64 readData(char *data, qint64 maxSize) override
Reads up to maxSize bytes from the device into data, and returns the number of bytes read or -1 if an...
Definition
qstdweb.cpp:745
qstdweb
Definition
qstdweb.cpp:29
qstdweb::canBlockCallingThread
bool canBlockCallingThread()
Definition
qstdweb.cpp:665
qstdweb::uint53_t
double uint53_t
Definition
qstdweb.cpp:46
qstdweb::haveAsyncify
bool haveAsyncify()
Definition
qstdweb.cpp:659
qstdweb::haveJspi
bool haveJspi()
Definition
qstdweb.cpp:653
qstdweb::usePotentialyUnusedSymbols
static void usePotentialyUnusedSymbols()
Definition
qstdweb.cpp:31
std
[33]
Definition
src_corelib_tools_qhash.cpp:421
qtbase
src
corelib
platform
wasm
qstdweb.cpp
Generated on
for Qt by
1.18.0