1use crate::{
2 js::{object::JsObject, value::JsValue},
3 js_cast::JsCast,
4 link::{Browser, Error, RpcCell},
5 protocol::{ERR, GET, IMPORT, SET},
6 retrieve::RetrieveFuture,
7 serialize::{ToJs, UseInJsCode, UseInJsCodeWriter},
8 Endpoint, RpcHandle,
9};
10use alloc::{borrow::ToOwned, sync::Arc};
11use core::{
12 fmt::Write,
13 marker::PhantomData,
14 task::Waker,
16};
17use futures_util::task::noop_waker_ref;
18use sha3::Digest;
19use spin::Mutex;
20
21impl Browser {
22 pub fn new_rpc<T, C>(&self, a: &str) -> crate::RpcHandle<Endpoint<T, C>> {
24 let mut lock = self.0.lock();
25 let a = lock
26 .rpc_state
27 .entry(a.to_owned())
28 .or_insert_with(|| {
29 crate::RpcCellAM(Arc::new(Mutex::new(RpcCell {
30 waker: noop_waker_ref().clone(),
31 queue: Default::default(),
32 })))
33 })
34 .clone();
35 return RpcHandle {
36 browser: self.clone(),
37 recv: a,
38 data: Endpoint {
39 phantom: PhantomData,
40 },
41 };
42 }
43 pub fn call_function<'a>(
63 &'a self,
64 function_name: &'a str,
65 args: impl IntoIterator<Item = &'a dyn UseInJsCode>,
66 last_arg_variadic: bool,
67 ) -> JsValue {
68 self.call_function_inner(&format_args!("{}", function_name), args, last_arg_variadic)
69 }
70
71 pub fn call_constructor<'a>(
91 &'a self,
92 class_name: &'a str,
93 args: impl IntoIterator<Item = &'a dyn UseInJsCode>,
94 last_arg_variadic: bool,
95 ) -> JsValue {
96 self.call_function_inner(&format_args!("new {}", class_name), args, last_arg_variadic)
97 }
98
99 fn call_function_inner<'a>(
100 &'a self,
101 function: &core::fmt::Arguments<'_>,
102 args: impl IntoIterator<Item = &'a dyn UseInJsCode>,
103 last_arg_variadic: bool,
104 ) -> JsValue {
105 let id = {
106 let mut link = self.0.lock();
107 let out_id = link.get_new_id();
108 write!(link.raw_commands_buf(), "try{{{SET}({out_id},{function}(").unwrap();
109 let mut iter = args.into_iter().peekable();
110 while let Some(arg) = iter.next() {
111 let arg = UseInJsCodeWriter(arg);
112 let res = if last_arg_variadic && iter.peek().is_none() {
113 write!(link.raw_commands_buf(), "...{arg},")
114 } else {
115 write!(link.raw_commands_buf(), "{arg},")
116 };
117 if let Err(e) = res {
118 link.kill(Error::CommandSerialize(e));
119 }
120 }
121 write!(
122 link.raw_commands_buf(),
123 "))}}catch($){{{ERR}({out_id},$)}};\n"
124 )
125 .unwrap();
126 link.wake_outgoing();
127 out_id
128 };
129 JsValue {
130 id,
131 browser: self.clone(),
132 }
133 }
134
135 pub fn get_field(&self, base_obj: &dyn UseInJsCode, property: &dyn UseInJsCode) -> JsValue {
139 let browser = self.clone();
140 let id = {
141 let mut link = browser.0.lock();
142 let out_id = link.get_new_id();
143 let base_obj = UseInJsCodeWriter(base_obj);
144 let property = UseInJsCodeWriter(property);
145 if let Err(e) = writeln!(
146 link.raw_commands_buf(),
147 "try{{{SET}({out_id},({base_obj})[{property}])}}catch($){{{ERR}({out_id},$)}};"
148 ) {
149 link.kill(Error::CommandSerialize(e));
150 }
151 link.wake_outgoing_lazy();
152 out_id
153 };
154 JsValue { id, browser }
155 }
156
157 pub fn set_field(
161 &self,
162 base_obj: &dyn UseInJsCode,
163 property: &dyn UseInJsCode,
164 value: &dyn UseInJsCode,
165 ) {
166 let mut link = self.0.lock();
167 let (base_obj, property, value) = (
168 UseInJsCodeWriter(base_obj),
169 UseInJsCodeWriter(property),
170 UseInJsCodeWriter(value),
171 );
172 if let Err(e) = writeln!(link.raw_commands_buf(), "({base_obj})[{property}]={value};") {
173 link.kill(Error::CommandSerialize(e));
174 }
175 link.wake_outgoing();
176 }
177
178 pub fn new_value<'a, T: JsCast>(&'a self, value: &'a dyn ToJs<T>) -> T {
180 let val = self.value_from_raw_code(format_args!("{}", UseInJsCodeWriter(value)));
181 JsCast::unchecked_from_js(val)
182 }
183
184 pub fn run_raw_code<'a>(&'a self, code: core::fmt::Arguments<'a>) {
188 let mut link = self.0.lock();
189 if let Err(e) = writeln!(link.raw_commands_buf(), "{{ {code} }}") {
190 link.kill(Error::CommandSerialize(e));
191 }
192 link.wake_outgoing();
193 }
194
195 pub fn value_from_raw_code<'a>(&'a self, code: core::fmt::Arguments<'a>) -> JsValue {
199 let mut link = self.0.lock();
200 let out_id = link.get_new_id();
201 writeln!(
202 link.raw_commands_buf(),
203 "try{{{SET}({out_id},{code})}}catch($){{{ERR}({out_id},$)}};"
204 )
205 .unwrap();
206 link.wake_outgoing();
207 JsValue {
208 id: out_id,
209 browser: self.to_owned(),
210 }
211 }
212
213 pub fn value_from_pure_raw_code(&self, x: &str) -> JsValue {
217 let mut link = self.0.lock();
218 let a = match link.pure_values.get(x).cloned() {
219 None => {
220 let out = self.value_from_raw_code(format_args!("{x}"));
221 link.pure_values.insert(x.to_owned(), out.clone());
222 out
223 }
224 Some(a) => a,
225 };
226 return a;
227 }
228
229 pub fn import(&self, name: &str) -> JsValue {
231 let mut link = self.0.lock();
232 let a = match link.imports.get(name).cloned() {
233 None => {
234 let out_id = link.get_new_id();
235 writeln!(
236 link.raw_commands_buf(),
237 "try{{{SET}({out_id},{IMPORT}._{})}}catch($){{{ERR}({out_id},$)}};",
238 hex::encode(&sha3::Sha3_256::digest(name.as_bytes()))
239 )
240 .unwrap();
241 link.wake_outgoing();
242 let out = JsValue {
243 id: out_id,
244 browser: self.to_owned(),
245 };
246 link.imports.insert(name.to_owned(), out.clone());
247 out
248 }
249 Some(a) => a,
250 };
251 return a;
252 }
253}
254
255impl JsValue {
256 pub(crate) fn retrieve_and_deserialize<U: serde::de::DeserializeOwned>(
257 &self,
258 ) -> RetrieveFuture<'_, U> {
259 RetrieveFuture::new(self.id, &self.browser)
260 }
261 pub fn retrieve_json(&self) -> RetrieveFuture<'_, serde_json::Value> {
272 self.retrieve_and_deserialize()
273 }
274}
275impl JsObject {
276 pub fn js_get_field(&self, property: &dyn UseInJsCode) -> JsValue {
294 let browser = self.browser.clone();
295 let id = {
296 let mut link = browser.0.lock();
297 let out_id = link.get_new_id();
298 let self_id = self.id;
299 let property = UseInJsCodeWriter(property);
300 if let Err(e) = writeln!(
301 link.raw_commands_buf(),
302 "try{{{SET}({out_id},{GET}({self_id})[{property}])}}catch($){{{ERR}({out_id},$)}};"
303 ) {
304 link.kill(Error::CommandSerialize(e));
305 }
306 link.wake_outgoing_lazy();
307 out_id
308 };
309 JsValue { id, browser }
310 }
311 pub fn js_set_field(&self, property: &dyn UseInJsCode, value: &dyn UseInJsCode) {
329 let self_id = self.id;
330 let mut link = self.browser.0.lock();
331 let (property, value) = (UseInJsCodeWriter(property), UseInJsCodeWriter(value));
332 if let Err(e) = writeln!(
333 link.raw_commands_buf(),
334 "{GET}({self_id})[{property}]={value};"
335 ) {
336 link.kill(Error::CommandSerialize(e));
337 }
338 link.wake_outgoing();
339 }
340
341 pub fn js_call_method<'a>(
361 &'a self,
362 method_name: &'a str,
363 args: impl IntoIterator<Item = &'a dyn UseInJsCode>,
364 last_arg_variadic: bool,
365 ) -> JsValue {
366 let self_id = self.id;
367 self.browser.call_function_inner(
368 &format_args!("{GET}({self_id}).{method_name}"),
369 args,
370 last_arg_variadic,
371 )
372 }
373 pub fn js_call_self<'a>(
377 &'a self,
378 args: impl IntoIterator<Item = &'a dyn UseInJsCode>,
379 last_arg_variadic: bool,
380 ) -> JsValue {
381 let self_id = self.id;
382 self.browser.call_function_inner(
383 &format_args!("({GET}({self_id}))"),
384 args,
385 last_arg_variadic,
386 )
387 }
388}
389
390struct CommandSerializeFailed;
391
392impl core::fmt::Display for CommandSerializeFailed {
393 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
394 core::fmt::Debug::fmt(self, f)
395 }
396}
397impl core::fmt::Debug for CommandSerializeFailed {
398 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
399 f.debug_struct("CommandSerializeFailed").finish()
400 }
401}
402
403impl core::error::Error for CommandSerializeFailed {}