mirror of
https://github.com/slint-ui/slint.git
synced 2025-10-01 14:21:16 +00:00
Prepare the compiler to be async
This will allow the online editor to load imports from URL asynchroniously later Since currently the compiler is only working on a single thread, and that we never await on a future that could block, it is allowed to use the spin_on executor
This commit is contained in:
parent
a7abfea961
commit
359f42c5f7
22 changed files with 110 additions and 89 deletions
|
@ -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"
|
||||
|
|
|
@ -63,18 +63,17 @@ fn load(mut cx: FunctionContext) -> JsResult<JsValue> {
|
|||
}
|
||||
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();
|
||||
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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"
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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 }
|
||||
|
|
|
@ -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 <canvas> 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<WrappedCompiledComp, JsValue> {
|
||||
#[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<sixtyfps_interpreter::ComponentDescription>);
|
||||
|
||||
#[wasm_bindgen]
|
||||
impl WrappedCompiledComp {
|
||||
/// Run this compiled component in a canvas.
|
||||
/// The HTML must contains a <canvas> 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());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -43,5 +43,6 @@ once_cell = "1"
|
|||
[dev-dependencies]
|
||||
regex = "1.3.7"
|
||||
parser_test_macro = { path = "./parser_test_macro" }
|
||||
spin_on = "0.1"
|
||||
|
||||
|
||||
|
|
|
@ -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<std::path::PathBuf>,
|
||||
/// the name of the style. (eg: "native")
|
||||
pub style: Option<&'a str>,
|
||||
pub style: Option<String>,
|
||||
}
|
||||
|
||||
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)
|
||||
|
|
|
@ -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]
|
||||
|
|
|
@ -433,20 +433,20 @@ fn rtti_for_flickable() -> (&'static str, Rc<ItemRTTI>) {
|
|||
/// 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<Rc<ComponentDescription<'id>>, ()>, 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);
|
||||
}
|
||||
|
|
|
@ -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<Rc<ComponentDescription>, ()>, 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
|
||||
}
|
||||
|
|
|
@ -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" }
|
||||
|
|
|
@ -15,13 +15,14 @@ use std::ops::Deref;
|
|||
pub fn test(testcase: &test_driver_lib::TestCase) -> Result<(), Box<dyn Error>> {
|
||||
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::<Vec<_>>();
|
||||
|
||||
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();
|
||||
|
|
|
@ -12,23 +12,24 @@ use std::error::Error;
|
|||
pub fn test(testcase: &test_driver_lib::TestCase) -> Result<(), Box<dyn Error>> {
|
||||
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::<Vec<_>>();
|
||||
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();
|
||||
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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();
|
||||
|
||||
|
|
|
@ -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 = "<pre style='color: red; background-color:#fee; margin:0'>" + p.innerHTML + "</pre>";
|
||||
|
||||
|
@ -114,6 +110,7 @@ function render_or_error(source, base_url, div) {
|
|||
|
||||
throw e;
|
||||
}
|
||||
compiled_component.run(canvas_id)
|
||||
}
|
||||
|
||||
let keystorke_timeout_handle;
|
||||
|
|
|
@ -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]]
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue