1 //! Representations of JavaScript's core builtin types.
2 //!
3 //! ## Modeling JavaScript Types
4 //!
5 //! All JavaScript values in Neon implement the abstract [`Value`] trait, which
6 //! is the most generic way to work with JavaScript values. Neon provides a
7 //! number of types that implement this trait, each representing a particular
8 //! type of JavaScript value.
9 //!
10 //! By convention, JavaScript types in Neon have the prefix `Js` in their name,
11 //! such as [`JsNumber`](crate::types::JsNumber) (for the JavaScript `number`
12 //! type) or [`JsFunction`](crate::types::JsFunction) (for the JavaScript
13 //! `function` type).
14 //!
15 //! ### Handles and Casts
16 //!
17 //! Access to JavaScript values in Neon works through [handles](crate::handle),
18 //! which ensure the safe interoperation between Rust and the JavaScript garbage
19 //! collector. This means, for example, a Rust variable that stores a JavaScript string
20 //! will have the type `Handle<JsString>` rather than [`JsString`](crate::types::JsString).
21 //!
22 //! Neon types model the JavaScript type hierarchy through the use of *casts*.
23 //! The [`Handle::upcast()`](crate::handle::Handle::upcast) method safely converts
24 //! a handle to a JavaScript value of one type into a handle to a value of its
25 //! supertype. For example, it's safe to treat a [`JsArray`](crate::types::JsArray)
26 //! as a [`JsObject`](crate::types::JsObject), so you can do an "upcast" and it will
27 //! never fail:
28 //!
29 //! ```
30 //! # use neon::prelude::*;
31 //! fn as_object(array: Handle<JsArray>) -> Handle<JsObject> {
32 //! let object: Handle<JsObject> = array.upcast();
33 //! object
34 //! }
35 //! ```
36 //!
37 //! Unlike upcasts, the [`Handle::downcast()`](crate::handle::Handle::downcast) method
38 //! requires a runtime check to test a value's type at runtime, so it can fail with
39 //! a [`DowncastError`](crate::handle::DowncastError):
40 //!
41 //! ```
42 //! # use neon::prelude::*;
43 //! fn as_array<'a>(
44 //! cx: &mut impl Context<'a>,
45 //! object: Handle<'a, JsObject>
46 //! ) -> JsResult<'a, JsArray> {
47 //! object.downcast(cx).or_throw(cx)
48 //! }
49 //! ```
50 //!
51 //! ### The JavaScript Type Hierarchy
52 //!
53 //! ![The Neon type hierarchy, described in detail below.][types]
54 //!
55 //! The JavaScript type hierarchy includes:
56 //!
57 //! - [`JsValue`](JsValue): This is the top of the type hierarchy, and can refer to
58 //! any JavaScript value. (For TypeScript programmers, this can be thought of as
59 //! similar to TypeScript's [`unknown`][unknown] type.)
60 //! - [`JsObject`](JsObject): This is the top of the object type hierarchy. Object
61 //! types all implement the [`Object`](crate::object::Object) trait, which allows
62 //! getting and setting properties.
63 //! - **Standard object types:** [`JsFunction`](JsFunction), [`JsArray`](JsArray),
64 //! [`JsDate`](JsDate), and [`JsError`](JsError).
65 //! - **Typed arrays:** [`JsBuffer`](JsBuffer) and [`JsArrayBuffer`](JsArrayBuffer).
66 //! - **Custom types:** [`JsBox`](JsBox), a special Neon type that allows the creation
67 //! of custom objects that own Rust data structures.
68 //! - **Primitive types:** These are the built-in JavaScript datatypes that are not
69 //! object types: [`JsNumber`](JsNumber), [`JsBoolean`](JsBoolean),
70 //! [`JsString`](JsString), [`JsNull`](JsNull), and [`JsUndefined`](JsUndefined).
71 //!
72 //! [types]: https://raw.githubusercontent.com/neon-bindings/neon/main/doc/types.jpg
73 //! [unknown]: https://mariusschulz.com/blog/the-unknown-type-in-typescript#the-unknown-type
74
75 pub(crate) mod binary;
76 #[cfg(feature = "napi-1")]
77 pub(crate) mod boxed;
78 #[cfg(feature = "napi-5")]
79 pub(crate) mod date;
80 pub(crate) mod error;
81
82 pub(crate) mod internal;
83 pub(crate) mod utf8;
84
85 use self::internal::{FunctionCallback, ValueInternal};
86 use self::utf8::Utf8;
87 use crate::context::internal::Env;
88 use crate::context::{Context, FunctionContext};
89 use crate::handle::internal::SuperType;
90 use crate::handle::{Handle, Managed};
91 use crate::object::{Object, This};
92 use crate::result::{JsResult, JsResultExt, NeonResult, Throw};
93 use crate::types::internal::Callback;
94 use neon_runtime;
95 use neon_runtime::raw;
96 use smallvec::SmallVec;
97 use std::fmt;
98 use std::fmt::Debug;
99 use std::marker::PhantomData;
100 use std::os::raw::c_void;
101
102 pub use self::binary::{BinaryData, BinaryViewType, JsArrayBuffer, JsBuffer};
103 #[cfg(feature = "napi-1")]
104 pub use self::boxed::{Finalize, JsBox};
105 #[cfg(feature = "napi-5")]
106 pub use self::date::{DateError, DateErrorKind, JsDate};
107 pub use self::error::JsError;
108
build<'a, T: Managed, F: FnOnce(&mut raw::Local) -> bool>( env: Env, init: F, ) -> JsResult<'a, T>109 pub(crate) fn build<'a, T: Managed, F: FnOnce(&mut raw::Local) -> bool>(
110 env: Env,
111 init: F,
112 ) -> JsResult<'a, T> {
113 unsafe {
114 let mut local: raw::Local = std::mem::zeroed();
115 if init(&mut local) {
116 Ok(Handle::new_internal(T::from_raw(env, local)))
117 } else {
118 Err(Throw)
119 }
120 }
121 }
122
123 impl<T: Value> SuperType<T> for JsValue {
upcast_internal(v: T) -> JsValue124 fn upcast_internal(v: T) -> JsValue {
125 JsValue(v.to_raw())
126 }
127 }
128
129 impl<T: Object> SuperType<T> for JsObject {
upcast_internal(v: T) -> JsObject130 fn upcast_internal(v: T) -> JsObject {
131 JsObject(v.to_raw())
132 }
133 }
134
135 /// The trait shared by all JavaScript values.
136 pub trait Value: ValueInternal {
to_string<'a, C: Context<'a>>(self, cx: &mut C) -> JsResult<'a, JsString>137 fn to_string<'a, C: Context<'a>>(self, cx: &mut C) -> JsResult<'a, JsString> {
138 let env = cx.env();
139 build(env, |out| unsafe {
140 neon_runtime::convert::to_string(out, env.to_raw(), self.to_raw())
141 })
142 }
143
as_value<'a, C: Context<'a>>(self, _: &mut C) -> Handle<'a, JsValue>144 fn as_value<'a, C: Context<'a>>(self, _: &mut C) -> Handle<'a, JsValue> {
145 JsValue::new_internal(self.to_raw())
146 }
147 }
148
149 /// A JavaScript value of any type.
150 #[repr(C)]
151 #[derive(Clone, Copy)]
152 pub struct JsValue(raw::Local);
153
154 impl Value for JsValue {}
155
156 impl Managed for JsValue {
to_raw(self) -> raw::Local157 fn to_raw(self) -> raw::Local {
158 self.0
159 }
160
from_raw(_: Env, h: raw::Local) -> Self161 fn from_raw(_: Env, h: raw::Local) -> Self {
162 JsValue(h)
163 }
164 }
165
166 impl ValueInternal for JsValue {
name() -> String167 fn name() -> String {
168 "any".to_string()
169 }
170
is_typeof<Other: Value>(_env: Env, _other: Other) -> bool171 fn is_typeof<Other: Value>(_env: Env, _other: Other) -> bool {
172 true
173 }
174 }
175
176 unsafe impl This for JsValue {
177 #[cfg(feature = "legacy-runtime")]
as_this(h: raw::Local) -> Self178 fn as_this(h: raw::Local) -> Self {
179 JsValue(h)
180 }
181
182 #[cfg(feature = "napi-1")]
as_this(_env: Env, h: raw::Local) -> Self183 fn as_this(_env: Env, h: raw::Local) -> Self {
184 JsValue(h)
185 }
186 }
187
188 impl JsValue {
new_internal<'a>(value: raw::Local) -> Handle<'a, JsValue>189 pub(crate) fn new_internal<'a>(value: raw::Local) -> Handle<'a, JsValue> {
190 Handle::new_internal(JsValue(value))
191 }
192 }
193
194 /// The JavaScript `undefined` value.
195 #[repr(C)]
196 #[derive(Clone, Copy)]
197 pub struct JsUndefined(raw::Local);
198
199 impl JsUndefined {
200 #[cfg(feature = "legacy-runtime")]
new<'a>() -> Handle<'a, JsUndefined>201 pub fn new<'a>() -> Handle<'a, JsUndefined> {
202 JsUndefined::new_internal(Env::current())
203 }
204
205 #[cfg(feature = "napi-1")]
new<'a, C: Context<'a>>(cx: &mut C) -> Handle<'a, JsUndefined>206 pub fn new<'a, C: Context<'a>>(cx: &mut C) -> Handle<'a, JsUndefined> {
207 JsUndefined::new_internal(cx.env())
208 }
209
new_internal<'a>(env: Env) -> Handle<'a, JsUndefined>210 pub(crate) fn new_internal<'a>(env: Env) -> Handle<'a, JsUndefined> {
211 unsafe {
212 let mut local: raw::Local = std::mem::zeroed();
213 neon_runtime::primitive::undefined(&mut local, env.to_raw());
214 Handle::new_internal(JsUndefined(local))
215 }
216 }
217
218 #[allow(clippy::wrong_self_convention)]
as_this_compat(env: Env, _: raw::Local) -> Self219 fn as_this_compat(env: Env, _: raw::Local) -> Self {
220 unsafe {
221 let mut local: raw::Local = std::mem::zeroed();
222 neon_runtime::primitive::undefined(&mut local, env.to_raw());
223 JsUndefined(local)
224 }
225 }
226 }
227
228 impl Value for JsUndefined {}
229
230 impl Managed for JsUndefined {
to_raw(self) -> raw::Local231 fn to_raw(self) -> raw::Local {
232 self.0
233 }
234
from_raw(_: Env, h: raw::Local) -> Self235 fn from_raw(_: Env, h: raw::Local) -> Self {
236 JsUndefined(h)
237 }
238 }
239
240 unsafe impl This for JsUndefined {
241 #[cfg(feature = "legacy-runtime")]
as_this(h: raw::Local) -> Self242 fn as_this(h: raw::Local) -> Self {
243 JsUndefined::as_this_compat(Env::current(), h)
244 }
245
246 #[cfg(feature = "napi-1")]
as_this(env: Env, h: raw::Local) -> Self247 fn as_this(env: Env, h: raw::Local) -> Self {
248 JsUndefined::as_this_compat(env, h)
249 }
250 }
251
252 impl ValueInternal for JsUndefined {
name() -> String253 fn name() -> String {
254 "undefined".to_string()
255 }
256
is_typeof<Other: Value>(env: Env, other: Other) -> bool257 fn is_typeof<Other: Value>(env: Env, other: Other) -> bool {
258 unsafe { neon_runtime::tag::is_undefined(env.to_raw(), other.to_raw()) }
259 }
260 }
261
262 /// The JavaScript `null` value.
263 #[repr(C)]
264 #[derive(Clone, Copy)]
265 pub struct JsNull(raw::Local);
266
267 impl JsNull {
268 #[cfg(feature = "legacy-runtime")]
new<'a>() -> Handle<'a, JsNull>269 pub fn new<'a>() -> Handle<'a, JsNull> {
270 JsNull::new_internal(Env::current())
271 }
272
273 #[cfg(feature = "napi-1")]
new<'a, C: Context<'a>>(cx: &mut C) -> Handle<'a, JsNull>274 pub fn new<'a, C: Context<'a>>(cx: &mut C) -> Handle<'a, JsNull> {
275 JsNull::new_internal(cx.env())
276 }
277
new_internal<'a>(env: Env) -> Handle<'a, JsNull>278 pub(crate) fn new_internal<'a>(env: Env) -> Handle<'a, JsNull> {
279 unsafe {
280 let mut local: raw::Local = std::mem::zeroed();
281 neon_runtime::primitive::null(&mut local, env.to_raw());
282 Handle::new_internal(JsNull(local))
283 }
284 }
285 }
286
287 impl Value for JsNull {}
288
289 impl Managed for JsNull {
to_raw(self) -> raw::Local290 fn to_raw(self) -> raw::Local {
291 self.0
292 }
293
from_raw(_: Env, h: raw::Local) -> Self294 fn from_raw(_: Env, h: raw::Local) -> Self {
295 JsNull(h)
296 }
297 }
298
299 impl ValueInternal for JsNull {
name() -> String300 fn name() -> String {
301 "null".to_string()
302 }
303
is_typeof<Other: Value>(env: Env, other: Other) -> bool304 fn is_typeof<Other: Value>(env: Env, other: Other) -> bool {
305 unsafe { neon_runtime::tag::is_null(env.to_raw(), other.to_raw()) }
306 }
307 }
308
309 /// A JavaScript boolean primitive value.
310 #[repr(C)]
311 #[derive(Clone, Copy)]
312 pub struct JsBoolean(raw::Local);
313
314 impl JsBoolean {
new<'a, C: Context<'a>>(cx: &mut C, b: bool) -> Handle<'a, JsBoolean>315 pub fn new<'a, C: Context<'a>>(cx: &mut C, b: bool) -> Handle<'a, JsBoolean> {
316 JsBoolean::new_internal(cx.env(), b)
317 }
318
new_internal<'a>(env: Env, b: bool) -> Handle<'a, JsBoolean>319 pub(crate) fn new_internal<'a>(env: Env, b: bool) -> Handle<'a, JsBoolean> {
320 unsafe {
321 let mut local: raw::Local = std::mem::zeroed();
322 neon_runtime::primitive::boolean(&mut local, env.to_raw(), b);
323 Handle::new_internal(JsBoolean(local))
324 }
325 }
326
327 #[cfg(feature = "legacy-runtime")]
value(self) -> bool328 pub fn value(self) -> bool {
329 unsafe { neon_runtime::primitive::boolean_value(self.to_raw()) }
330 }
331
332 #[cfg(feature = "napi-1")]
value<'a, C: Context<'a>>(self, cx: &mut C) -> bool333 pub fn value<'a, C: Context<'a>>(self, cx: &mut C) -> bool {
334 let env = cx.env().to_raw();
335 unsafe { neon_runtime::primitive::boolean_value(env, self.to_raw()) }
336 }
337 }
338
339 impl Value for JsBoolean {}
340
341 impl Managed for JsBoolean {
to_raw(self) -> raw::Local342 fn to_raw(self) -> raw::Local {
343 self.0
344 }
345
from_raw(_: Env, h: raw::Local) -> Self346 fn from_raw(_: Env, h: raw::Local) -> Self {
347 JsBoolean(h)
348 }
349 }
350
351 impl ValueInternal for JsBoolean {
name() -> String352 fn name() -> String {
353 "boolean".to_string()
354 }
355
is_typeof<Other: Value>(env: Env, other: Other) -> bool356 fn is_typeof<Other: Value>(env: Env, other: Other) -> bool {
357 unsafe { neon_runtime::tag::is_boolean(env.to_raw(), other.to_raw()) }
358 }
359 }
360
361 /// A JavaScript string primitive value.
362 #[repr(C)]
363 #[derive(Clone, Copy)]
364 pub struct JsString(raw::Local);
365
366 /// An error produced when constructing a string that exceeds the JS engine's maximum string size.
367 #[derive(PartialEq, Eq, PartialOrd, Ord, Clone, Debug)]
368 pub struct StringOverflow(usize);
369
370 impl fmt::Display for StringOverflow {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result371 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
372 write!(f, "string size out of range: {}", self.0)
373 }
374 }
375
376 /// The result of constructing a new `JsString`.
377 pub type StringResult<'a> = Result<Handle<'a, JsString>, StringOverflow>;
378
379 impl<'a> JsResultExt<'a, JsString> for StringResult<'a> {
or_throw<'b, C: Context<'b>>(self, cx: &mut C) -> JsResult<'a, JsString>380 fn or_throw<'b, C: Context<'b>>(self, cx: &mut C) -> JsResult<'a, JsString> {
381 match self {
382 Ok(v) => Ok(v),
383 Err(e) => cx.throw_range_error(&e.to_string()),
384 }
385 }
386 }
387
388 impl Value for JsString {}
389
390 impl Managed for JsString {
to_raw(self) -> raw::Local391 fn to_raw(self) -> raw::Local {
392 self.0
393 }
394
from_raw(_: Env, h: raw::Local) -> Self395 fn from_raw(_: Env, h: raw::Local) -> Self {
396 JsString(h)
397 }
398 }
399
400 impl ValueInternal for JsString {
name() -> String401 fn name() -> String {
402 "string".to_string()
403 }
404
is_typeof<Other: Value>(env: Env, other: Other) -> bool405 fn is_typeof<Other: Value>(env: Env, other: Other) -> bool {
406 unsafe { neon_runtime::tag::is_string(env.to_raw(), other.to_raw()) }
407 }
408 }
409
410 impl JsString {
411 #[cfg(feature = "legacy-runtime")]
size(self) -> isize412 pub fn size(self) -> isize {
413 unsafe { neon_runtime::string::utf8_len(self.to_raw()) }
414 }
415
416 #[cfg(feature = "napi-1")]
size<'a, C: Context<'a>>(self, cx: &mut C) -> isize417 pub fn size<'a, C: Context<'a>>(self, cx: &mut C) -> isize {
418 let env = cx.env().to_raw();
419
420 unsafe { neon_runtime::string::utf8_len(env, self.to_raw()) }
421 }
422
423 #[cfg(feature = "legacy-runtime")]
value(self) -> String424 pub fn value(self) -> String {
425 unsafe {
426 let capacity = neon_runtime::string::utf8_len(self.to_raw());
427 let mut buffer: Vec<u8> = Vec::with_capacity(capacity as usize);
428 let p = buffer.as_mut_ptr();
429 std::mem::forget(buffer);
430 let len = neon_runtime::string::data(p, capacity, self.to_raw());
431 String::from_raw_parts(p, len as usize, capacity as usize)
432 }
433 }
434
435 #[cfg(feature = "napi-1")]
value<'a, C: Context<'a>>(self, cx: &mut C) -> String436 pub fn value<'a, C: Context<'a>>(self, cx: &mut C) -> String {
437 let env = cx.env().to_raw();
438
439 unsafe {
440 let capacity = neon_runtime::string::utf8_len(env, self.to_raw()) + 1;
441 let mut buffer: Vec<u8> = Vec::with_capacity(capacity as usize);
442 let p = buffer.as_mut_ptr();
443 std::mem::forget(buffer);
444 let len = neon_runtime::string::data(env, p, capacity, self.to_raw());
445 String::from_raw_parts(p, len as usize, capacity as usize)
446 }
447 }
448
new<'a, C: Context<'a>, S: AsRef<str>>(cx: &mut C, val: S) -> Handle<'a, JsString>449 pub fn new<'a, C: Context<'a>, S: AsRef<str>>(cx: &mut C, val: S) -> Handle<'a, JsString> {
450 JsString::try_new(cx, val).unwrap()
451 }
452
try_new<'a, C: Context<'a>, S: AsRef<str>>(cx: &mut C, val: S) -> StringResult<'a>453 pub fn try_new<'a, C: Context<'a>, S: AsRef<str>>(cx: &mut C, val: S) -> StringResult<'a> {
454 let val = val.as_ref();
455 match JsString::new_internal(cx.env(), val) {
456 Some(s) => Ok(s),
457 None => Err(StringOverflow(val.len())),
458 }
459 }
460
new_internal<'a>(env: Env, val: &str) -> Option<Handle<'a, JsString>>461 pub(crate) fn new_internal<'a>(env: Env, val: &str) -> Option<Handle<'a, JsString>> {
462 let (ptr, len) = if let Some(small) = Utf8::from(val).into_small() {
463 small.lower()
464 } else {
465 return None;
466 };
467
468 unsafe {
469 let mut local: raw::Local = std::mem::zeroed();
470 if neon_runtime::string::new(&mut local, env.to_raw(), ptr, len) {
471 Some(Handle::new_internal(JsString(local)))
472 } else {
473 None
474 }
475 }
476 }
477 }
478
479 /// A JavaScript number value.
480 #[repr(C)]
481 #[derive(Clone, Copy)]
482 pub struct JsNumber(raw::Local);
483
484 impl JsNumber {
new<'a, C: Context<'a>, T: Into<f64>>(cx: &mut C, x: T) -> Handle<'a, JsNumber>485 pub fn new<'a, C: Context<'a>, T: Into<f64>>(cx: &mut C, x: T) -> Handle<'a, JsNumber> {
486 JsNumber::new_internal(cx.env(), x.into())
487 }
488
new_internal<'a>(env: Env, v: f64) -> Handle<'a, JsNumber>489 pub(crate) fn new_internal<'a>(env: Env, v: f64) -> Handle<'a, JsNumber> {
490 unsafe {
491 let mut local: raw::Local = std::mem::zeroed();
492 neon_runtime::primitive::number(&mut local, env.to_raw(), v);
493 Handle::new_internal(JsNumber(local))
494 }
495 }
496
497 #[cfg(feature = "legacy-runtime")]
value(self) -> f64498 pub fn value(self) -> f64 {
499 unsafe { neon_runtime::primitive::number_value(self.to_raw()) }
500 }
501
502 #[cfg(feature = "napi-1")]
value<'a, C: Context<'a>>(self, cx: &mut C) -> f64503 pub fn value<'a, C: Context<'a>>(self, cx: &mut C) -> f64 {
504 let env = cx.env().to_raw();
505 unsafe { neon_runtime::primitive::number_value(env, self.to_raw()) }
506 }
507 }
508
509 impl Value for JsNumber {}
510
511 impl Managed for JsNumber {
to_raw(self) -> raw::Local512 fn to_raw(self) -> raw::Local {
513 self.0
514 }
515
from_raw(_: Env, h: raw::Local) -> Self516 fn from_raw(_: Env, h: raw::Local) -> Self {
517 JsNumber(h)
518 }
519 }
520
521 impl ValueInternal for JsNumber {
name() -> String522 fn name() -> String {
523 "number".to_string()
524 }
525
is_typeof<Other: Value>(env: Env, other: Other) -> bool526 fn is_typeof<Other: Value>(env: Env, other: Other) -> bool {
527 unsafe { neon_runtime::tag::is_number(env.to_raw(), other.to_raw()) }
528 }
529 }
530
531 /// A JavaScript object.
532 #[repr(C)]
533 #[derive(Clone, Copy)]
534 pub struct JsObject(raw::Local);
535
536 impl Value for JsObject {}
537
538 impl Managed for JsObject {
to_raw(self) -> raw::Local539 fn to_raw(self) -> raw::Local {
540 self.0
541 }
542
from_raw(_: Env, h: raw::Local) -> Self543 fn from_raw(_: Env, h: raw::Local) -> Self {
544 JsObject(h)
545 }
546 }
547
548 unsafe impl This for JsObject {
549 #[cfg(feature = "legacy-runtime")]
as_this(h: raw::Local) -> Self550 fn as_this(h: raw::Local) -> Self {
551 JsObject(h)
552 }
553
554 #[cfg(feature = "napi-1")]
as_this(_env: Env, h: raw::Local) -> Self555 fn as_this(_env: Env, h: raw::Local) -> Self {
556 JsObject(h)
557 }
558 }
559
560 impl ValueInternal for JsObject {
name() -> String561 fn name() -> String {
562 "object".to_string()
563 }
564
is_typeof<Other: Value>(env: Env, other: Other) -> bool565 fn is_typeof<Other: Value>(env: Env, other: Other) -> bool {
566 unsafe { neon_runtime::tag::is_object(env.to_raw(), other.to_raw()) }
567 }
568 }
569
570 impl Object for JsObject {}
571
572 impl JsObject {
new<'a, C: Context<'a>>(c: &mut C) -> Handle<'a, JsObject>573 pub fn new<'a, C: Context<'a>>(c: &mut C) -> Handle<'a, JsObject> {
574 JsObject::new_internal(c.env())
575 }
576
new_internal<'a>(env: Env) -> Handle<'a, JsObject>577 pub(crate) fn new_internal<'a>(env: Env) -> Handle<'a, JsObject> {
578 JsObject::build(|out| unsafe { neon_runtime::object::new(out, env.to_raw()) })
579 }
580
build<'a, F: FnOnce(&mut raw::Local)>(init: F) -> Handle<'a, JsObject>581 pub(crate) fn build<'a, F: FnOnce(&mut raw::Local)>(init: F) -> Handle<'a, JsObject> {
582 unsafe {
583 let mut local: raw::Local = std::mem::zeroed();
584 init(&mut local);
585 Handle::new_internal(JsObject(local))
586 }
587 }
588 }
589
590 /// A JavaScript array object, i.e. a value for which `Array.isArray`
591 /// would return `true`.
592 #[repr(C)]
593 #[derive(Clone, Copy)]
594 pub struct JsArray(raw::Local);
595
596 impl JsArray {
new<'a, C: Context<'a>>(cx: &mut C, len: u32) -> Handle<'a, JsArray>597 pub fn new<'a, C: Context<'a>>(cx: &mut C, len: u32) -> Handle<'a, JsArray> {
598 JsArray::new_internal(cx.env(), len)
599 }
600
new_internal<'a>(env: Env, len: u32) -> Handle<'a, JsArray>601 pub(crate) fn new_internal<'a>(env: Env, len: u32) -> Handle<'a, JsArray> {
602 unsafe {
603 let mut local: raw::Local = std::mem::zeroed();
604 neon_runtime::array::new(&mut local, env.to_raw(), len);
605 Handle::new_internal(JsArray(local))
606 }
607 }
608
to_vec<'a, C: Context<'a>>(self, cx: &mut C) -> NeonResult<Vec<Handle<'a, JsValue>>>609 pub fn to_vec<'a, C: Context<'a>>(self, cx: &mut C) -> NeonResult<Vec<Handle<'a, JsValue>>> {
610 let mut result = Vec::with_capacity(self.len_inner(cx.env()) as usize);
611 let mut i = 0;
612 loop {
613 // Since getting a property can trigger arbitrary code,
614 // we have to re-check the length on every iteration.
615 if i >= self.len_inner(cx.env()) {
616 return Ok(result);
617 }
618 result.push(self.get(cx, i)?);
619 i += 1;
620 }
621 }
622
len_inner(self, env: Env) -> u32623 fn len_inner(self, env: Env) -> u32 {
624 unsafe { neon_runtime::array::len(env.to_raw(), self.to_raw()) }
625 }
626
627 #[cfg(feature = "legacy-runtime")]
len(self) -> u32628 pub fn len(self) -> u32 {
629 self.len_inner(Env::current())
630 }
631
632 #[cfg(feature = "napi-1")]
len<'a, C: Context<'a>>(self, cx: &mut C) -> u32633 pub fn len<'a, C: Context<'a>>(self, cx: &mut C) -> u32 {
634 self.len_inner(cx.env())
635 }
636
637 #[cfg(feature = "legacy-runtime")]
is_empty(self) -> bool638 pub fn is_empty(self) -> bool {
639 self.len() == 0
640 }
641
642 #[cfg(feature = "napi-1")]
is_empty<'a, C: Context<'a>>(self, cx: &mut C) -> bool643 pub fn is_empty<'a, C: Context<'a>>(self, cx: &mut C) -> bool {
644 self.len(cx) == 0
645 }
646 }
647
648 impl Value for JsArray {}
649
650 impl Managed for JsArray {
to_raw(self) -> raw::Local651 fn to_raw(self) -> raw::Local {
652 self.0
653 }
654
from_raw(_: Env, h: raw::Local) -> Self655 fn from_raw(_: Env, h: raw::Local) -> Self {
656 JsArray(h)
657 }
658 }
659
660 impl ValueInternal for JsArray {
name() -> String661 fn name() -> String {
662 "Array".to_string()
663 }
664
is_typeof<Other: Value>(env: Env, other: Other) -> bool665 fn is_typeof<Other: Value>(env: Env, other: Other) -> bool {
666 unsafe { neon_runtime::tag::is_array(env.to_raw(), other.to_raw()) }
667 }
668 }
669
670 impl Object for JsArray {}
671
672 /// A JavaScript function object.
673 #[repr(C)]
674 #[derive(Clone, Copy)]
675 pub struct JsFunction<T: Object = JsObject> {
676 raw: raw::Local,
677 marker: PhantomData<T>,
678 }
679
680 impl<T: Object> Object for JsFunction<T> {}
681
682 // Maximum number of function arguments in V8.
683 const V8_ARGC_LIMIT: usize = 65535;
684
prepare_call<'a, 'b, C: Context<'a>, A>( cx: &mut C, args: &mut [Handle<'b, A>], ) -> NeonResult<(i32, *mut c_void)> where A: Value + 'b,685 unsafe fn prepare_call<'a, 'b, C: Context<'a>, A>(
686 cx: &mut C,
687 args: &mut [Handle<'b, A>],
688 ) -> NeonResult<(i32, *mut c_void)>
689 where
690 A: Value + 'b,
691 {
692 let argv = args.as_mut_ptr();
693 let argc = args.len();
694 if argc > V8_ARGC_LIMIT {
695 return cx.throw_range_error("too many arguments");
696 }
697 Ok((argc as i32, argv as *mut c_void))
698 }
699
700 impl JsFunction {
new<'a, C, U>( cx: &mut C, f: fn(FunctionContext) -> JsResult<U>, ) -> JsResult<'a, JsFunction> where C: Context<'a>, U: Value,701 pub fn new<'a, C, U>(
702 cx: &mut C,
703 f: fn(FunctionContext) -> JsResult<U>,
704 ) -> JsResult<'a, JsFunction>
705 where
706 C: Context<'a>,
707 U: Value,
708 {
709 build(cx.env(), |out| {
710 let env = cx.env().to_raw();
711 unsafe {
712 let callback = FunctionCallback(f).into_c_callback();
713 neon_runtime::fun::new(out, env, callback)
714 }
715 })
716 }
717 }
718
719 impl<CL: Object> JsFunction<CL> {
call<'a, 'b, C: Context<'a>, T, A, AS>( self, cx: &mut C, this: Handle<'b, T>, args: AS, ) -> JsResult<'a, JsValue> where T: Value, A: Value + 'b, AS: IntoIterator<Item = Handle<'b, A>>,720 pub fn call<'a, 'b, C: Context<'a>, T, A, AS>(
721 self,
722 cx: &mut C,
723 this: Handle<'b, T>,
724 args: AS,
725 ) -> JsResult<'a, JsValue>
726 where
727 T: Value,
728 A: Value + 'b,
729 AS: IntoIterator<Item = Handle<'b, A>>,
730 {
731 let mut args = args.into_iter().collect::<SmallVec<[_; 8]>>();
732 let (argc, argv) = unsafe { prepare_call(cx, &mut args) }?;
733 let env = cx.env().to_raw();
734 build(cx.env(), |out| unsafe {
735 neon_runtime::fun::call(out, env, self.to_raw(), this.to_raw(), argc, argv)
736 })
737 }
738
construct<'a, 'b, C: Context<'a>, A, AS>(self, cx: &mut C, args: AS) -> JsResult<'a, CL> where A: Value + 'b, AS: IntoIterator<Item = Handle<'b, A>>,739 pub fn construct<'a, 'b, C: Context<'a>, A, AS>(self, cx: &mut C, args: AS) -> JsResult<'a, CL>
740 where
741 A: Value + 'b,
742 AS: IntoIterator<Item = Handle<'b, A>>,
743 {
744 let mut args = args.into_iter().collect::<SmallVec<[_; 8]>>();
745 let (argc, argv) = unsafe { prepare_call(cx, &mut args) }?;
746 let env = cx.env().to_raw();
747 build(cx.env(), |out| unsafe {
748 neon_runtime::fun::construct(out, env, self.to_raw(), argc, argv)
749 })
750 }
751 }
752
753 impl<T: Object> Value for JsFunction<T> {}
754
755 impl<T: Object> Managed for JsFunction<T> {
to_raw(self) -> raw::Local756 fn to_raw(self) -> raw::Local {
757 self.raw
758 }
759
from_raw(_: Env, h: raw::Local) -> Self760 fn from_raw(_: Env, h: raw::Local) -> Self {
761 JsFunction {
762 raw: h,
763 marker: PhantomData,
764 }
765 }
766 }
767
768 impl<T: Object> ValueInternal for JsFunction<T> {
name() -> String769 fn name() -> String {
770 "function".to_string()
771 }
772
is_typeof<Other: Value>(env: Env, other: Other) -> bool773 fn is_typeof<Other: Value>(env: Env, other: Other) -> bool {
774 unsafe { neon_runtime::tag::is_function(env.to_raw(), other.to_raw()) }
775 }
776 }
777