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