From c028b09a3048f3ed971f08e2905f970af4c48784 Mon Sep 17 00:00:00 2001 From: Simon Binder Date: Fri, 26 Jul 2024 23:06:27 +0200 Subject: [PATCH 01/12] Prepare 2.4.5 patch release --- sqlite3/CHANGELOG.md | 7 ++----- sqlite3/pubspec.yaml | 2 +- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/sqlite3/CHANGELOG.md b/sqlite3/CHANGELOG.md index 7c5c0ea9..d867926f 100644 --- a/sqlite3/CHANGELOG.md +++ b/sqlite3/CHANGELOG.md @@ -1,12 +1,9 @@ -## 2.4.6 - -- Fix selecting large integers (being represented as a `BigInt` in Dart) - not working when compiled with dartdevc. - ## 2.4.5 - Fix a bug in the OPFS-locks implementation causing a deadlock when the `xSleep` VFS call is issued. +- Fix selecting large integers (being represented as a `BigInt` in Dart) + not working when compiled with dartdevc. ## 2.4.4 diff --git a/sqlite3/pubspec.yaml b/sqlite3/pubspec.yaml index 339a3632..19ad0523 100644 --- a/sqlite3/pubspec.yaml +++ b/sqlite3/pubspec.yaml @@ -1,6 +1,6 @@ name: sqlite3 description: Provides lightweight yet convenient bindings to SQLite by using dart:ffi -version: 2.4.6 +version: 2.4.5 homepage: https://github.com/simolus3/sqlite3.dart/tree/main/sqlite3 issue_tracker: https://github.com/simolus3/sqlite3.dart/issues From 07ef17b26c1765dd4a5627f53509f495bef3488f Mon Sep 17 00:00:00 2001 From: Simon Binder Date: Wed, 7 Aug 2024 21:32:56 +0200 Subject: [PATCH 02/12] Fix link in custom wasm build readme --- sqlite3/example/custom_wasm_build/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sqlite3/example/custom_wasm_build/README.md b/sqlite3/example/custom_wasm_build/README.md index 6deb8428..2af135cf 100644 --- a/sqlite3/example/custom_wasm_build/README.md +++ b/sqlite3/example/custom_wasm_build/README.md @@ -27,7 +27,7 @@ rustup target add wasm32-wasi ``` Additionally, you need to download WASI compiler builtins and the associated libc -as described in the [build instructions](https://github.com/simolus3/sqlite3.dart/tree/rust-wasm-build/sqlite3#compiling). +as described in the [build instructions](https://github.com/simolus3/sqlite3.dart/tree/main/sqlite3#compiling). ## Building From 719fde0e5384ef73ede34e1504af9ecd54013d28 Mon Sep 17 00:00:00 2001 From: Simon Binder Date: Thu, 8 Aug 2024 15:27:41 +0200 Subject: [PATCH 03/12] Fix sqlcipher on Android with Flutter 3.24 Squashed commit of the following: commit dfaff6affbcbe01c5add6d12bee2522e16a393c9 Author: The one with the braid Date: Thu Aug 8 13:53:05 2024 +0200 chore: update Gradle configuration - match minimum SDK version to Flutter's minimum - migrate compileSdkVersion to compileSdk - update compileSdk to Flutter's target Signed-off-by: The one with the braid --- .../sqlcipher_flutter/android/app/build.gradle | 12 +++++++++++- .../sqlcipher_flutter/android/build.gradle | 4 ++-- .../android/gradle/wrapper/gradle-wrapper.properties | 2 +- sqlcipher_flutter_libs/CHANGELOG.md | 4 ++++ sqlcipher_flutter_libs/android/build.gradle | 7 +++---- sqlcipher_flutter_libs/pubspec.yaml | 2 +- 6 files changed, 22 insertions(+), 9 deletions(-) diff --git a/integration_tests/sqlcipher_flutter/android/app/build.gradle b/integration_tests/sqlcipher_flutter/android/app/build.gradle index 23976460..59eeeed3 100644 --- a/integration_tests/sqlcipher_flutter/android/app/build.gradle +++ b/integration_tests/sqlcipher_flutter/android/app/build.gradle @@ -26,7 +26,17 @@ apply plugin: 'kotlin-android' apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" android { - compileSdkVersion 31 + compileSdkVersion 34 + namespace "com.example.sqlcipher_flutter" + + compileOptions { + sourceCompatibility = 1.8 + targetCompatibility = 1.8 + } + + kotlinOptions { + jvmTarget = "1.8" + } sourceSets { main.java.srcDirs += 'src/main/kotlin' diff --git a/integration_tests/sqlcipher_flutter/android/build.gradle b/integration_tests/sqlcipher_flutter/android/build.gradle index c732d03f..c7245c8c 100644 --- a/integration_tests/sqlcipher_flutter/android/build.gradle +++ b/integration_tests/sqlcipher_flutter/android/build.gradle @@ -1,12 +1,12 @@ buildscript { - ext.kotlin_version = '1.6.10' + ext.kotlin_version = '2.0.10' repositories { google() jcenter() } dependencies { - classpath 'com.android.tools.build:gradle:7.0.0' + classpath 'com.android.tools.build:gradle:8.5.0' classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" } } diff --git a/integration_tests/sqlcipher_flutter/android/gradle/wrapper/gradle-wrapper.properties b/integration_tests/sqlcipher_flutter/android/gradle/wrapper/gradle-wrapper.properties index 562c5e44..d955bd4e 100644 --- a/integration_tests/sqlcipher_flutter/android/gradle/wrapper/gradle-wrapper.properties +++ b/integration_tests/sqlcipher_flutter/android/gradle/wrapper/gradle-wrapper.properties @@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-7.3.3-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.9-bin.zip diff --git a/sqlcipher_flutter_libs/CHANGELOG.md b/sqlcipher_flutter_libs/CHANGELOG.md index ed404eb9..3da95b63 100644 --- a/sqlcipher_flutter_libs/CHANGELOG.md +++ b/sqlcipher_flutter_libs/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.6.4 + +- Fix compilation on Android by upgrading `compileSdk` version. + ## 0.6.3 - Enable extension loading on Windows and Linux to match the compile options diff --git a/sqlcipher_flutter_libs/android/build.gradle b/sqlcipher_flutter_libs/android/build.gradle index a5a7e7fe..901870b7 100644 --- a/sqlcipher_flutter_libs/android/build.gradle +++ b/sqlcipher_flutter_libs/android/build.gradle @@ -8,7 +8,7 @@ buildscript { } dependencies { - classpath 'com.android.tools.build:gradle:8.0.1' + classpath 'com.android.tools.build:gradle:8.5.1' } } @@ -22,15 +22,14 @@ rootProject.allprojects { apply plugin: 'com.android.library' android { - compileSdkVersion 28 - // Conditional for compatibility with AGP <4.2. if (project.android.hasProperty("namespace")) { namespace 'eu.simonbinder.sqlite3_flutter_libs' } defaultConfig { - minSdkVersion 16 + minSdkVersion 21 + compileSdk 34 } lintOptions { disable 'InvalidPackage' diff --git a/sqlcipher_flutter_libs/pubspec.yaml b/sqlcipher_flutter_libs/pubspec.yaml index df39fc88..405ebe53 100644 --- a/sqlcipher_flutter_libs/pubspec.yaml +++ b/sqlcipher_flutter_libs/pubspec.yaml @@ -1,6 +1,6 @@ name: sqlcipher_flutter_libs description: Flutter plugin to include native SQLCipher libraries in your app -version: 0.6.3 +version: 0.6.4 homepage: https://github.com/simolus3/sqlite3.dart/tree/main/sqlcipher_flutter_libs issue_tracker: https://github.com/simolus3/sqlite3.dart/issues From 842b81c48b13e2823cd8e137cdeea8586a369d21 Mon Sep 17 00:00:00 2001 From: Simon Binder Date: Sun, 11 Aug 2024 14:29:56 +0200 Subject: [PATCH 04/12] Wasm: Compile as reactor module --- .github/workflows/main.yml | 2 +- sqlite3/CHANGELOG.md | 4 ++++ sqlite3/assets/wasm/CMakeLists.txt | 3 ++- sqlite3/lib/src/wasm/js_interop/wasm.dart | 8 ++++++++ sqlite3/pubspec.yaml | 2 +- 5 files changed, 16 insertions(+), 3 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 831486df..15903429 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -176,7 +176,7 @@ jobs: - name: Web tests run: | - curl https://storage.googleapis.com/simon-public-euw3/assets/sqlite3/wasm/2.4.3/sqlite3.wasm -o example/web/sqlite3.wasm + curl https://storage.googleapis.com/simon-public-euw3/assets/sqlite3/wasm/2.4.6/sqlite3.wasm -o example/web/sqlite3.wasm dart test -P web -r expanded # If browsers behave differently on different platforms, surely that's not our fault... # So, only run browser tests on Linux to be faster. diff --git a/sqlite3/CHANGELOG.md b/sqlite3/CHANGELOG.md index d867926f..fd55b57b 100644 --- a/sqlite3/CHANGELOG.md +++ b/sqlite3/CHANGELOG.md @@ -1,3 +1,7 @@ +## 2.4.6 + +- WebAssembly: Call `_initialize` function of sqlite3 module if one is present. + ## 2.4.5 - Fix a bug in the OPFS-locks implementation causing a deadlock when the `xSleep` diff --git a/sqlite3/assets/wasm/CMakeLists.txt b/sqlite3/assets/wasm/CMakeLists.txt index 95baed69..b701353c 100644 --- a/sqlite3/assets/wasm/CMakeLists.txt +++ b/sqlite3/assets/wasm/CMakeLists.txt @@ -32,7 +32,8 @@ macro(base_sqlite3_target name) helpers.c ) - target_link_options(${name} PRIVATE -nostartfiles -Wl,--import-memory -Wl,--no-entry -Wl,--export-dynamic) + target_link_options(${name} PRIVATE -mexec-model=reactor -mcpu=generic -Wl,--import-memory -Wl,--no-entry -Wl,--export-dynamic) + target_compile_options(${name} PRIVATE -std=c23 -mcpu=generic) target_include_directories(${name} PRIVATE "${PROJECT_SOURCE_DIR}/") target_include_directories(${name} PRIVATE ${sqlite3_SOURCE_DIR}) target_compile_definitions(${name} PRIVATE diff --git a/sqlite3/lib/src/wasm/js_interop/wasm.dart b/sqlite3/lib/src/wasm/js_interop/wasm.dart index 8f8e2561..bb807c58 100644 --- a/sqlite3/lib/src/wasm/js_interop/wasm.dart +++ b/sqlite3/lib/src/wasm/js_interop/wasm.dart @@ -54,6 +54,14 @@ class WasmInstance { }); final native = await _instantiateStreaming(response, importsJs).toDart; + + // If the module has an `_initialize` export, it needs to be called to run + // C constructors and set up memory. + final exports = native.instance.exports; + if (exports.has('_initialize')) { + (exports['_initialize'] as JSFunction).callAsFunction(); + } + return WasmInstance._(native.instance); } } diff --git a/sqlite3/pubspec.yaml b/sqlite3/pubspec.yaml index 19ad0523..bbd1afff 100644 --- a/sqlite3/pubspec.yaml +++ b/sqlite3/pubspec.yaml @@ -1,6 +1,6 @@ name: sqlite3 description: Provides lightweight yet convenient bindings to SQLite by using dart:ffi -version: 2.4.5 +version: 2.4.6-dev homepage: https://github.com/simolus3/sqlite3.dart/tree/main/sqlite3 issue_tracker: https://github.com/simolus3/sqlite3.dart/issues From ca9d78ad179021aab38cab9c505be7df50ccfaea Mon Sep 17 00:00:00 2001 From: Simon Binder Date: Sun, 11 Aug 2024 17:33:31 +0200 Subject: [PATCH 05/12] Use clang driver to compile sqlite3 to wasm --- sqlite3/README.md | 4 +- sqlite3/assets/wasm/CMakeLists.txt | 122 +++++++++++------- sqlite3/assets/wasm/toolchain.cmake | 10 -- sqlite3/example/custom_wasm_build/build.rs | 3 +- .../tool/{wasm_dce.dart => wasm_symbols.dart} | 36 +----- 5 files changed, 86 insertions(+), 89 deletions(-) delete mode 100644 sqlite3/assets/wasm/toolchain.cmake rename sqlite3/tool/{wasm_dce.dart => wasm_symbols.dart} (53%) diff --git a/sqlite3/README.md b/sqlite3/README.md index ae44af4e..9ee18f7e 100644 --- a/sqlite3/README.md +++ b/sqlite3/README.md @@ -175,7 +175,7 @@ With wasi in `/usr/share/wasi-sysroot` and the default clang compiler having the required builtins, you can setup the build with: ``` -cmake -S assets/wasm -B .dart_tool/sqlite3_build --toolchain toolchain.cmake +cmake -S assets/wasm -B .dart_tool/sqlite3_build ``` ##### macOS @@ -196,7 +196,7 @@ Replace `clang/18` with the correct directory if you're using a different versio Then, set up the build with ``` -cmake -Dwasi_sysroot=/opt/wasi-sysroot -Dclang=/opt/homebrew/opt/llvm/bin/clang -S assets/wasm -B .dart_tool/sqlite3_build --toolchain toolchain.cmake +cmake -Dwasi_sysroot=/opt/wasi-sysroot -Dclang=/opt/homebrew/opt/llvm/bin/clang -S assets/wasm -B .dart_tool/sqlite3_build ``` #### Building diff --git a/sqlite3/assets/wasm/CMakeLists.txt b/sqlite3/assets/wasm/CMakeLists.txt index b701353c..c557d782 100644 --- a/sqlite3/assets/wasm/CMakeLists.txt +++ b/sqlite3/assets/wasm/CMakeLists.txt @@ -1,65 +1,97 @@ -cmake_minimum_required(VERSION 3.14) +cmake_minimum_required(VERSION 3.24) + set(PROJECT_NAME "sqlite3_web") project(${PROJECT_NAME} LANGUAGES C) +set(triple wasm32-unknown-wasi) +set(wasi_sysroot "/usr/share/wasi-sysroot" CACHE PATH "Path to wasi sysroot") +set(clang "clang" CACHE FILEPATH "Path to wasm-capable clang executable") + include(FetchContent) -if (CMAKE_VERSION VERSION_GREATER_EQUAL "3.24.0") - # cmake 3.24.0 added the `DOWNLOAD_EXTRACT_TIMESTAMP` and prints an ugly warning when - # the default is used, so override it to the recommended behavior. - # We can't really ask users to use a cmake that recent, so there's this if here. - FetchContent_Declare( + +FetchContent_Declare( sqlite3 # NOTE: When changing this, also update `test/wasm/sqlite3_test.dart` URL https://sqlite.org/2024/sqlite-autoconf-3460000.tar.gz DOWNLOAD_EXTRACT_TIMESTAMP NEW +) + +FetchContent_MakeAvailable(sqlite3) + +file(DOWNLOAD https://raw.githubusercontent.com/sqlite/sqlite/master/src/test_vfstrace.c "${CMAKE_BINARY_DIR}/vfstrace.c") + +# Generate symbols we need to export from the sqlite3.wasm build +add_custom_command( + OUTPUT required_symbols.txt + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/../../ + COMMAND dart run tool/wasm_symbols.dart ${CMAKE_CURRENT_BINARY_DIR}/required_symbols.txt + DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/../../tool/wasm_symbols.dart + VERBATIM +) +add_custom_target(required_symbols DEPENDS required_symbols.txt) + +macro(base_sqlite3_target name debug) + set(clang_output ${name}.clang.wasm) + set(init_output ${name}.init.wasm) + set(output ${init_output}) + + set(sources + ${CMAKE_CURRENT_SOURCE_DIR}/os_web.c + ${CMAKE_CURRENT_SOURCE_DIR}/helpers.c + ${sqlite3_SOURCE_DIR}/sqlite3.c ) -else() - FetchContent_Declare( - sqlite3 - # NOTE: When changing this, also update `test/wasm/sqlite3_test.dart` - URL https://sqlite.org/2024/sqlite-autoconf-3460000.tar.gz + set(flags -Wall -Wextra -Wno-unused-parameter -Wno-unused-function) + + if(${debug}) + list(APPEND sources "${CMAKE_BINARY_DIR}/vfstrace.c") + list(APPEND flags "-g" "-DDEBUG") + else() + list(APPEND flags "-Oz" "-DNDEBUG" "-flto") + endif() + + add_custom_command( + OUTPUT ${clang_output} + COMMAND ${clang} --target=${triple} -std=c23 + ${flags} + -o ${clang_output} + -I ${PROJECT_SOURCE_DIR} -I ${sqlite3_SOURCE_DIR} + -D_HAVE_SQLITE_CONFIG_H + -mcpu=generic + -mexec-model=reactor + -fno-stack-protector -fno-stack-clash-protection + -Wl,--import-memory + --sysroot /usr/share/wasi-sysroot + ${sources} + @${CMAKE_CURRENT_BINARY_DIR}/required_symbols.txt + DEPENDS ${sources} required_symbols + VERBATIM ) -endif() -FetchContent_MakeAvailable(sqlite3) + add_custom_command( + OUTPUT ${init_output} + COMMAND wasm-ctor-eval -c _initialize ${clang_output} -o ${init_output} + VERBATIM + DEPENDS ${clang_output} + ) -set(wasm_visibility "__attribute__((visibility(\"default\")))") + if(NOT ${debug}) + set(output ${name}.wasm) -macro(base_sqlite3_target name) - add_executable(${name} - "${sqlite3_SOURCE_DIR}/sqlite3.c" - os_web.c - helpers.c + add_custom_command( + OUTPUT ${output} + COMMAND wasm-opt --strip --strip-producers -c -O4 ${init_output} -o ${output} + VERBATIM + DEPENDS ${init_output} ) + endif() - target_link_options(${name} PRIVATE -mexec-model=reactor -mcpu=generic -Wl,--import-memory -Wl,--no-entry -Wl,--export-dynamic) - target_compile_options(${name} PRIVATE -std=c23 -mcpu=generic) - target_include_directories(${name} PRIVATE "${PROJECT_SOURCE_DIR}/") - target_include_directories(${name} PRIVATE ${sqlite3_SOURCE_DIR}) - target_compile_definitions(${name} PRIVATE - _HAVE_SQLITE_CONFIG_H - SQLITE_API=${wasm_visibility} - ) - set_property(TARGET ${name} PROPERTY INTERPROCEDURAL_OPTIMIZATION TRUE) + add_custom_target(${name} DEPENDS ${output}) endmacro() -base_sqlite3_target(sqlite3_debug) -file(DOWNLOAD https://raw.githubusercontent.com/sqlite/sqlite/master/src/test_vfstrace.c "${CMAKE_BINARY_DIR}/vfstrace.c") -target_sources(sqlite3_debug PRIVATE "${CMAKE_BINARY_DIR}/vfstrace.c") -target_compile_options(sqlite3_debug PRIVATE -g) -target_compile_definitions(sqlite3_debug PRIVATE SQLITE_ENABLE_VFSTRACE SQLITE_ENABLE_API_ARMOR) -set_target_properties(sqlite3_debug PROPERTIES OUTPUT_NAME "sqlite3" SUFFIX ".debug.wasm") - -base_sqlite3_target(sqlite3_opt) -target_compile_options(sqlite3_opt PRIVATE -Oz) -set_target_properties(sqlite3_opt PROPERTIES OUTPUT_NAME "sqlite3" SUFFIX ".tmp.wasm") -add_custom_command(TARGET sqlite3_opt POST_BUILD - WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/../../ - COMMAND dart run tool/wasm_dce.dart ${CMAKE_CURRENT_BINARY_DIR}/sqlite3.tmp.wasm ${CMAKE_CURRENT_BINARY_DIR}/sqlite3.dce.wasm - COMMAND wasm-opt ${CMAKE_CURRENT_BINARY_DIR}/sqlite3.dce.wasm -O4 -o ${CMAKE_CURRENT_BINARY_DIR}/sqlite3.wasm -) +base_sqlite3_target(sqlite3_debug true) +base_sqlite3_target(sqlite3_opt false) add_custom_target(output) -add_custom_command(TARGET output COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_BINARY_DIR}/sqlite3.wasm ${PROJECT_SOURCE_DIR}/../../example/web/sqlite3.wasm) -add_custom_command(TARGET output COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_BINARY_DIR}/sqlite3.debug.wasm ${PROJECT_SOURCE_DIR}/../../example/web/sqlite3.debug.wasm) +add_custom_command(TARGET output COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_BINARY_DIR}/sqlite3_opt.wasm ${PROJECT_SOURCE_DIR}/../../example/web/sqlite3.wasm) +add_custom_command(TARGET output COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_BINARY_DIR}/sqlite3_debug.init.wasm ${PROJECT_SOURCE_DIR}/../../example/web/sqlite3.debug.wasm) add_dependencies(output sqlite3_debug sqlite3_opt) diff --git a/sqlite3/assets/wasm/toolchain.cmake b/sqlite3/assets/wasm/toolchain.cmake deleted file mode 100644 index 7a04f226..00000000 --- a/sqlite3/assets/wasm/toolchain.cmake +++ /dev/null @@ -1,10 +0,0 @@ -set(CMAKE_SYSTEM_NAME wasm) - -set(triple wasm32-unknown-wasi) -set(wasi_sysroot "/usr/share/wasi-sysroot" CACHE PATH "Path to wasi sysroot") -set(clang "clang" CACHE FILEPATH "Path to wasm-capable clang executable") - -set(CMAKE_C_COMPILER ${clang}) -set(CMAKE_C_COMPILER_TARGET ${triple}) -set(CMAKE_SYSROOT ${wasi_sysroot}) -set(CMAKE_C_COMPILER_WORKS 1) diff --git a/sqlite3/example/custom_wasm_build/build.rs b/sqlite3/example/custom_wasm_build/build.rs index 69814792..60ba6436 100644 --- a/sqlite3/example/custom_wasm_build/build.rs +++ b/sqlite3/example/custom_wasm_build/build.rs @@ -7,9 +7,8 @@ fn main() { env::var("WASI_SYSROOT").unwrap_or_else(|_| "/usr/share/wasi-sysroot".to_string()); let cmake_dir = Config::new("../../assets/wasm/") - .configure_arg("--toolchain") - .configure_arg(std::fs::canonicalize("../../assets/wasm/toolchain.cmake").unwrap()) .define("wasi_sysroot", &sysroot) + .define("CMAKE_C_COMPILER_WORKS", "1") .build_target("sqlite3_opt_lib") .build_target("help") // We only need the sources .build(); diff --git a/sqlite3/tool/wasm_dce.dart b/sqlite3/tool/wasm_symbols.dart similarity index 53% rename from sqlite3/tool/wasm_dce.dart rename to sqlite3/tool/wasm_symbols.dart index c45de1b4..3e5497c0 100644 --- a/sqlite3/tool/wasm_dce.dart +++ b/sqlite3/tool/wasm_symbols.dart @@ -1,45 +1,21 @@ -import 'dart:convert'; import 'dart:io'; import 'package:analyzer/dart/analysis/utilities.dart'; import 'package:analyzer/dart/ast/visitor.dart'; import 'package:analyzer/src/dart/ast/ast.dart'; -void main(List args) async { - if (args.length != 2) { - print( - 'Removes elements from a WASM file that are not accessed by the Dart bindings.'); - print('Usage: dart run tool/wasm_dce.dart in.wasm out.wasm'); - exit(1); - } - +/// Writes flags for clang that will `-Wl,--export` all symbols used by the +/// `sqlite3` Dart package. +void main(List args) { final file = File('lib/src/wasm/wasm_interop.dart'); final ast = parseString(content: file.readAsStringSync(), path: file.path); final finder = _FindUsedSymbols(); ast.unit.accept(finder); - final list = File('.dart_tool/used_wasm_symbols.json'); - list.writeAsStringSync(json.encode([ - for (final entry in finder.symbols) - { - 'name': '_$entry', - 'export': entry, - 'root': true, - } - ])); - - final process = await Process.start( - 'wasm-metadce', - [ - args[0], - '--graph-file=${list.path}', - '--output', - args[1], - ], - mode: ProcessStartMode.inheritStdio, - ); - exit(await process.exitCode); + final output = File(args[0]); + output.writeAsStringSync( + finder.symbols.map((e) => '-Wl,--export=$e').join(' ')); } class _FindUsedSymbols extends RecursiveAstVisitor { From 54e6650b6bdb03a9541f7d648a4885f843639971 Mon Sep 17 00:00:00 2001 From: Simon Binder Date: Mon, 12 Aug 2024 11:58:57 +0200 Subject: [PATCH 06/12] Support latest dependencies --- sqlite3/CHANGELOG.md | 1 + sqlite3/example/web/worker.dart | 2 +- sqlite3/pubspec.yaml | 4 ++-- sqlite3/test/wasm/sqlite3_test.dart | 2 +- sqlite3/test/wasm/worker.dart | 2 +- 5 files changed, 6 insertions(+), 5 deletions(-) diff --git a/sqlite3/CHANGELOG.md b/sqlite3/CHANGELOG.md index fd55b57b..0d00910d 100644 --- a/sqlite3/CHANGELOG.md +++ b/sqlite3/CHANGELOG.md @@ -1,6 +1,7 @@ ## 2.4.6 - WebAssembly: Call `_initialize` function of sqlite3 module if one is present. +- Support version 1.0.0 of `package:web`. ## 2.4.5 diff --git a/sqlite3/example/web/worker.dart b/sqlite3/example/web/worker.dart index cd9e998d..aafa4c46 100644 --- a/sqlite3/example/web/worker.dart +++ b/sqlite3/example/web/worker.dart @@ -20,7 +20,7 @@ void main() { if (data.equals('start'.toJS).toDart) { final options = WasmVfs.createOptions(); - final worker = web.Worker(''); // Clone this worker + final worker = web.Worker(''.toJS); // Clone this worker worker.postMessage(options); // Now, wait for the worker to report that it has been initialized. diff --git a/sqlite3/pubspec.yaml b/sqlite3/pubspec.yaml index bbd1afff..ba6d2208 100644 --- a/sqlite3/pubspec.yaml +++ b/sqlite3/pubspec.yaml @@ -26,14 +26,14 @@ dependencies: ffi: '>=1.2.1 <3.0.0' meta: ^1.3.0 path: ^1.8.0 - web: ^0.5.0 + web: ^1.0.0 dev_dependencies: analyzer: ^6.4.1 build_daemon: ^4.0.0 build_runner: ^2.1.7 build_web_compilers: ^4.0.3 - ffigen: ^12.0.0 + ffigen: ^13.0.0 http: ^1.2.1 lints: ^4.0.0 shelf: ^1.4.0 diff --git a/sqlite3/test/wasm/sqlite3_test.dart b/sqlite3/test/wasm/sqlite3_test.dart index 5c4c9cd2..468bf474 100644 --- a/sqlite3/test/wasm/sqlite3_test.dart +++ b/sqlite3/test/wasm/sqlite3_test.dart @@ -93,7 +93,7 @@ void main() { test( backend, () async { - final worker = web.Worker(workerUri); + final worker = web.Worker(workerUri.toJS); web.EventStreamProviders.errorEvent .forTarget(worker) diff --git a/sqlite3/test/wasm/worker.dart b/sqlite3/test/wasm/worker.dart index 561d88b3..c162234d 100644 --- a/sqlite3/test/wasm/worker.dart +++ b/sqlite3/test/wasm/worker.dart @@ -69,7 +69,7 @@ Future _startTest(String fsImplementation, Uri wasmUri) async { // server needed for synchronous access. final options = WasmVfs.createOptions(); - final worker = web.Worker(scope.location.href); + final worker = web.Worker(scope.location.href.toJS); worker.postMessage(options); // Wait for the worker to acknowledge it being ready From e1552ce23289ad7e0f6bc8d29fe7ce80e7523287 Mon Sep 17 00:00:00 2001 From: Simon Binder Date: Mon, 12 Aug 2024 12:06:25 +0200 Subject: [PATCH 07/12] Prepare pkg:sqlite3 2.4.6 for release --- sqlite3/pubspec.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sqlite3/pubspec.yaml b/sqlite3/pubspec.yaml index ba6d2208..76a9c9b0 100644 --- a/sqlite3/pubspec.yaml +++ b/sqlite3/pubspec.yaml @@ -1,6 +1,6 @@ name: sqlite3 description: Provides lightweight yet convenient bindings to SQLite by using dart:ffi -version: 2.4.6-dev +version: 2.4.6 homepage: https://github.com/simolus3/sqlite3.dart/tree/main/sqlite3 issue_tracker: https://github.com/simolus3/sqlite3.dart/issues From 2a4778b54a75e0b09dd06f8930ea64d331532678 Mon Sep 17 00:00:00 2001 From: Simon Binder Date: Mon, 26 Aug 2024 17:41:41 +0200 Subject: [PATCH 08/12] Update package:web in sqlite3_web --- sqlite3_web/CHANGELOG.md | 4 ++++ sqlite3_web/lib/src/client.dart | 4 ++-- sqlite3_web/lib/src/worker.dart | 4 ++-- sqlite3_web/pubspec.yaml | 2 +- sqlite3_web/test/integration_test.dart | 27 +++++++++++++++++++++++++- sqlite3_web/tool/server.dart | 19 ++++++++++++++++-- sqlite3_web/web/main.dart | 12 +++++++----- 7 files changed, 59 insertions(+), 13 deletions(-) diff --git a/sqlite3_web/CHANGELOG.md b/sqlite3_web/CHANGELOG.md index ea0cbd74..490494e5 100644 --- a/sqlite3_web/CHANGELOG.md +++ b/sqlite3_web/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.1.3 + +- Support latest version of `package:web`. + ## 0.1.2-wip - Fix preferred databases not being sorted correctly. diff --git a/sqlite3_web/lib/src/client.dart b/sqlite3_web/lib/src/client.dart index 92a9264b..6c240d13 100644 --- a/sqlite3_web/lib/src/client.dart +++ b/sqlite3_web/lib/src/client.dart @@ -169,7 +169,7 @@ final class DatabaseClient implements WebSqlite { if (globalContext.has('Worker')) { final dedicated = Worker( - workerUri.toString(), + workerUri.toString().toJS, WorkerOptions(name: 'sqlite3_worker'), ); @@ -184,7 +184,7 @@ final class DatabaseClient implements WebSqlite { } if (globalContext.has('SharedWorker')) { - final shared = SharedWorker(workerUri.toString()); + final shared = SharedWorker(workerUri.toString().toJS); shared.port.start(); final (endpoint, channel) = await createChannel(); diff --git a/sqlite3_web/lib/src/worker.dart b/sqlite3_web/lib/src/worker.dart index 4be2c83f..da8a1cfe 100644 --- a/sqlite3_web/lib/src/worker.dart +++ b/sqlite3_web/lib/src/worker.dart @@ -282,7 +282,7 @@ final class DatabaseState { switch (mode) { case FileSystemImplementation.opfsLocks: final options = WasmVfs.createOptions(root: pathForOpfs(name)); - final worker = Worker(Uri.base.toString()); + final worker = Worker(Uri.base.toString().toJS); StartFileSystemServer(options: options).sendToWorker(worker); @@ -474,7 +474,7 @@ final class WorkerRunner { } Worker useOrSpawnInnerWorker() { - return _innerWorker ??= Worker(Uri.base.toString()); + return _innerWorker ??= Worker(Uri.base.toString().toJS); } } diff --git a/sqlite3_web/pubspec.yaml b/sqlite3_web/pubspec.yaml index 35a05bd3..ac1ec552 100644 --- a/sqlite3_web/pubspec.yaml +++ b/sqlite3_web/pubspec.yaml @@ -10,7 +10,7 @@ environment: dependencies: sqlite3: ^2.4.3 stream_channel: ^2.1.2 - web: ^0.5.0 + web: ^1.0.0 dev_dependencies: lints: ^2.1.0 diff --git a/sqlite3_web/test/integration_test.dart b/sqlite3_web/test/integration_test.dart index 0cdd06f1..89bcd88e 100644 --- a/sqlite3_web/test/integration_test.dart +++ b/sqlite3_web/test/integration_test.dart @@ -14,16 +14,25 @@ enum Browser { (StorageMode.opfs, AccessMode.throughSharedWorker) }, missingFeatures: {MissingBrowserFeature.dedicatedWorkersInSharedWorkers}, + defaultImplementation: ( + StorageMode.opfs, + AccessMode.throughDedicatedWorker + ), ), - firefox(driverUriString: 'http://localhost:4444/'); + firefox( + driverUriString: 'http://localhost:4444/', + defaultImplementation: (StorageMode.opfs, AccessMode.throughSharedWorker), + ); final bool isChromium; final String driverUriString; final Set<(StorageMode, AccessMode)> unsupportedImplementations; final Set missingFeatures; + final (StorageMode, AccessMode) defaultImplementation; const Browser({ required this.driverUriString, + required this.defaultImplementation, this.isChromium = false, this.unsupportedImplementations = const {}, this.missingFeatures = const {}, @@ -81,6 +90,17 @@ void main() { final rawDriver = await createDriver( spec: browser.isChromium ? WebDriverSpec.JsonWire : WebDriverSpec.W3c, uri: browser.driverUri, + desired: { + 'goog:chromeOptions': { + 'args': [ + '--headless=new', + '--disable-search-engine-choice-screen', + ], + }, + 'moz:firefoxOptions': { + 'args': ['-headless'] + }, + }, ); driver = TestWebDriver(server, rawDriver); @@ -107,6 +127,11 @@ void main() { expect(result.impls, browser.availableImplementations); }); + test('picks recommended option', () async { + final (storage, access) = await driver.openDatabase(); + expect((storage, access), browser.defaultImplementation); + }); + for (final (storage, access) in browser.availableImplementations) { test('$storage through $access', () async { await driver.openDatabase((storage, access)); diff --git a/sqlite3_web/tool/server.dart b/sqlite3_web/tool/server.dart index 86aa095f..2d29a81b 100644 --- a/sqlite3_web/tool/server.dart +++ b/sqlite3_web/tool/server.dart @@ -136,13 +136,28 @@ class TestWebDriver { ); } - Future openDatabase([(StorageMode, AccessMode)? implementation]) async { + Future<(StorageMode, AccessMode)> openDatabase( + [(StorageMode, AccessMode)? implementation]) async { final desc = switch (implementation) { null => null, (var storage, var access) => '${storage.name}:${access.name}' }; - await driver.executeAsync('open(arguments[0], arguments[1])', [desc]); + final res = await driver + .executeAsync('open(arguments[0], arguments[1])', [desc]) as String?; + + if (res == null) { + return implementation!; + } else { + // If we're using connectToRecommended, this returns the storage/access + // mode actually chosen. + final split = res.split(':'); + + return ( + StorageMode.values.byName(split[0]), + AccessMode.values.byName(split[1]) + ); + } } Future closeDatabase() async { diff --git a/sqlite3_web/web/main.dart b/sqlite3_web/web/main.dart index 88b4cffa..7f95d8ad 100644 --- a/sqlite3_web/web/main.dart +++ b/sqlite3_web/web/main.dart @@ -8,7 +8,7 @@ import 'package:sqlite3_web/sqlite3_web.dart'; final sqlite3WasmUri = Uri.parse('sqlite3.wasm'); final workerUri = Uri.parse('worker.dart.js'); -const databasName = 'database'; +const databaseName = 'database'; WebSqlite? webSqlite; @@ -28,7 +28,7 @@ void main() { document.getElementById('selfcheck')?.onClick.listen((event) async { print('starting'); final sqlite = initializeSqlite(); - final database = await sqlite.connectToRecommended(databasName); + final database = await sqlite.connectToRecommended(databaseName); print('selected storage: ${database.storage} through ${database.access}'); print('missing features: ${database.features.missingFeatures}'); @@ -87,15 +87,17 @@ Future _waitForUpdate(String? _) async { Future _open(String? implementationName) async { final sqlite = initializeSqlite(); Database db; + var returnValue = implementationName; if (implementationName != null) { final split = implementationName.split(':'); - db = await sqlite.connect(databasName, StorageMode.values.byName(split[0]), + db = await sqlite.connect(databaseName, StorageMode.values.byName(split[0]), AccessMode.values.byName(split[1])); } else { - final result = await sqlite.connectToRecommended(databasName); + final result = await sqlite.connectToRecommended(databaseName); db = result.database; + returnValue = '${result.storage.name}:${result.access.name}'; } // Make sure it works! @@ -103,7 +105,7 @@ Future _open(String? implementationName) async { updates = StreamQueue(db.updates); database = db; - return null; + return returnValue?.toJS; } Future _exec(String? sql) async { From 7ecc4cc01c36531e02d2194a3c8fe7b16b0d9838 Mon Sep 17 00:00:00 2001 From: Simon Binder Date: Mon, 26 Aug 2024 22:18:22 +0200 Subject: [PATCH 09/12] sqlite web: Export message port to database --- sqlite3_web/lib/src/client.dart | 28 ++++++++++++++++++ sqlite3_web/lib/src/database.dart | 31 ++++++++++++++++++++ sqlite3_web/lib/src/protocol.dart | 47 ++++++++++++++++++++++++++++++- sqlite3_web/lib/src/worker.dart | 26 +++++++++++++---- 4 files changed, 126 insertions(+), 6 deletions(-) diff --git a/sqlite3_web/lib/src/client.dart b/sqlite3_web/lib/src/client.dart index 6c240d13..bbc106e7 100644 --- a/sqlite3_web/lib/src/client.dart +++ b/sqlite3_web/lib/src/client.dart @@ -53,6 +53,11 @@ final class RemoteDatabase implements Database { } } + @override + Future get closed { + return connection.closed; + } + @override Future dispose() async { _isClosed = true; @@ -125,6 +130,16 @@ final class RemoteDatabase implements Database { final result = await select('pragma user_version;'); return result.single[0] as int; } + + @override + Future additionalConnection() async { + final response = await connection.sendRequest( + OpenAdditonalConnection(requestId: 0, databaseId: databaseId), + MessageType.endpointResponse, + ); + final endpoint = response.endpoint; + return (endpoint.port, endpoint.lockName!); + } } final class WorkerConnection extends ProtocolChannel { @@ -301,6 +316,19 @@ final class DatabaseClient implements WebSqlite { ); } + Future connectToExisting(SqliteWebEndpoint endpoint) async { + final channel = WorkerConnection( + WebEndpoint(port: endpoint.$1, lockName: endpoint.$2).connect()); + + return RemoteDatabase( + connection: channel, + // The database id for this pre-existing connection is always zero. + // It gets assigned by the worker handling the OpenAdditonalConnection + // request. + databaseId: 0, + ); + } + @override Future connect( String name, StorageMode type, AccessMode access) async { diff --git a/sqlite3_web/lib/src/database.dart b/sqlite3_web/lib/src/database.dart index d6f91837..cb4d113a 100644 --- a/sqlite3_web/lib/src/database.dart +++ b/sqlite3_web/lib/src/database.dart @@ -1,6 +1,7 @@ import 'dart:js_interop'; import 'package:sqlite3/wasm.dart'; +import 'package:web/web.dart' hide FileSystem; import 'types.dart'; import 'client.dart'; @@ -29,6 +30,16 @@ abstract base class DatabaseController { ClientConnection connection, JSAny? request); } +/// An endpoint that can be used, by any running JavaScript context in the same +/// website, to connect to an existing [Database]. +/// +/// These endpoints are created by calling [Database.additionalConnection] and +/// consist of a [MessagePort] and a [String] internally identifying the +/// connection. Both objects can be transferred over send ports towards another +/// worker or context. That context can then use [WebSqlite.connectToPort] to +/// connect to the port already opened. +typedef SqliteWebEndpoint = (MessagePort, String); + /// Abstraction over a database either available locally or in a remote worker. abstract class Database { FileSystem get fileSystem; @@ -39,6 +50,15 @@ abstract class Database { /// stream is active. Stream get updates; + /// A future that resolves when the database is closed. + /// + /// Typically, databases are closed because [dispose] is called. For databases + /// opened with [WebSqlite.connectToPort] however, it's possible that the + /// original worker hosting the database gets closed without this [Database] + /// instance being explicitly [dispose]d. In those cases, monitoring [closed] + /// is useful to react to databases closing. + Future get closed; + /// Closes this database and instructs the worker to release associated /// resources. /// @@ -67,6 +87,12 @@ abstract class Database { /// Custom requests are handled by implementing `handleCustomRequest` in your /// `WorkerDatabase` subclass. Future customRequest(JSAny? request); + + /// Creates a [MessagePort] (a transferrable object that can be sent to + /// another JavaScript context like a worker) that can be used with + /// [WebSqlite.connectToPort] to open another instance of this database + /// remotely. + Future additionalConnection(); } /// A connection from a client from the perspective of a worker. @@ -157,4 +183,9 @@ abstract class WebSqlite { }) { return DatabaseClient(worker, wasmModule); } + + static Future connectToPort(SqliteWebEndpoint endpoint) { + final client = DatabaseClient(Uri.base, Uri.base); + return client.connectToExisting(endpoint); + } } diff --git a/sqlite3_web/lib/src/protocol.dart b/sqlite3_web/lib/src/protocol.dart index fb82ff0e..79e2fa7b 100644 --- a/sqlite3_web/lib/src/protocol.dart +++ b/sqlite3_web/lib/src/protocol.dart @@ -28,7 +28,9 @@ enum MessageType { simpleSuccessResponse(), rowsResponse(), errorResponse(), + endpointResponse(), closeDatabase(), + openAdditionalConnection(), notifyUpdate(), ; @@ -86,9 +88,12 @@ sealed class Message { MessageType.fileSystemAccess => FileSystemAccess.deserialize(object), MessageType.connect => ConnectRequest.deserialize(object), MessageType.closeDatabase => CloseDatabase.deserialize(object), + MessageType.openAdditionalConnection => + OpenAdditonalConnection.deserialize(object), MessageType.updateRequest => UpdateStreamRequest.deserialize(object), MessageType.simpleSuccessResponse => SimpleSuccessResponse.deserialize(object), + MessageType.endpointResponse => EndpointResponse.deserialize(object), MessageType.rowsResponse => RowsResponse.deserialize(object), MessageType.errorResponse => ErrorResponse.deserialize(object), MessageType.notifyUpdate => UpdateNotification.deserialize(object), @@ -428,7 +433,7 @@ final class RunQuery extends Request { } } -class CloseDatabase extends Request { +final class CloseDatabase extends Request { CloseDatabase({required super.requestId, required super.databaseId}); factory CloseDatabase.deserialize(JSObject object) { @@ -440,6 +445,23 @@ class CloseDatabase extends Request { MessageType get type => MessageType.closeDatabase; } +final class OpenAdditonalConnection extends Request { + OpenAdditonalConnection({ + required super.requestId, + super.databaseId, + }); + + factory OpenAdditonalConnection.deserialize(JSObject object) { + return OpenAdditonalConnection( + requestId: object.requestId, + databaseId: object.databaseId, + ); + } + + @override + MessageType get type => MessageType.openAdditionalConnection; +} + final class SimpleSuccessResponse extends Response { final JSAny? response; @@ -462,6 +484,29 @@ final class SimpleSuccessResponse extends Response { } } +final class EndpointResponse extends Response { + final WebEndpoint endpoint; + + EndpointResponse({required super.requestId, required this.endpoint}); + + factory EndpointResponse.deserialize(JSObject object) { + return EndpointResponse( + requestId: object.requestId, + endpoint: object[_UniqueFieldNames.responseData] as WebEndpoint, + ); + } + + @override + MessageType get type => MessageType.endpointResponse; + + @override + void serialize(JSObject object, List transferred) { + super.serialize(object, transferred); + object[_UniqueFieldNames.responseData] = endpoint; + transferred.add(endpoint.port); + } +} + final class RowsResponse extends Response { final ResultSet resultSet; diff --git a/sqlite3_web/lib/src/worker.dart b/sqlite3_web/lib/src/worker.dart index da8a1cfe..d8bab3f0 100644 --- a/sqlite3_web/lib/src/worker.dart +++ b/sqlite3_web/lib/src/worker.dart @@ -106,9 +106,11 @@ final class Shared extends WorkerEnvironment { /// A database opened by a client. final class _ConnectionDatabase { final DatabaseState database; + final int id; + StreamSubscription? updates; - _ConnectionDatabase(this.database); + _ConnectionDatabase(this.database, [int? id]) : id = id ?? database.id; Future close() async { updates?.cancel(); @@ -173,17 +175,19 @@ final class _ClientConnection extends ProtocolChannel case OpenRequest(): await _runner.loadWasmModule(request.wasmUri); DatabaseState? database; + _ConnectionDatabase? connectionDatabase; try { database = _runner.findDatabase(request.databaseName, request.storageMode); await database.opened; - _openedDatabases.add(_ConnectionDatabase(database)); + connectionDatabase = _ConnectionDatabase(database); + _openedDatabases.add(connectionDatabase); return SimpleSuccessResponse( response: database.id.toJS, requestId: request.requestId); } catch (e) { if (database != null) { - _openedDatabases.remove(database.id); + _openedDatabases.remove(connectionDatabase); await database.decrementRefCount(); } @@ -220,6 +224,16 @@ final class _ClientConnection extends ProtocolChannel } return SimpleSuccessResponse( response: null, requestId: request.requestId); + case OpenAdditonalConnection(): + final database = _databaseFor(request)!.database; + database.refCount++; + final (endpoint, channel) = await createChannel(); + + final client = _runner._accept(channel); + client._openedDatabases.add(_ConnectionDatabase(database, 0)); + + return EndpointResponse( + requestId: request.requestId, endpoint: endpoint); case CloseDatabase(): _openedDatabases.remove(database!); await database.close(); @@ -247,7 +261,7 @@ final class _ClientConnection extends ProtocolChannel _ConnectionDatabase? _databaseFor(Request request) { if (request.databaseId case final id?) { - return _openedDatabases.firstWhere((e) => e.database.id == id); + return _openedDatabases.firstWhere((e) => e.id == id); } else { return null; } @@ -381,11 +395,13 @@ final class WorkerRunner { } } - void _accept(StreamChannel channel) { + _ClientConnection _accept(StreamChannel channel) { final connection = _ClientConnection( runner: this, channel: channel, id: _nextConnectionId++); _connections.add(connection); connection.closed.whenComplete(() => _connections.remove(connection)); + + return connection; } Future checkCompatibility(CompatibilityCheck check) { From b9fb48fdf35e47e4c9012b4bf9c0674185dddd24 Mon Sep 17 00:00:00 2001 From: Simon Binder Date: Mon, 2 Sep 2024 00:03:45 +0200 Subject: [PATCH 10/12] Fix sqlite web integration tests --- sqlite3_web/lib/src/database.dart | 11 +++++++++ sqlite3_web/pubspec.yaml | 2 +- sqlite3_web/test/integration_test.dart | 8 +++++-- sqlite3_web/tool/server.dart | 32 +++++++++++++++----------- sqlite3_web/web/main.dart | 22 ++++++++++-------- 5 files changed, 49 insertions(+), 26 deletions(-) diff --git a/sqlite3_web/lib/src/database.dart b/sqlite3_web/lib/src/database.dart index cb4d113a..fb19ae7e 100644 --- a/sqlite3_web/lib/src/database.dart +++ b/sqlite3_web/lib/src/database.dart @@ -184,6 +184,17 @@ abstract class WebSqlite { return DatabaseClient(worker, wasmModule); } + /// Connects to an endpoint previously obtained with [Database.additionalConnection]. + /// + /// As a [SqliteWebEndpoint] record only consists of fields that are + /// transferrable in JavaScript, these endpoints can be sent to other workers, + /// which can then call [connectToPort] to open a database connection + /// originally established by another JavaScript connection. + /// + /// Note that, depending on the access mode, the returned [Database] may only + /// be valid as long as the original [Database] where [Database.additionalConnection] + /// was called. This limitation does not exist for databases hosted by shared + /// workers. static Future connectToPort(SqliteWebEndpoint endpoint) { final client = DatabaseClient(Uri.base, Uri.base); return client.connectToExisting(endpoint); diff --git a/sqlite3_web/pubspec.yaml b/sqlite3_web/pubspec.yaml index ac1ec552..08b6ca0f 100644 --- a/sqlite3_web/pubspec.yaml +++ b/sqlite3_web/pubspec.yaml @@ -1,6 +1,6 @@ name: sqlite3_web description: Utilities to simplify accessing sqlite3 on the web, with automated feature detection. -version: 0.1.2-wip +version: 0.1.3 homepage: https://github.com/simolus3/sqlite3.dart/tree/main/sqlite3_web repository: https://github.com/simolus3/sqlite3.dart diff --git a/sqlite3_web/test/integration_test.dart b/sqlite3_web/test/integration_test.dart index 89bcd88e..659108c2 100644 --- a/sqlite3_web/test/integration_test.dart +++ b/sqlite3_web/test/integration_test.dart @@ -103,6 +103,10 @@ void main() { }, ); + rawDriver.logs.get(LogType.browser).listen((entry) { + print('[console]: ${entry.message}'); + }); + driver = TestWebDriver(server, rawDriver); await driver.driver.get('http://localhost:8080/'); @@ -136,9 +140,9 @@ void main() { test('$storage through $access', () async { await driver.openDatabase((storage, access)); await driver.execute('CREATE TABLE foo (bar TEXT);'); - final event = driver.waitForUpdate(); + expect(await driver.countUpdateEvents(), 0); await driver.execute("INSERT INTO foo (bar) VALUES ('hello');"); - await event; + expect(await driver.countUpdateEvents(), 1); }); } }); diff --git a/sqlite3_web/tool/server.dart b/sqlite3_web/tool/server.dart index 2d29a81b..a20ec2d2 100644 --- a/sqlite3_web/tool/server.dart +++ b/sqlite3_web/tool/server.dart @@ -146,29 +146,33 @@ class TestWebDriver { final res = await driver .executeAsync('open(arguments[0], arguments[1])', [desc]) as String?; - if (res == null) { - return implementation!; - } else { - // If we're using connectToRecommended, this returns the storage/access - // mode actually chosen. - final split = res.split(':'); - - return ( - StorageMode.values.byName(split[0]), - AccessMode.values.byName(split[1]) - ); - } + // This returns the storage/access mode actually chosen. + final split = res!.split(':'); + + return ( + StorageMode.values.byName(split[0]), + AccessMode.values.byName(split[1]) + ); } Future closeDatabase() async { await driver.executeAsync("close('', arguments[0])", []); } - Future waitForUpdate() async { - await driver.executeAsync('wait_for_update("", arguments[0])', []); + Future countUpdateEvents() async { + final result = + await driver.executeAsync('get_updates("", arguments[0])', []); + return result as int; } Future execute(String sql) async { await driver.executeAsync('exec(arguments[0], arguments[1])', [sql]); } + + Future testSecond(String sql) async { + final res = await driver.executeAsync('test_second("", arguments[0])', []); + if (res != true) { + throw 'test_second failed! More information may be available in the console.'; + } + } } diff --git a/sqlite3_web/web/main.dart b/sqlite3_web/web/main.dart index 7f95d8ad..6e63df33 100644 --- a/sqlite3_web/web/main.dart +++ b/sqlite3_web/web/main.dart @@ -3,7 +3,6 @@ import 'dart:html'; import 'dart:js_interop'; import 'dart:js_interop_unsafe'; -import 'package:async/async.dart'; import 'package:sqlite3_web/sqlite3_web.dart'; final sqlite3WasmUri = Uri.parse('sqlite3.wasm'); @@ -13,7 +12,7 @@ const databaseName = 'database'; WebSqlite? webSqlite; Database? database; -StreamQueue? updates; +int updates = 0; void main() { _addCallbackForWebDriver('detectImplementations', _detectImplementations); @@ -21,9 +20,19 @@ void main() { await database?.dispose(); return null; }); - _addCallbackForWebDriver('wait_for_update', _waitForUpdate); + _addCallbackForWebDriver('get_updates', (arg) async { + return updates.toJS; + }); _addCallbackForWebDriver('open', _open); _addCallbackForWebDriver('exec', _exec); + _addCallbackForWebDriver('test_second', (arg) async { + final endpoint = await database!.additionalConnection(); + final second = await WebSqlite.connectToPort(endpoint); + + await second.execute('SELECT 1'); + await second.dispose(); + return true.toJS; + }); document.getElementById('selfcheck')?.onClick.listen((event) async { print('starting'); @@ -79,11 +88,6 @@ Future _detectImplementations(String? _) async { }).toJS; } -Future _waitForUpdate(String? _) async { - await updates!.next; - return null; -} - Future _open(String? implementationName) async { final sqlite = initializeSqlite(); Database db; @@ -103,7 +107,7 @@ Future _open(String? implementationName) async { // Make sure it works! await db.select('SELECT database_host()'); - updates = StreamQueue(db.updates); + db.updates.listen((_) => updates++); database = db; return returnValue?.toJS; } From 0f466963aaf654a176aa7e48352d7016db0db9e7 Mon Sep 17 00:00:00 2001 From: David Martos Date: Wed, 9 Oct 2024 23:04:57 +0200 Subject: [PATCH 11/12] read sysroot from variable (#255) --- sqlite3/assets/wasm/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sqlite3/assets/wasm/CMakeLists.txt b/sqlite3/assets/wasm/CMakeLists.txt index c557d782..a50bc105 100644 --- a/sqlite3/assets/wasm/CMakeLists.txt +++ b/sqlite3/assets/wasm/CMakeLists.txt @@ -60,7 +60,7 @@ macro(base_sqlite3_target name debug) -mexec-model=reactor -fno-stack-protector -fno-stack-clash-protection -Wl,--import-memory - --sysroot /usr/share/wasi-sysroot + --sysroot ${wasi_sysroot} ${sources} @${CMAKE_CURRENT_BINARY_DIR}/required_symbols.txt DEPENDS ${sources} required_symbols From 661eb26571725aa7504624f2290eb818343cc31b Mon Sep 17 00:00:00 2001 From: David Martos Date: Thu, 10 Oct 2024 00:19:11 +0200 Subject: [PATCH 12/12] remove toolchain.cmake --- .github/actions/build-wasm/action.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/actions/build-wasm/action.yml b/.github/actions/build-wasm/action.yml index 9dec99d9..3c02a0ab 100644 --- a/.github/actions/build-wasm/action.yml +++ b/.github/actions/build-wasm/action.yml @@ -43,5 +43,5 @@ runs: working-directory: sqlite3 shell: bash run: | - cmake -Dwasi_sysroot=/opt/wasi-sysroot -Dclang=/opt/homebrew/opt/llvm@18/bin/clang -S assets/wasm -B .dart_tool/sqlite3_build --toolchain toolchain.cmake + cmake -Dwasi_sysroot=/opt/wasi-sysroot -Dclang=/opt/homebrew/opt/llvm@18/bin/clang -S assets/wasm -B .dart_tool/sqlite3_build cmake --build .dart_tool/sqlite3_build/ -t output -j