compile for WASM

main
Ziyang Hu 2 years ago
parent 398fa15f15
commit d7831dd48d

69
Cargo.lock generated

@ -452,6 +452,16 @@ dependencies = [
"memchr", "memchr",
] ]
[[package]]
name = "console_error_panic_hook"
version = "0.1.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a06aeb73f470f66dcdbf7223caeebb85984942f22f1adb2a088cf9668146bbbc"
dependencies = [
"cfg-if 1.0.0",
"wasm-bindgen",
]
[[package]] [[package]]
name = "core-foundation" name = "core-foundation"
version = "0.9.3" version = "0.9.3"
@ -516,6 +526,17 @@ dependencies = [
"uuid", "uuid",
] ]
[[package]]
name = "cozo-lib-wasm"
version = "0.1.0"
dependencies = [
"console_error_panic_hook",
"cozo",
"wasm-bindgen",
"wasm-bindgen-test",
"wee_alloc",
]
[[package]] [[package]]
name = "cozo_c" name = "cozo_c"
version = "0.1.3" version = "0.1.3"
@ -1502,6 +1523,12 @@ dependencies = [
"autocfg", "autocfg",
] ]
[[package]]
name = "memory_units"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8452105ba047068f40ff7093dd1d9da90898e63dd61736462e9cdda6a90ad3c3"
[[package]] [[package]]
name = "miette" name = "miette"
version = "5.3.0" version = "5.3.0"
@ -2558,6 +2585,12 @@ dependencies = [
"windows-sys 0.36.1", "windows-sys 0.36.1",
] ]
[[package]]
name = "scoped-tls"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e1cf6437eb19a8f4a6cc0f7dca544973b0b78843adbfeb3683d1a94a0024a294"
[[package]] [[package]]
name = "scopeguard" name = "scopeguard"
version = "1.1.0" version = "1.1.0"
@ -3408,6 +3441,30 @@ version = "0.2.83"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1c38c045535d93ec4f0b4defec448e4291638ee608530863b1e2ba115d4fff7f" checksum = "1c38c045535d93ec4f0b4defec448e4291638ee608530863b1e2ba115d4fff7f"
[[package]]
name = "wasm-bindgen-test"
version = "0.3.33"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "09d2fff962180c3fadf677438054b1db62bee4aa32af26a45388af07d1287e1d"
dependencies = [
"console_error_panic_hook",
"js-sys",
"scoped-tls",
"wasm-bindgen",
"wasm-bindgen-futures",
"wasm-bindgen-test-macro",
]
[[package]]
name = "wasm-bindgen-test-macro"
version = "0.3.33"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4683da3dfc016f704c9f82cf401520c4f1cb3ee440f7f52b3d6ac29506a49ca7"
dependencies = [
"proc-macro2",
"quote",
]
[[package]] [[package]]
name = "web-sys" name = "web-sys"
version = "0.3.60" version = "0.3.60"
@ -3437,6 +3494,18 @@ dependencies = [
"webpki", "webpki",
] ]
[[package]]
name = "wee_alloc"
version = "0.4.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dbb3b5a6b2bb17cb6ad44a2e68a43e8d2722c997da10e928665c72ec6c0a0b8e"
dependencies = [
"cfg-if 0.1.10",
"libc",
"memory_units",
"winapi",
]
[[package]] [[package]]
name = "which" name = "which"
version = "4.3.0" version = "4.3.0"

@ -2,4 +2,4 @@
lto = true lto = true
[workspace] [workspace]
members = ["cozorocks", "cozo-lib-c", "cozo-lib-java", "cozo-core", "cozoserver"] members = ["cozorocks", "cozo-lib-c", "cozo-lib-java", "cozo-core", "cozoserver", "cozo-lib-wasm"]

@ -1432,6 +1432,11 @@ pub(crate) fn op_to_uuid(args: &[DataValue]) -> Result<DataValue> {
} }
define_op!(OP_NOW, 0, false); define_op!(OP_NOW, 0, false);
#[cfg(feature = "wasm")]
pub(crate) fn op_now(_args: &[DataValue]) -> Result<DataValue> {
bail!("`op_now` is not supported under WASM runtime")
}
#[cfg(not(feature = "wasm"))]
pub(crate) fn op_now(_args: &[DataValue]) -> Result<DataValue> { pub(crate) fn op_now(_args: &[DataValue]) -> Result<DataValue> {
let now = SystemTime::now(); let now = SystemTime::now();
Ok(DataValue::from( Ok(DataValue::from(
@ -1440,6 +1445,11 @@ pub(crate) fn op_now(_args: &[DataValue]) -> Result<DataValue> {
} }
define_op!(OP_FORMAT_TIMESTAMP, 1, true); define_op!(OP_FORMAT_TIMESTAMP, 1, true);
#[cfg(feature = "wasm")]
pub(crate) fn op_format_timestamp(_args: &[DataValue]) -> Result<DataValue> {
bail!("`format_timestamp` is not supported under WASM")
}
#[cfg(not(feature = "wasm"))]
pub(crate) fn op_format_timestamp(args: &[DataValue]) -> Result<DataValue> { pub(crate) fn op_format_timestamp(args: &[DataValue]) -> Result<DataValue> {
let f = args[0] let f = args[0]
.get_float() .get_float()
@ -1468,6 +1478,11 @@ pub(crate) fn op_format_timestamp(args: &[DataValue]) -> Result<DataValue> {
} }
define_op!(OP_PARSE_TIMESTAMP, 1, false); define_op!(OP_PARSE_TIMESTAMP, 1, false);
#[cfg(feature = "wasm")]
pub(crate) fn op_parse_timestamp(_args: &[DataValue]) -> Result<DataValue> {
bail!("`parse_timestamp` is not supported under WASM")
}
#[cfg(not(feature = "wasm"))]
pub(crate) fn op_parse_timestamp(args: &[DataValue]) -> Result<DataValue> { pub(crate) fn op_parse_timestamp(args: &[DataValue]) -> Result<DataValue> {
let s = args[0] let s = args[0]
.get_string() .get_string()
@ -1480,6 +1495,11 @@ pub(crate) fn op_parse_timestamp(args: &[DataValue]) -> Result<DataValue> {
} }
define_op!(OP_RAND_UUID_V1, 0, false); define_op!(OP_RAND_UUID_V1, 0, false);
#[cfg(feature = "wasm")]
pub(crate) fn op_rand_uuid_v1(_args: &[DataValue]) -> Result<DataValue> {
bail!("`rand_uuid_v1` is not supported under WASM")
}
#[cfg(not(feature = "wasm"))]
pub(crate) fn op_rand_uuid_v1(_args: &[DataValue]) -> Result<DataValue> { pub(crate) fn op_rand_uuid_v1(_args: &[DataValue]) -> Result<DataValue> {
let mut rng = rand::thread_rng(); let mut rng = rand::thread_rng();
let uuid_ctx = uuid::v1::Context::new(rng.gen()); let uuid_ctx = uuid::v1::Context::new(rng.gen());

@ -234,15 +234,19 @@ pub(crate) fn parse_query(
out_opts.timeout = Some(timeout); out_opts.timeout = Some(timeout);
} }
Rule::sleep_option => { Rule::sleep_option => {
let pair = pair.into_inner().next().unwrap(); #[cfg(feature = "wasm")]
let span = pair.extract_span(); bail!(":sleep is not supported under WASM");
let sleep = build_expr(pair, param_pool)? #[cfg(not(feature = "wasm"))] {
.eval_to_const() let pair = pair.into_inner().next().unwrap();
.map_err(|err| OptionNotConstantError("sleep", span, [err]))? let span = pair.extract_span();
.get_float() let sleep = build_expr(pair, param_pool)?
.ok_or(OptionNotNonNegIntError("sleep", span))?; .eval_to_const()
ensure!(sleep > 0., OptionNotPosIntError("sleep", span)); .map_err(|err| OptionNotConstantError("sleep", span, [err]))?
out_opts.sleep = Some(sleep); .get_float()
.ok_or(OptionNotNonNegIntError("sleep", span))?;
ensure!(sleep > 0., OptionNotPosIntError("sleep", span));
out_opts.sleep = Some(sleep);
}
} }
Rule::limit_option => { Rule::limit_option => {
let pair = pair.into_inner().next().unwrap(); let pair = pair.into_inner().next().unwrap();

@ -8,8 +8,8 @@
use std::collections::BTreeMap; use std::collections::BTreeMap;
use std::fmt::{Debug, Formatter}; use std::fmt::{Debug, Formatter};
use std::sync::atomic::{AtomicBool, AtomicU64, Ordering};
use std::sync::{Arc, Mutex}; use std::sync::{Arc, Mutex};
use std::sync::atomic::{AtomicBool, AtomicU64, Ordering};
use std::thread; use std::thread;
use std::time::{Duration, Instant, SystemTime, UNIX_EPOCH}; use std::time::{Duration, Instant, SystemTime, UNIX_EPOCH};
@ -17,7 +17,7 @@ use either::{Left, Right};
use itertools::Itertools; use itertools::Itertools;
use lazy_static::lazy_static; use lazy_static::lazy_static;
use miette::{ use miette::{
bail, ensure, Diagnostic, GraphicalReportHandler, GraphicalTheme, IntoDiagnostic, bail, Diagnostic, ensure, GraphicalReportHandler, GraphicalTheme, IntoDiagnostic,
JSONReportHandler, Result, WrapErr, JSONReportHandler, Result, WrapErr,
}; };
use serde_json::{json, Map}; use serde_json::{json, Map};
@ -28,8 +28,8 @@ use crate::data::json::JsonValue;
use crate::data::program::{InputProgram, QueryAssertion, RelationOp}; use crate::data::program::{InputProgram, QueryAssertion, RelationOp};
use crate::data::tuple::Tuple; use crate::data::tuple::Tuple;
use crate::data::value::{DataValue, LARGEST_UTF_CHAR}; use crate::data::value::{DataValue, LARGEST_UTF_CHAR};
use crate::parse::{CozoScript, parse_script, SourceSpan};
use crate::parse::sys::SysOp; use crate::parse::sys::SysOp;
use crate::parse::{parse_script, CozoScript, SourceSpan};
use crate::query::compile::{CompiledProgram, CompiledRule, CompiledRuleSet}; use crate::query::compile::{CompiledProgram, CompiledRule, CompiledRuleSet};
use crate::query::relation::{ use crate::query::relation::{
FilteredRA, InMemRelationRA, InnerJoin, NegJoin, RelAlgebra, ReorderRA, StoredRA, UnificationRA, FilteredRA, InMemRelationRA, InnerJoin, NegJoin, RelAlgebra, ReorderRA, StoredRA, UnificationRA,
@ -144,13 +144,18 @@ impl<'s, S: Storage<'s>> Db<S> {
payload: &str, payload: &str,
params: &Map<String, JsonValue>, params: &Map<String, JsonValue>,
) -> Result<JsonValue> { ) -> Result<JsonValue> {
#[cfg(not(feature = "wasm"))]
let start = Instant::now(); let start = Instant::now();
match self.do_run_script(payload, params) { match self.do_run_script(payload, params) {
Ok(mut json) => { Ok(mut json) => {
let took = start.elapsed().as_secs_f64(); {
let map = json.as_object_mut().unwrap(); #[cfg(not(feature = "wasm"))]
map.insert("ok".to_string(), json!(true)); let took = start.elapsed().as_secs_f64();
map.insert("took".to_string(), json!(took)); let map = json.as_object_mut().unwrap();
map.insert("ok".to_string(), json!(true));
#[cfg(not(feature = "wasm"))]
map.insert("took".to_string(), json!(took));
}
Ok(json) Ok(json)
} }
err => err, err => err,
@ -231,6 +236,7 @@ impl<'s, S: Storage<'s>> Db<S> {
let (q_res, q_cleanups) = self.run_query(&mut tx, p)?; let (q_res, q_cleanups) = self.run_query(&mut tx, p)?;
res = q_res; res = q_res;
cleanups.extend(q_cleanups); cleanups.extend(q_cleanups);
#[cfg(not(feature = "wasm"))]
if let Some(secs) = sleep_opt { if let Some(secs) = sleep_opt {
thread::sleep(Duration::from_micros((secs * 1000000.) as u64)); thread::sleep(Duration::from_micros((secs * 1000000.) as u64));
} }
@ -558,12 +564,17 @@ impl<'s, S: Storage<'s>> Db<S> {
} }
let id = self.queries_count.fetch_add(1, Ordering::AcqRel); let id = self.queries_count.fetch_add(1, Ordering::AcqRel);
#[cfg(not(feature = "wasm"))]
let now = SystemTime::now(); let now = SystemTime::now();
#[cfg(not(feature = "wasm"))]
let since_the_epoch = now let since_the_epoch = now
.duration_since(UNIX_EPOCH) .duration_since(UNIX_EPOCH)
.into_diagnostic()? .into_diagnostic()?
.as_secs_f64(); .as_secs_f64();
#[cfg(feature = "wasm")]
let since_the_epoch = 0.;
let handle = RunningQueryHandle { let handle = RunningQueryHandle {
started_at: since_the_epoch, started_at: since_the_epoch,
poison: poison.clone(), poison: poison.clone(),

@ -0,0 +1,11 @@
install:
- appveyor-retry appveyor DownloadFile https://win.rustup.rs/ -FileName rustup-init.exe
- if not defined RUSTFLAGS rustup-init.exe -y --default-host x86_64-pc-windows-msvc --default-toolchain nightly
- set PATH=%PATH%;C:\Users\appveyor\.cargo\bin
- rustc -V
- cargo -V
build: false
test_script:
- cargo test --locked

@ -0,0 +1,6 @@
/target
**/*.rs.bk
Cargo.lock
bin/
pkg/
wasm-pack.log

@ -0,0 +1,69 @@
language: rust
sudo: false
cache: cargo
matrix:
include:
# Builds with wasm-pack.
- rust: beta
env: RUST_BACKTRACE=1
addons:
firefox: latest
chrome: stable
before_script:
- (test -x $HOME/.cargo/bin/cargo-install-update || cargo install cargo-update)
- (test -x $HOME/.cargo/bin/cargo-generate || cargo install --vers "^0.2" cargo-generate)
- cargo install-update -a
- curl https://rustwasm.github.io/wasm-pack/installer/init.sh -sSf | sh -s -- -f
script:
- cargo generate --git . --name testing
# Having a broken Cargo.toml (in that it has curlies in fields) anywhere
# in any of our parent dirs is problematic.
- mv Cargo.toml Cargo.toml.tmpl
- cd testing
- wasm-pack build
- wasm-pack test --chrome --firefox --headless
# Builds on nightly.
- rust: nightly
env: RUST_BACKTRACE=1
before_script:
- (test -x $HOME/.cargo/bin/cargo-install-update || cargo install cargo-update)
- (test -x $HOME/.cargo/bin/cargo-generate || cargo install --vers "^0.2" cargo-generate)
- cargo install-update -a
- rustup target add wasm32-unknown-unknown
script:
- cargo generate --git . --name testing
- mv Cargo.toml Cargo.toml.tmpl
- cd testing
- cargo check
- cargo check --target wasm32-unknown-unknown
- cargo check --no-default-features
- cargo check --target wasm32-unknown-unknown --no-default-features
- cargo check --no-default-features --features console_error_panic_hook
- cargo check --target wasm32-unknown-unknown --no-default-features --features console_error_panic_hook
- cargo check --no-default-features --features "console_error_panic_hook wee_alloc"
- cargo check --target wasm32-unknown-unknown --no-default-features --features "console_error_panic_hook wee_alloc"
# Builds on beta.
- rust: beta
env: RUST_BACKTRACE=1
before_script:
- (test -x $HOME/.cargo/bin/cargo-install-update || cargo install cargo-update)
- (test -x $HOME/.cargo/bin/cargo-generate || cargo install --vers "^0.2" cargo-generate)
- cargo install-update -a
- rustup target add wasm32-unknown-unknown
script:
- cargo generate --git . --name testing
- mv Cargo.toml Cargo.toml.tmpl
- cd testing
- cargo check
- cargo check --target wasm32-unknown-unknown
- cargo check --no-default-features
- cargo check --target wasm32-unknown-unknown --no-default-features
- cargo check --no-default-features --features console_error_panic_hook
- cargo check --target wasm32-unknown-unknown --no-default-features --features console_error_panic_hook
# Note: no enabling the `wee_alloc` feature here because it requires
# nightly for now.

@ -0,0 +1,34 @@
[package]
name = "cozo-lib-wasm"
version = "0.1.0"
authors = ["Ziyang Hu <hu.ziyang@cantab.net>"]
edition = "2021"
license = "MPL-2.0"
[lib]
crate-type = ["cdylib", "rlib"]
[features]
default = ["console_error_panic_hook"]
[dependencies]
wasm-bindgen = "0.2.63"
cozo = { version = "0.1.7", path = "../cozo-core", default-features = false, features = ["wasm", "graph-algo", "nothread"] }
# The `console_error_panic_hook` crate provides better debugging of panics by
# logging them with `console.error`. This is great for development, but requires
# all the `std::fmt` and `std::panicking` infrastructure, so isn't great for
# code size when deploying.
console_error_panic_hook = { version = "0.1.6", optional = true }
# `wee_alloc` is a tiny allocator for wasm that is only ~1K in code size
# compared to the default allocator's ~10K. It is slower than the default
# allocator, however.
wee_alloc = { version = "0.4.5", optional = true }
[dev-dependencies]
wasm-bindgen-test = "0.3.13"
[profile.release]
# Tell `rustc` to optimize for small code size.
opt-level = "s"

@ -0,0 +1,41 @@
/*
* Copyright 2022, The Cozo Project Authors.
*
* This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
* If a copy of the MPL was not distributed with this file,
* You can obtain one at https://mozilla.org/MPL/2.0/.
*/
use wasm_bindgen::prelude::*;
use cozo::*;
mod utils;
// When the `wee_alloc` feature is enabled, use `wee_alloc` as the global
// allocator.
#[cfg(feature = "wee_alloc")]
#[global_allocator]
static ALLOC: wee_alloc::WeeAlloc = wee_alloc::WeeAlloc::INIT;
#[wasm_bindgen]
extern "C" {
fn alert(s: &str);
}
#[wasm_bindgen]
pub struct CozoDb {
db: Db<MemStorage>,
}
#[wasm_bindgen]
impl CozoDb {
pub fn new() -> Self {
utils::set_panic_hook();
let db = new_cozo_mem().unwrap();
Self { db }
}
pub fn run(&self, script: &str, params: &str) -> String {
self.db.run_script_str(script, params)
}
}

@ -0,0 +1,18 @@
/*
* Copyright 2022, The Cozo Project Authors.
*
* This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
* If a copy of the MPL was not distributed with this file,
* You can obtain one at https://mozilla.org/MPL/2.0/.
*/
pub fn set_panic_hook() {
// When the `console_error_panic_hook` feature is enabled, we can call the
// `set_panic_hook` function at least once during initialization, and then
// we will get better error messages if our code ever panics.
//
// For more details see
// https://github.com/rustwasm/console_error_panic_hook#readme
#[cfg(feature = "console_error_panic_hook")]
console_error_panic_hook::set_once();
}

@ -0,0 +1,21 @@
/*
* Copyright 2022, The Cozo Project Authors.
*
* This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
* If a copy of the MPL was not distributed with this file,
* You can obtain one at https://mozilla.org/MPL/2.0/.
*/
//! Test suite for the Web and headless browsers.
#![cfg(target_arch = "wasm32")]
extern crate wasm_bindgen_test;
use wasm_bindgen_test::*;
wasm_bindgen_test_configure!(run_in_browser);
#[wasm_bindgen_test]
fn pass() {
assert_eq!(1 + 1, 2);
}
Loading…
Cancel
Save