diff options
Diffstat (limited to 'crates/libanalysis/src/db')
-rw-r--r-- | crates/libanalysis/src/db/imp.rs | 155 | ||||
-rw-r--r-- | crates/libanalysis/src/db/mod.rs | 214 | ||||
-rw-r--r-- | crates/libanalysis/src/db/queries.rs | 43 |
3 files changed, 214 insertions, 198 deletions
diff --git a/crates/libanalysis/src/db/imp.rs b/crates/libanalysis/src/db/imp.rs new file mode 100644 index 000000000..1b4ee5cf3 --- /dev/null +++ b/crates/libanalysis/src/db/imp.rs | |||
@@ -0,0 +1,155 @@ | |||
1 | use std::{ | ||
2 | sync::Arc, | ||
3 | any::Any, | ||
4 | hash::{Hash, Hasher}, | ||
5 | collections::hash_map::{DefaultHasher, HashMap}, | ||
6 | iter, | ||
7 | }; | ||
8 | use salsa; | ||
9 | use {FileId, imp::FileResolverImp}; | ||
10 | use super::{State, Query, QueryCtx}; | ||
11 | |||
12 | pub(super) type Data = Arc<Any + Send + Sync + 'static>; | ||
13 | |||
14 | #[derive(Debug)] | ||
15 | pub(super) struct Db { | ||
16 | names: Arc<HashMap<salsa::QueryTypeId, &'static str>>, | ||
17 | pub(super) imp: salsa::Db<State, Data>, | ||
18 | } | ||
19 | |||
20 | impl Db { | ||
21 | pub(super) fn new(mut reg: QueryRegistry) -> Db { | ||
22 | let config = reg.config.take().unwrap(); | ||
23 | Db { | ||
24 | names: Arc::new(reg.names), | ||
25 | imp: salsa::Db::new(config, State::default()) | ||
26 | } | ||
27 | } | ||
28 | pub(crate) fn with_changes(&self, new_state: State, changed_files: &[FileId], resolver_changed: bool) -> Db { | ||
29 | let names = self.names.clone(); | ||
30 | let mut invalidations = salsa::Invalidations::new(); | ||
31 | invalidations.invalidate(FILE_TEXT, changed_files.iter().map(hash).map(salsa::InputFingerprint)); | ||
32 | if resolver_changed { | ||
33 | invalidations.invalidate(FILE_SET, iter::once(salsa::InputFingerprint(hash(&())))); | ||
34 | } else { | ||
35 | invalidations.invalidate(FILE_SET, iter::empty()); | ||
36 | } | ||
37 | let imp = self.imp.with_ground_data( | ||
38 | new_state, | ||
39 | invalidations, | ||
40 | ); | ||
41 | Db { names, imp } | ||
42 | } | ||
43 | pub(super) fn extract_trace(&self, ctx: &salsa::QueryCtx<State, Data>) -> Vec<&'static str> { | ||
44 | ctx.trace().into_iter().map(|it| self.names[&it]).collect() | ||
45 | } | ||
46 | } | ||
47 | |||
48 | pub(crate) trait EvalQuery { | ||
49 | type Params; | ||
50 | type Output; | ||
51 | fn query_type(&self) -> salsa::QueryTypeId; | ||
52 | fn f(&self) -> salsa::QueryFn<State, Data>; | ||
53 | fn get(&self, &QueryCtx, Self::Params) -> Arc<Self::Output>; | ||
54 | } | ||
55 | |||
56 | impl<T, R> EvalQuery for Query<T, R> | ||
57 | where | ||
58 | T: Hash + Send + Sync + 'static, | ||
59 | R: Hash + Send + Sync + 'static, | ||
60 | { | ||
61 | type Params = T; | ||
62 | type Output = R; | ||
63 | fn query_type(&self) -> salsa::QueryTypeId { | ||
64 | salsa::QueryTypeId(self.0) | ||
65 | } | ||
66 | fn f(&self) -> salsa::QueryFn<State, Data> { | ||
67 | let f = self.1; | ||
68 | Box::new(move |ctx, data| { | ||
69 | let ctx = QueryCtx { imp: ctx }; | ||
70 | let data: &T = data.downcast_ref().unwrap(); | ||
71 | let res = f(ctx, data); | ||
72 | let h = hash(&res); | ||
73 | (Arc::new(res), salsa::OutputFingerprint(h)) | ||
74 | }) | ||
75 | } | ||
76 | fn get(&self, ctx: &QueryCtx, params: Self::Params) -> Arc<Self::Output> { | ||
77 | let query_id = salsa::QueryId( | ||
78 | self.query_type(), | ||
79 | salsa::InputFingerprint(hash(¶ms)), | ||
80 | ); | ||
81 | let res = ctx.imp.get(query_id, Arc::new(params)); | ||
82 | res.downcast().unwrap() | ||
83 | } | ||
84 | } | ||
85 | |||
86 | pub(super) struct QueryRegistry { | ||
87 | config: Option<salsa::QueryConfig<State, Data>>, | ||
88 | names: HashMap<salsa::QueryTypeId, &'static str>, | ||
89 | } | ||
90 | |||
91 | impl QueryRegistry { | ||
92 | pub(super) fn new() -> QueryRegistry { | ||
93 | let mut config = salsa::QueryConfig::<State, Data>::new(); | ||
94 | config = config.with_ground_query( | ||
95 | FILE_TEXT, Box::new(|state, params| { | ||
96 | let file_id: &FileId = params.downcast_ref().unwrap(); | ||
97 | let res = state.file_map[file_id].clone(); | ||
98 | let fingerprint = salsa::OutputFingerprint(hash(&res)); | ||
99 | (res, fingerprint) | ||
100 | }) | ||
101 | ); | ||
102 | config = config.with_ground_query( | ||
103 | FILE_SET, Box::new(|state, _params| { | ||
104 | let file_ids: Vec<FileId> = state.file_map.keys().cloned().collect(); | ||
105 | let hash = hash(&file_ids); | ||
106 | let file_resolver = state.file_resolver.clone(); | ||
107 | let res = (file_ids, file_resolver); | ||
108 | let fingerprint = salsa::OutputFingerprint(hash); | ||
109 | (Arc::new(res), fingerprint) | ||
110 | }) | ||
111 | ); | ||
112 | let mut names = HashMap::new(); | ||
113 | names.insert(FILE_TEXT, "FILE_TEXT"); | ||
114 | names.insert(FILE_SET, "FILE_SET"); | ||
115 | QueryRegistry { config: Some(config), names } | ||
116 | } | ||
117 | pub(super) fn add<Q: EvalQuery>(&mut self, q: Q, name: &'static str) { | ||
118 | let id = q.query_type(); | ||
119 | let prev = self.names.insert(id, name); | ||
120 | assert!(prev.is_none(), "duplicate query: {:?}", id); | ||
121 | let config = self.config.take().unwrap(); | ||
122 | let config = config.with_query(id, q.f()); | ||
123 | self.config= Some(config); | ||
124 | } | ||
125 | pub(super) fn finish(mut self) -> salsa::QueryConfig<State, Data> { | ||
126 | self.config.take().unwrap() | ||
127 | } | ||
128 | } | ||
129 | |||
130 | fn hash<T: Hash>(x: &T) -> u64 { | ||
131 | let mut hasher = DefaultHasher::new(); | ||
132 | x.hash(&mut hasher); | ||
133 | hasher.finish() | ||
134 | } | ||
135 | |||
136 | const FILE_TEXT: salsa::QueryTypeId = salsa::QueryTypeId(0); | ||
137 | pub(super) fn file_text(ctx: QueryCtx, file_id: FileId) -> Arc<String> { | ||
138 | let query_id = salsa::QueryId( | ||
139 | FILE_TEXT, | ||
140 | salsa::InputFingerprint(hash(&file_id)), | ||
141 | ); | ||
142 | let res = ctx.imp.get(query_id, Arc::new(file_id)); | ||
143 | res.downcast().unwrap() | ||
144 | } | ||
145 | |||
146 | const FILE_SET: salsa::QueryTypeId = salsa::QueryTypeId(1); | ||
147 | pub(super) fn file_set(ctx: QueryCtx) -> Arc<(Vec<FileId>, FileResolverImp)> { | ||
148 | let query_id = salsa::QueryId( | ||
149 | FILE_SET, | ||
150 | salsa::InputFingerprint(hash(&())), | ||
151 | ); | ||
152 | let res = ctx.imp.get(query_id, Arc::new(())); | ||
153 | res.downcast().unwrap() | ||
154 | } | ||
155 | |||
diff --git a/crates/libanalysis/src/db/mod.rs b/crates/libanalysis/src/db/mod.rs index 38ba40273..a775b5f75 100644 --- a/crates/libanalysis/src/db/mod.rs +++ b/crates/libanalysis/src/db/mod.rs | |||
@@ -1,195 +1,99 @@ | |||
1 | mod queries; | 1 | mod imp; |
2 | 2 | ||
3 | use std::{ | 3 | use std::{ |
4 | hash::{Hash}, | ||
5 | sync::Arc, | 4 | sync::Arc, |
6 | fmt::Debug, | ||
7 | any::Any, | ||
8 | iter, | ||
9 | }; | 5 | }; |
10 | use im; | 6 | use im; |
11 | use salsa; | 7 | use salsa; |
12 | use { | 8 | use {FileId, imp::FileResolverImp}; |
13 | FileId, | ||
14 | imp::{FileResolverImp}, | ||
15 | }; | ||
16 | |||
17 | 9 | ||
18 | #[derive(Clone, Default)] | 10 | #[derive(Debug, Default, Clone)] |
19 | pub(crate) struct State { | 11 | pub(crate) struct State { |
20 | pub(crate) resolver: FileResolverImp, | 12 | pub(crate) file_map: im::HashMap<FileId, Arc<String>>, |
21 | pub(crate) file_map: im::HashMap<FileId, Arc<str>>, | 13 | pub(crate) file_resolver: FileResolverImp |
22 | } | ||
23 | |||
24 | type Data = Arc<Any + Send + Sync + 'static>; | ||
25 | |||
26 | pub(crate) struct QueryCtx<'a> { | ||
27 | inner: &'a salsa::QueryCtx<State, Data> | ||
28 | } | 14 | } |
29 | 15 | ||
16 | #[derive(Debug)] | ||
30 | pub(crate) struct Db { | 17 | pub(crate) struct Db { |
31 | inner: salsa::Db<State, Data> | 18 | imp: imp::Db, |
32 | } | 19 | } |
33 | 20 | ||
34 | struct GroundQuery<T, R> { | 21 | #[derive(Clone, Copy)] |
35 | id: u16, | 22 | pub(crate) struct QueryCtx<'a> { |
36 | f: fn(&State, &T) -> R, | 23 | imp: &'a salsa::QueryCtx<State, imp::Data>, |
37 | h: fn(&R) -> u64, | ||
38 | } | 24 | } |
39 | 25 | ||
40 | pub(crate) struct Query<T, R> { | 26 | pub(crate) struct Query<T, R>(pub(crate) u16, pub(crate) fn(QueryCtx, &T) -> R); |
41 | pub(crate) id: u16, | 27 | |
42 | pub(crate) f: fn(QueryCtx, &T) -> R, | 28 | pub(crate) struct QueryRegistry { |
29 | imp: imp::QueryRegistry, | ||
43 | } | 30 | } |
44 | 31 | ||
45 | impl Db { | 32 | impl Db { |
46 | pub(crate) fn new() -> Db { | 33 | pub(crate) fn new() -> Db { |
47 | let state = Default::default(); | 34 | let reg = QueryRegistry::new(); |
48 | Db { inner: salsa::Db::new(query_config(), state) } | 35 | Db { imp: imp::Db::new(reg.imp) } |
49 | } | 36 | } |
50 | pub(crate) fn state(&self) -> &State { | 37 | pub(crate) fn state(&self) -> &State { |
51 | self.inner.ground_data() | 38 | self.imp.imp.ground_data() |
52 | } | 39 | } |
53 | pub(crate) fn with_state( | 40 | pub(crate) fn with_changes(&self, new_state: State, changed_files: &[FileId], resolver_changed: bool) -> Db { |
54 | &self, | 41 | Db { imp: self.imp.with_changes(new_state, changed_files, resolver_changed) } |
55 | new_state: State, | ||
56 | updated_files: &[FileId], | ||
57 | file_set_changed: bool, | ||
58 | ) -> Db { | ||
59 | let mut inv = salsa::Invalidations::new(); | ||
60 | if file_set_changed { | ||
61 | inv.invalidate( | ||
62 | salsa::QueryTypeId(queries::FILE_SET.id), | ||
63 | iter::once(salsa::InputFingerprint(hash(&()))), | ||
64 | ); | ||
65 | } else { | ||
66 | inv.invalidate( | ||
67 | salsa::QueryTypeId(queries::FILE_SET.id), | ||
68 | iter::empty(), | ||
69 | ); | ||
70 | } | ||
71 | inv.invalidate( | ||
72 | salsa::QueryTypeId(queries::FILE_TEXT.id), | ||
73 | updated_files.iter().map(hash).map(salsa::InputFingerprint), | ||
74 | ); | ||
75 | Db { inner: self.inner.with_ground_data(new_state, inv) } | ||
76 | } | 42 | } |
77 | pub(crate) fn get<T, R>(&self, q: Query<T, R>, params: T) -> (Arc<R>, Vec<u16>) | 43 | pub(crate) fn make_query<F: FnOnce(QueryCtx) -> R, R>(&self, f: F) -> R { |
78 | where | 44 | let ctx = QueryCtx { imp: &self.imp.imp.query_ctx() }; |
79 | T: Hash + Send + Sync + 'static, | 45 | f(ctx) |
80 | R: Send + Sync + 'static, | 46 | } |
81 | { | 47 | pub(crate) fn trace_query<F: FnOnce(QueryCtx) -> R, R>(&self, f: F) -> (R, Vec<&'static str>) { |
82 | let query_id = salsa::QueryId( | 48 | let ctx = QueryCtx { imp: &self.imp.imp.query_ctx() }; |
83 | salsa::QueryTypeId(q.id), | 49 | let res = f(ctx); |
84 | salsa::InputFingerprint(hash(¶ms)), | 50 | let trace = self.imp.extract_trace(ctx.imp); |
85 | ); | 51 | (res, trace) |
86 | let params = Arc::new(params); | ||
87 | let (res, events) = self.inner.get(query_id, params); | ||
88 | let res = res.downcast().unwrap(); | ||
89 | let events = events.into_iter().map(|it| it.0).collect(); | ||
90 | (res, events) | ||
91 | } | 52 | } |
92 | |||
93 | } | 53 | } |
94 | 54 | ||
95 | impl<'a> QueryCtx<'a> { | 55 | impl<'a> QueryCtx<'a> { |
96 | fn get_g<T, R>(&self, q: GroundQuery<T, R>, params: T) -> Arc<R> | 56 | pub(crate) fn get<Q: imp::EvalQuery>(&self, q: Q, params: Q::Params) -> Arc<Q::Output> { |
97 | where | 57 | q.get(self, params) |
98 | T: Hash + Send + Sync + 'static, | ||
99 | R: Send + Sync + 'static, | ||
100 | { | ||
101 | let query_id = salsa::QueryId( | ||
102 | salsa::QueryTypeId(q.id), | ||
103 | salsa::InputFingerprint(hash(¶ms)), | ||
104 | ); | ||
105 | let res = self.inner.get(query_id, Arc::new(params)); | ||
106 | res.downcast().unwrap() | ||
107 | } | ||
108 | pub(crate) fn get<T, R>(&self, q: Query<T, R>, params: T) -> Arc<R> | ||
109 | where | ||
110 | T: Hash + Send + Sync + 'static, | ||
111 | R: Send + Sync + 'static, | ||
112 | { | ||
113 | let query_id = salsa::QueryId( | ||
114 | salsa::QueryTypeId(q.id), | ||
115 | salsa::InputFingerprint(hash(¶ms)), | ||
116 | ); | ||
117 | let res = self.inner.get(query_id, Arc::new(params)); | ||
118 | res.downcast().unwrap() | ||
119 | } | 58 | } |
120 | } | 59 | } |
121 | 60 | ||
122 | fn query_config() -> salsa::QueryConfig<State, Data> { | 61 | pub(crate) fn file_text(ctx: QueryCtx, file_id: FileId) -> Arc<String> { |
123 | let mut res = salsa::QueryConfig::new(); | 62 | imp::file_text(ctx, file_id) |
124 | let queries: Vec<BoxedGroundQuery> = vec![ | ||
125 | queries::FILE_TEXT.into(), | ||
126 | queries::FILE_SET.into(), | ||
127 | ]; | ||
128 | for q in queries { | ||
129 | res = res.with_ground_query(q.query_type, q.f) | ||
130 | } | ||
131 | let mut queries: Vec<BoxedQuery> = vec![ | ||
132 | queries::FILE_SYNTAX.into(), | ||
133 | ]; | ||
134 | ::module_map_db::queries(&mut queries); | ||
135 | for q in queries { | ||
136 | res = res.with_query(q.query_type, q.f); | ||
137 | } | ||
138 | res | ||
139 | } | 63 | } |
140 | 64 | ||
141 | struct BoxedGroundQuery { | 65 | pub(crate) fn file_set(ctx: QueryCtx) -> Arc<(Vec<FileId>, FileResolverImp)> { |
142 | query_type: salsa::QueryTypeId, | 66 | imp::file_set(ctx) |
143 | f: Box<Fn(&State, &Data) -> (Data, salsa::OutputFingerprint) + Send + Sync + 'static>, | ||
144 | } | 67 | } |
68 | pub(crate) use self::queries::file_syntax; | ||
145 | 69 | ||
146 | impl<T, R> From<GroundQuery<T, R>> for BoxedGroundQuery | 70 | mod queries { |
147 | where | 71 | use libsyntax2::File; |
148 | T: Send + Sync + 'static, | 72 | use {FileId}; |
149 | R: Send + Sync + 'static, | 73 | use super::{Query, QueryCtx, QueryRegistry, file_text}; |
150 | { | ||
151 | fn from(q: GroundQuery<T, R>) -> BoxedGroundQuery | ||
152 | { | ||
153 | BoxedGroundQuery { | ||
154 | query_type: salsa::QueryTypeId(q.id), | ||
155 | f: Box::new(move |state, data| { | ||
156 | let data: &T = data.downcast_ref().unwrap(); | ||
157 | let res = (q.f)(state, data); | ||
158 | let h = (q.h)(&res); | ||
159 | (Arc::new(res), salsa::OutputFingerprint(h)) | ||
160 | }) | ||
161 | } | ||
162 | } | ||
163 | } | ||
164 | 74 | ||
165 | pub(crate) struct BoxedQuery { | 75 | pub(crate) fn register_queries(reg: &mut QueryRegistry) { |
166 | query_type: salsa::QueryTypeId, | 76 | reg.add(FILE_SYNTAX, "FILE_SYNTAX") |
167 | f: Box<Fn(&salsa::QueryCtx<State, Data>, &Data) -> (Data, salsa::OutputFingerprint) + Send + Sync + 'static>, | 77 | } |
168 | } | ||
169 | 78 | ||
170 | impl<T, R> From<Query<T, R>> for BoxedQuery | 79 | pub(crate) fn file_syntax(ctx: QueryCtx, file_id: FileId) -> File { |
171 | where | 80 | (&*ctx.get(FILE_SYNTAX, file_id)).clone() |
172 | T: Hash + Send + Sync + 'static, | ||
173 | R: Hash + Send + Sync + 'static, | ||
174 | { | ||
175 | fn from(q: Query<T, R>) -> BoxedQuery | ||
176 | { | ||
177 | BoxedQuery { | ||
178 | query_type: salsa::QueryTypeId(q.id), | ||
179 | f: Box::new(move |ctx, data| { | ||
180 | let ctx = QueryCtx { inner: ctx }; | ||
181 | let data: &T = data.downcast_ref().unwrap(); | ||
182 | let res = (q.f)(ctx, data); | ||
183 | let h = hash(&res); | ||
184 | (Arc::new(res), salsa::OutputFingerprint(h)) | ||
185 | }) | ||
186 | } | ||
187 | } | 81 | } |
82 | |||
83 | pub(super) const FILE_SYNTAX: Query<FileId, File> = Query(16, |ctx, file_id: &FileId| { | ||
84 | let text = file_text(ctx, *file_id); | ||
85 | File::parse(&*text) | ||
86 | }); | ||
188 | } | 87 | } |
189 | 88 | ||
190 | fn hash<T: ::std::hash::Hash>(x: &T) -> u64 { | 89 | impl QueryRegistry { |
191 | use std::hash::Hasher; | 90 | fn new() -> QueryRegistry { |
192 | let mut hasher = ::std::collections::hash_map::DefaultHasher::new(); | 91 | let mut reg = QueryRegistry { imp: imp::QueryRegistry::new() }; |
193 | ::std::hash::Hash::hash(x, &mut hasher); | 92 | queries::register_queries(&mut reg); |
194 | hasher.finish() | 93 | ::module_map_db::register_queries(&mut reg); |
94 | reg | ||
95 | } | ||
96 | pub(crate) fn add<Q: imp::EvalQuery>(&mut self, q: Q, name: &'static str) { | ||
97 | self.imp.add(q, name) | ||
98 | } | ||
195 | } | 99 | } |
diff --git a/crates/libanalysis/src/db/queries.rs b/crates/libanalysis/src/db/queries.rs deleted file mode 100644 index 2d4aac6e9..000000000 --- a/crates/libanalysis/src/db/queries.rs +++ /dev/null | |||
@@ -1,43 +0,0 @@ | |||
1 | use std::sync::Arc; | ||
2 | use libsyntax2::{File}; | ||
3 | use { | ||
4 | FileId, FileResolverImp, | ||
5 | db::{Query, GroundQuery, QueryCtx, hash}, | ||
6 | }; | ||
7 | |||
8 | |||
9 | impl<'a> QueryCtx<'a> { | ||
10 | pub(crate) fn file_set(&self) -> Arc<(Vec<FileId>, FileResolverImp)> { | ||
11 | self.get_g(FILE_SET, ()) | ||
12 | } | ||
13 | pub(crate) fn file_text(&self, file_id: FileId) -> Arc<str> { | ||
14 | Arc::clone(&*self.get_g(FILE_TEXT, file_id)) | ||
15 | } | ||
16 | pub(crate) fn file_syntax(&self, file_id: FileId) -> File { | ||
17 | (&*self.get(FILE_SYNTAX, file_id)).clone() | ||
18 | } | ||
19 | } | ||
20 | |||
21 | pub(super) const FILE_TEXT: GroundQuery<FileId, Arc<str>> = GroundQuery { | ||
22 | id: 10, | ||
23 | f: |state, id| state.file_map[&id].clone(), | ||
24 | h: hash, | ||
25 | }; | ||
26 | |||
27 | pub(super) const FILE_SET: GroundQuery<(), (Vec<FileId>, FileResolverImp)> = GroundQuery { | ||
28 | id: 11, | ||
29 | f: |state, &()| { | ||
30 | let files = state.file_map.keys().cloned().collect(); | ||
31 | let resolver = state.resolver.clone(); | ||
32 | (files, resolver) | ||
33 | }, | ||
34 | h: |(files, _)| hash(files), | ||
35 | }; | ||
36 | |||
37 | pub(super) const FILE_SYNTAX: Query<FileId, File> = Query { | ||
38 | id: 20, | ||
39 | f: |ctx, file_id: &FileId| { | ||
40 | let text = ctx.file_text(*file_id); | ||
41 | File::parse(&*text) | ||
42 | } | ||
43 | }; | ||