1use smallvec::smallvec;
35
36use crate::{
37 context::{internal::ContextInternal, Context, Cx},
38 handle::{Handle, Root},
39 result::{NeonResult, Throw},
40 sys::{self, raw},
41 types::{
42 build,
43 extract::{TryFromJs, TryIntoJs},
44 function::{BindOptions, CallOptions},
45 private::ValueInternal,
46 utf8::Utf8,
47 JsFunction, JsUndefined, JsValue, Value,
48 },
49};
50
51#[cfg(feature = "napi-6")]
52use crate::{result::JsResult, types::JsArray};
53
54pub trait PropertyKey: Copy {
56 unsafe fn get_from<'c, C: Context<'c>>(
57 self,
58 cx: &mut C,
59 out: &mut raw::Local,
60 obj: raw::Local,
61 ) -> bool;
62
63 unsafe fn set_from<'c, C: Context<'c>>(
64 self,
65 cx: &mut C,
66 out: &mut bool,
67 obj: raw::Local,
68 val: raw::Local,
69 ) -> bool;
70}
71
72impl PropertyKey for u32 {
73 unsafe fn get_from<'c, C: Context<'c>>(
74 self,
75 cx: &mut C,
76 out: &mut raw::Local,
77 obj: raw::Local,
78 ) -> bool {
79 sys::object::get_index(out, cx.env().to_raw(), obj, self)
80 }
81
82 unsafe fn set_from<'c, C: Context<'c>>(
83 self,
84 cx: &mut C,
85 out: &mut bool,
86 obj: raw::Local,
87 val: raw::Local,
88 ) -> bool {
89 sys::object::set_index(out, cx.env().to_raw(), obj, self, val)
90 }
91}
92
93impl<'a, K: Value> PropertyKey for Handle<'a, K> {
94 unsafe fn get_from<'c, C: Context<'c>>(
95 self,
96 cx: &mut C,
97 out: &mut raw::Local,
98 obj: raw::Local,
99 ) -> bool {
100 let env = cx.env().to_raw();
101
102 sys::object::get(out, env, obj, self.to_local())
103 }
104
105 unsafe fn set_from<'c, C: Context<'c>>(
106 self,
107 cx: &mut C,
108 out: &mut bool,
109 obj: raw::Local,
110 val: raw::Local,
111 ) -> bool {
112 let env = cx.env().to_raw();
113
114 sys::object::set(out, env, obj, self.to_local(), val)
115 }
116}
117
118impl<'a> PropertyKey for &'a str {
119 unsafe fn get_from<'c, C: Context<'c>>(
120 self,
121 cx: &mut C,
122 out: &mut raw::Local,
123 obj: raw::Local,
124 ) -> bool {
125 let (ptr, len) = Utf8::from(self).into_small_unwrap().lower();
126 let env = cx.env().to_raw();
127
128 sys::object::get_string(env, out, obj, ptr, len)
129 }
130
131 unsafe fn set_from<'c, C: Context<'c>>(
132 self,
133 cx: &mut C,
134 out: &mut bool,
135 obj: raw::Local,
136 val: raw::Local,
137 ) -> bool {
138 let (ptr, len) = Utf8::from(self).into_small_unwrap().lower();
139 let env = cx.env().to_raw();
140
141 sys::object::set_string(env, out, obj, ptr, len, val)
142 }
143}
144
145pub struct PropOptions<'a, 'cx, O, K>
165where
166 'cx: 'a,
167 O: Object,
168 K: PropertyKey,
169{
170 pub(crate) cx: &'a mut Cx<'cx>,
171 pub(crate) this: Handle<'cx, O>,
172 pub(crate) key: K,
173}
174
175impl<'a, 'cx, O, K> PropOptions<'a, 'cx, O, K>
176where
177 'cx: 'a,
178 O: Object,
179 K: PropertyKey,
180{
181 pub fn this(&self) -> Handle<'cx, O> {
183 self.this
184 }
185
186 pub fn prop(&mut self, key: K) -> &mut Self {
205 self.key = key;
206 self
207 }
208
209 pub fn get<R: TryFromJs<'cx>>(&mut self) -> NeonResult<R> {
214 let v = self.this.get_value(self.cx, self.key)?;
215 R::from_js(self.cx, v)
216 }
217
218 pub fn set<V: TryIntoJs<'cx>>(&mut self, v: V) -> NeonResult<&mut Self> {
222 let v = v.try_into_js(self.cx)?;
223 self.this.set(self.cx, self.key, v)?;
224 Ok(self)
225 }
226
227 pub fn set_with<R, F>(&mut self, f: F) -> NeonResult<&mut Self>
231 where
232 R: TryIntoJs<'cx>,
233 F: FnOnce(&mut Cx<'cx>) -> R,
234 {
235 let v = f(self.cx).try_into_js(self.cx)?;
236 self.this.set(self.cx, self.key, v)?;
237 Ok(self)
238 }
239
240 pub fn bind(&'a mut self) -> NeonResult<BindOptions<'a, 'cx>> {
246 let callee: Handle<JsValue> = self.this.get(self.cx, self.key)?;
247 let this = Some(self.this.upcast());
248 Ok(BindOptions {
249 cx: self.cx,
250 callee,
251 this,
252 args: smallvec![],
253 })
254 }
255}
256
257pub trait Object: Value {
259 fn prop<'a, 'cx: 'a, K: PropertyKey>(
269 &self,
270 cx: &'a mut Cx<'cx>,
271 key: K,
272 ) -> PropOptions<'a, 'cx, Self, K> {
273 let this: Handle<'_, Self> =
274 Handle::new_internal(unsafe { ValueInternal::from_local(cx.env(), self.to_local()) });
275 PropOptions { cx, this, key }
276 }
277
278 fn method<'a, 'cx: 'a, K: PropertyKey>(
284 &self,
285 cx: &'a mut Cx<'cx>,
286 key: K,
287 ) -> NeonResult<BindOptions<'a, 'cx>> {
288 let callee: Handle<JsValue> = self.prop(cx, key).get()?;
289 let this = Some(self.as_value(cx));
290 Ok(BindOptions {
291 cx,
292 callee,
293 this,
294 args: smallvec![],
295 })
296 }
297
298 #[deprecated(since = "TBD", note = "use `Object::prop()` instead")]
299 fn get_opt<'a, V: Value, C: Context<'a>, K: PropertyKey>(
300 &self,
301 cx: &mut C,
302 key: K,
303 ) -> NeonResult<Option<Handle<'a, V>>> {
304 let v = self.get_value(cx, key)?;
305
306 if v.is_a::<JsUndefined, _>(cx) {
307 return Ok(None);
308 }
309
310 v.downcast_or_throw(cx).map(Some)
311 }
312
313 #[deprecated(since = "TBD", note = "use `Object::prop()` instead")]
314 fn get_value<'a, C: Context<'a>, K: PropertyKey>(
315 &self,
316 cx: &mut C,
317 key: K,
318 ) -> NeonResult<Handle<'a, JsValue>> {
319 build(cx.env(), |out| unsafe {
320 key.get_from(cx, out, self.to_local())
321 })
322 }
323
324 #[deprecated(since = "TBD", note = "use `Object::prop()` instead")]
325 fn get<'a, V: Value, C: Context<'a>, K: PropertyKey>(
326 &self,
327 cx: &mut C,
328 key: K,
329 ) -> NeonResult<Handle<'a, V>> {
330 self.get_value(cx, key)?.downcast_or_throw(cx)
331 }
332
333 #[cfg(feature = "napi-6")]
334 #[cfg_attr(docsrs, doc(cfg(feature = "napi-6")))]
335 fn get_own_property_names<'a, C: Context<'a>>(&self, cx: &mut C) -> JsResult<'a, JsArray> {
336 let env = cx.env();
337
338 build(cx.env(), |out| unsafe {
339 sys::object::get_own_property_names(out, env.to_raw(), self.to_local())
340 })
341 }
342
343 #[cfg(feature = "napi-8")]
344 fn freeze<'a, C: Context<'a>>(&self, cx: &mut C) -> NeonResult<&Self> {
345 let env = cx.env().to_raw();
346 let obj = self.to_local();
347 unsafe {
348 match sys::object::freeze(env, obj) {
349 Ok(()) => Ok(self),
350 Err(sys::Status::PendingException) => Err(Throw::new()),
351 _ => cx.throw_type_error("object cannot be frozen"),
352 }
353 }
354 }
355
356 #[cfg(feature = "napi-8")]
357 fn seal<'a, C: Context<'a>>(&self, cx: &mut C) -> NeonResult<&Self> {
358 let env = cx.env().to_raw();
359 let obj = self.to_local();
360 unsafe {
361 match sys::object::seal(env, obj) {
362 Ok(()) => Ok(self),
363 Err(sys::Status::PendingException) => Err(Throw::new()),
364 _ => cx.throw_type_error("object cannot be sealed"),
365 }
366 }
367 }
368
369 #[deprecated(since = "TBD", note = "use `Object::prop()` instead")]
370 fn set<'a, C: Context<'a>, K: PropertyKey, W: Value>(
371 &self,
372 cx: &mut C,
373 key: K,
374 val: Handle<W>,
375 ) -> NeonResult<bool> {
376 let mut result = false;
377 unsafe {
378 if key.set_from(cx, &mut result, self.to_local(), val.to_local()) {
379 Ok(result)
380 } else {
381 Err(Throw::new())
382 }
383 }
384 }
385
386 fn root<'a, C: Context<'a>>(&self, cx: &mut C) -> Root<Self> {
387 Root::new(cx, self)
388 }
389
390 #[deprecated(since = "TBD", note = "use `Object::method()` instead")]
391 fn call_method_with<'a, C, K>(&self, cx: &mut C, method: K) -> NeonResult<CallOptions<'a>>
392 where
393 C: Context<'a>,
394 K: PropertyKey,
395 {
396 let mut options = self.get::<JsFunction, _, _>(cx, method)?.call_with(cx);
397 options.this(JsValue::new_internal(self.to_local()));
398 Ok(options)
399 }
400}