WASM work
This commit is contained in:
parent
031c998cfc
commit
ca85b555aa
7
Cargo.lock
generated
7
Cargo.lock
generated
@ -4316,8 +4316,13 @@ version = "0.1.0"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"cfg-if 1.0.0",
|
"cfg-if 1.0.0",
|
||||||
"console_error_panic_hook",
|
"console_error_panic_hook",
|
||||||
|
"futures-util",
|
||||||
"js-sys",
|
"js-sys",
|
||||||
|
"lazy_static",
|
||||||
"log",
|
"log",
|
||||||
|
"send_wrapper",
|
||||||
|
"serde 1.0.136",
|
||||||
|
"serde_json",
|
||||||
"veilid-core",
|
"veilid-core",
|
||||||
"wasm-bindgen",
|
"wasm-bindgen",
|
||||||
"wasm-bindgen-futures",
|
"wasm-bindgen-futures",
|
||||||
@ -4374,6 +4379,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "25f1af7423d8588a3d840681122e72e6a24ddbcb3f0ec385cac0d12d24256c06"
|
checksum = "25f1af7423d8588a3d840681122e72e6a24ddbcb3f0ec385cac0d12d24256c06"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cfg-if 1.0.0",
|
"cfg-if 1.0.0",
|
||||||
|
"serde 1.0.136",
|
||||||
|
"serde_json",
|
||||||
"wasm-bindgen-macro",
|
"wasm-bindgen-macro",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -63,6 +63,6 @@ cargo install cargo-ndk
|
|||||||
cargo install cargo-apk
|
cargo install cargo-apk
|
||||||
|
|
||||||
# Ensure packages are installed
|
# Ensure packages are installed
|
||||||
sudo apt-get install libc6-dev-i386 libc6:i386 libncurses5:i386 libstdc++6:i386 lib32z1 libbz2-1.0:i386 openjdk-11-jdk
|
sudo apt-get install libc6-dev-i386 libc6:i386 libncurses5:i386 libstdc++6:i386 lib32z1 libbz2-1.0:i386 openjdk-11-jdk llvm
|
||||||
|
|
||||||
|
|
||||||
|
@ -74,11 +74,8 @@ libc = "^0"
|
|||||||
# Dependencies for WASM builds only
|
# Dependencies for WASM builds only
|
||||||
[target.'cfg(target_arch = "wasm32")'.dependencies]
|
[target.'cfg(target_arch = "wasm32")'.dependencies]
|
||||||
wasm-bindgen = "^0"
|
wasm-bindgen = "^0"
|
||||||
console_error_panic_hook = "^0"
|
|
||||||
wee_alloc = "^0"
|
|
||||||
js-sys = "^0"
|
js-sys = "^0"
|
||||||
wasm-bindgen-futures = "^0"
|
wasm-bindgen-futures = "^0"
|
||||||
wasm-logger = "^0"
|
|
||||||
hashbrown = "^0"
|
hashbrown = "^0"
|
||||||
lru = {version = "^0", features = ["hashbrown"] }
|
lru = {version = "^0", features = ["hashbrown"] }
|
||||||
no-std-net = { path = "../external/no-std-net", features = ["serde"] }
|
no-std-net = { path = "../external/no-std-net", features = ["serde"] }
|
||||||
@ -149,6 +146,9 @@ simplelog = { version = "^0", features=["test"] }
|
|||||||
|
|
||||||
[target.'cfg(target_arch = "wasm32")'.dev-dependencies]
|
[target.'cfg(target_arch = "wasm32")'.dev-dependencies]
|
||||||
wasm-bindgen-test = "^0"
|
wasm-bindgen-test = "^0"
|
||||||
|
console_error_panic_hook = "^0"
|
||||||
|
wee_alloc = "^0"
|
||||||
|
wasm-logger = "^0"
|
||||||
|
|
||||||
### BUILD OPTIONS
|
### BUILD OPTIONS
|
||||||
|
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
use crate::api_logger::*;
|
|
||||||
use crate::attachment_manager::*;
|
use crate::attachment_manager::*;
|
||||||
use crate::dht::crypto::Crypto;
|
use crate::dht::crypto::Crypto;
|
||||||
use crate::intf::*;
|
use crate::intf::*;
|
||||||
@ -10,6 +9,8 @@ cfg_if! {
|
|||||||
if #[cfg(target_arch = "wasm32")] {
|
if #[cfg(target_arch = "wasm32")] {
|
||||||
pub type UpdateCallback = Arc<dyn Fn(VeilidUpdate)>;
|
pub type UpdateCallback = Arc<dyn Fn(VeilidUpdate)>;
|
||||||
} else {
|
} else {
|
||||||
|
use crate::api_logger::*;
|
||||||
|
|
||||||
pub type UpdateCallback = Arc<dyn Fn(VeilidUpdate) + Send + Sync>;
|
pub type UpdateCallback = Arc<dyn Fn(VeilidUpdate) + Send + Sync>;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -59,18 +60,23 @@ impl ServicesContext {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub async fn startup(&mut self) -> Result<(), VeilidAPIError> {
|
pub async fn startup(&mut self) -> Result<(), VeilidAPIError> {
|
||||||
let api_log_level: VeilidConfigLogLevel = self.config.get().api_log_level;
|
let log_level: VeilidConfigLogLevel = self.config.get().log_level;
|
||||||
if api_log_level != VeilidConfigLogLevel::Off {
|
if log_level != VeilidConfigLogLevel::Off {
|
||||||
|
cfg_if! {
|
||||||
|
if #[cfg(target_arch = "wasm32")] {
|
||||||
|
// Logging is managed by application
|
||||||
|
} else {
|
||||||
ApiLogger::init(
|
ApiLogger::init(
|
||||||
api_log_level.to_level_filter(),
|
log_level.to_level_filter(),
|
||||||
self.update_callback.clone(),
|
self.update_callback.clone(),
|
||||||
)
|
)
|
||||||
.await;
|
.await;
|
||||||
for ig in crate::DEFAULT_LOG_IGNORE_LIST {
|
for ig in crate::DEFAULT_LOG_IGNORE_LIST {
|
||||||
ApiLogger::add_filter_ignore_str(ig);
|
ApiLogger::add_filter_ignore_str(ig);
|
||||||
}
|
}
|
||||||
|
info!("Veilid logging initialized");
|
||||||
info!("Veilid API logging initialized");
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
info!("Veilid API starting up");
|
info!("Veilid API starting up");
|
||||||
@ -165,7 +171,13 @@ impl ServicesContext {
|
|||||||
info!("Veilid API shutdown complete");
|
info!("Veilid API shutdown complete");
|
||||||
|
|
||||||
// api logger terminate is idempotent
|
// api logger terminate is idempotent
|
||||||
|
cfg_if! {
|
||||||
|
if #[cfg(target_arch = "wasm32")] {
|
||||||
|
// Logging is managed by application
|
||||||
|
} else {
|
||||||
ApiLogger::terminate().await;
|
ApiLogger::terminate().await;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// send final shutdown update
|
// send final shutdown update
|
||||||
(self.update_callback)(VeilidUpdate::Shutdown);
|
(self.update_callback)(VeilidUpdate::Shutdown);
|
||||||
|
@ -4,16 +4,6 @@ use crate::xx::*;
|
|||||||
use core::sync::atomic::{AtomicI8, Ordering};
|
use core::sync::atomic::{AtomicI8, Ordering};
|
||||||
use js_sys::{global, Reflect};
|
use js_sys::{global, Reflect};
|
||||||
|
|
||||||
cfg_if! {
|
|
||||||
if #[cfg(feature = "wee_alloc")] {
|
|
||||||
// When the `wee_alloc` feature is enabled, use `wee_alloc` as the global
|
|
||||||
// allocator.
|
|
||||||
extern crate wee_alloc;
|
|
||||||
#[global_allocator]
|
|
||||||
static ALLOC: wee_alloc::WeeAlloc = wee_alloc::WeeAlloc::INIT;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[wasm_bindgen]
|
#[wasm_bindgen]
|
||||||
extern "C" {
|
extern "C" {
|
||||||
// Use `js_namespace` here to bind `console.log(..)` instead of just
|
// Use `js_namespace` here to bind `console.log(..)` instead of just
|
||||||
|
@ -4,7 +4,9 @@
|
|||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate alloc;
|
extern crate alloc;
|
||||||
|
|
||||||
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
mod api_logger;
|
mod api_logger;
|
||||||
|
|
||||||
mod attachment_manager;
|
mod attachment_manager;
|
||||||
mod callback_state_machine;
|
mod callback_state_machine;
|
||||||
mod connection_manager;
|
mod connection_manager;
|
||||||
|
@ -169,7 +169,7 @@ fn config_callback(key: String) -> ConfigCallbackReturn {
|
|||||||
match key.as_str() {
|
match key.as_str() {
|
||||||
"program_name" => Ok(Box::new(String::from("Veilid"))),
|
"program_name" => Ok(Box::new(String::from("Veilid"))),
|
||||||
"namespace" => Ok(Box::new(String::from(""))),
|
"namespace" => Ok(Box::new(String::from(""))),
|
||||||
"api_log_level" => Ok(Box::new(VeilidConfigLogLevel::Off)),
|
"log_level" => Ok(Box::new(VeilidConfigLogLevel::Off)),
|
||||||
"capabilities.protocol_udp" => Ok(Box::new(true)),
|
"capabilities.protocol_udp" => Ok(Box::new(true)),
|
||||||
"capabilities.protocol_connect_tcp" => Ok(Box::new(true)),
|
"capabilities.protocol_connect_tcp" => Ok(Box::new(true)),
|
||||||
"capabilities.protocol_accept_tcp" => Ok(Box::new(true)),
|
"capabilities.protocol_accept_tcp" => Ok(Box::new(true)),
|
||||||
@ -270,7 +270,7 @@ pub async fn test_config() {
|
|||||||
let inner = vc.get();
|
let inner = vc.get();
|
||||||
assert_eq!(inner.program_name, String::from("Veilid"));
|
assert_eq!(inner.program_name, String::from("Veilid"));
|
||||||
assert_eq!(inner.namespace, String::from(""));
|
assert_eq!(inner.namespace, String::from(""));
|
||||||
assert_eq!(inner.api_log_level, VeilidConfigLogLevel::Off);
|
assert_eq!(inner.log_level, VeilidConfigLogLevel::Off);
|
||||||
assert_eq!(inner.capabilities.protocol_udp, true);
|
assert_eq!(inner.capabilities.protocol_udp, true);
|
||||||
assert_eq!(inner.capabilities.protocol_connect_tcp, true);
|
assert_eq!(inner.capabilities.protocol_connect_tcp, true);
|
||||||
assert_eq!(inner.capabilities.protocol_accept_tcp, true);
|
assert_eq!(inner.capabilities.protocol_accept_tcp, true);
|
||||||
|
@ -21,7 +21,6 @@ pub use network_manager::NetworkManager;
|
|||||||
pub use routing_table::RoutingTable;
|
pub use routing_table::RoutingTable;
|
||||||
pub use rpc_processor::InfoAnswer;
|
pub use rpc_processor::InfoAnswer;
|
||||||
|
|
||||||
use api_logger::*;
|
|
||||||
use core::fmt;
|
use core::fmt;
|
||||||
use core_context::{api_shutdown, VeilidCoreContext};
|
use core_context::{api_shutdown, VeilidCoreContext};
|
||||||
use rpc_processor::{RPCError, RPCProcessor};
|
use rpc_processor::{RPCError, RPCProcessor};
|
||||||
@ -1219,9 +1218,16 @@ impl VeilidAPI {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Change api logging level if it is enabled
|
// Change api logging level if it is enabled
|
||||||
pub async fn change_api_log_level(&self, log_level: VeilidConfigLogLevel) {
|
pub async fn change_log_level(&self, log_level: VeilidConfigLogLevel) {
|
||||||
|
cfg_if! {
|
||||||
|
if #[cfg(target_arch = "wasm32")] {
|
||||||
|
set_max_level(log_level.to_level_filter());
|
||||||
|
} else {
|
||||||
|
use api_logger::ApiLogger;
|
||||||
ApiLogger::change_log_level(log_level.to_level_filter());
|
ApiLogger::change_log_level(log_level.to_level_filter());
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////
|
||||||
// Direct Node Access (pretty much for testing only)
|
// Direct Node Access (pretty much for testing only)
|
||||||
|
@ -206,7 +206,7 @@ impl Default for VeilidConfigLogLevel {
|
|||||||
pub struct VeilidConfigInner {
|
pub struct VeilidConfigInner {
|
||||||
pub program_name: String,
|
pub program_name: String,
|
||||||
pub namespace: String,
|
pub namespace: String,
|
||||||
pub api_log_level: VeilidConfigLogLevel,
|
pub log_level: VeilidConfigLogLevel,
|
||||||
pub capabilities: VeilidConfigCapabilities,
|
pub capabilities: VeilidConfigCapabilities,
|
||||||
pub protected_store: VeilidConfigProtectedStore,
|
pub protected_store: VeilidConfigProtectedStore,
|
||||||
pub table_store: VeilidConfigTableStore,
|
pub table_store: VeilidConfigTableStore,
|
||||||
@ -262,7 +262,7 @@ impl VeilidConfig {
|
|||||||
let mut inner = self.inner.write();
|
let mut inner = self.inner.write();
|
||||||
get_config!(inner.program_name);
|
get_config!(inner.program_name);
|
||||||
get_config!(inner.namespace);
|
get_config!(inner.namespace);
|
||||||
get_config!(inner.api_log_level);
|
get_config!(inner.log_level);
|
||||||
get_config!(inner.capabilities.protocol_udp);
|
get_config!(inner.capabilities.protocol_udp);
|
||||||
get_config!(inner.capabilities.protocol_connect_tcp);
|
get_config!(inner.capabilities.protocol_connect_tcp);
|
||||||
get_config!(inner.capabilities.protocol_accept_tcp);
|
get_config!(inner.capabilities.protocol_accept_tcp);
|
||||||
|
@ -8,6 +8,10 @@ use wasm_bindgen_test::*;
|
|||||||
|
|
||||||
wasm_bindgen_test_configure!();
|
wasm_bindgen_test_configure!();
|
||||||
|
|
||||||
|
extern crate wee_alloc;
|
||||||
|
#[global_allocator]
|
||||||
|
static ALLOC: wee_alloc::WeeAlloc = wee_alloc::WeeAlloc::INIT;
|
||||||
|
|
||||||
static SETUP_ONCE: Once = Once::new();
|
static SETUP_ONCE: Once = Once::new();
|
||||||
pub fn setup() -> () {
|
pub fn setup() -> () {
|
||||||
SETUP_ONCE.call_once(|| {
|
SETUP_ONCE.call_once(|| {
|
||||||
|
@ -8,6 +8,10 @@ use wasm_bindgen_test::*;
|
|||||||
|
|
||||||
wasm_bindgen_test_configure!(run_in_browser);
|
wasm_bindgen_test_configure!(run_in_browser);
|
||||||
|
|
||||||
|
extern crate wee_alloc;
|
||||||
|
#[global_allocator]
|
||||||
|
static ALLOC: wee_alloc::WeeAlloc = wee_alloc::WeeAlloc::INIT;
|
||||||
|
|
||||||
static SETUP_ONCE: Once = Once::new();
|
static SETUP_ONCE: Once = Once::new();
|
||||||
pub fn setup() -> () {
|
pub fn setup() -> () {
|
||||||
SETUP_ONCE.call_once(|| {
|
SETUP_ONCE.call_once(|| {
|
||||||
|
@ -7,7 +7,7 @@ Future<VeilidConfig> getDefaultVeilidConfig() async {
|
|||||||
return VeilidConfig(
|
return VeilidConfig(
|
||||||
programName: "Veilid Plugin Test",
|
programName: "Veilid Plugin Test",
|
||||||
namespace: "",
|
namespace: "",
|
||||||
apiLogLevel: VeilidConfigLogLevel.info,
|
logLevel: VeilidConfigLogLevel.info,
|
||||||
capabilities: VeilidConfigCapabilities(
|
capabilities: VeilidConfigCapabilities(
|
||||||
protocolUDP: !kIsWeb,
|
protocolUDP: !kIsWeb,
|
||||||
protocolConnectTCP: !kIsWeb,
|
protocolConnectTCP: !kIsWeb,
|
||||||
@ -24,12 +24,16 @@ Future<VeilidConfig> getDefaultVeilidConfig() async {
|
|||||||
delete: false,
|
delete: false,
|
||||||
),
|
),
|
||||||
tableStore: VeilidConfigTableStore(
|
tableStore: VeilidConfigTableStore(
|
||||||
directory: p.join((await getApplicationSupportDirectory()).absolute.path,
|
directory: kIsWeb
|
||||||
|
? ""
|
||||||
|
: p.join((await getApplicationSupportDirectory()).absolute.path,
|
||||||
"table_store"),
|
"table_store"),
|
||||||
delete: false,
|
delete: false,
|
||||||
),
|
),
|
||||||
blockStore: VeilidConfigBlockStore(
|
blockStore: VeilidConfigBlockStore(
|
||||||
directory: p.join((await getApplicationSupportDirectory()).absolute.path,
|
directory: kIsWeb
|
||||||
|
? ""
|
||||||
|
: p.join((await getApplicationSupportDirectory()).absolute.path,
|
||||||
"block_store"),
|
"block_store"),
|
||||||
delete: false,
|
delete: false,
|
||||||
),
|
),
|
||||||
|
@ -705,7 +705,7 @@ class VeilidConfigCapabilities {
|
|||||||
class VeilidConfig {
|
class VeilidConfig {
|
||||||
String programName;
|
String programName;
|
||||||
String namespace;
|
String namespace;
|
||||||
VeilidConfigLogLevel apiLogLevel;
|
VeilidConfigLogLevel logLevel;
|
||||||
VeilidConfigCapabilities capabilities;
|
VeilidConfigCapabilities capabilities;
|
||||||
VeilidConfigProtectedStore protectedStore;
|
VeilidConfigProtectedStore protectedStore;
|
||||||
VeilidConfigTableStore tableStore;
|
VeilidConfigTableStore tableStore;
|
||||||
@ -715,7 +715,7 @@ class VeilidConfig {
|
|||||||
VeilidConfig({
|
VeilidConfig({
|
||||||
required this.programName,
|
required this.programName,
|
||||||
required this.namespace,
|
required this.namespace,
|
||||||
required this.apiLogLevel,
|
required this.logLevel,
|
||||||
required this.capabilities,
|
required this.capabilities,
|
||||||
required this.protectedStore,
|
required this.protectedStore,
|
||||||
required this.tableStore,
|
required this.tableStore,
|
||||||
@ -727,7 +727,7 @@ class VeilidConfig {
|
|||||||
return {
|
return {
|
||||||
'program_name': programName,
|
'program_name': programName,
|
||||||
'namespace': namespace,
|
'namespace': namespace,
|
||||||
'api_log_level': apiLogLevel.json,
|
'log_level': logLevel.json,
|
||||||
'capabilities': capabilities.json,
|
'capabilities': capabilities.json,
|
||||||
'protected_store': protectedStore.json,
|
'protected_store': protectedStore.json,
|
||||||
'table_store': tableStore.json,
|
'table_store': tableStore.json,
|
||||||
@ -739,7 +739,7 @@ class VeilidConfig {
|
|||||||
VeilidConfig.fromJson(Map<String, dynamic> json)
|
VeilidConfig.fromJson(Map<String, dynamic> json)
|
||||||
: programName = json['program_name'],
|
: programName = json['program_name'],
|
||||||
namespace = json['namespace'],
|
namespace = json['namespace'],
|
||||||
apiLogLevel = json['api_log_level'],
|
logLevel = json['log_level'],
|
||||||
capabilities = VeilidConfigCapabilities.fromJson(json['capabilities']),
|
capabilities = VeilidConfigCapabilities.fromJson(json['capabilities']),
|
||||||
protectedStore =
|
protectedStore =
|
||||||
VeilidConfigProtectedStore.fromJson(json['protected_store']),
|
VeilidConfigProtectedStore.fromJson(json['protected_store']),
|
||||||
@ -993,7 +993,7 @@ abstract class Veilid {
|
|||||||
|
|
||||||
Stream<VeilidUpdate> startupVeilidCore(VeilidConfig config);
|
Stream<VeilidUpdate> startupVeilidCore(VeilidConfig config);
|
||||||
Future<VeilidState> getVeilidState();
|
Future<VeilidState> getVeilidState();
|
||||||
Future<void> changeApiLogLevel(VeilidConfigLogLevel logLevel);
|
Future<void> changeLogLevel(VeilidConfigLogLevel logLevel);
|
||||||
Future<void> shutdownVeilidCore();
|
Future<void> shutdownVeilidCore();
|
||||||
Future<String> debug(String command);
|
Future<String> debug(String command);
|
||||||
String veilidVersionString();
|
String veilidVersionString();
|
||||||
|
@ -35,9 +35,9 @@ typedef _StartupVeilidCoreDart = void Function(int, Pointer<Utf8>);
|
|||||||
// fn get_veilid_state(port: i64)
|
// fn get_veilid_state(port: i64)
|
||||||
typedef _GetVeilidStateC = Void Function(Int64);
|
typedef _GetVeilidStateC = Void Function(Int64);
|
||||||
typedef _GetVeilidStateDart = void Function(int);
|
typedef _GetVeilidStateDart = void Function(int);
|
||||||
// fn change_api_log_level(port: i64, log_level: FfiStr)
|
// fn change_log_level(port: i64, log_level: FfiStr)
|
||||||
typedef _ChangeApiLogLevelC = Void Function(Int64, Pointer<Utf8>);
|
typedef _ChangeLogLevelC = Void Function(Int64, Pointer<Utf8>);
|
||||||
typedef _ChangeApiLogLevelDart = void Function(int, Pointer<Utf8>);
|
typedef _ChangeLogLevelDart = void Function(int, Pointer<Utf8>);
|
||||||
// fn debug(port: i64, log_level: FfiStr)
|
// fn debug(port: i64, log_level: FfiStr)
|
||||||
typedef _DebugC = Void Function(Int64, Pointer<Utf8>);
|
typedef _DebugC = Void Function(Int64, Pointer<Utf8>);
|
||||||
typedef _DebugDart = void Function(int, Pointer<Utf8>);
|
typedef _DebugDart = void Function(int, Pointer<Utf8>);
|
||||||
@ -243,7 +243,7 @@ class VeilidFFI implements Veilid {
|
|||||||
final _FreeStringDart _freeString;
|
final _FreeStringDart _freeString;
|
||||||
final _StartupVeilidCoreDart _startupVeilidCore;
|
final _StartupVeilidCoreDart _startupVeilidCore;
|
||||||
final _GetVeilidStateDart _getVeilidState;
|
final _GetVeilidStateDart _getVeilidState;
|
||||||
final _ChangeApiLogLevelDart _changeApiLogLevel;
|
final _ChangeLogLevelDart _changeLogLevel;
|
||||||
final _ShutdownVeilidCoreDart _shutdownVeilidCore;
|
final _ShutdownVeilidCoreDart _shutdownVeilidCore;
|
||||||
final _DebugDart _debug;
|
final _DebugDart _debug;
|
||||||
final _VeilidVersionStringDart _veilidVersionString;
|
final _VeilidVersionStringDart _veilidVersionString;
|
||||||
@ -259,9 +259,9 @@ class VeilidFFI implements Veilid {
|
|||||||
_getVeilidState =
|
_getVeilidState =
|
||||||
dylib.lookupFunction<_GetVeilidStateC, _GetVeilidStateDart>(
|
dylib.lookupFunction<_GetVeilidStateC, _GetVeilidStateDart>(
|
||||||
'get_veilid_state'),
|
'get_veilid_state'),
|
||||||
_changeApiLogLevel =
|
_changeLogLevel =
|
||||||
dylib.lookupFunction<_ChangeApiLogLevelC, _ChangeApiLogLevelDart>(
|
dylib.lookupFunction<_ChangeLogLevelC, _ChangeLogLevelDart>(
|
||||||
'change_api_log_level'),
|
'change_log_level'),
|
||||||
_shutdownVeilidCore =
|
_shutdownVeilidCore =
|
||||||
dylib.lookupFunction<_ShutdownVeilidCoreC, _ShutdownVeilidCoreDart>(
|
dylib.lookupFunction<_ShutdownVeilidCoreC, _ShutdownVeilidCoreDart>(
|
||||||
'shutdown_veilid_core'),
|
'shutdown_veilid_core'),
|
||||||
@ -299,11 +299,11 @@ class VeilidFFI implements Veilid {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<void> changeApiLogLevel(VeilidConfigLogLevel logLevel) async {
|
Future<void> changeLogLevel(VeilidConfigLogLevel logLevel) async {
|
||||||
var nativeLogLevel = logLevel.json.toNativeUtf8();
|
var nativeLogLevel = logLevel.json.toNativeUtf8();
|
||||||
final recvPort = ReceivePort("change_api_log_level");
|
final recvPort = ReceivePort("change_log_level");
|
||||||
final sendPort = recvPort.sendPort;
|
final sendPort = recvPort.sendPort;
|
||||||
_changeApiLogLevel(sendPort.nativePort, nativeLogLevel);
|
_changeLogLevel(sendPort.nativePort, nativeLogLevel);
|
||||||
malloc.free(nativeLogLevel);
|
malloc.free(nativeLogLevel);
|
||||||
return processFutureVoid(recvPort.first);
|
return processFutureVoid(recvPort.first);
|
||||||
}
|
}
|
||||||
|
@ -20,7 +20,7 @@ class VeilidJS implements Veilid {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<void> changeApiLogLevel(VeilidConfigLogLevel logLevel) {
|
Future<void> changeLogLevel(VeilidConfigLogLevel logLevel) {
|
||||||
throw UnimplementedError();
|
throw UnimplementedError();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -48,7 +48,7 @@ flutter:
|
|||||||
|
|
||||||
# To add assets to your plugin package, add an assets section, like this:
|
# To add assets to your plugin package, add an assets section, like this:
|
||||||
# assets:
|
# assets:
|
||||||
# - images/a_dot_burr.jpeg
|
# - assets/foo.wasm
|
||||||
# - images/a_dot_ham.jpeg
|
# - images/a_dot_ham.jpeg
|
||||||
#
|
#
|
||||||
# For details regarding assets in packages, see
|
# For details regarding assets in packages, see
|
||||||
|
@ -143,12 +143,12 @@ pub extern "C" fn get_veilid_state(port: i64) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub extern "C" fn change_api_log_level(port: i64, log_level: FfiStr) {
|
pub extern "C" fn change_log_level(port: i64, log_level: FfiStr) {
|
||||||
let log_level = log_level.into_opt_string();
|
let log_level = log_level.into_opt_string();
|
||||||
DartIsolateWrapper::new(port).spawn_result_json(async move {
|
DartIsolateWrapper::new(port).spawn_result_json(async move {
|
||||||
let log_level: veilid_core::VeilidConfigLogLevel = deserialize_opt_json(log_level)?;
|
let log_level: veilid_core::VeilidConfigLogLevel = deserialize_opt_json(log_level)?;
|
||||||
let veilid_api = get_veilid_api().await?;
|
let veilid_api = get_veilid_api().await?;
|
||||||
veilid_api.change_api_log_level(log_level).await;
|
veilid_api.change_log_level(log_level).await;
|
||||||
APIRESULT_VOID
|
APIRESULT_VOID
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -6,7 +6,7 @@ cfg_if! {
|
|||||||
mod dart_isolate_wrapper;
|
mod dart_isolate_wrapper;
|
||||||
mod dart_serialize;
|
mod dart_serialize;
|
||||||
} else {
|
} else {
|
||||||
mod wasm;
|
//mod wasm;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -746,7 +746,7 @@ impl Settings {
|
|||||||
} else {
|
} else {
|
||||||
format!("subnode{}", inner.testing.subnode_index)
|
format!("subnode{}", inner.testing.subnode_index)
|
||||||
})),
|
})),
|
||||||
"api_log_level" => Ok(Box::new(veilid_core::VeilidConfigLogLevel::Off)),
|
"log_level" => Ok(Box::new(veilid_core::VeilidConfigLogLevel::Off)),
|
||||||
"capabilities.protocol_udp" => Ok(Box::new(true)),
|
"capabilities.protocol_udp" => Ok(Box::new(true)),
|
||||||
"capabilities.protocol_connect_tcp" => Ok(Box::new(true)),
|
"capabilities.protocol_connect_tcp" => Ok(Box::new(true)),
|
||||||
"capabilities.protocol_accept_tcp" => Ok(Box::new(true)),
|
"capabilities.protocol_accept_tcp" => Ok(Box::new(true)),
|
||||||
|
@ -9,7 +9,7 @@ license = "LGPL-2.0-or-later OR MPL-2.0 OR (MIT AND BSD-3-Clause)"
|
|||||||
crate-type = ["cdylib", "rlib"]
|
crate-type = ["cdylib", "rlib"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
wasm-bindgen = "^0"
|
wasm-bindgen = { version = "^0", features = ["serde-serialize"] }
|
||||||
console_error_panic_hook = "^0"
|
console_error_panic_hook = "^0"
|
||||||
wee_alloc = "^0"
|
wee_alloc = "^0"
|
||||||
wasm-logger = "^0"
|
wasm-logger = "^0"
|
||||||
@ -18,6 +18,11 @@ veilid-core = { path = "../veilid-core" }
|
|||||||
cfg-if = "^1"
|
cfg-if = "^1"
|
||||||
wasm-bindgen-futures = "^0"
|
wasm-bindgen-futures = "^0"
|
||||||
js-sys = "^0"
|
js-sys = "^0"
|
||||||
|
serde_json = "^1"
|
||||||
|
serde = "^1"
|
||||||
|
lazy_static = "^1"
|
||||||
|
send_wrapper = "^0"
|
||||||
|
futures-util = { version = "^0", default_features = false, features = ["alloc"] }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
wasm-bindgen-test = "^0"
|
wasm-bindgen-test = "^0"
|
||||||
|
@ -1,278 +0,0 @@
|
|||||||
use crate::*;
|
|
||||||
pub use wasm_bindgen_futures::*;
|
|
||||||
|
|
||||||
#[wasm_bindgen(js_name = VeilidStateChange)]
|
|
||||||
pub struct JsVeilidStateChange {
|
|
||||||
kind: String, // "attachment" => AttachmentState(String)
|
|
||||||
from: JsValue,
|
|
||||||
to: JsValue,
|
|
||||||
}
|
|
||||||
#[wasm_bindgen(js_name = VeilidState)]
|
|
||||||
pub struct JsVeilidState {
|
|
||||||
kind: String, // "attachment" => AttachmentState(String)
|
|
||||||
state: JsValue,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[wasm_bindgen(js_name = VeilidCore)]
|
|
||||||
pub struct JsVeilidCore {
|
|
||||||
core: VeilidCore,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[wasm_bindgen(js_class = VeilidCore)]
|
|
||||||
impl JsVeilidCore {
|
|
||||||
#[wasm_bindgen(constructor)]
|
|
||||||
pub fn new() -> Self {
|
|
||||||
set_panic_hook();
|
|
||||||
JsVeilidCore {
|
|
||||||
core: VeilidCore::new(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fn value_to_string(val: JsValue) -> Result<Box<dyn core::any::Any>, ()> {
|
|
||||||
Ok(Box::new(val.as_string().ok_or(())?))
|
|
||||||
}
|
|
||||||
fn value_to_option_string(val: JsValue) -> Result<Box<dyn core::any::Any>, ()> {
|
|
||||||
if val.is_null() || val.is_undefined() {
|
|
||||||
return Ok(Box::new(Option::<String>::None));
|
|
||||||
}
|
|
||||||
Ok(Box::new(Some(val.as_string().ok_or(())?)))
|
|
||||||
}
|
|
||||||
fn value_to_bool(val: JsValue) -> Result<Box<dyn core::any::Any>, ()> {
|
|
||||||
Ok(Box::new(val.is_truthy()))
|
|
||||||
}
|
|
||||||
fn value_to_u8(val: JsValue) -> Result<Box<dyn core::any::Any>, ()> {
|
|
||||||
Ok(Box::new(f64_try_to_unsigned::<u8>(
|
|
||||||
val.as_f64().ok_or(())?,
|
|
||||||
)?))
|
|
||||||
}
|
|
||||||
fn value_to_u32(val: JsValue) -> Result<Box<dyn core::any::Any>, ()> {
|
|
||||||
Ok(Box::new(f64_try_to_unsigned::<u32>(
|
|
||||||
val.as_f64().ok_or(())?,
|
|
||||||
)?))
|
|
||||||
}
|
|
||||||
fn value_to_u64(val: JsValue) -> Result<Box<dyn core::any::Any>, ()> {
|
|
||||||
Ok(Box::new(f64_try_to_unsigned::<u64>(
|
|
||||||
val.as_f64().ok_or(())?,
|
|
||||||
)?))
|
|
||||||
}
|
|
||||||
fn value_to_option_u64(val: JsValue) -> Result<Box<dyn core::any::Any>, ()> {
|
|
||||||
if val.is_null() || val.is_undefined() {
|
|
||||||
return Ok(Box::new(Option::<u64>::None));
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(Box::new(Some(f64_try_to_unsigned::<u64>(
|
|
||||||
val.as_f64().ok_or(())?,
|
|
||||||
)?)))
|
|
||||||
}
|
|
||||||
fn value_to_dht_key(val: JsValue) -> Result<Box<dyn core::any::Any>, ()> {
|
|
||||||
Ok(Box::new(
|
|
||||||
DHTKey::try_decode(val.as_string().ok_or(())?.as_str()).map_err(drop)?,
|
|
||||||
))
|
|
||||||
}
|
|
||||||
fn value_to_dht_key_secret(val: JsValue) -> Result<Box<dyn core::any::Any>, ()> {
|
|
||||||
Ok(Box::new(
|
|
||||||
DHTKeySecret::try_decode(val.as_string().ok_or(())?.as_str()).map_err(drop)?,
|
|
||||||
))
|
|
||||||
}
|
|
||||||
fn value_to_vec_string(val: JsValue) -> Result<Box<dyn core::any::Any>, ()> {
|
|
||||||
let arrval = val.dyn_into::<Array>().map_err(drop)?.to_vec();
|
|
||||||
let mut out = Vec::<String>::with_capacity(arrval.len());
|
|
||||||
for v in arrval {
|
|
||||||
out.push(v.as_string().ok_or(())?);
|
|
||||||
}
|
|
||||||
Ok(Box::new(out))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn translate_config_callback(key: &str, val: JsValue) -> Result<Box<dyn core::any::Any>, ()> {
|
|
||||||
match key {
|
|
||||||
// xxx: lots of missing keys here
|
|
||||||
"namespace" => Self::value_to_string(val),
|
|
||||||
"capabilities.protocol_udp" => Self::value_to_bool(val),
|
|
||||||
"capabilities.protocol_connect_tcp" => Self::value_to_bool(val),
|
|
||||||
"capabilities.protocol_accept_tcp" => Self::value_to_bool(val),
|
|
||||||
"capabilities.protocol_connect_ws" => Self::value_to_bool(val),
|
|
||||||
"capabilities.protocol_accept_ws" => Self::value_to_bool(val),
|
|
||||||
"capabilities.protocol_connect_wss" => Self::value_to_bool(val),
|
|
||||||
"capabilities.protocol_accept_wss" => Self::value_to_bool(val),
|
|
||||||
"tablestore.directory" => Self::value_to_string(val),
|
|
||||||
"network.max_connections" => Self::value_to_u32(val),
|
|
||||||
"network.node_id" => Self::value_to_dht_key(val),
|
|
||||||
"network.node_id_secret" => Self::value_to_dht_key_secret(val),
|
|
||||||
"network.bootstrap" => Self::value_to_vec_string(val),
|
|
||||||
"network.rpc.concurrency" => Self::value_to_u32(val),
|
|
||||||
"network.rpc.queue_size" => Self::value_to_u32(val),
|
|
||||||
"network.rpc.max_timestamp_behind" => Self::value_to_option_u64(val),
|
|
||||||
"network.rpc.max_timestamp_ahead" => Self::value_to_option_u64(val),
|
|
||||||
"network.rpc.timeout" => Self::value_to_u64(val),
|
|
||||||
"network.rpc.max_route_hop_count" => Self::value_to_u8(val),
|
|
||||||
"network.dht.resolve_node_timeout" => Self::value_to_option_u64(val),
|
|
||||||
"network.dht.resolve_node_count" => Self::value_to_u32(val),
|
|
||||||
"network.dht.resolve_node_fanout" => Self::value_to_u32(val),
|
|
||||||
"network.dht.max_find_node_count" => Self::value_to_u32(val),
|
|
||||||
"network.dht.get_value_timeout" => Self::value_to_option_u64(val),
|
|
||||||
"network.dht.get_value_count" => Self::value_to_u32(val),
|
|
||||||
"network.dht.get_value_fanout" => Self::value_to_u32(val),
|
|
||||||
"network.dht.set_value_timeout" => Self::value_to_option_u64(val),
|
|
||||||
"network.dht.set_value_count" => Self::value_to_u32(val),
|
|
||||||
"network.dht.set_value_fanout" => Self::value_to_u32(val),
|
|
||||||
"network.dht.min_peer_count" => Self::value_to_u32(val),
|
|
||||||
"network.dht.min_peer_refresh_time" => Self::value_to_u64(val),
|
|
||||||
"network.dht.validate_dial_info_receipt_time" => Self::value_to_u64(val),
|
|
||||||
"network.upnp" => Self::value_to_bool(val),
|
|
||||||
"network.natpmp" => Self::value_to_bool(val),
|
|
||||||
"network.address_filter" => Self::value_to_bool(val),
|
|
||||||
"network.restricted_nat_retries" => Self::value_to_u32(val),
|
|
||||||
"network.tls.certificate_path" => Self::value_to_string(val),
|
|
||||||
"network.tls.private_key_path" => Self::value_to_string(val),
|
|
||||||
"network.application.path" => Self::value_to_string(val),
|
|
||||||
"network.application.https.enabled" => Self::value_to_bool(val),
|
|
||||||
"network.application.https.listen_address" => Self::value_to_string(val),
|
|
||||||
"network.application.http.enabled" => Self::value_to_bool(val),
|
|
||||||
"network.application.http.listen_address" => Self::value_to_string(val),
|
|
||||||
"network.protocol.udp.enabled" => Self::value_to_bool(val),
|
|
||||||
"network.protocol.udp.socket_pool_size" => Self::value_to_u32(val),
|
|
||||||
"network.protocol.udp.listen_address" => Self::value_to_string(val),
|
|
||||||
"network.protocol.udp.public_address" => Self::value_to_option_string(val),
|
|
||||||
"network.protocol.tcp.connect" => Self::value_to_bool(val),
|
|
||||||
"network.protocol.tcp.listen" => Self::value_to_bool(val),
|
|
||||||
"network.protocol.tcp.max_connections" => Self::value_to_u32(val),
|
|
||||||
"network.protocol.tcp.listen_address" => Self::value_to_string(val),
|
|
||||||
"network.protocol.tcp.public_address" => Self::value_to_option_string(val),
|
|
||||||
"network.protocol.ws.connect" => Self::value_to_bool(val),
|
|
||||||
"network.protocol.ws.listen" => Self::value_to_bool(val),
|
|
||||||
"network.protocol.ws.max_connections" => Self::value_to_u32(val),
|
|
||||||
"network.protocol.ws.listen_address" => Self::value_to_string(val),
|
|
||||||
"network.protocol.ws.path" => Self::value_to_string(val),
|
|
||||||
"network.protocol.ws.public_address" => Self::value_to_option_string(val),
|
|
||||||
"network.protocol.wss.connect" => Self::value_to_bool(val),
|
|
||||||
"network.protocol.wss.listen" => Self::value_to_bool(val),
|
|
||||||
"network.protocol.wss.max_connections" => Self::value_to_u32(val),
|
|
||||||
"network.protocol.wss.listen_address" => Self::value_to_string(val),
|
|
||||||
"network.protocol.wss.path" => Self::value_to_string(val),
|
|
||||||
"network.protocol.wss.public_address" => Self::value_to_option_string(val),
|
|
||||||
_ => return Err(()),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fn translate_veilid_state(state: JsVeilidState) -> Result<VeilidState, JsValue> {
|
|
||||||
Ok(match state.kind.as_str() {
|
|
||||||
"attachment" => {
|
|
||||||
let state_string = state
|
|
||||||
.state
|
|
||||||
.as_string()
|
|
||||||
.ok_or(JsValue::from_str("state should be a string"))?;
|
|
||||||
let astate = AttachmentState::try_from(state_string)
|
|
||||||
.map_err(|e| JsValue::from_str(format!("invalid state: {:?}", e).as_str()))?;
|
|
||||||
VeilidState::Attachment(astate)
|
|
||||||
}
|
|
||||||
_ => return Err(JsValue::from_str("unknown state kind")),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
// xxx rework this for new veilid_api mechanism which should be its own js object now
|
|
||||||
pub fn startup(
|
|
||||||
&self,
|
|
||||||
js_state_change_callback: Function,
|
|
||||||
js_config_callback: Function,
|
|
||||||
) -> Promise {
|
|
||||||
let core = self.core.clone();
|
|
||||||
future_to_promise(async move {
|
|
||||||
let vcs = VeilidCoreSetup {
|
|
||||||
state_change_callback: Arc::new(
|
|
||||||
move |change: VeilidStateChange| -> SystemPinBoxFuture<()> {
|
|
||||||
let js_state_change_callback = js_state_change_callback.clone();
|
|
||||||
Box::pin(async move {
|
|
||||||
let js_change = match change {
|
|
||||||
VeilidStateChange::Attachment {
|
|
||||||
old_state,
|
|
||||||
new_state,
|
|
||||||
} => JsVeilidStateChange {
|
|
||||||
kind: "attachment".to_owned(),
|
|
||||||
from: JsValue::from_str(old_state.to_string().as_str()),
|
|
||||||
to: JsValue::from_str(new_state.to_string().as_str()),
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
let ret = match Function::call1(
|
|
||||||
&js_state_change_callback,
|
|
||||||
&JsValue::UNDEFINED,
|
|
||||||
&JsValue::from(js_change),
|
|
||||||
) {
|
|
||||||
Ok(v) => v,
|
|
||||||
Err(e) => {
|
|
||||||
error!("calling state change callback failed: {:?}", e);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
let retp: Promise = match ret.dyn_into() {
|
|
||||||
Ok(v) => v,
|
|
||||||
Err(e) => {
|
|
||||||
error!(
|
|
||||||
"state change callback did not return a promise: {:?}",
|
|
||||||
e
|
|
||||||
);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
match JsFuture::from(retp).await {
|
|
||||||
Ok(_) => (),
|
|
||||||
Err(e) => {
|
|
||||||
error!("state change callback returned an error: {:?}", e);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
})
|
|
||||||
},
|
|
||||||
),
|
|
||||||
config_callback: Arc::new(
|
|
||||||
move |key: String| -> Result<Box<dyn core::any::Any>, String> {
|
|
||||||
let val = Function::call1(
|
|
||||||
&js_config_callback,
|
|
||||||
&JsValue::UNDEFINED,
|
|
||||||
&JsValue::from_str(key.as_str()),
|
|
||||||
)
|
|
||||||
.map_err(|_| {
|
|
||||||
format!("Failed to get config from callback for key '{}'", key)
|
|
||||||
})?;
|
|
||||||
|
|
||||||
Self::translate_config_callback(key.as_str(), val)
|
|
||||||
.map_err(|_| format!("invalid value type for config key '{}'", key))
|
|
||||||
},
|
|
||||||
),
|
|
||||||
};
|
|
||||||
|
|
||||||
match core.startup(vcs).await {
|
|
||||||
Ok(_) => Ok(JsValue::UNDEFINED),
|
|
||||||
Err(e) => Err(JsValue::from_str(
|
|
||||||
format!("VeilidCore startup() failed: {}", e.to_string()).as_str(),
|
|
||||||
)),
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn send_state_update(&self) {
|
|
||||||
self.core.send_state_update();
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn shutdown(&self) -> Promise {
|
|
||||||
let core = self.core.clone();
|
|
||||||
future_to_promise(async move {
|
|
||||||
core.shutdown().await;
|
|
||||||
Ok(JsValue::UNDEFINED)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn attach(&self) -> Promise {
|
|
||||||
let core = self.core.clone();
|
|
||||||
future_to_promise(async move {
|
|
||||||
core.attach();
|
|
||||||
Ok(JsValue::UNDEFINED)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn detach(&self) -> Promise {
|
|
||||||
let core = self.core.clone();
|
|
||||||
future_to_promise(async move {
|
|
||||||
core.detach();
|
|
||||||
Ok(JsValue::UNDEFINED)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,25 +1,191 @@
|
|||||||
|
// wasm-bindgen and clippy don't play well together yet
|
||||||
|
#![allow(clippy::all)]
|
||||||
#![cfg(target_arch = "wasm32")]
|
#![cfg(target_arch = "wasm32")]
|
||||||
#![no_std]
|
#![no_std]
|
||||||
|
|
||||||
#[macro_use]
|
|
||||||
extern crate alloc;
|
extern crate alloc;
|
||||||
|
use alloc::string::String;
|
||||||
|
use alloc::sync::Arc;
|
||||||
|
use core::any::{Any, TypeId};
|
||||||
|
use core::cell::RefCell;
|
||||||
|
use futures_util::FutureExt;
|
||||||
|
use js_sys::*;
|
||||||
|
use lazy_static::*;
|
||||||
|
use log::*;
|
||||||
|
use send_wrapper::*;
|
||||||
|
use serde::*;
|
||||||
|
use veilid_core::xx::*;
|
||||||
|
use veilid_core::*;
|
||||||
|
use wasm_bindgen_futures::*;
|
||||||
|
|
||||||
pub use log::*;
|
// Allocator
|
||||||
pub use wasm_bindgen::prelude::*;
|
extern crate wee_alloc;
|
||||||
pub use wasm_bindgen::JsCast;
|
#[global_allocator]
|
||||||
|
static ALLOC: wee_alloc::WeeAlloc = wee_alloc::WeeAlloc::INIT;
|
||||||
|
|
||||||
pub use alloc::boxed::Box;
|
static SETUP_ONCE: Once = Once::new();
|
||||||
pub use alloc::string::String;
|
pub fn setup() -> () {
|
||||||
pub use alloc::sync::Arc;
|
SETUP_ONCE.call_once(|| {});
|
||||||
pub use alloc::vec::Vec;
|
}
|
||||||
pub use core::convert::TryFrom;
|
|
||||||
pub use js_sys::*;
|
|
||||||
pub use js_veilid_core::*;
|
|
||||||
pub use utils::*;
|
|
||||||
pub use veilid_core::dht::key::*;
|
|
||||||
pub use veilid_core::xx::*;
|
|
||||||
pub use veilid_core::*;
|
|
||||||
pub use wasm_logger::*;
|
|
||||||
|
|
||||||
mod js_veilid_core;
|
// API Singleton
|
||||||
mod utils;
|
|
||||||
|
lazy_static! {
|
||||||
|
static ref VEILID_API: SendWrapper<RefCell<Option<veilid_core::VeilidAPI>>> =
|
||||||
|
SendWrapper::new(RefCell::new(None));
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_veilid_api() -> Result<veilid_core::VeilidAPI, veilid_core::VeilidAPIError> {
|
||||||
|
(*VEILID_API)
|
||||||
|
.borrow()
|
||||||
|
.clone()
|
||||||
|
.ok_or(veilid_core::VeilidAPIError::NotInitialized)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn take_veilid_api() -> Result<veilid_core::VeilidAPI, veilid_core::VeilidAPIError> {
|
||||||
|
(**VEILID_API)
|
||||||
|
.take()
|
||||||
|
.ok_or(veilid_core::VeilidAPIError::NotInitialized)
|
||||||
|
}
|
||||||
|
|
||||||
|
// JSON Marshalling
|
||||||
|
|
||||||
|
pub fn serialize_json<T: Serialize>(val: T) -> String {
|
||||||
|
serde_json::to_string(&val).expect("failed to serialize json value")
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn deserialize_json<T: de::DeserializeOwned>(
|
||||||
|
arg: &str,
|
||||||
|
) -> Result<T, veilid_core::VeilidAPIError> {
|
||||||
|
serde_json::from_str(arg).map_err(|e| veilid_core::VeilidAPIError::ParseError {
|
||||||
|
message: e.to_string(),
|
||||||
|
value: String::new(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn to_json<T: Serialize>(val: T) -> JsValue {
|
||||||
|
JsValue::from_str(&serialize_json(val))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn from_json<T: de::DeserializeOwned>(val: JsValue) -> Result<T, veilid_core::VeilidAPIError> {
|
||||||
|
let s = val
|
||||||
|
.as_string()
|
||||||
|
.ok_or_else(|| veilid_core::VeilidAPIError::ParseError {
|
||||||
|
message: "Value is not String".to_owned(),
|
||||||
|
value: String::new(),
|
||||||
|
})?;
|
||||||
|
deserialize_json(&s)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Utility types for async API results
|
||||||
|
type APIResult<T> = Result<T, veilid_core::VeilidAPIError>;
|
||||||
|
const APIRESULT_UNDEFINED: APIResult<()> = APIResult::Ok(());
|
||||||
|
|
||||||
|
pub fn wrap_api_future<F, T>(future: F) -> Promise
|
||||||
|
where
|
||||||
|
F: Future<Output = APIResult<T>> + 'static,
|
||||||
|
T: Serialize + 'static,
|
||||||
|
{
|
||||||
|
future_to_promise(future.map(|res| {
|
||||||
|
res.map(|v| {
|
||||||
|
if TypeId::of::<()>() == v.type_id() {
|
||||||
|
JsValue::UNDEFINED
|
||||||
|
} else {
|
||||||
|
to_json(v)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.map_err(|e| to_json(e))
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
// WASM Bindings
|
||||||
|
|
||||||
|
#[wasm_bindgen(js_namespace = veilid)]
|
||||||
|
pub fn initialize_veilid_wasm() {
|
||||||
|
console_error_panic_hook::set_once();
|
||||||
|
wasm_logger::init(wasm_logger::Config::new(Level::Info));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[wasm_bindgen(js_namespace = veilid)]
|
||||||
|
pub fn startup_veilid_core(update_callback: Function, json_config: String) -> Promise {
|
||||||
|
wrap_api_future(async move {
|
||||||
|
let update_callback = Arc::new(move |update: VeilidUpdate| {
|
||||||
|
let _ret =
|
||||||
|
match Function::call1(&update_callback, &JsValue::UNDEFINED, &to_json(update)) {
|
||||||
|
Ok(v) => v,
|
||||||
|
Err(e) => {
|
||||||
|
error!("calling update callback failed: {:?}", e);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
if VEILID_API.borrow().is_some() {
|
||||||
|
return Err(veilid_core::VeilidAPIError::AlreadyInitialized);
|
||||||
|
}
|
||||||
|
|
||||||
|
let veilid_api = veilid_core::api_startup_json(update_callback, json_config).await?;
|
||||||
|
VEILID_API.replace(Some(veilid_api));
|
||||||
|
APIRESULT_UNDEFINED
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
#[wasm_bindgen(js_namespace = veilid)]
|
||||||
|
pub fn get_veilid_state() -> Promise {
|
||||||
|
wrap_api_future(async move {
|
||||||
|
let veilid_api = get_veilid_api()?;
|
||||||
|
let core_state = veilid_api.get_state().await?;
|
||||||
|
Ok(core_state)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
#[wasm_bindgen(js_namespace = veilid)]
|
||||||
|
pub fn change_log_level(log_level: String) -> Promise {
|
||||||
|
wrap_api_future(async move {
|
||||||
|
let veilid_api = get_veilid_api()?;
|
||||||
|
let log_level: veilid_core::VeilidConfigLogLevel = deserialize_json(&log_level)?;
|
||||||
|
veilid_api.change_log_level(log_level).await;
|
||||||
|
APIRESULT_UNDEFINED
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
#[wasm_bindgen(js_namespace = veilid)]
|
||||||
|
pub fn shutdown_veilid_core() -> Promise {
|
||||||
|
wrap_api_future(async move {
|
||||||
|
let veilid_api = take_veilid_api()?;
|
||||||
|
veilid_api.shutdown().await;
|
||||||
|
APIRESULT_UNDEFINED
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
#[wasm_bindgen(js_namespace = veilid)]
|
||||||
|
pub fn debug(command: String) -> Promise {
|
||||||
|
wrap_api_future(async move {
|
||||||
|
let veilid_api = get_veilid_api()?;
|
||||||
|
let out = veilid_api.debug(command).await?;
|
||||||
|
Ok(out)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
#[wasm_bindgen(js_namespace = veilid)]
|
||||||
|
pub fn veilid_version_string() -> String {
|
||||||
|
veilid_core::veilid_version_string()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize)]
|
||||||
|
pub struct VeilidVersion {
|
||||||
|
pub major: u32,
|
||||||
|
pub minor: u32,
|
||||||
|
pub patch: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[wasm_bindgen(js_namespace = veilid)]
|
||||||
|
pub fn veilid_version() -> JsValue {
|
||||||
|
let (major, minor, patch) = veilid_core::veilid_version();
|
||||||
|
let vv = VeilidVersion {
|
||||||
|
major,
|
||||||
|
minor,
|
||||||
|
patch,
|
||||||
|
};
|
||||||
|
JsValue::from_serde(&vv).unwrap()
|
||||||
|
}
|
||||||
|
@ -1,38 +0,0 @@
|
|||||||
use cfg_if::*;
|
|
||||||
use wasm_bindgen::prelude::*;
|
|
||||||
//use wasm_bindgen_futures::*;
|
|
||||||
|
|
||||||
cfg_if! {
|
|
||||||
if #[cfg(feature = "wee_alloc")] {
|
|
||||||
// When the `wee_alloc` feature is enabled, use `wee_alloc` as the global
|
|
||||||
// allocator.
|
|
||||||
extern crate wee_alloc;
|
|
||||||
#[global_allocator]
|
|
||||||
static ALLOC: wee_alloc::WeeAlloc = wee_alloc::WeeAlloc::INIT;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[wasm_bindgen]
|
|
||||||
extern "C" {
|
|
||||||
#[wasm_bindgen(js_namespace = console, js_name = log)]
|
|
||||||
pub fn console_log(s: &str);
|
|
||||||
|
|
||||||
#[wasm_bindgen]
|
|
||||||
pub fn alert(s: &str);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set_panic_hook() {
|
|
||||||
#[cfg(feature = "console_error_panic_hook")]
|
|
||||||
console_error_panic_hook::set_once();
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn f64_try_to_unsigned<T>(f: f64) -> Result<T, ()>
|
|
||||||
where
|
|
||||||
T: core::convert::TryFrom<u64>,
|
|
||||||
{
|
|
||||||
let rf = f.floor();
|
|
||||||
if rf < 0.0 {
|
|
||||||
return Err(());
|
|
||||||
}
|
|
||||||
T::try_from(rf as u64).map_err(drop)
|
|
||||||
}
|
|
377
veilid-wasm/wasm-sourcemap.py
Executable file
377
veilid-wasm/wasm-sourcemap.py
Executable file
@ -0,0 +1,377 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
# Copyright 2018 The Emscripten Authors. All rights reserved.
|
||||||
|
# Emscripten is available under two separate licenses, the MIT license and the
|
||||||
|
# University of Illinois/NCSA Open Source License. Both these licenses can be
|
||||||
|
# found in the LICENSE file.
|
||||||
|
|
||||||
|
"""Utility tools that extracts DWARF information encoded in a wasm output
|
||||||
|
produced by the LLVM tools, and encodes it as a wasm source map. Additionally,
|
||||||
|
it can collect original sources, change files prefixes, and strip debug
|
||||||
|
sections from a wasm file.
|
||||||
|
"""
|
||||||
|
|
||||||
|
import argparse
|
||||||
|
from collections import OrderedDict
|
||||||
|
import json
|
||||||
|
import logging
|
||||||
|
from math import floor, log
|
||||||
|
import os
|
||||||
|
import re
|
||||||
|
from subprocess import Popen, PIPE
|
||||||
|
from pathlib import Path
|
||||||
|
import sys
|
||||||
|
|
||||||
|
__scriptdir__ = os.path.dirname(os.path.abspath(__file__))
|
||||||
|
__rootdir__ = os.path.dirname(__scriptdir__)
|
||||||
|
sys.path.append(__rootdir__)
|
||||||
|
|
||||||
|
logger = logging.getLogger('wasm-sourcemap')
|
||||||
|
|
||||||
|
|
||||||
|
def parse_args():
|
||||||
|
parser = argparse.ArgumentParser(
|
||||||
|
prog='wasm-sourcemap.py', description=__doc__)
|
||||||
|
parser.add_argument('wasm', help='wasm file')
|
||||||
|
parser.add_argument('-o', '--output', help='output source map')
|
||||||
|
parser.add_argument('-p', '--prefix', nargs='*',
|
||||||
|
help='replace source debug filename prefix for source map', default=[])
|
||||||
|
parser.add_argument('-s', '--sources', action='store_true',
|
||||||
|
help='read and embed source files from file system into source map')
|
||||||
|
parser.add_argument('-l', '--load-prefix', nargs='*',
|
||||||
|
help='replace source debug filename prefix for reading sources from file system (see also --sources)', default=[])
|
||||||
|
parser.add_argument('-w', nargs='?', help='set output wasm file')
|
||||||
|
parser.add_argument('-x', '--strip', action='store_true',
|
||||||
|
help='removes debug and linking sections')
|
||||||
|
parser.add_argument('-u', '--source-map-url', nargs='?',
|
||||||
|
help='specifies sourceMappingURL section contest')
|
||||||
|
parser.add_argument(
|
||||||
|
'--dwarfdump', help="path to llvm-dwarfdump executable")
|
||||||
|
parser.add_argument('--dwarfdump-output', nargs='?',
|
||||||
|
help=argparse.SUPPRESS)
|
||||||
|
parser.add_argument(
|
||||||
|
'--basepath', help='base path for source files, which will be relative to this')
|
||||||
|
return parser.parse_args()
|
||||||
|
|
||||||
|
|
||||||
|
class Prefixes:
|
||||||
|
def __init__(self, args):
|
||||||
|
prefixes = []
|
||||||
|
for p in args:
|
||||||
|
if '=' in p:
|
||||||
|
prefix, replacement = p.split('=')
|
||||||
|
prefixes.append({'prefix': prefix, 'replacement': replacement})
|
||||||
|
else:
|
||||||
|
prefixes.append({'prefix': p, 'replacement': None})
|
||||||
|
self.prefixes = prefixes
|
||||||
|
self.cache = {}
|
||||||
|
|
||||||
|
def resolve(self, name):
|
||||||
|
if name in self.cache:
|
||||||
|
return self.cache[name]
|
||||||
|
|
||||||
|
for p in self.prefixes:
|
||||||
|
if name.startswith(p['prefix']):
|
||||||
|
if p['replacement'] is None:
|
||||||
|
result = name[len(p['prefix'])::]
|
||||||
|
else:
|
||||||
|
result = p['replacement'] + name[len(p['prefix'])::]
|
||||||
|
break
|
||||||
|
self.cache[name] = result
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
# SourceMapPrefixes contains resolver for file names that are:
|
||||||
|
# - "sources" is for names that output to source maps JSON
|
||||||
|
# - "load" is for paths that used to load source text
|
||||||
|
class SourceMapPrefixes:
|
||||||
|
def __init__(self, sources, load):
|
||||||
|
self.sources = sources
|
||||||
|
self.load = load
|
||||||
|
|
||||||
|
def provided(self):
|
||||||
|
return bool(self.sources.prefixes or self.load.prefixes)
|
||||||
|
|
||||||
|
|
||||||
|
def encode_vlq(n):
|
||||||
|
VLQ_CHARS = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
|
||||||
|
x = (n << 1) if n >= 0 else ((-n << 1) + 1)
|
||||||
|
result = ""
|
||||||
|
while x > 31:
|
||||||
|
result = result + VLQ_CHARS[32 + (x & 31)]
|
||||||
|
x = x >> 5
|
||||||
|
return result + VLQ_CHARS[x]
|
||||||
|
|
||||||
|
|
||||||
|
def read_var_uint(wasm, pos):
|
||||||
|
n = 0
|
||||||
|
shift = 0
|
||||||
|
b = ord(wasm[pos:pos + 1])
|
||||||
|
pos = pos + 1
|
||||||
|
while b >= 128:
|
||||||
|
n = n | ((b - 128) << shift)
|
||||||
|
b = ord(wasm[pos:pos + 1])
|
||||||
|
pos = pos + 1
|
||||||
|
shift += 7
|
||||||
|
return n + (b << shift), pos
|
||||||
|
|
||||||
|
|
||||||
|
def strip_debug_sections(wasm):
|
||||||
|
logger.debug('Strip debug sections')
|
||||||
|
pos = 8
|
||||||
|
stripped = wasm[:pos]
|
||||||
|
|
||||||
|
while pos < len(wasm):
|
||||||
|
section_start = pos
|
||||||
|
section_id, pos_ = read_var_uint(wasm, pos)
|
||||||
|
section_size, section_body = read_var_uint(wasm, pos_)
|
||||||
|
pos = section_body + section_size
|
||||||
|
if section_id == 0:
|
||||||
|
name_len, name_pos = read_var_uint(wasm, section_body)
|
||||||
|
name_end = name_pos + name_len
|
||||||
|
name = wasm[name_pos:name_end]
|
||||||
|
if name == "linking" or name == "sourceMappingURL" or name.startswith("reloc..debug_") or name.startswith(".debug_"):
|
||||||
|
continue # skip debug related sections
|
||||||
|
stripped = stripped + wasm[section_start:pos]
|
||||||
|
|
||||||
|
return stripped
|
||||||
|
|
||||||
|
|
||||||
|
def encode_uint_var(n):
|
||||||
|
result = bytearray()
|
||||||
|
while n > 127:
|
||||||
|
result.append(128 | (n & 127))
|
||||||
|
n = n >> 7
|
||||||
|
result.append(n)
|
||||||
|
return bytes(result)
|
||||||
|
|
||||||
|
|
||||||
|
def append_source_mapping(wasm, url):
|
||||||
|
logger.debug('Append sourceMappingURL section')
|
||||||
|
section_name = "sourceMappingURL"
|
||||||
|
section_content = encode_uint_var(
|
||||||
|
len(section_name)) + section_name + encode_uint_var(len(url)) + url
|
||||||
|
return wasm + encode_uint_var(0) + encode_uint_var(len(section_content)) + section_content
|
||||||
|
|
||||||
|
|
||||||
|
def get_code_section_offset(wasm):
|
||||||
|
logger.debug('Read sections index')
|
||||||
|
pos = 8
|
||||||
|
|
||||||
|
while pos < len(wasm):
|
||||||
|
section_id, pos_ = read_var_uint(wasm, pos)
|
||||||
|
section_size, pos = read_var_uint(wasm, pos_)
|
||||||
|
if section_id == 10:
|
||||||
|
return pos
|
||||||
|
pos = pos + section_size
|
||||||
|
|
||||||
|
|
||||||
|
def remove_dead_entries(entries):
|
||||||
|
# Remove entries for dead functions. It is a heuristics to ignore data if the
|
||||||
|
# function starting address near to 0 (is equal to its size field length).
|
||||||
|
block_start = 0
|
||||||
|
cur_entry = 0
|
||||||
|
while cur_entry < len(entries):
|
||||||
|
if not entries[cur_entry]['eos']:
|
||||||
|
cur_entry += 1
|
||||||
|
continue
|
||||||
|
fn_start = entries[block_start]['address']
|
||||||
|
# Calculate the LEB encoded function size (including size field)
|
||||||
|
fn_size_length = floor(
|
||||||
|
log(entries[cur_entry]['address'] - fn_start + 1, 128)) + 1
|
||||||
|
min_live_offset = 1 + fn_size_length # 1 byte is for code section entries
|
||||||
|
if fn_start < min_live_offset:
|
||||||
|
# Remove dead code debug info block.
|
||||||
|
del entries[block_start:cur_entry + 1]
|
||||||
|
cur_entry = block_start
|
||||||
|
continue
|
||||||
|
cur_entry += 1
|
||||||
|
block_start = cur_entry
|
||||||
|
|
||||||
|
|
||||||
|
def read_dwarf_entries(wasm, options):
|
||||||
|
if options.dwarfdump_output:
|
||||||
|
output = Path(options.dwarfdump_output).read_bytes()
|
||||||
|
elif options.dwarfdump:
|
||||||
|
logger.debug('Reading DWARF information from %s' % wasm)
|
||||||
|
if not os.path.exists(options.dwarfdump):
|
||||||
|
logger.error('llvm-dwarfdump not found: ' + options.dwarfdump)
|
||||||
|
sys.exit(1)
|
||||||
|
process = Popen([options.dwarfdump, '-debug-info',
|
||||||
|
'-debug-line', '--recurse-depth=0', wasm], stdout=PIPE)
|
||||||
|
output, err = process.communicate()
|
||||||
|
exit_code = process.wait()
|
||||||
|
if exit_code != 0:
|
||||||
|
logger.error(
|
||||||
|
'Error during llvm-dwarfdump execution (%s)' % exit_code)
|
||||||
|
sys.exit(1)
|
||||||
|
else:
|
||||||
|
logger.error('Please specify either --dwarfdump or --dwarfdump-output')
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
entries = []
|
||||||
|
debug_line_chunks = re.split(
|
||||||
|
r"debug_line\[(0x[0-9a-f]*)\]", output.decode('utf-8'))
|
||||||
|
maybe_debug_info_content = debug_line_chunks[0]
|
||||||
|
for i in range(1, len(debug_line_chunks), 2):
|
||||||
|
stmt_list = debug_line_chunks[i]
|
||||||
|
comp_dir_match = re.search(r"DW_AT_stmt_list\s+\(" + stmt_list + r"\)\s+" +
|
||||||
|
r"DW_AT_comp_dir\s+\(\"([^\"]+)", maybe_debug_info_content)
|
||||||
|
comp_dir = comp_dir_match.group(
|
||||||
|
1) if comp_dir_match is not None else ""
|
||||||
|
|
||||||
|
line_chunk = debug_line_chunks[i + 1]
|
||||||
|
|
||||||
|
# include_directories[ 1] = "/Users/yury/Work/junk/sqlite-playground/src"
|
||||||
|
# file_names[ 1]:
|
||||||
|
# name: "playground.c"
|
||||||
|
# dir_index: 1
|
||||||
|
# mod_time: 0x00000000
|
||||||
|
# length: 0x00000000
|
||||||
|
#
|
||||||
|
# Address Line Column File ISA Discriminator Flags
|
||||||
|
# ------------------ ------ ------ ------ --- ------------- -------------
|
||||||
|
# 0x0000000000000006 22 0 1 0 0 is_stmt
|
||||||
|
# 0x0000000000000007 23 10 1 0 0 is_stmt prologue_end
|
||||||
|
# 0x000000000000000f 23 3 1 0 0
|
||||||
|
# 0x0000000000000010 23 3 1 0 0 end_sequence
|
||||||
|
# 0x0000000000000011 28 0 1 0 0 is_stmt
|
||||||
|
|
||||||
|
include_directories = {'0': comp_dir}
|
||||||
|
for dir in re.finditer(r"include_directories\[\s*(\d+)\] = \"([^\"]*)", line_chunk):
|
||||||
|
include_directories[dir.group(1)] = dir.group(2)
|
||||||
|
|
||||||
|
files = {}
|
||||||
|
for file in re.finditer(r"file_names\[\s*(\d+)\]:\s+name: \"([^\"]*)\"\s+dir_index: (\d+)", line_chunk):
|
||||||
|
dir = include_directories[file.group(3)]
|
||||||
|
file_path = (dir + '/' if file.group(2)
|
||||||
|
[0] != '/' else '') + file.group(2)
|
||||||
|
files[file.group(1)] = file_path
|
||||||
|
|
||||||
|
for line in re.finditer(r"\n0x([0-9a-f]+)\s+(\d+)\s+(\d+)\s+(\d+)(.*?end_sequence)?", line_chunk):
|
||||||
|
entry = {'address': int(line.group(1), 16), 'line': int(line.group(2)), 'column': int(
|
||||||
|
line.group(3)), 'file': files[line.group(4)], 'eos': line.group(5) is not None}
|
||||||
|
if not entry['eos']:
|
||||||
|
entries.append(entry)
|
||||||
|
else:
|
||||||
|
# move end of function to the last END operator
|
||||||
|
entry['address'] -= 1
|
||||||
|
if entries[-1]['address'] == entry['address']:
|
||||||
|
# last entry has the same address, reusing
|
||||||
|
entries[-1]['eos'] = True
|
||||||
|
else:
|
||||||
|
entries.append(entry)
|
||||||
|
|
||||||
|
remove_dead_entries(entries)
|
||||||
|
|
||||||
|
# return entries sorted by the address field
|
||||||
|
return sorted(entries, key=lambda entry: entry['address'])
|
||||||
|
|
||||||
|
|
||||||
|
def normalize_path(path):
|
||||||
|
return path.replace('\\', '/').replace('//', '/')
|
||||||
|
|
||||||
|
|
||||||
|
def build_sourcemap(entries, code_section_offset, prefixes, collect_sources, base_path):
|
||||||
|
sources = []
|
||||||
|
sources_content = [] if collect_sources else None
|
||||||
|
mappings = []
|
||||||
|
sources_map = {}
|
||||||
|
last_address = 0
|
||||||
|
last_source_id = 0
|
||||||
|
last_line = 1
|
||||||
|
last_column = 1
|
||||||
|
for entry in entries:
|
||||||
|
line = entry['line']
|
||||||
|
column = entry['column']
|
||||||
|
# ignore entries with line 0
|
||||||
|
if line == 0:
|
||||||
|
continue
|
||||||
|
# start at least at column 1
|
||||||
|
if column == 0:
|
||||||
|
column = 1
|
||||||
|
address = entry['address'] + code_section_offset
|
||||||
|
file_name = entry['file']
|
||||||
|
file_name = normalize_path(file_name)
|
||||||
|
# if prefixes were provided, we use that; otherwise, we emit a relative
|
||||||
|
# path
|
||||||
|
if prefixes.provided():
|
||||||
|
source_name = prefixes.sources.resolve(file_name)
|
||||||
|
else:
|
||||||
|
try:
|
||||||
|
file_name = os.path.relpath(file_name, base_path)
|
||||||
|
except ValueError:
|
||||||
|
file_name = os.path.abspath(file_name)
|
||||||
|
file_name = normalize_path(file_name)
|
||||||
|
source_name = file_name
|
||||||
|
if source_name not in sources_map:
|
||||||
|
source_id = len(sources)
|
||||||
|
sources_map[source_name] = source_id
|
||||||
|
sources.append(source_name)
|
||||||
|
if collect_sources:
|
||||||
|
load_name = prefixes.load.resolve(file_name)
|
||||||
|
try:
|
||||||
|
with open(load_name, 'r') as infile:
|
||||||
|
source_content = infile.read()
|
||||||
|
sources_content.append(source_content)
|
||||||
|
except IOError:
|
||||||
|
print('Failed to read source: %s' % load_name)
|
||||||
|
sources_content.append(None)
|
||||||
|
else:
|
||||||
|
source_id = sources_map[source_name]
|
||||||
|
|
||||||
|
address_delta = address - last_address
|
||||||
|
source_id_delta = source_id - last_source_id
|
||||||
|
line_delta = line - last_line
|
||||||
|
column_delta = column - last_column
|
||||||
|
mappings.append(encode_vlq(address_delta) + encode_vlq(source_id_delta) +
|
||||||
|
encode_vlq(line_delta) + encode_vlq(column_delta))
|
||||||
|
last_address = address
|
||||||
|
last_source_id = source_id
|
||||||
|
last_line = line
|
||||||
|
last_column = column
|
||||||
|
return OrderedDict([('version', 3),
|
||||||
|
('names', []),
|
||||||
|
('sources', sources),
|
||||||
|
('sourcesContent', sources_content),
|
||||||
|
('mappings', ','.join(mappings))])
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
options = parse_args()
|
||||||
|
|
||||||
|
wasm_input = options.wasm
|
||||||
|
with open(wasm_input, 'rb') as infile:
|
||||||
|
wasm = infile.read()
|
||||||
|
|
||||||
|
entries = read_dwarf_entries(wasm_input, options)
|
||||||
|
|
||||||
|
code_section_offset = get_code_section_offset(wasm)
|
||||||
|
|
||||||
|
prefixes = SourceMapPrefixes(sources=Prefixes(
|
||||||
|
options.prefix), load=Prefixes(options.load_prefix))
|
||||||
|
|
||||||
|
logger.debug('Saving to %s' % options.output)
|
||||||
|
map = build_sourcemap(entries, code_section_offset,
|
||||||
|
prefixes, options.sources, options.basepath)
|
||||||
|
with open(options.output, 'w') as outfile:
|
||||||
|
json.dump(map, outfile, separators=(',', ':'))
|
||||||
|
|
||||||
|
if options.strip:
|
||||||
|
wasm = strip_debug_sections(wasm)
|
||||||
|
|
||||||
|
if options.source_map_url:
|
||||||
|
wasm = append_source_mapping(wasm, options.source_map_url)
|
||||||
|
|
||||||
|
if options.w:
|
||||||
|
logger.debug('Saving wasm to %s' % options.w)
|
||||||
|
with open(options.w, 'wb') as outfile:
|
||||||
|
outfile.write(wasm)
|
||||||
|
|
||||||
|
logger.debug('Done')
|
||||||
|
return 0
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
logging.basicConfig(level=logging.DEBUG if os.environ.get(
|
||||||
|
'EMCC_DEBUG') else logging.INFO)
|
||||||
|
sys.exit(main())
|
16
veilid-wasm/wasm_build.sh
Executable file
16
veilid-wasm/wasm_build.sh
Executable file
@ -0,0 +1,16 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
SCRIPTDIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
|
||||||
|
|
||||||
|
if [[ "$1" == "release" ]]; then
|
||||||
|
RELEASE=-r
|
||||||
|
GENERATE_SOURCE_MAP=
|
||||||
|
else
|
||||||
|
RELEASE=
|
||||||
|
RUSTFLAGS="-O -g"
|
||||||
|
GENERATE_SOURCE_MAP="./wasm-sourcemap.py ../target/wasm32-unknown-unknown/debug/veilid_flutter.wasm -o ../target/wasm32-unknown-unknown/debug/veilid_flutter.wasm.map --dwarfdump `which llvm-dwarfdump`"
|
||||||
|
fi
|
||||||
|
|
||||||
|
pushd $SCRIPTDIR 2> /dev/null
|
||||||
|
cargo build --target wasm32-unknown-unknown $RELEASE
|
||||||
|
$GENERATE_SOURCE_MAP
|
||||||
|
popd 2> /dev/null
|
Loading…
Reference in New Issue
Block a user