diff --git a/api/sixtyfps-node/native/Cargo.toml b/api/sixtyfps-node/native/Cargo.toml index e77e92f56..04a263eee 100644 --- a/api/sixtyfps-node/native/Cargo.toml +++ b/api/sixtyfps-node/native/Cargo.toml @@ -24,6 +24,7 @@ sixtyfps-corelib = { version = "=0.0.2", path="../../../sixtyfps_runtime/corelib scoped-tls-hkt = "0.1" neon = "0.5.0" css-color-parser2 = "1.0.1" +spin_on = "0.1" #FIXME: remove and delegate to async JS instead [build-dependencies] neon-build = "0.5.0" diff --git a/api/sixtyfps-node/native/lib.rs b/api/sixtyfps-node/native/lib.rs index 64b56b19e..c04f155c2 100644 --- a/api/sixtyfps-node/native/lib.rs +++ b/api/sixtyfps-node/native/lib.rs @@ -63,18 +63,17 @@ fn load(mut cx: FunctionContext) -> JsResult { } None => vec![], }; - let compiler_config = sixtyfps_compilerlib::CompilerConfiguration { - include_paths: &include_paths, - ..Default::default() - }; + let compiler_config = + sixtyfps_compilerlib::CompilerConfiguration { include_paths, ..Default::default() }; let source = std::fs::read_to_string(&path).or_else(|e| cx.throw_error(e.to_string()))?; - let (c, warnings) = match sixtyfps_interpreter::load(source, &path, compiler_config) { - (Ok(c), warnings) => (c, warnings), - (Err(()), errors) => { - errors.print(); - return cx.throw_error("Compilation error"); - } - }; + let (c, warnings) = + match spin_on::spin_on(sixtyfps_interpreter::load(source, path.into(), compiler_config)) { + (Ok(c), warnings) => (c, warnings), + (Err(()), errors) => { + errors.print(); + return cx.throw_error("Compilation error"); + } + }; warnings.print(); diff --git a/api/sixtyfps-rs/sixtyfps-build/Cargo.toml b/api/sixtyfps-rs/sixtyfps-build/Cargo.toml index 808ba85cb..dd6eebb08 100644 --- a/api/sixtyfps-rs/sixtyfps-build/Cargo.toml +++ b/api/sixtyfps-rs/sixtyfps-build/Cargo.toml @@ -15,3 +15,4 @@ path = "lib.rs" [dependencies] sixtyfps-compilerlib = { version = "=0.0.2", path = "../../../sixtyfps_compiler", features = ["rust", "display-diagnostics"] } thiserror = "1" +spin_on = "0.1" diff --git a/api/sixtyfps-rs/sixtyfps-build/lib.rs b/api/sixtyfps-rs/sixtyfps-build/lib.rs index 323712024..44bc445d6 100644 --- a/api/sixtyfps-rs/sixtyfps-build/lib.rs +++ b/api/sixtyfps-rs/sixtyfps-build/lib.rs @@ -167,9 +167,14 @@ pub fn compile_with_config( compiler_config.embed_resources = true; } }; + let embed_resources = compiler_config.embed_resources; - let (doc, mut diag) = - sixtyfps_compilerlib::compile_syntax_node(syntax_node, diag, &compiler_config); + // 'spin_on' is ok here because the compiler in single threaded and does not block if there is no blocking future + let (doc, mut diag) = spin_on::spin_on(sixtyfps_compilerlib::compile_syntax_node( + syntax_node, + diag, + compiler_config, + )); if diag.has_error() { let vec = diag.to_string_vec(); @@ -207,7 +212,7 @@ pub fn compile_with_config( write!(code_formater, "{}", generated).map_err(CompileError::SaveError)?; println!("cargo:rerun-if-changed={}", path.display()); - if !compiler_config.embed_resources { + if embed_resources { for resource in doc.root_component.referenced_file_resources.borrow().keys() { println!("cargo:rerun-if-changed={}", resource); } diff --git a/api/sixtyfps-rs/sixtyfps-docs-integration.html b/api/sixtyfps-rs/sixtyfps-docs-integration.html index 1d0f33990..63aa617aa 100644 --- a/api/sixtyfps-rs/sixtyfps-docs-integration.html +++ b/api/sixtyfps-rs/sixtyfps-docs-integration.html @@ -6,7 +6,7 @@ "use strict"; import * as sixtyfps from 'https://sixtyfps.io/wasm-interpreter/sixtyfps_wasm_interpreter.js'; - function render_or_error(source, div) { + async function render_or_error(source, div) { let canvas_id = 'canvas_' + Math.random().toString(36).substr(2, 9); let canvas = document.createElement("canvas"); canvas.width = 100; @@ -14,11 +14,8 @@ canvas.id = canvas_id; div.appendChild(canvas); try { - sixtyfps.instantiate_from_string(source, document.location.href, canvas_id); + var compiled_component = await sixtyfps.compile_from_string(source, ""); } catch (e) { - if (e.message === "Using exceptions for control flow, don't mind me. This isn't actually an error!") { - throw e; - } var text = document.createTextNode(e.message); var p = document.createElement('pre'); p.appendChild(text); @@ -26,6 +23,7 @@ throw e; } + compiled_component.run(canvas_id); } async function run() { diff --git a/api/sixtyfps-rs/sixtyfps-macros/Cargo.toml b/api/sixtyfps-rs/sixtyfps-macros/Cargo.toml index 5cec220cb..b6d10527c 100644 --- a/api/sixtyfps-rs/sixtyfps-macros/Cargo.toml +++ b/api/sixtyfps-rs/sixtyfps-macros/Cargo.toml @@ -17,4 +17,5 @@ path = "lib.rs" quote = "1.0" proc-macro2 = "1.0.17" sixtyfps-compilerlib = { version = "=0.0.2", path = "../../../sixtyfps_compiler", features = ["proc_macro_span", "rust"] } +spin_on = "0.1" diff --git a/api/sixtyfps-rs/sixtyfps-macros/lib.rs b/api/sixtyfps-rs/sixtyfps-macros/lib.rs index 53ec8024b..866867e6b 100644 --- a/api/sixtyfps-rs/sixtyfps-macros/lib.rs +++ b/api/sixtyfps-rs/sixtyfps-macros/lib.rs @@ -331,9 +331,9 @@ pub fn sixtyfps(stream: TokenStream) -> TokenStream { let syntax_node = parser::SyntaxNodeWithSourceFile { node: syntax_node, source_file }; //println!("{:#?}", syntax_node); - let compiler_config = - CompilerConfiguration { include_paths: &include_paths, ..Default::default() }; - let (root_component, mut diag) = compile_syntax_node(syntax_node, diag, &compiler_config); + let compiler_config = CompilerConfiguration { include_paths, ..Default::default() }; + let (root_component, mut diag) = + spin_on::spin_on(compile_syntax_node(syntax_node, diag, compiler_config)); //println!("{:#?}", tree); if diag.has_error() { diag.map_offsets_to_span(&tokens); diff --git a/api/sixtyfps-wasm-interpreter/Cargo.toml b/api/sixtyfps-wasm-interpreter/Cargo.toml index b5859423f..24f216ae9 100644 --- a/api/sixtyfps-wasm-interpreter/Cargo.toml +++ b/api/sixtyfps-wasm-interpreter/Cargo.toml @@ -14,7 +14,8 @@ crate-type = ["cdylib"] [dependencies] sixtyfps-interpreter = { path = "../../sixtyfps_runtime/interpreter" } -wasm-bindgen = { version = "0.2.67" } +wasm-bindgen = { version = "0.2.66" } +wasm-bindgen-futures = { version = "0.4.18" } js-sys = "0.3.44" console_error_panic_hook = { version = "0.1.6", optional = true } wee_alloc = { version = "0.4.5", optional = true } diff --git a/api/sixtyfps-wasm-interpreter/src/lib.rs b/api/sixtyfps-wasm-interpreter/src/lib.rs index 88558936d..fe425aa1b 100644 --- a/api/sixtyfps-wasm-interpreter/src/lib.rs +++ b/api/sixtyfps-wasm-interpreter/src/lib.rs @@ -10,29 +10,25 @@ LICENSE END */ //! This wasm library can be loaded from JS to load and display the content of .60 files #![cfg(target_arch = "wasm32")] +use std::rc::Rc; use wasm_bindgen::prelude::*; #[cfg(feature = "wee_alloc")] #[global_allocator] static ALLOC: wee_alloc::WeeAlloc = wee_alloc::WeeAlloc::INIT; -/// Compile and display the content of a string. -/// The HTML must contains a element with the given `canvas_id` -/// where the result is gonna be rendered +/// Compile the content of a string. +/// +/// Returns a promise to a compiled component which can be run with ".run()" #[wasm_bindgen] -pub fn instantiate_from_string( - source: &str, - base_url: &str, - canvas_id: String, -) -> Result<(), JsValue> { +pub async fn compile_from_string( + source: String, + base_url: String, +) -> Result { #[cfg(feature = "console_error_panic_hook")] console_error_panic_hook::set_once(); - let c = match sixtyfps_interpreter::load( - source.to_owned(), - &std::path::Path::new(base_url), - Default::default(), - ) { + let c = match sixtyfps_interpreter::load(source, base_url.into(), Default::default()).await { (Ok(c), ..) => { //TODO: warnings.print(); c @@ -84,7 +80,20 @@ pub fn instantiate_from_string( } }; - let component = c.clone().create(canvas_id); - component.window().run(component.borrow(), component.root_item()); - Ok(()) + Ok(WrappedCompiledComp(c)) +} + +#[wasm_bindgen] +pub struct WrappedCompiledComp(Rc); + +#[wasm_bindgen] +impl WrappedCompiledComp { + /// Run this compiled component in a canvas. + /// The HTML must contains a element with the given `canvas_id` + /// where the result is gonna be rendered + #[wasm_bindgen] + pub fn run(&self, canvas_id: String) { + let component = self.0.clone().create(canvas_id); + component.window().run(component.borrow(), component.root_item()); + } } diff --git a/sixtyfps_compiler/Cargo.toml b/sixtyfps_compiler/Cargo.toml index 53b14cc6c..16a3051af 100644 --- a/sixtyfps_compiler/Cargo.toml +++ b/sixtyfps_compiler/Cargo.toml @@ -43,5 +43,6 @@ once_cell = "1" [dev-dependencies] regex = "1.3.7" parser_test_macro = { path = "./parser_test_macro" } +spin_on = "0.1" diff --git a/sixtyfps_compiler/lib.rs b/sixtyfps_compiler/lib.rs index bb0bf0392..13be8b469 100644 --- a/sixtyfps_compiler/lib.rs +++ b/sixtyfps_compiler/lib.rs @@ -55,20 +55,20 @@ mod passes { #[derive(Default)] /// CompilationConfiguration allows configuring different aspects of the compiler. -pub struct CompilerConfiguration<'a> { +pub struct CompilerConfiguration { /// Indicate whether to embed resources such as images in the generated output or whether /// to retain references to the resources on the file system. pub embed_resources: bool, /// The compiler will look in these paths for components used in the file to compile. - pub include_paths: &'a [std::path::PathBuf], + pub include_paths: Vec, /// the name of the style. (eg: "native") - pub style: Option<&'a str>, + pub style: Option, } -pub fn compile_syntax_node( +pub async fn compile_syntax_node( doc_node: parser::SyntaxNodeWithSourceFile, mut diagnostics: diagnostics::FileDiagnostics, - compiler_config: &CompilerConfiguration, + compiler_config: CompilerConfiguration, ) -> (object_tree::Document, diagnostics::BuildDiagnostics) { let mut build_diagnostics = diagnostics::BuildDiagnostics::default(); @@ -80,6 +80,7 @@ pub fn compile_syntax_node( let style = compiler_config .style + .as_ref() .map(Cow::from) .or_else(|| std::env::var("SIXTYFPS_STYLE").map(Cow::from).ok()) .unwrap_or_else(|| { @@ -97,13 +98,14 @@ pub fn compile_syntax_node( let mut all_docs = typeloader::LoadedDocuments::default(); if doc_node.source_file.is_some() { + let builtin_lib = library::widget_library().iter().find(|x| x.0 == style).map(|x| x.1); typeloader::load_dependencies_recursively( &doc_node, &mut diagnostics, &type_registry, &global_type_registry, &compiler_config, - library::widget_library().iter().find(|x| x.0 == style).map(|x| x.1), + builtin_lib, &mut all_docs, &mut build_diagnostics, ); @@ -115,7 +117,7 @@ pub fn compile_syntax_node( if !build_diagnostics.has_error() { // FIXME: ideally we would be able to run more passes, but currently we panic because invariant are not met. - run_passes(&doc, &mut build_diagnostics, compiler_config); + run_passes(&doc, &mut build_diagnostics, &compiler_config); } (doc, build_diagnostics) diff --git a/sixtyfps_compiler/tests/syntax_tests.rs b/sixtyfps_compiler/tests/syntax_tests.rs index e076e69b3..3d14f6444 100644 --- a/sixtyfps_compiler/tests/syntax_tests.rs +++ b/sixtyfps_compiler/tests/syntax_tests.rs @@ -140,14 +140,14 @@ fn process_file_source( sixtyfps_compilerlib::parser::parse(source.clone(), Some(path)); let compile_diagnostics = if !parse_diagnostics.has_error() { let compiler_config = sixtyfps_compilerlib::CompilerConfiguration { - style: "ugly".into(), + style: Some("ugly".into()), ..Default::default() }; - let (_, build_diags) = sixtyfps_compilerlib::compile_syntax_node( + let (_, build_diags) = spin_on::spin_on(sixtyfps_compilerlib::compile_syntax_node( syntax_node, parse_diagnostics, - &compiler_config, - ); + compiler_config, + )); build_diags.into_iter().collect() } else { vec![parse_diagnostics] diff --git a/sixtyfps_runtime/interpreter/dynamic_component.rs b/sixtyfps_runtime/interpreter/dynamic_component.rs index 7253be575..af8780dac 100644 --- a/sixtyfps_runtime/interpreter/dynamic_component.rs +++ b/sixtyfps_runtime/interpreter/dynamic_component.rs @@ -433,20 +433,20 @@ fn rtti_for_flickable() -> (&'static str, Rc) { /// Create a ComponentDescription from a source. /// The path corresponding to the source need to be passed as well (path is used for diagnostics /// and loading relative assets) -pub fn load<'id>( +pub async fn load<'id>( source: String, - path: &std::path::Path, - compiler_config: &CompilerConfiguration, + path: std::path::PathBuf, + compiler_config: CompilerConfiguration, guard: generativity::Guard<'id>, ) -> (Result>, ()>, sixtyfps_compilerlib::diagnostics::BuildDiagnostics) { - let (syntax_node, diag) = parser::parse(source, Some(path)); + let (syntax_node, diag) = parser::parse(source, Some(path.as_path())); if diag.has_error() { let mut d = sixtyfps_compilerlib::diagnostics::BuildDiagnostics::default(); d.add(diag); return (Err(()), d); } - let (doc, diag) = compile_syntax_node(syntax_node, diag, &compiler_config); + let (doc, diag) = compile_syntax_node(syntax_node, diag, compiler_config).await; if diag.has_error() { return (Err(()), diag); } diff --git a/sixtyfps_runtime/interpreter/lib.rs b/sixtyfps_runtime/interpreter/lib.rs index cf6293d88..c60f27cb9 100644 --- a/sixtyfps_runtime/interpreter/lib.rs +++ b/sixtyfps_runtime/interpreter/lib.rs @@ -175,20 +175,21 @@ impl<'id> dynamic_component::ComponentDescription<'id> { pub type ComponentDescription = dynamic_component::ComponentDescription<'static>; pub type ComponentBox = dynamic_component::ComponentBox<'static>; -pub fn load( +pub async fn load( source: String, - path: &std::path::Path, + path: std::path::PathBuf, mut compiler_config: CompilerConfiguration, ) -> (Result, ()>, sixtyfps_compilerlib::diagnostics::BuildDiagnostics) { if compiler_config.style.is_none() && std::env::var("SIXTYFPS_STYLE").is_err() { // Defaults to native if it exists: compiler_config.style = Some(if sixtyfps_rendering_backend_default::HAS_NATIVE_STYLE { - "native" + "native".to_owned() } else { - "ugly" + "ugly".to_owned() }); } - dynamic_component::load(source, path, &compiler_config, unsafe { + dynamic_component::load(source, path, compiler_config, unsafe { generativity::Guard::new(generativity::Id::new()) }) + .await } diff --git a/tests/driver/Cargo.toml b/tests/driver/Cargo.toml index 863cb2136..ea23c79b8 100644 --- a/tests/driver/Cargo.toml +++ b/tests/driver/Cargo.toml @@ -19,6 +19,7 @@ scopeguard = "1.1.0" test_driver_lib = { path = "../driver_lib" } lazy_static = "1.4.0" which = "4.0.2" +spin_on = "0.1" [build-dependencies] test_driver_lib = { path = "../driver_lib" } diff --git a/tests/driver/cppdriver.rs b/tests/driver/cppdriver.rs index 2b7ab8cba..3a650a9de 100644 --- a/tests/driver/cppdriver.rs +++ b/tests/driver/cppdriver.rs @@ -15,13 +15,14 @@ use std::ops::Deref; pub fn test(testcase: &test_driver_lib::TestCase) -> Result<(), Box> { let source = std::fs::read_to_string(&testcase.absolute_path)?; - let include_paths = &test_driver_lib::extract_include_paths(&source) + let include_paths = test_driver_lib::extract_include_paths(&source) .map(std::path::PathBuf::from) .collect::>(); let (syntax_node, diag) = parser::parse(source.clone(), Some(&testcase.absolute_path)); let compiler_config = CompilerConfiguration { include_paths, ..Default::default() }; - let (root_component, mut diag) = compile_syntax_node(syntax_node, diag, &compiler_config); + let (root_component, mut diag) = + spin_on::spin_on(compile_syntax_node(syntax_node, diag, compiler_config)); if diag.has_error() { let vec = diag.to_string_vec(); diff --git a/tests/driver/interpreter.rs b/tests/driver/interpreter.rs index a51a87b9b..921ea31a5 100644 --- a/tests/driver/interpreter.rs +++ b/tests/driver/interpreter.rs @@ -12,23 +12,24 @@ use std::error::Error; pub fn test(testcase: &test_driver_lib::TestCase) -> Result<(), Box> { let source = std::fs::read_to_string(&testcase.absolute_path)?; - let include_paths = &test_driver_lib::extract_include_paths(&source) + let include_paths = test_driver_lib::extract_include_paths(&source) .map(std::path::PathBuf::from) .collect::>(); - let config = sixtyfps_compilerlib::CompilerConfiguration { - include_paths: &include_paths, - ..Default::default() - }; + let config = + sixtyfps_compilerlib::CompilerConfiguration { include_paths, ..Default::default() }; - let (component, _warnings) = - match sixtyfps_interpreter::load(source, &testcase.absolute_path, config) { - (Ok(c), diagnostics) => (c, diagnostics), - (Err(()), errors) => { - let vec = errors.to_string_vec(); - errors.print(); - return Err(vec.join("\n").into()); - } - }; + let (component, _warnings) = match spin_on::spin_on(sixtyfps_interpreter::load( + source, + testcase.absolute_path.clone(), + config, + )) { + (Ok(c), diagnostics) => (c, diagnostics), + (Err(()), errors) => { + let vec = errors.to_string_vec(); + errors.print(); + return Err(vec.join("\n").into()); + } + }; component.create(); diff --git a/tools/compiler/Cargo.toml b/tools/compiler/Cargo.toml index 7f38b849a..7e0e81238 100644 --- a/tools/compiler/Cargo.toml +++ b/tools/compiler/Cargo.toml @@ -15,3 +15,4 @@ path = "main.rs" [dependencies] sixtyfps-compilerlib = { version = "=0.0.2", path = "../../sixtyfps_compiler", features = ["display-diagnostics", "cpp", "rust"]} structopt = "0.3.14" +spin_on = "0.1" diff --git a/tools/compiler/main.rs b/tools/compiler/main.rs index a2b344749..1de8796d5 100644 --- a/tools/compiler/main.rs +++ b/tools/compiler/main.rs @@ -32,8 +32,8 @@ fn main() -> std::io::Result<()> { std::process::exit(-1); } let compiler_config = - CompilerConfiguration { include_paths: &args.include_paths, ..Default::default() }; - let (doc, diag) = compile_syntax_node(syntax_node, diag, &compiler_config); + CompilerConfiguration { include_paths: args.include_paths, ..Default::default() }; + let (doc, diag) = spin_on::spin_on(compile_syntax_node(syntax_node, diag, compiler_config)); let mut diag = diag.check_and_exit_on_error(); diff --git a/tools/online_editor/index.ts b/tools/online_editor/index.ts index 5b7cd9d5a..de95e1327 100644 --- a/tools/online_editor/index.ts +++ b/tools/online_editor/index.ts @@ -77,7 +77,7 @@ function update() { } -function render_or_error(source, base_url, div) { +async function render_or_error(source, base_url, div) { let canvas_id = 'canvas_' + Math.random().toString(36).substr(2, 9); let canvas = document.createElement("canvas"); canvas.width = 800; @@ -86,14 +86,10 @@ function render_or_error(source, base_url, div) { div.innerHTML = ""; div.appendChild(canvas); try { - sixtyfps.instantiate_from_string(source, base_url, canvas_id); + var compiled_component = await sixtyfps.compile_from_string(source, base_url); } catch (e) { - if (e.message === "Using exceptions for control flow, don't mind me. This isn't actually an error!") { - monaco.editor.setModelMarkers(editor.getModel(), "sixtyfps", []); - throw e; - } - var text = document.createTextNode(e.message); - var p = document.createElement('pre'); + let text = document.createTextNode(e.message); + let p = document.createElement('pre'); p.appendChild(text); div.innerHTML = "
" + p.innerHTML + "
"; @@ -114,6 +110,7 @@ function render_or_error(source, base_url, div) { throw e; } + compiled_component.run(canvas_id) } let keystorke_timeout_handle; diff --git a/tools/viewer/Cargo.toml b/tools/viewer/Cargo.toml index 64ed5d14c..0aa4f4197 100644 --- a/tools/viewer/Cargo.toml +++ b/tools/viewer/Cargo.toml @@ -16,6 +16,7 @@ vtable = { version = "0.1", path="../../helper_crates/vtable" } structopt = "0.3.14" codemap-diagnostic = "0.1.1" codemap = "0.1" +spin_on = "0.1" [[bin]] diff --git a/tools/viewer/main.rs b/tools/viewer/main.rs index 2de112b1c..7ad9c5b70 100644 --- a/tools/viewer/main.rs +++ b/tools/viewer/main.rs @@ -27,12 +27,12 @@ fn main() -> std::io::Result<()> { let source = std::fs::read_to_string(&args.path)?; let compiler_config = sixtyfps_compilerlib::CompilerConfiguration { - include_paths: &args.include_paths, - style: if args.style.is_empty() { None } else { Some(&args.style) }, + include_paths: args.include_paths, + style: if args.style.is_empty() { None } else { Some(args.style) }, ..Default::default() }; - let c = match sixtyfps_interpreter::load(source, &args.path, compiler_config) { + let c = match spin_on::spin_on(sixtyfps_interpreter::load(source, args.path, compiler_config)) { (Ok(c), warnings) => { warnings.print(); c