aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_proc_macro
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ra_proc_macro')
-rw-r--r--crates/ra_proc_macro/Cargo.toml18
-rw-r--r--crates/ra_proc_macro/src/lib.rs112
-rw-r--r--crates/ra_proc_macro/src/msg.rs88
-rw-r--r--crates/ra_proc_macro/src/process.rs203
-rw-r--r--crates/ra_proc_macro/src/rpc.rs266
5 files changed, 0 insertions, 687 deletions
diff --git a/crates/ra_proc_macro/Cargo.toml b/crates/ra_proc_macro/Cargo.toml
deleted file mode 100644
index c4b6e9e7b..000000000
--- a/crates/ra_proc_macro/Cargo.toml
+++ /dev/null
@@ -1,18 +0,0 @@
1[package]
2edition = "2018"
3name = "ra_proc_macro"
4version = "0.1.0"
5authors = ["rust-analyzer developers"]
6publish = false
7license = "MIT OR Apache-2.0"
8
9[lib]
10doctest = false
11
12[dependencies]
13ra_tt = { path = "../ra_tt" }
14serde = { version = "1.0", features = ["derive"] }
15serde_json = "1.0"
16log = "0.4.8"
17crossbeam-channel = "0.4.0"
18jod-thread = "0.1.1"
diff --git a/crates/ra_proc_macro/src/lib.rs b/crates/ra_proc_macro/src/lib.rs
deleted file mode 100644
index 004943b9e..000000000
--- a/crates/ra_proc_macro/src/lib.rs
+++ /dev/null
@@ -1,112 +0,0 @@
1//! Client-side Proc-Macro crate
2//!
3//! We separate proc-macro expanding logic to an extern program to allow
4//! different implementations (e.g. wasm or dylib loading). And this crate
5//! is used to provide basic infrastructure for communication between two
6//! processes: Client (RA itself), Server (the external program)
7
8mod rpc;
9mod process;
10pub mod msg;
11
12use process::{ProcMacroProcessSrv, ProcMacroProcessThread};
13use ra_tt::{SmolStr, Subtree};
14use std::{
15 ffi::OsStr,
16 io,
17 path::{Path, PathBuf},
18 sync::Arc,
19};
20
21pub use rpc::{ExpansionResult, ExpansionTask, ListMacrosResult, ListMacrosTask, ProcMacroKind};
22
23#[derive(Debug, Clone)]
24pub struct ProcMacroProcessExpander {
25 process: Arc<ProcMacroProcessSrv>,
26 dylib_path: PathBuf,
27 name: SmolStr,
28}
29
30impl Eq for ProcMacroProcessExpander {}
31impl PartialEq for ProcMacroProcessExpander {
32 fn eq(&self, other: &Self) -> bool {
33 self.name == other.name
34 && self.dylib_path == other.dylib_path
35 && Arc::ptr_eq(&self.process, &other.process)
36 }
37}
38
39impl ra_tt::TokenExpander for ProcMacroProcessExpander {
40 fn expand(
41 &self,
42 subtree: &Subtree,
43 _attr: Option<&Subtree>,
44 ) -> Result<Subtree, ra_tt::ExpansionError> {
45 self.process.custom_derive(&self.dylib_path, subtree, &self.name)
46 }
47}
48
49#[derive(Debug)]
50enum ProcMacroClientKind {
51 Process { process: Arc<ProcMacroProcessSrv>, thread: ProcMacroProcessThread },
52 Dummy,
53}
54
55#[derive(Debug)]
56pub struct ProcMacroClient {
57 kind: ProcMacroClientKind,
58}
59
60impl ProcMacroClient {
61 pub fn extern_process(
62 process_path: PathBuf,
63 args: impl IntoIterator<Item = impl AsRef<OsStr>>,
64 ) -> io::Result<ProcMacroClient> {
65 let (thread, process) = ProcMacroProcessSrv::run(process_path, args)?;
66 Ok(ProcMacroClient {
67 kind: ProcMacroClientKind::Process { process: Arc::new(process), thread },
68 })
69 }
70
71 pub fn dummy() -> ProcMacroClient {
72 ProcMacroClient { kind: ProcMacroClientKind::Dummy }
73 }
74
75 pub fn by_dylib_path(
76 &self,
77 dylib_path: &Path,
78 ) -> Vec<(SmolStr, Arc<dyn ra_tt::TokenExpander>)> {
79 match &self.kind {
80 ProcMacroClientKind::Dummy => vec![],
81 ProcMacroClientKind::Process { process, .. } => {
82 let macros = match process.find_proc_macros(dylib_path) {
83 Err(err) => {
84 eprintln!("Failed to find proc macros. Error: {:#?}", err);
85 return vec![];
86 }
87 Ok(macros) => macros,
88 };
89
90 macros
91 .into_iter()
92 .filter_map(|(name, kind)| {
93 // FIXME: Support custom derive only for now.
94 match kind {
95 ProcMacroKind::CustomDerive => {
96 let name = SmolStr::new(&name);
97 let expander: Arc<dyn ra_tt::TokenExpander> =
98 Arc::new(ProcMacroProcessExpander {
99 process: process.clone(),
100 name: name.clone(),
101 dylib_path: dylib_path.into(),
102 });
103 Some((name, expander))
104 }
105 _ => None,
106 }
107 })
108 .collect()
109 }
110 }
111 }
112}
diff --git a/crates/ra_proc_macro/src/msg.rs b/crates/ra_proc_macro/src/msg.rs
deleted file mode 100644
index 95d9b8804..000000000
--- a/crates/ra_proc_macro/src/msg.rs
+++ /dev/null
@@ -1,88 +0,0 @@
1//! Defines messages for cross-process message passing based on `ndjson` wire protocol
2
3use std::{
4 convert::TryFrom,
5 io::{self, BufRead, Write},
6};
7
8use crate::{
9 rpc::{ListMacrosResult, ListMacrosTask},
10 ExpansionResult, ExpansionTask,
11};
12use serde::{de::DeserializeOwned, Deserialize, Serialize};
13
14#[derive(Debug, Serialize, Deserialize, Clone)]
15pub enum Request {
16 ListMacro(ListMacrosTask),
17 ExpansionMacro(ExpansionTask),
18}
19
20#[derive(Debug, Serialize, Deserialize, Clone)]
21pub enum Response {
22 Error(ResponseError),
23 ListMacro(ListMacrosResult),
24 ExpansionMacro(ExpansionResult),
25}
26
27macro_rules! impl_try_from_response {
28 ($ty:ty, $tag:ident) => {
29 impl TryFrom<Response> for $ty {
30 type Error = &'static str;
31 fn try_from(value: Response) -> Result<Self, Self::Error> {
32 match value {
33 Response::$tag(res) => Ok(res),
34 _ => Err(concat!("Failed to convert response to ", stringify!($tag))),
35 }
36 }
37 }
38 };
39}
40
41impl_try_from_response!(ListMacrosResult, ListMacro);
42impl_try_from_response!(ExpansionResult, ExpansionMacro);
43
44#[derive(Debug, Serialize, Deserialize, Clone)]
45pub struct ResponseError {
46 pub code: ErrorCode,
47 pub message: String,
48}
49
50#[derive(Debug, Serialize, Deserialize, Clone)]
51pub enum ErrorCode {
52 ServerErrorEnd,
53 ExpansionError,
54}
55
56pub trait Message: Serialize + DeserializeOwned {
57 fn read(inp: &mut impl BufRead) -> io::Result<Option<Self>> {
58 Ok(match read_json(inp)? {
59 None => None,
60 Some(text) => Some(serde_json::from_str(&text)?),
61 })
62 }
63 fn write(self, out: &mut impl Write) -> io::Result<()> {
64 let text = serde_json::to_string(&self)?;
65 write_json(out, &text)
66 }
67}
68
69impl Message for Request {}
70impl Message for Response {}
71
72fn read_json(inp: &mut impl BufRead) -> io::Result<Option<String>> {
73 let mut buf = String::new();
74 inp.read_line(&mut buf)?;
75 buf.pop(); // Remove traling '\n'
76 Ok(match buf.len() {
77 0 => None,
78 _ => Some(buf),
79 })
80}
81
82fn write_json(out: &mut impl Write, msg: &str) -> io::Result<()> {
83 log::debug!("> {}", msg);
84 out.write_all(msg.as_bytes())?;
85 out.write_all(b"\n")?;
86 out.flush()?;
87 Ok(())
88}
diff --git a/crates/ra_proc_macro/src/process.rs b/crates/ra_proc_macro/src/process.rs
deleted file mode 100644
index 5bcdacb48..000000000
--- a/crates/ra_proc_macro/src/process.rs
+++ /dev/null
@@ -1,203 +0,0 @@
1//! Handle process life-time and message passing for proc-macro client
2
3use crossbeam_channel::{bounded, Receiver, Sender};
4use ra_tt::Subtree;
5
6use crate::msg::{ErrorCode, Message, Request, Response, ResponseError};
7use crate::rpc::{ExpansionResult, ExpansionTask, ListMacrosResult, ListMacrosTask, ProcMacroKind};
8
9use io::{BufRead, BufReader};
10use std::{
11 convert::{TryFrom, TryInto},
12 ffi::{OsStr, OsString},
13 io::{self, Write},
14 path::{Path, PathBuf},
15 process::{Child, Command, Stdio},
16 sync::{Arc, Weak},
17};
18
19#[derive(Debug, Default)]
20pub(crate) struct ProcMacroProcessSrv {
21 inner: Option<Weak<Sender<Task>>>,
22}
23
24#[derive(Debug)]
25pub(crate) struct ProcMacroProcessThread {
26 // XXX: drop order is significant
27 sender: Arc<Sender<Task>>,
28 handle: jod_thread::JoinHandle<()>,
29}
30
31impl ProcMacroProcessSrv {
32 pub fn run(
33 process_path: PathBuf,
34 args: impl IntoIterator<Item = impl AsRef<OsStr>>,
35 ) -> io::Result<(ProcMacroProcessThread, ProcMacroProcessSrv)> {
36 let process = Process::run(process_path, args)?;
37
38 let (task_tx, task_rx) = bounded(0);
39 let handle = jod_thread::spawn(move || {
40 client_loop(task_rx, process);
41 });
42
43 let task_tx = Arc::new(task_tx);
44 let srv = ProcMacroProcessSrv { inner: Some(Arc::downgrade(&task_tx)) };
45 let thread = ProcMacroProcessThread { handle, sender: task_tx };
46
47 Ok((thread, srv))
48 }
49
50 pub fn find_proc_macros(
51 &self,
52 dylib_path: &Path,
53 ) -> Result<Vec<(String, ProcMacroKind)>, ra_tt::ExpansionError> {
54 let task = ListMacrosTask { lib: dylib_path.to_path_buf() };
55
56 let result: ListMacrosResult = self.send_task(Request::ListMacro(task))?;
57 Ok(result.macros)
58 }
59
60 pub fn custom_derive(
61 &self,
62 dylib_path: &Path,
63 subtree: &Subtree,
64 derive_name: &str,
65 ) -> Result<Subtree, ra_tt::ExpansionError> {
66 let task = ExpansionTask {
67 macro_body: subtree.clone(),
68 macro_name: derive_name.to_string(),
69 attributes: None,
70 lib: dylib_path.to_path_buf(),
71 };
72
73 let result: ExpansionResult = self.send_task(Request::ExpansionMacro(task))?;
74 Ok(result.expansion)
75 }
76
77 pub fn send_task<R>(&self, req: Request) -> Result<R, ra_tt::ExpansionError>
78 where
79 R: TryFrom<Response, Error = &'static str>,
80 {
81 let sender = match &self.inner {
82 None => return Err(ra_tt::ExpansionError::Unknown("No sender is found.".to_string())),
83 Some(it) => it,
84 };
85
86 let (result_tx, result_rx) = bounded(0);
87 let sender = match sender.upgrade() {
88 None => {
89 return Err(ra_tt::ExpansionError::Unknown("Proc macro process is closed.".into()))
90 }
91 Some(it) => it,
92 };
93 sender.send(Task { req: req.into(), result_tx }).unwrap();
94 let res = result_rx
95 .recv()
96 .map_err(|_| ra_tt::ExpansionError::Unknown("Proc macro thread is closed.".into()))?;
97
98 match res {
99 Some(Response::Error(err)) => {
100 return Err(ra_tt::ExpansionError::ExpansionError(err.message));
101 }
102 Some(res) => Ok(res.try_into().map_err(|err| {
103 ra_tt::ExpansionError::Unknown(format!(
104 "Fail to get response, reason : {:#?} ",
105 err
106 ))
107 })?),
108 None => Err(ra_tt::ExpansionError::Unknown("Empty result".into())),
109 }
110 }
111}
112
113fn client_loop(task_rx: Receiver<Task>, mut process: Process) {
114 let (mut stdin, mut stdout) = match process.stdio() {
115 None => return,
116 Some(it) => it,
117 };
118
119 for task in task_rx {
120 let Task { req, result_tx } = task;
121
122 match send_request(&mut stdin, &mut stdout, req) {
123 Ok(res) => result_tx.send(res).unwrap(),
124 Err(_err) => {
125 let res = Response::Error(ResponseError {
126 code: ErrorCode::ServerErrorEnd,
127 message: "Server closed".into(),
128 });
129 result_tx.send(res.into()).unwrap();
130 // Restart the process
131 if process.restart().is_err() {
132 break;
133 }
134 let stdio = match process.stdio() {
135 None => break,
136 Some(it) => it,
137 };
138 stdin = stdio.0;
139 stdout = stdio.1;
140 }
141 }
142 }
143}
144
145struct Task {
146 req: Request,
147 result_tx: Sender<Option<Response>>,
148}
149
150struct Process {
151 path: PathBuf,
152 args: Vec<OsString>,
153 child: Child,
154}
155
156impl Drop for Process {
157 fn drop(&mut self) {
158 let _ = self.child.kill();
159 }
160}
161
162impl Process {
163 fn run(
164 path: PathBuf,
165 args: impl IntoIterator<Item = impl AsRef<OsStr>>,
166 ) -> io::Result<Process> {
167 let args = args.into_iter().map(|s| s.as_ref().into()).collect();
168 let child = mk_child(&path, &args)?;
169 Ok(Process { path, args, child })
170 }
171
172 fn restart(&mut self) -> io::Result<()> {
173 let _ = self.child.kill();
174 self.child = mk_child(&self.path, &self.args)?;
175 Ok(())
176 }
177
178 fn stdio(&mut self) -> Option<(impl Write, impl BufRead)> {
179 let stdin = self.child.stdin.take()?;
180 let stdout = self.child.stdout.take()?;
181 let read = BufReader::new(stdout);
182
183 Some((stdin, read))
184 }
185}
186
187fn mk_child(path: &Path, args: impl IntoIterator<Item = impl AsRef<OsStr>>) -> io::Result<Child> {
188 Command::new(&path)
189 .args(args)
190 .stdin(Stdio::piped())
191 .stdout(Stdio::piped())
192 .stderr(Stdio::inherit())
193 .spawn()
194}
195
196fn send_request(
197 mut writer: &mut impl Write,
198 mut reader: &mut impl BufRead,
199 req: Request,
200) -> io::Result<Option<Response>> {
201 req.write(&mut writer)?;
202 Ok(Response::read(&mut reader)?)
203}
diff --git a/crates/ra_proc_macro/src/rpc.rs b/crates/ra_proc_macro/src/rpc.rs
deleted file mode 100644
index 4ce485926..000000000
--- a/crates/ra_proc_macro/src/rpc.rs
+++ /dev/null
@@ -1,266 +0,0 @@
1//! Data struture serialization related stuff for RPC
2//!
3//! Defines all necessary rpc serialization data structures,
4//! which includes `ra_tt` related data and some task messages.
5//! Although adding `Serialize` and `Deserialize` traits to `ra_tt` directly seems
6//! to be much easier, we deliberately duplicate `ra_tt` structs with `#[serde(with = "XXDef")]`
7//! for separation of code responsibility.
8
9use ra_tt::{
10 Delimiter, DelimiterKind, Ident, Leaf, Literal, Punct, SmolStr, Spacing, Subtree, TokenId,
11 TokenTree,
12};
13use serde::{Deserialize, Serialize};
14use std::path::PathBuf;
15
16#[derive(Clone, Eq, PartialEq, Debug, Serialize, Deserialize)]
17pub struct ListMacrosTask {
18 pub lib: PathBuf,
19}
20
21#[derive(Clone, Eq, PartialEq, Debug, Serialize, Deserialize)]
22pub enum ProcMacroKind {
23 CustomDerive,
24 FuncLike,
25 Attr,
26}
27
28#[derive(Clone, Eq, PartialEq, Debug, Default, Serialize, Deserialize)]
29pub struct ListMacrosResult {
30 pub macros: Vec<(String, ProcMacroKind)>,
31}
32
33#[derive(Clone, Eq, PartialEq, Debug, Serialize, Deserialize)]
34pub struct ExpansionTask {
35 /// Argument of macro call.
36 ///
37 /// In custom derive this will be a struct or enum; in attribute-like macro - underlying
38 /// item; in function-like macro - the macro body.
39 #[serde(with = "SubtreeDef")]
40 pub macro_body: Subtree,
41
42 /// Name of macro to expand.
43 ///
44 /// In custom derive this is the name of the derived trait (`Serialize`, `Getters`, etc.).
45 /// In attribute-like and function-like macros - single name of macro itself (`show_streams`).
46 pub macro_name: String,
47
48 /// Possible attributes for the attribute-like macros.
49 #[serde(with = "opt_subtree_def")]
50 pub attributes: Option<Subtree>,
51
52 pub lib: PathBuf,
53}
54
55#[derive(Clone, Eq, PartialEq, Debug, Default, Serialize, Deserialize)]
56pub struct ExpansionResult {
57 #[serde(with = "SubtreeDef")]
58 pub expansion: Subtree,
59}
60
61#[derive(Serialize, Deserialize)]
62#[serde(remote = "DelimiterKind")]
63enum DelimiterKindDef {
64 Parenthesis,
65 Brace,
66 Bracket,
67}
68
69#[derive(Serialize, Deserialize)]
70#[serde(remote = "TokenId")]
71struct TokenIdDef(u32);
72
73#[derive(Serialize, Deserialize)]
74#[serde(remote = "Delimiter")]
75struct DelimiterDef {
76 #[serde(with = "TokenIdDef")]
77 pub id: TokenId,
78 #[serde(with = "DelimiterKindDef")]
79 pub kind: DelimiterKind,
80}
81
82#[derive(Serialize, Deserialize)]
83#[serde(remote = "Subtree")]
84struct SubtreeDef {
85 #[serde(default, with = "opt_delimiter_def")]
86 pub delimiter: Option<Delimiter>,
87 #[serde(with = "vec_token_tree")]
88 pub token_trees: Vec<TokenTree>,
89}
90
91#[derive(Serialize, Deserialize)]
92#[serde(remote = "TokenTree")]
93enum TokenTreeDef {
94 #[serde(with = "LeafDef")]
95 Leaf(Leaf),
96 #[serde(with = "SubtreeDef")]
97 Subtree(Subtree),
98}
99
100#[derive(Serialize, Deserialize)]
101#[serde(remote = "Leaf")]
102enum LeafDef {
103 #[serde(with = "LiteralDef")]
104 Literal(Literal),
105 #[serde(with = "PunctDef")]
106 Punct(Punct),
107 #[serde(with = "IdentDef")]
108 Ident(Ident),
109}
110
111#[derive(Serialize, Deserialize)]
112#[serde(remote = "Literal")]
113struct LiteralDef {
114 pub text: SmolStr,
115 #[serde(with = "TokenIdDef")]
116 pub id: TokenId,
117}
118
119#[derive(Serialize, Deserialize)]
120#[serde(remote = "Punct")]
121struct PunctDef {
122 pub char: char,
123 #[serde(with = "SpacingDef")]
124 pub spacing: Spacing,
125 #[serde(with = "TokenIdDef")]
126 pub id: TokenId,
127}
128
129#[derive(Serialize, Deserialize)]
130#[serde(remote = "Spacing")]
131enum SpacingDef {
132 Alone,
133 Joint,
134}
135
136#[derive(Serialize, Deserialize)]
137#[serde(remote = "Ident")]
138struct IdentDef {
139 pub text: SmolStr,
140 #[serde(with = "TokenIdDef")]
141 pub id: TokenId,
142}
143
144mod opt_delimiter_def {
145 use super::{Delimiter, DelimiterDef};
146 use serde::{Deserialize, Deserializer, Serialize, Serializer};
147
148 pub fn serialize<S>(value: &Option<Delimiter>, serializer: S) -> Result<S::Ok, S::Error>
149 where
150 S: Serializer,
151 {
152 #[derive(Serialize)]
153 struct Helper<'a>(#[serde(with = "DelimiterDef")] &'a Delimiter);
154 value.as_ref().map(Helper).serialize(serializer)
155 }
156
157 pub fn deserialize<'de, D>(deserializer: D) -> Result<Option<Delimiter>, D::Error>
158 where
159 D: Deserializer<'de>,
160 {
161 #[derive(Deserialize)]
162 struct Helper(#[serde(with = "DelimiterDef")] Delimiter);
163 let helper = Option::deserialize(deserializer)?;
164 Ok(helper.map(|Helper(external)| external))
165 }
166}
167
168mod opt_subtree_def {
169 use super::{Subtree, SubtreeDef};
170 use serde::{Deserialize, Deserializer, Serialize, Serializer};
171
172 pub fn serialize<S>(value: &Option<Subtree>, serializer: S) -> Result<S::Ok, S::Error>
173 where
174 S: Serializer,
175 {
176 #[derive(Serialize)]
177 struct Helper<'a>(#[serde(with = "SubtreeDef")] &'a Subtree);
178 value.as_ref().map(Helper).serialize(serializer)
179 }
180
181 pub fn deserialize<'de, D>(deserializer: D) -> Result<Option<Subtree>, D::Error>
182 where
183 D: Deserializer<'de>,
184 {
185 #[derive(Deserialize)]
186 struct Helper(#[serde(with = "SubtreeDef")] Subtree);
187 let helper = Option::deserialize(deserializer)?;
188 Ok(helper.map(|Helper(external)| external))
189 }
190}
191
192mod vec_token_tree {
193 use super::{TokenTree, TokenTreeDef};
194 use serde::{ser::SerializeSeq, Deserialize, Deserializer, Serialize, Serializer};
195
196 pub fn serialize<S>(value: &Vec<TokenTree>, serializer: S) -> Result<S::Ok, S::Error>
197 where
198 S: Serializer,
199 {
200 #[derive(Serialize)]
201 struct Helper<'a>(#[serde(with = "TokenTreeDef")] &'a TokenTree);
202
203 let items: Vec<_> = value.iter().map(Helper).collect();
204 let mut seq = serializer.serialize_seq(Some(items.len()))?;
205 for element in items {
206 seq.serialize_element(&element)?;
207 }
208 seq.end()
209 }
210
211 pub fn deserialize<'de, D>(deserializer: D) -> Result<Vec<TokenTree>, D::Error>
212 where
213 D: Deserializer<'de>,
214 {
215 #[derive(Deserialize)]
216 struct Helper(#[serde(with = "TokenTreeDef")] TokenTree);
217
218 let helper = Vec::deserialize(deserializer)?;
219 Ok(helper.into_iter().map(|Helper(external)| external).collect())
220 }
221}
222
223#[cfg(test)]
224mod tests {
225 use super::*;
226
227 fn fixture_token_tree() -> Subtree {
228 let mut subtree = Subtree::default();
229 subtree
230 .token_trees
231 .push(TokenTree::Leaf(Ident { text: "struct".into(), id: TokenId(0) }.into()));
232 subtree
233 .token_trees
234 .push(TokenTree::Leaf(Ident { text: "Foo".into(), id: TokenId(1) }.into()));
235 subtree.token_trees.push(TokenTree::Subtree(
236 Subtree {
237 delimiter: Some(Delimiter { id: TokenId(2), kind: DelimiterKind::Brace }),
238 token_trees: vec![],
239 }
240 .into(),
241 ));
242 subtree
243 }
244
245 #[test]
246 fn test_proc_macro_rpc_works() {
247 let tt = fixture_token_tree();
248 let task = ExpansionTask {
249 macro_body: tt.clone(),
250 macro_name: Default::default(),
251 attributes: None,
252 lib: Default::default(),
253 };
254
255 let json = serde_json::to_string(&task).unwrap();
256 let back: ExpansionTask = serde_json::from_str(&json).unwrap();
257
258 assert_eq!(task.macro_body, back.macro_body);
259
260 let result = ExpansionResult { expansion: tt.clone() };
261 let json = serde_json::to_string(&result).unwrap();
262 let back: ExpansionResult = serde_json::from_str(&json).unwrap();
263
264 assert_eq!(result, back);
265 }
266}