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
54#[cfg(feature = "napi-6")]
55pub use self::class::Class;
56
57#[doc(hidden)]
58pub use self::wrap::{unwrap, wrap};
59
60#[cfg(feature = "napi-6")]
61pub(crate) mod class;
62pub(crate) mod wrap;
63
64pub trait PropertyKey: Copy {
66 unsafe fn get_from<'c, C: Context<'c>>(
67 self,
68 cx: &mut C,
69 out: &mut raw::Local,
70 obj: raw::Local,
71 ) -> bool;
72
73 unsafe fn set_from<'c, C: Context<'c>>(
74 self,
75 cx: &mut C,
76 out: &mut bool,
77 obj: raw::Local,
78 val: raw::Local,
79 ) -> bool;
80}
81
82impl PropertyKey for u32 {
83 unsafe fn get_from<'c, C: Context<'c>>(
84 self,
85 cx: &mut C,
86 out: &mut raw::Local,
87 obj: raw::Local,
88 ) -> bool {
89 sys::object::get_index(out, cx.env().to_raw(), obj, self)
90 }
91
92 unsafe fn set_from<'c, C: Context<'c>>(
93 self,
94 cx: &mut C,
95 out: &mut bool,
96 obj: raw::Local,
97 val: raw::Local,
98 ) -> bool {
99 sys::object::set_index(out, cx.env().to_raw(), obj, self, val)
100 }
101}
102
103impl<'a, K: Value> PropertyKey for Handle<'a, K> {
104 unsafe fn get_from<'c, C: Context<'c>>(
105 self,
106 cx: &mut C,
107 out: &mut raw::Local,
108 obj: raw::Local,
109 ) -> bool {
110 let env = cx.env().to_raw();
111
112 sys::object::get(out, env, obj, self.to_local())
113 }
114
115 unsafe fn set_from<'c, C: Context<'c>>(
116 self,
117 cx: &mut C,
118 out: &mut bool,
119 obj: raw::Local,
120 val: raw::Local,
121 ) -> bool {
122 let env = cx.env().to_raw();
123
124 sys::object::set(out, env, obj, self.to_local(), val)
125 }
126}
127
128impl PropertyKey for &str {
129 unsafe fn get_from<'c, C: Context<'c>>(
130 self,
131 cx: &mut C,
132 out: &mut raw::Local,
133 obj: raw::Local,
134 ) -> bool {
135 let (ptr, len) = Utf8::from(self).into_small_unwrap().lower();
136 let env = cx.env().to_raw();
137
138 sys::object::get_string(env, out, obj, ptr, len)
139 }
140
141 unsafe fn set_from<'c, C: Context<'c>>(
142 self,
143 cx: &mut C,
144 out: &mut bool,
145 obj: raw::Local,
146 val: raw::Local,
147 ) -> bool {
148 let (ptr, len) = Utf8::from(self).into_small_unwrap().lower();
149 let env = cx.env().to_raw();
150
151 sys::object::set_string(env, out, obj, ptr, len, val)
152 }
153}
154
155pub struct PropOptions<'a, 'cx, O, K>
175where
176 'cx: 'a,
177 O: Object,
178 K: PropertyKey,
179{
180 pub(crate) cx: &'a mut Cx<'cx>,
181 pub(crate) this: Handle<'cx, O>,
182 pub(crate) key: K,
183}
184
185impl<'a, 'cx, O, K> PropOptions<'a, 'cx, O, K>
186where
187 'cx: 'a,
188 O: Object,
189 K: PropertyKey,
190{
191 pub fn this(&self) -> Handle<'cx, O> {
193 self.this
194 }
195
196 pub fn prop(&mut self, key: K) -> &mut Self {
215 self.key = key;
216 self
217 }
218
219 pub fn get<R: TryFromJs<'cx>>(&mut self) -> NeonResult<R> {
224 let v = self.this.get_value(self.cx, self.key)?;
225 R::from_js(self.cx, v)
226 }
227
228 pub fn set<V: TryIntoJs<'cx>>(&mut self, v: V) -> NeonResult<&mut Self> {
232 let v = v.try_into_js(self.cx)?;
233 self.this.set(self.cx, self.key, v)?;
234 Ok(self)
235 }
236
237 pub fn set_with<R, F>(&mut self, f: F) -> NeonResult<&mut Self>
241 where
242 R: TryIntoJs<'cx>,
243 F: FnOnce(&mut Cx<'cx>) -> R,
244 {
245 let v = f(self.cx).try_into_js(self.cx)?;
246 self.this.set(self.cx, self.key, v)?;
247 Ok(self)
248 }
249
250 pub fn bind(&'a mut self) -> NeonResult<BindOptions<'a, 'cx>> {
256 let callee: Handle<JsValue> = self.this.get(self.cx, self.key)?;
257 let this = Some(self.this.upcast());
258 Ok(BindOptions {
259 cx: self.cx,
260 callee,
261 this,
262 args: smallvec![],
263 })
264 }
265}
266
267pub trait Object: Value {
269 fn prop<'a, 'cx: 'a, K: PropertyKey>(
279 &self,
280 cx: &'a mut Cx<'cx>,
281 key: K,
282 ) -> PropOptions<'a, 'cx, Self, K> {
283 let this: Handle<'_, Self> =
284 Handle::new_internal(unsafe { ValueInternal::from_local(cx.env(), self.to_local()) });
285 PropOptions { cx, this, key }
286 }
287
288 fn method<'a, 'cx: 'a, K: PropertyKey>(
294 &self,
295 cx: &'a mut Cx<'cx>,
296 key: K,
297 ) -> NeonResult<BindOptions<'a, 'cx>> {
298 let callee: Handle<JsValue> = self.prop(cx, key).get()?;
299 let this = Some(self.as_value(cx));
300 Ok(BindOptions {
301 cx,
302 callee,
303 this,
304 args: smallvec![],
305 })
306 }
307
308 #[deprecated(since = "TBD", note = "use `Object::prop()` instead")]
309 fn get_opt<'a, V: Value, C: Context<'a>, K: PropertyKey>(
310 &self,
311 cx: &mut C,
312 key: K,
313 ) -> NeonResult<Option<Handle<'a, V>>> {
314 let v = self.get_value(cx, key)?;
315
316 if v.is_a::<JsUndefined, _>(cx) {
317 return Ok(None);
318 }
319
320 v.downcast_or_throw(cx).map(Some)
321 }
322
323 #[deprecated(since = "TBD", note = "use `Object::prop()` instead")]
324 fn get_value<'a, C: Context<'a>, K: PropertyKey>(
325 &self,
326 cx: &mut C,
327 key: K,
328 ) -> NeonResult<Handle<'a, JsValue>> {
329 build(cx.env(), |out| unsafe {
330 key.get_from(cx, out, self.to_local())
331 })
332 }
333
334 #[deprecated(since = "TBD", note = "use `Object::prop()` instead")]
335 fn get<'a, V: Value, C: Context<'a>, K: PropertyKey>(
336 &self,
337 cx: &mut C,
338 key: K,
339 ) -> NeonResult<Handle<'a, V>> {
340 self.get_value(cx, key)?.downcast_or_throw(cx)
341 }
342
343 #[cfg(feature = "napi-6")]
344 #[cfg_attr(docsrs, doc(cfg(feature = "napi-6")))]
345 fn get_own_property_names<'a, C: Context<'a>>(&self, cx: &mut C) -> JsResult<'a, JsArray> {
346 let env = cx.env();
347
348 build(cx.env(), |out| unsafe {
349 sys::object::get_own_property_names(out, env.to_raw(), self.to_local())
350 })
351 }
352
353 #[cfg(feature = "napi-8")]
354 fn freeze<'a, C: Context<'a>>(&self, cx: &mut C) -> NeonResult<&Self> {
355 let env = cx.env().to_raw();
356 let obj = self.to_local();
357 unsafe {
358 match sys::object::freeze(env, obj) {
359 Ok(()) => Ok(self),
360 Err(sys::Status::PendingException) => Err(Throw::new()),
361 _ => cx.throw_type_error("object cannot be frozen"),
362 }
363 }
364 }
365
366 #[cfg(feature = "napi-8")]
367 fn seal<'a, C: Context<'a>>(&self, cx: &mut C) -> NeonResult<&Self> {
368 let env = cx.env().to_raw();
369 let obj = self.to_local();
370 unsafe {
371 match sys::object::seal(env, obj) {
372 Ok(()) => Ok(self),
373 Err(sys::Status::PendingException) => Err(Throw::new()),
374 _ => cx.throw_type_error("object cannot be sealed"),
375 }
376 }
377 }
378
379 #[deprecated(since = "TBD", note = "use `Object::prop()` instead")]
380 fn set<'a, C: Context<'a>, K: PropertyKey, W: Value>(
381 &self,
382 cx: &mut C,
383 key: K,
384 val: Handle<W>,
385 ) -> NeonResult<bool> {
386 let mut result = false;
387 unsafe {
388 if key.set_from(cx, &mut result, self.to_local(), val.to_local()) {
389 Ok(result)
390 } else {
391 Err(Throw::new())
392 }
393 }
394 }
395
396 fn root<'a, C: Context<'a>>(&self, cx: &mut C) -> Root<Self> {
397 Root::new(cx, self)
398 }
399
400 #[deprecated(since = "TBD", note = "use `Object::method()` instead")]
401 fn call_method_with<'a, C, K>(&self, cx: &mut C, method: K) -> NeonResult<CallOptions<'a>>
402 where
403 C: Context<'a>,
404 K: PropertyKey,
405 {
406 let mut options = self.get::<JsFunction, _, _>(cx, method)?.call_with(cx);
407 options.this(JsValue::new_internal(self.to_local()));
408 Ok(options)
409 }
410}