Don't load document twice if they are imported twice

Also protect against cyclic imports
This commit is contained in:
Olivier Goffart 2020-10-30 10:14:27 +01:00
parent a4b476bdf3
commit 2d55b2a51a
5 changed files with 123 additions and 38 deletions

View file

@ -95,7 +95,7 @@ pub fn compile_syntax_node(
Cow::from("ugly") Cow::from("ugly")
}); });
let mut all_docs = vec![]; let mut all_docs = typeloader::LoadedDocuments::default();
if doc_node.source_file.is_some() { if doc_node.source_file.is_some() {
typeloader::load_dependencies_recursively( typeloader::load_dependencies_recursively(
&doc_node, &doc_node,

View file

@ -0,0 +1,17 @@
/* LICENSE BEGIN
This file is part of the SixtyFPS Project -- https://sixtyfps.io
Copyright (c) 2020 Olivier Goffart <olivier.goffart@sixtyfps.io>
Copyright (c) 2020 Simon Hausmann <simon.hausmann@sixtyfps.io>
SPDX-License-Identifier: GPL-3.0-only
This file is also available under commercial licensing terms.
Please contact info@sixtyfps.io for more information.
LICENSE END */
import { Rec12 } from "../../typeloader/recursive_import1.60";
// ^error{No exported type called Rec12 found in.*recursive_import1.60}
Blah := Rec12 {
// ^error{Unknown type Rec12}
width: 100px;
}

View file

@ -0,0 +1,18 @@
/* LICENSE BEGIN
This file is part of the SixtyFPS Project -- https://sixtyfps.io
Copyright (c) 2020 Olivier Goffart <olivier.goffart@sixtyfps.io>
Copyright (c) 2020 Simon Hausmann <simon.hausmann@sixtyfps.io>
SPDX-License-Identifier: GPL-3.0-only
This file is also available under commercial licensing terms.
Please contact info@sixtyfps.io for more information.
LICENSE END */
import { Rec2 } from "./recursive_import2.60";
export Rec1 := Rectangle {
property <int> Hello: 42;
}
export Rect12 := Rec2 {
property <int> World: 43;
}

View file

@ -0,0 +1,20 @@
/* LICENSE BEGIN
This file is part of the SixtyFPS Project -- https://sixtyfps.io
Copyright (c) 2020 Olivier Goffart <olivier.goffart@sixtyfps.io>
Copyright (c) 2020 Simon Hausmann <simon.hausmann@sixtyfps.io>
SPDX-License-Identifier: GPL-3.0-only
This file is also available under commercial licensing terms.
Please contact info@sixtyfps.io for more information.
LICENSE END */
import { Rec1 } from "./recursive_import1.60";
// ^error{Recursive import of .*recursive_import1.60}
export Rec2 := Rectangle {
property <int> Foo: 44;
}
export Rect21 := Rec1 {
// ^error{Unknown type Rec1}
property <int> Bar: 45;
}

View file

@ -7,10 +7,11 @@
This file is also available under commercial licensing terms. This file is also available under commercial licensing terms.
Please contact info@sixtyfps.io for more information. Please contact info@sixtyfps.io for more information.
LICENSE END */ LICENSE END */
use std::cell::RefCell;
use std::collections::{BTreeMap, HashMap, HashSet};
use std::io::Read; use std::io::Read;
use std::path::PathBuf; use std::path::PathBuf;
use std::rc::Rc; use std::rc::Rc;
use std::{cell::RefCell, collections::BTreeMap};
use crate::diagnostics::{BuildDiagnostics, FileDiagnostics, SourceFile}; use crate::diagnostics::{BuildDiagnostics, FileDiagnostics, SourceFile};
use crate::object_tree::Document; use crate::object_tree::Document;
@ -18,19 +19,26 @@ use crate::parser::{syntax_nodes, SyntaxKind, SyntaxTokenWithSourceFile};
use crate::typeregister::TypeRegister; use crate::typeregister::TypeRegister;
use crate::CompilerConfiguration; use crate::CompilerConfiguration;
pub struct OpenFile<'a> { /// Storage for a cache of all loaded documents
pub path: PathBuf, #[derive(Default)]
pub file: Box<dyn Read + 'a>, pub struct LoadedDocuments {
/// maps from the canonical file name to the object_tree::Document
docs: HashMap<PathBuf, Document>,
currently_loading: HashSet<PathBuf>,
} }
pub trait DirectoryAccess<'a> { struct OpenFile<'a> {
path: PathBuf,
file: Box<dyn Read + 'a>,
}
trait DirectoryAccess<'a> {
fn try_open(&self, file_path: String) -> Option<OpenFile<'a>>; fn try_open(&self, file_path: String) -> Option<OpenFile<'a>>;
} }
impl<'a> DirectoryAccess<'a> for PathBuf { impl<'a> DirectoryAccess<'a> for PathBuf {
fn try_open(&self, file_path: String) -> Option<OpenFile<'a>> { fn try_open(&self, file_path: String) -> Option<OpenFile<'a>> {
let mut candidate = (*self).clone(); let candidate = self.join(file_path);
candidate.push(file_path);
std::fs::File::open(&candidate) std::fs::File::open(&candidate)
.ok() .ok()
@ -66,7 +74,7 @@ pub fn load_dependencies_recursively<'a>(
global_type_registry: &Rc<RefCell<TypeRegister>>, global_type_registry: &Rc<RefCell<TypeRegister>>,
compiler_config: &CompilerConfiguration, compiler_config: &CompilerConfiguration,
builtin_library: Option<&'a VirtualDirectory<'a>>, builtin_library: Option<&'a VirtualDirectory<'a>>,
all_documents: &mut Vec<Document>, all_documents: &mut LoadedDocuments,
build_diagnostics: &mut BuildDiagnostics, build_diagnostics: &mut BuildDiagnostics,
) { ) {
let dependencies = let dependencies =
@ -94,13 +102,26 @@ fn load_dependency<'a>(
importer_diagnostics: &mut FileDiagnostics, importer_diagnostics: &mut FileDiagnostics,
compiler_config: &CompilerConfiguration, compiler_config: &CompilerConfiguration,
builtin_library: Option<&'a VirtualDirectory<'a>>, builtin_library: Option<&'a VirtualDirectory<'a>>,
all_documents: &mut Vec<Document>, all_documents: &mut LoadedDocuments,
build_diagnostics: &mut BuildDiagnostics, build_diagnostics: &mut BuildDiagnostics,
) { ) {
let path_canon = path.canonicalize().unwrap_or(path.clone());
let doc = if let Some(doc) = all_documents.docs.get(path_canon.as_path()) {
doc
} else {
if !all_documents.currently_loading.insert(path_canon.clone()) {
importer_diagnostics.push_error(
format!("Recursive import of {}", path.display()),
&imported_types.import_token,
);
return;
}
let (dependency_doc, mut dependency_diagnostics) = let (dependency_doc, mut dependency_diagnostics) =
crate::parser::parse(imported_types.source_code, Some(&path)); crate::parser::parse(imported_types.source_code, Some(&path));
dependency_diagnostics.current_path = SourceFile::new(path); dependency_diagnostics.current_path = SourceFile::new(path.clone());
let dependency_doc: syntax_nodes::Document = dependency_doc.into(); let dependency_doc: syntax_nodes::Document = dependency_doc.into();
@ -122,6 +143,20 @@ fn load_dependency<'a>(
&dependency_registry, &dependency_registry,
); );
crate::passes::resolving::resolve_expressions(&doc, build_diagnostics); crate::passes::resolving::resolve_expressions(&doc, build_diagnostics);
// Add diagnostics regardless whether they're empty or not. This is used by the syntax_tests to
// also verify that imported files have no errors.
build_diagnostics.add(dependency_diagnostics);
let _ok = all_documents.currently_loading.remove(path_canon.as_path());
assert!(_ok);
match all_documents.docs.entry(path_canon) {
std::collections::hash_map::Entry::Occupied(_) => unreachable!(),
std::collections::hash_map::Entry::Vacant(e) => e.insert(doc),
}
};
let exports = doc.exports(); let exports = doc.exports();
for import_name in imported_types.type_names { for import_name in imported_types.type_names {
@ -140,7 +175,7 @@ fn load_dependency<'a>(
format!( format!(
"No exported type called {} found in {}", "No exported type called {} found in {}",
import_name.external_name, import_name.external_name,
dependency_diagnostics.current_path.to_string_lossy() path.display()
), ),
&imported_types.import_token, &imported_types.import_token,
); );
@ -150,11 +185,6 @@ fn load_dependency<'a>(
registry_to_populate.borrow_mut().add_with_name(import_name.internal_name, imported_type); registry_to_populate.borrow_mut().add_with_name(import_name.internal_name, imported_type);
} }
all_documents.push(doc);
// Add diagnostics regardless whether they're empty or not. This is used by the syntax_tests to
// also verify that imported files have no errors.
build_diagnostics.add(dependency_diagnostics);
} }
pub struct ImportedName { pub struct ImportedName {
@ -292,7 +322,7 @@ fn test_dependency_loading() {
let registry = Rc::new(RefCell::new(TypeRegister::new(&global_registry))); let registry = Rc::new(RefCell::new(TypeRegister::new(&global_registry)));
let mut build_diagnostics = BuildDiagnostics::default(); let mut build_diagnostics = BuildDiagnostics::default();
let mut docs = vec![]; let mut docs = Default::default();
load_dependencies_recursively( load_dependencies_recursively(
&doc_node, &doc_node,