Skip to content

Commit 70617c0

Browse files
committed
Fix fs-in-worker test
1 parent e9163d3 commit 70617c0

File tree

13 files changed

+136
-88
lines changed

13 files changed

+136
-88
lines changed

sqlite3/assets/wasm/bridge.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ import_dart("xAccess") extern int xAccess(int vfs, const char *zName, int flags,
1616
import_dart("xFullPathname") extern int xFullPathname(int vfs, const char *zName, int nOut, char *zOut);
1717
import_dart("xRandomness") extern int xRandomness(int vfs, int nByte, char* zOut);
1818
import_dart("xSleep") extern int xSleep(int vfs, int microseconds);
19-
import_dart("xCurrentTimeInt64") extern int xCurrentTimeInt64(int vfs, int* target);
19+
import_dart("xCurrentTimeInt64") extern int xCurrentTimeInt64(int vfs, int64_t* target);
2020

2121
import_dart("xClose") extern int xClose(int file);
2222
import_dart("xRead") extern int xRead(int, void*, int iAmt, sqlite3_int64 iOfst);

sqlite3/assets/wasm/helpers.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -150,7 +150,7 @@ static int dartvfs_sleep(sqlite3_vfs* vfs, int microseconds) {
150150
}
151151

152152
static int dartvfs_currentTimeInt64(sqlite3_vfs* vfs, sqlite3_int64* timeOut) {
153-
int milliseconds;
153+
int64_t milliseconds;
154154
int rc = xCurrentTimeInt64((int) vfs->pAppData, &milliseconds);
155155

156156
// https://github.com/sqlite/sqlite/blob/8ee75f7c3ac1456b8d941781857be27bfddb57d6/src/os_unix.c#L6757

sqlite3/example/web/worker.dart

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -33,16 +33,17 @@ void main() {
3333

3434
final sqlite3 =
3535
await WasmSqlite3.loadFromUrl(Uri.parse('sqlite3.debug.wasm'));
36-
sqlite3.registerVirtualFileSystem(WasmVfs(workerOptions: options),
36+
sqlite3.registerVirtualFileSystem(
37+
await SimpleOpfsFileSystem.loadFromStorage('worker-test'),
3738
makeDefault: true);
3839

39-
sqlite3.open('/tmp/foo.db')
40+
sqlite3.open('/database')
4041
..execute('pragma user_version = 1')
4142
..execute('CREATE TABLE foo (bar INTEGER NOT NULL);')
4243
..execute('INSERT INTO foo (bar) VALUES (?)', [3])
4344
..dispose();
4445

45-
final db = sqlite3.open('/tmp/foo.db');
46+
final db = sqlite3.open('/database');
4647
print(db.select('SELECT * FROM foo'));
4748
} else {
4849
final message = data as WorkerOptions;

sqlite3/lib/common.dart

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,3 +9,4 @@ export 'src/functions.dart';
99
export 'src/result_set.dart';
1010
export 'src/sqlite3.dart';
1111
export 'src/statement.dart';
12+
export 'src/vfs.dart';

sqlite3/lib/src/wasm/js_interop/file_system_access.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,7 @@ extension FileSystemDirectoryHandleApi on FileSystemDirectoryHandle {
118118
}
119119
}
120120

121-
extension FileSystemFileHandleAPi on FileSystemFileHandle {
121+
extension FileSystemFileHandleApi on FileSystemFileHandle {
122122
@JS('createSyncAccessHandle')
123123
external Object _createSyncAccessHandle();
124124

sqlite3/lib/src/wasm/js_interop/typed_data.dart

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ import 'dart:typed_data';
44
import 'package:js/js.dart';
55
import 'package:js/js_util.dart';
66

7+
import 'core.dart';
8+
79
@JS('Uint8Array')
810
external Object get _uint8Array;
911

@@ -53,3 +55,10 @@ extension NativeUint8List on Uint8List {
5355
callMethod<void>(this, 'set', [from, offset]);
5456
}
5557
}
58+
59+
extension NativeDataView on ByteData {
60+
void setBigInt64(int offset, JsBigInt value, bool littleEndian) {
61+
callMethod<void>(
62+
this, 'setBigInt64', [offset, value.jsObject, littleEndian]);
63+
}
64+
}

sqlite3/lib/src/wasm/file_system/indexed_db.dart renamed to sqlite3/lib/src/wasm/vfs/indexed_db.dart

Lines changed: 78 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,10 @@ import 'package:js/js.dart';
1010
import 'package:meta/meta.dart';
1111

1212
import '../../constants.dart';
13-
import '../file_system.dart';
13+
import '../../vfs.dart';
1414
import '../js_interop.dart';
1515
import 'memory.dart';
16+
import 'utils.dart';
1617

1718
/// An (asynchronous) file system implementation backed by IndexedDB.
1819
///
@@ -400,7 +401,7 @@ class _OffsetAndBuffer {
400401
///
401402
/// In the future, we may want to store individual blocks instead.
402403
403-
class IndexedDbFileSystem implements FileSystem {
404+
class IndexedDbFileSystem extends BaseVirtualFileSystem {
404405
final AsynchronousIndexedDbFileSystem _asynchronous;
405406

406407
var _isClosing = false;
@@ -415,7 +416,8 @@ class IndexedDbFileSystem implements FileSystem {
415416

416417
IndexedDbFileSystem._(String dbName)
417418
: _asynchronous = AsynchronousIndexedDbFileSystem(dbName),
418-
_memory = InMemoryFileSystem();
419+
_memory = InMemoryFileSystem(),
420+
super(name: 'indexeddb');
419421

420422
/// Loads an IndexedDB file system identified by the [dbName].
421423
///
@@ -443,8 +445,7 @@ class IndexedDbFileSystem implements FileSystem {
443445
// https://github.com/dart-lang/sdk/issues/48854
444446
await self.indexedDB!.deleteDatabase(dbName).timeout(
445447
const Duration(milliseconds: 1000),
446-
onTimeout: () => throw FileSystemException(
447-
0, "Failed to delete database. Database is still open"));
448+
onTimeout: () => throw VfsException(1));
448449
}
449450

450451
/// Whether this file system is closing or closed.
@@ -500,7 +501,7 @@ class IndexedDbFileSystem implements FileSystem {
500501

501502
void _checkClosed() {
502503
if (isClosed) {
503-
throw FileSystemException(SqlError.SQLITE_IOERR, 'FileSystem closed');
504+
throw VfsException(SqlError.SQLITE_IOERR);
504505
}
505506
}
506507

@@ -534,94 +535,110 @@ class IndexedDbFileSystem implements FileSystem {
534535
}
535536

536537
@override
537-
void createFile(
538-
String path, {
539-
bool errorIfNotExists = false,
540-
bool errorIfAlreadyExists = false,
541-
}) {
542-
_checkClosed();
543-
final existsBefore = _memory.exists(path);
544-
_memory.createFile(
545-
path,
546-
errorIfAlreadyExists: errorIfAlreadyExists,
547-
errorIfNotExists: errorIfNotExists,
548-
);
538+
int xAccess(String path, int flags) => _memory.xAccess(path, flags);
549539

550-
if (!existsBefore) {
551-
_submitWork(_CreateFileWorkItem(this, path));
540+
@override
541+
void xDelete(String path, int syncDir) {
542+
_memory.xDelete(path, syncDir);
543+
544+
if (!_inMemoryOnlyFiles.remove(path)) {
545+
_submitWork(_DeleteFileWorkItem(this, path));
552546
}
553547
}
554548

555549
@override
556-
String createTemporaryFile() {
557-
_checkClosed();
558-
final path = _memory.createTemporaryFile();
559-
_inMemoryOnlyFiles.add(path);
560-
return path;
561-
}
550+
String xFullPathName(String path) => _memory.xFullPathName(path);
562551

563552
@override
564-
void deleteFile(String path) {
565-
_memory.deleteFile(path);
566-
567-
if (!_inMemoryOnlyFiles.remove(path)) {
568-
_submitWork(_DeleteFileWorkItem(this, path));
553+
XOpenResult xOpen(Sqlite3Filename path, int flags) {
554+
final pathStr = path.path ?? random.randomFileName(prefix: '/');
555+
final existedBefore = _memory.xAccess(pathStr, 0) != 0;
556+
557+
final inMemoryFile = _memory.xOpen(Sqlite3Filename(pathStr), flags);
558+
final deleteOnClose = (flags & SqlFlag.SQLITE_OPEN_DELETEONCLOSE) != 0;
559+
560+
if (!existedBefore) {
561+
if (deleteOnClose) {
562+
// No point in persisting this file, it doesn't exist and won't exist
563+
// after we're done.
564+
_inMemoryOnlyFiles.add(pathStr);
565+
} else {
566+
_submitWork(_CreateFileWorkItem(this, pathStr));
567+
}
569568
}
569+
570+
return XOpenResult(
571+
outFlags: 0, file: _IndexedDbFile(this, inMemoryFile.file, pathStr));
570572
}
571573

572574
@override
573-
Future<void> clear() async {
574-
_memory.clear();
575-
await _submitWorkFunction(_asynchronous.clear, 'clear');
575+
void xSleep(Duration duration) {
576+
// noop
576577
}
578+
}
579+
580+
class _IndexedDbFile extends VirtualFileSystemFile {
581+
final IndexedDbFileSystem vfs;
582+
final VirtualFileSystemFile memoryFile;
583+
final String path;
584+
585+
_IndexedDbFile(this.vfs, this.memoryFile, this.path);
577586

578587
@override
579-
bool exists(String path) {
580-
_checkClosed();
581-
return _memory.exists(path);
588+
void xRead(Uint8List target, int fileOffset) {
589+
memoryFile.xRead(target, fileOffset);
582590
}
583591

584592
@override
585-
List<String> get files {
586-
_checkClosed();
587-
return _memory.files;
588-
}
593+
int get xDeviceCharacteristics => 0;
589594

590595
@override
591-
int read(String path, Uint8List target, int offset) {
592-
_checkClosed();
593-
return _memory.read(path, target, offset);
594-
}
596+
int xCheckReservedLock() => memoryFile.xCheckReservedLock();
595597

596598
@override
597-
int sizeOfFile(String path) {
598-
_checkClosed();
599-
return _memory.sizeOfFile(path);
599+
void xClose() {}
600+
601+
@override
602+
int xFileSize() => memoryFile.xFileSize();
603+
604+
@override
605+
void xLock(int mode) => memoryFile.xLock(mode);
606+
607+
@override
608+
void xSync(int flags) {
609+
// We can't wait for a sync either way, so this just has to be a noop
600610
}
601611

602612
@override
603-
void truncateFile(String path, int length) {
604-
_checkClosed();
605-
_memory.truncateFile(path, length);
613+
void xTruncate(int size) {
614+
vfs._checkClosed();
615+
memoryFile.xTruncate(size);
606616

607-
if (!_inMemoryOnlyFiles.contains(path)) {
608-
_submitWorkFunction(
609-
() async => _asynchronous.truncate(await _fileId(path), length),
617+
if (!vfs._inMemoryOnlyFiles.contains(path)) {
618+
vfs._submitWorkFunction(
619+
() async => vfs._asynchronous.truncate(await vfs._fileId(path), size),
610620
'truncate $path');
611621
}
612622
}
613623

614624
@override
615-
void write(String path, Uint8List bytes, int offset) {
616-
_checkClosed();
625+
void xUnlock(int mode) => memoryFile.xUnlock(mode);
626+
627+
@override
628+
void xWrite(Uint8List buffer, int fileOffset) {
629+
vfs._checkClosed();
617630

618-
final previousContent = _memory.fileData[path] ?? Uint8List(0);
631+
final previousContent = vfs._memory.fileData[path] ?? Uint8List(0);
632+
memoryFile.xWrite(buffer, fileOffset);
619633

620-
_memory.write(path, bytes, offset);
634+
if (!vfs._inMemoryOnlyFiles.contains(path)) {
635+
// We need to copy the buffer for the write because it will become invalid
636+
// after this synchronous method returns.
637+
final copy = Uint8List(buffer.length);
638+
copy.setAll(0, buffer);
621639

622-
if (!_inMemoryOnlyFiles.contains(path)) {
623-
_submitWork(_WriteFileWorkItem(this, path, previousContent)
624-
..writes.add(_OffsetAndBuffer(offset, bytes)));
640+
vfs._submitWork(_WriteFileWorkItem(vfs, path, previousContent)
641+
..writes.add(_OffsetAndBuffer(fileOffset, copy)));
625642
}
626643
}
627644
}

sqlite3/lib/src/wasm/vfs/simple_opfs.dart

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -170,6 +170,15 @@ class SimpleOpfsFileSystem extends BaseVirtualFileSystem {
170170

171171
@override
172172
void xSleep(Duration duration) {}
173+
174+
/// Closes the synchronous access handles kept open while this file system is
175+
/// active.
176+
void close() {
177+
_metaHandle.close();
178+
for (final file in _files.values) {
179+
file.close();
180+
}
181+
}
173182
}
174183

175184
class _SimpleOpfsFile extends BaseVfsFile {
@@ -194,7 +203,7 @@ class _SimpleOpfsFile extends BaseVfsFile {
194203

195204
@override
196205
void xClose() {
197-
syncHandle.close();
206+
syncHandle.flush();
198207

199208
if (deleteOnClose) {
200209
vfs._markExists(type, false);

sqlite3/lib/src/wasm/wasm_interop.dart

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ import 'package:js/js.dart';
77

88
import '../../wasm.dart';
99
import '../implementation/bindings.dart';
10-
import '../vfs.dart';
1110
import 'bindings.dart';
1211
import 'js_interop.dart';
1312

@@ -449,6 +448,11 @@ extension WrappedMemory on Memory {
449448
buffer.asInt32List()[pointer >> 2] = value;
450449
}
451450

451+
void setInt64Value(Pointer pointer, JsBigInt value) {
452+
assert(pointer != 0, 'Null pointer dereference');
453+
buffer.asByteData().setBigInt64(pointer, value, true);
454+
}
455+
452456
String readString(int address, [int? length]) {
453457
assert(address != 0, 'Null pointer dereference');
454458
return utf8.decode(buffer.asUint8List(address, length ?? strlen(address)));
@@ -554,7 +558,8 @@ class _InjectedValues {
554558

555559
// dartvfs_currentTimeInt64 will turn this into the right value, it's
556560
// annoying to do in JS due to the lack of proper ints.
557-
memory.setInt32Value(target, time.millisecondsSinceEpoch);
561+
memory.setInt64Value(
562+
target, JsBigInt.fromInt(time.millisecondsSinceEpoch));
558563
}),
559564
'xDeviceCharacteristics': allowInterop((int fd) {
560565
final file = callbacks.openedFiles[fd]!;

sqlite3/lib/wasm.dart

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,5 +18,7 @@ import 'package:meta/meta.dart';
1818

1919
export 'common.dart' hide CommmonSqlite3;
2020

21+
export 'src/wasm/vfs/memory.dart' show InMemoryFileSystem;
2122
export 'src/wasm/vfs/simple_opfs.dart' show SimpleOpfsFileSystem;
23+
export 'src/wasm/vfs/indexed_db.dart' show IndexedDbFileSystem;
2224
export 'src/wasm/sqlite3.dart';

sqlite3/test/wasm/sqlite3_test.dart

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@ void main() {
3030
}
3131

3232
sqlite3 = await WasmSqlite3.load(response.bodyBytes);
33+
sqlite3.registerVirtualFileSystem(InMemoryFileSystem(),
34+
makeDefault: true);
3335
}
3436
});
3537

@@ -75,7 +77,7 @@ void main() {
7577
});
7678

7779
// See worker.dart for the supported backends
78-
for (final backend in ['memory', 'opfs', 'indexeddb']) {
80+
for (final backend in ['memory', 'opfs-simple', 'indexeddb']) {
7981
test(backend, () async {
8082
final worker = Worker(workerUri);
8183

sqlite3/test/wasm/utils.dart

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,17 @@
11
import 'package:sqlite3/wasm.dart';
22
import 'package:test/scaffolding.dart';
33

4-
Future<WasmSqlite3> loadSqlite3([SqliteEnvironment? environment]) async {
4+
Future<WasmSqlite3> loadSqlite3([VirtualFileSystem? defaultVfs]) async {
55
final channel = spawnHybridUri('/test/wasm/asset_server.dart');
66
final port = await channel.stream.first as int;
77

88
final sqliteWasm =
99
Uri.parse('http://localhost:$port/example/web/sqlite3.wasm');
1010

11-
return WasmSqlite3.loadFromUrl(sqliteWasm, environment: environment);
11+
final sqlite3 = await WasmSqlite3.loadFromUrl(sqliteWasm);
12+
sqlite3.registerVirtualFileSystem(
13+
defaultVfs ?? InMemoryFileSystem(),
14+
makeDefault: true,
15+
);
16+
return sqlite3;
1217
}

0 commit comments

Comments
 (0)