nanvm_lib/vm/any/mod.rs
1mod add;
2mod bitand;
3mod bitor;
4mod bitxor;
5mod call;
6mod conditional;
7mod div;
8mod dot;
9mod from;
10mod get_iterator;
11#[cfg(test)]
12mod inspection_test;
13mod instanceof_;
14mod mul;
15mod neg;
16mod not;
17mod object_spread;
18mod option_call;
19mod partial_eq;
20mod relational;
21mod rem;
22mod shl;
23mod shr;
24mod sub;
25mod to_json;
26mod typeof_;
27
28pub mod to_any;
29
30pub use instanceof_::Constructor;
31pub use to_json::JsonError;
32
33use crate::vm::{
34 IVm, Number, String, ToAny, Unpacked, bigint_coercion::BigIntCoercion, dispatch::Dispatch,
35 member_access::non_nullish_receiver, nullish::Nullish, number_coercion::NumberCoercion,
36 numeric::Numeric, numeric_coercion, primitive_coercion::ToPrimitivePreferredType,
37 primitive_dispatch::ViaPrimitive, string_coercion::StringCoercion,
38};
39
40/// ```
41/// use nanvm_lib::{
42/// vm::{Any, IVm, ToAny, String, Array, ToArray, ToObject, Object, Nullish, Number, BigInt},
43/// naive::Naive
44/// };
45/// fn any_test<A: IVm>() {
46/// let b: Any<A> = true.to_any();
47/// let n: Any<A> = Nullish::Null.to_any();
48/// let n: Any<A> = Number::from(42.0).to_any();
49/// let c: String<A> = "Hello".into();
50/// let m: Any<A> = c.to_any();
51/// let a: Array<A> = [].to_array();
52/// let o: Any<A> = a.to_any();
53/// let x: Object<A> = [].to_object();
54/// let p: Any<A> = x.to_any();
55/// let u: BigInt<A> = 123u64.into();
56/// let q: Any<A> = u.to_any();
57/// }
58///
59/// any_test::<Naive>();
60/// ```
61#[derive(Clone)]
62pub struct Any<A: IVm>(A);
63
64impl<A: IVm> Any<A> {
65 /// `undefined`.
66 pub fn undefined() -> Self {
67 Nullish::Undefined.to_any()
68 }
69
70 /// Whether `self` is `undefined` or `null`, inspecting its borrowed
71 /// representation. A caller that already holds the `Unpacked` asks it
72 /// instead and keeps its single conversion.
73 pub fn is_nullish(&self) -> bool {
74 self.with_unpacked(Unpacked::is_nullish)
75 }
76
77 /// `None` for `undefined`, `Some(self)` for every other value, `null`
78 /// included: the argument an optional parameter was passed, if it was
79 /// passed one. Borrows to inspect, then returns the owned value.
80 pub fn defined(self) -> Option<Self> {
81 (!self.with_unpacked(Unpacked::is_undefined)).then_some(self)
82 }
83
84 /// Unary plus is nothing but coercion to number.
85 /// We use unary_plus as ECMAScript unary plus operator, and we use coerce_to_number for
86 /// internals in places where ECMAScript's abstract function ToNumber is needed, and also when
87 /// we need Result<Number, Any<A>> result type; here unary_plus returns Result<Any<A>, Any<A>> to
88 /// match public API type of unary plus operator.
89 /// <https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Unary_plus>
90 /// <https://tc39.es/ecma262/#sec-unary-plus-operator>
91 pub fn unary_plus(self) -> Result<Any<A>, Any<A>> {
92 self.to_number().map(ToAny::to_any)
93 }
94
95 /// `Number(value)`: `ToNumeric` of the value, a bigint converted to the
96 /// nearest number and anything else as `unary_plus` has it. They differ in
97 /// the bigint alone: `+1n` throws, `Number(1n)` is `1`.
98 /// <https://tc39.es/ecma262/#sec-number-constructor-number-value>
99 pub fn number(self) -> Result<Any<A>, Any<A>> {
100 Ok(match self.to_numeric()? {
101 Numeric::Number(n) => n,
102 Numeric::BigInt(b) => b.to_number(),
103 }
104 .to_any())
105 }
106
107 /// `BigInt(value)`: ordinary primitive conversion with the number hint,
108 /// then an exact integer conversion. This is an operator, not a value.
109 /// <https://tc39.es/ecma262/#sec-bigint-constructor>
110 pub fn bigint(self) -> Result<Any<A>, Any<A>> {
111 self.dispatch(ViaPrimitive(BigIntCoercion))
112 .map(ToAny::to_any)
113 }
114
115 /// `**`. Not a `core::ops` trait — Rust has no operator for
116 /// exponentiation, so this is a plain method, the same as `unary_plus`.
117 pub fn pow(self, rhs: Self) -> Result<Self, Self> {
118 self.numeric_op(rhs, Numeric::pow)
119 }
120
121 /// `~`. Not a `core::ops` trait — `Not` is already claimed by this
122 /// type's own logical `!` (`any/not.rs`) — so this is a plain method,
123 /// the same as `unary_plus`/`pow`.
124 /// <https://tc39.es/ecma262/#sec-bitwise-not-operator>
125 pub fn bitwise_not(self) -> Result<Self, Self> {
126 Ok(Unpacked::from(self.to_numeric()?.bitwise_not()?).into())
127 }
128
129 /// `>>>`. Not a `core::ops` trait — Rust has no unsigned-right-shift
130 /// operator — so this is a plain method, the same as `pow`/`bitwise_not`.
131 /// <https://tc39.es/ecma262/#sec-unsigned-right-shift-operator>
132 pub fn unsigned_right_shift(self, rhs: Self) -> Result<Self, Self> {
133 self.numeric_op(rhs, Numeric::unsigned_right_shift)
134 }
135
136 /// The shape every numeric binary operator shares: `ToNumeric` of the
137 /// left operand, then of the right, then `op` on the two (`+` once it
138 /// has ruled out a string concatenation).
139 /// <https://tc39.es/ecma262/#sec-applystringornumericbinaryoperator>
140 fn numeric_op(
141 self,
142 rhs: Self,
143 op: impl FnOnce(Numeric<A>, Numeric<A>) -> Result<Numeric<A>, Self>,
144 ) -> Result<Self, Self> {
145 Ok(Unpacked::from(op(self.to_numeric()?, rhs.to_numeric()?)?).into())
146 }
147
148 /// The `entry` helper, `(a, b) => { const x =
149 /// Object.getOwnPropertyDescriptor(a, b); return x?.enumerable ? x.value :
150 /// undefined; }`, as a method: the value of the enumerable own property
151 /// the key names — an `Object`'s field (`Object::own_property`), an
152 /// `Array`'s element or a `String`'s code unit by its canonical index
153 /// string (`Array::entry`, `String::entry`) — and `undefined` for none,
154 /// an entry holding `undefined` included, which [`Any::has_entry`] tells
155 /// apart, and for nothing a value owns without enumerating it — never a
156 /// `length`, and nothing of a function. No getter invocation and no
157 /// prototype chain (`nanvm-lib` objects have no `__proto__` to walk in
158 /// the first place). Not a `core::ops` trait — no Rust operator fits a
159 /// keyed property lookup — so this is a plain method, the same as
160 /// `pow`/`bitwise_not`/`unsigned_right_shift`.
161 ///
162 /// The receiver is checked before the key is converted
163 /// (`member_access::non_nullish_receiver`), so a nullish receiver
164 /// throws regardless of what the key is, and a key whose conversion
165 /// throws does so under a receiver that is not. The key converts as
166 /// `ToPropertyKey` converts it, which is `to_string` here, the language
167 /// having no symbols: `0` and `"0"` name one entry, and an object
168 /// converts through its own `toString`. A `Number`, `Boolean` or
169 /// `BigInt` receiver owns no entry and answers `undefined`, as every
170 /// absent key does.
171 pub fn entry(self, key: Self) -> Result<Self, Self> {
172 Ok(self.own_entry(key)?.unwrap_or_else(Any::undefined))
173 }
174
175 /// The `hasEntry` helper: whether the enumerable own property `key`
176 /// names is there, `true` for an entry holding `undefined`, which
177 /// [`Any::entry`] reads as none, the receiver and the key treated as
178 /// `entry` treats them, so a nullish receiver throws and a key converts
179 /// first (`spec/property-access/entries/README.md`).
180 pub fn has_entry(self, key: Self) -> Result<bool, Self> {
181 Ok(self.own_entry(key)?.is_some())
182 }
183
184 /// The enumerable own property `key` names, or `None`: what `entry` and
185 /// `has_entry` share, the receiver checked before the key is converted.
186 fn own_entry(self, key: Self) -> Result<Option<Self>, Self> {
187 let unpacked = non_nullish_receiver(self)?;
188 let key = key.to_string()?;
189 Ok(match unpacked {
190 Unpacked::Object(o) => o.own_property(&key),
191 Unpacked::Array(a) => a.entry(&key),
192 Unpacked::String(s) => s.entry(&key),
193 _ => None,
194 })
195 }
196
197 /// Same as `Number.isNaN` in ECMAScript.
198 pub fn is_nan(&self) -> bool {
199 self.with_unpacked(|v| matches!(v, Unpacked::Number(n) if n.is_nan()))
200 }
201
202 pub fn to_string(self) -> Result<String<A>, Any<A>> {
203 self.dispatch(ViaPrimitive(StringCoercion))
204 }
205
206 /// `ToString` where the algorithm discards the result: its throws still
207 /// happen, and an object with its own `toString` is still refused, but a
208 /// function's text, which cannot throw and is never read here, is not
209 /// needed — so `[].join(f)` answers `""`, as it does in JavaScript
210 /// (`nanvm/lib/todo/to-primitive.md`, Stage 1). An array converts
211 /// through its elements alone, since it owns no `toString`, so a
212 /// function inside one is skipped too: `[].join([f])` is `""`.
213 pub fn to_string_unused(self) -> Result<(), Any<A>> {
214 match self.clone().into() {
215 Unpacked::Function(_) => Ok(()),
216 Unpacked::Array(a) => a.into_iter().try_for_each(Self::to_string_unused),
217 _ => self.to_string().map(|_| ()),
218 }
219 }
220
221 pub fn to_number(self) -> Result<Number, Any<A>> {
222 self.dispatch(ViaPrimitive(NumberCoercion))
223 }
224
225 /// Never fails, unlike `to_number`/`to_string`/`to_numeric` — `ToBoolean`
226 /// inspects the operand's type directly and never calls `ToPrimitive`.
227 pub fn to_boolean(&self) -> bool {
228 self.with_unpacked(Unpacked::to_boolean)
229 }
230
231 pub fn to_numeric(self) -> Result<Numeric<A>, Any<A>> {
232 // https://tc39.es/ecma262/#sec-tonumeric
233 // A function's primitive, its text, is a string and not a bigint, so
234 // its `ToNumber` answers without converting it.
235 if self.with_unpacked(|v| matches!(v, Unpacked::Function(_))) {
236 return Ok(Numeric::Number(self.to_number()?));
237 }
238 numeric_coercion::to_numeric(self.to_primitive(Some(ToPrimitivePreferredType::Number))?)
239 }
240
241 fn dispatch<T: Dispatch<A>>(self, o: T) -> T::Result {
242 self.0.to_unpacked().dispatch(o)
243 }
244
245 pub(crate) fn with_unpacked<R>(&self, f: impl FnOnce(&Unpacked<A>) -> R) -> R {
246 self.0.with_unpacked(f)
247 }
248}
249
250#[cfg(test)]
251mod tests {
252 use crate::{
253 naive::Naive,
254 vm::{Any, Nullish, ToAny, ToArray, ToObject, Unpacked, error},
255 };
256
257 type A = Naive;
258
259 /// The receiver is checked before the key is converted (see `entry`'s
260 /// own doc comment), so a nullish receiver throws the nullish
261 /// `TypeError` whatever the key is — a `throws` expectation alone
262 /// could not tell the two orders apart, so this compares the error.
263 #[test]
264 fn entry_nullish_receiver_outranks_the_key() {
265 let key = (1f64).to_any::<A>();
266 assert_eq!(
267 Nullish::Null.to_any::<A>().entry(key.clone()),
268 Err(error::nullish_to_object())
269 );
270 assert_eq!(Any::undefined().entry(key), Err(error::nullish_to_object()));
271 }
272
273 /// The key converts as `ToPropertyKey` converts it: a number names the
274 /// entry its decimal spelling names, `-0` the entry `"0"` does.
275 #[test]
276 fn entry_converts_the_key() {
277 let o = [("1".into(), 42.0.to_any())].to_object().to_any::<A>();
278 assert_eq!(o.clone().entry("1".into()), Ok(42.0.to_any()));
279 assert_eq!(o.clone().entry(1.0.to_any()), Ok(42.0.to_any()));
280 assert_eq!(o.entry("01".into()), Ok(Any::undefined()));
281 let z = [("0".into(), 7.0.to_any())].to_object().to_any::<A>();
282 assert_eq!(z.entry((-0.0f64).to_any()), Ok(7.0.to_any()));
283 }
284
285 /// `has_entry` is the presence beside the value: `true` for an entry holding
286 /// `undefined`, `false` for none, a `length` or anything of a function,
287 /// with `entry`'s throws for a nullish receiver and a key whose
288 /// conversion throws.
289 #[test]
290 fn has_entry_is_the_presence() {
291 let o = [("u".into(), Any::undefined()), ("1".into(), 42.0.to_any())]
292 .to_object()
293 .to_any::<A>();
294 assert_eq!(o.clone().has_entry("u".into()), Ok(true));
295 assert_eq!(o.clone().has_entry("v".into()), Ok(false));
296 assert_eq!(o.clone().has_entry(1.0.to_any()), Ok(true));
297 assert_eq!(o.has_entry("toString".into()), Ok(false));
298 let a = [7.0.to_any()].to_array().to_any::<A>();
299 assert_eq!(a.clone().has_entry("0".into()), Ok(true));
300 assert_eq!(a.has_entry("length".into()), Ok(false));
301 let s: Any<A> = "ab".into();
302 assert_eq!(s.clone().has_entry(1.0.to_any()), Ok(true));
303 assert_eq!(s.has_entry("length".into()), Ok(false));
304 assert_eq!(5.0.to_any::<A>().has_entry("0".into()), Ok(false));
305 assert_eq!(
306 Nullish::Null.to_any::<A>().has_entry("x".into()),
307 Err(error::nullish_to_object())
308 );
309 }
310
311 /// An entry holding `undefined` reads as none does, `undefined`, and
312 /// `has_entry` is what tells the two apart.
313 #[test]
314 fn entry_holds_undefined() {
315 let o = [("u".into(), Any::undefined())].to_object().to_any::<A>();
316 assert_eq!(o.clone().entry("u".into()), Ok(Any::undefined()));
317 assert_eq!(o.clone().entry("v".into()), Ok(Any::undefined()));
318 assert_eq!(o.clone().has_entry("u".into()), Ok(true));
319 assert_eq!(o.has_entry("v".into()), Ok(false));
320 }
321
322 /// An array's and a string's elements are entries and their `length`
323 /// is not; a function has none, and neither has any other primitive.
324 #[test]
325 fn entry_reads_elements_and_no_length() {
326 let a = [7.0.to_any(), 8.0.to_any()].to_array().to_any::<A>();
327 assert_eq!(a.clone().entry(1.0.to_any()), Ok(8.0.to_any()));
328 assert_eq!(a.clone().entry("1".into()), Ok(8.0.to_any()));
329 assert_eq!(a.clone().entry("2".into()), Ok(Any::undefined()));
330 assert_eq!(a.entry("length".into()), Ok(Any::undefined()));
331 let s: Any<A> = "ab".into();
332 assert_eq!(s.clone().entry(1.0.to_any()), Ok("b".into()));
333 assert_eq!(s.entry("length".into()), Ok(Any::undefined()));
334 assert_eq!(true.to_any::<A>().entry("0".into()), Ok(Any::undefined()));
335 assert_eq!(5.0.to_any::<A>().entry("0".into()), Ok(Any::undefined()));
336 }
337
338 /// `undefined` and `null` are nullish, and nothing else is: not the
339 /// falsy values, which `??` returns unchanged, nor an empty container.
340 #[test]
341 fn is_nullish() {
342 assert_eq!(Any::<A>::undefined(), Nullish::Undefined.to_any());
343 assert!(Any::<A>::undefined().is_nullish());
344 assert!(Nullish::Null.to_any::<A>().is_nullish());
345 for v in [
346 false.to_any::<A>(),
347 0.0.to_any(),
348 f64::NAN.to_any(),
349 "".into(),
350 [].to_array().to_any(),
351 [].to_object().to_any(),
352 ] {
353 assert!(!v.is_nullish());
354 }
355 }
356
357 /// `undefined` alone is absent: `null` and the falsy values are
358 /// arguments that were passed, and come back unchanged.
359 #[test]
360 fn defined() {
361 assert!(Unpacked::from(Any::<A>::undefined()).is_undefined());
362 assert!(!Unpacked::from(Nullish::Null.to_any::<A>()).is_undefined());
363 assert_eq!(Any::<A>::undefined().defined(), None);
364 for v in [
365 Nullish::Null.to_any::<A>(),
366 false.to_any(),
367 0.0.to_any(),
368 "".into(),
369 [].to_array().to_any(),
370 [].to_object().to_any(),
371 ] {
372 assert!(!Unpacked::from(v.clone()).is_undefined());
373 assert_eq!(v.clone().defined(), Some(v));
374 }
375 }
376}