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