boa/builtins/object/mod.rs
1//! This module implements the global `Object` object.
2//!
3//! The `Object` class represents one of JavaScript's data types.
4//!
5//! It is used to store various keyed collections and more complex entities.
6//! Objects can be created using the `Object()` constructor or the
7//! object initializer / literal syntax.
8//!
9//! More information:
10//! - [ECMAScript reference][spec]
11//! - [MDN documentation][mdn]
12//!
13//! [spec]: https://tc39.es/ecma262/#sec-objects
14//! [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object
15
16use crate::{
17 builtins::{BuiltIn, JsArgs},
18 context::StandardObjects,
19 object::{
20 internal_methods::get_prototype_from_constructor, ConstructorBuilder, IntegrityLevel,
21 JsObject, Object as BuiltinObject, ObjectData, ObjectInitializer, ObjectKind,
22 },
23 property::{Attribute, DescriptorKind, PropertyDescriptor, PropertyKey, PropertyNameKind},
24 symbol::WellKnownSymbols,
25 value::{JsValue, Type},
26 BoaProfiler, Context, JsResult,
27};
28
29use super::Array;
30
31pub mod for_in_iterator;
32#[cfg(test)]
33mod tests;
34
35/// The global JavaScript object.
36#[derive(Debug, Clone, Copy)]
37pub struct Object;
38
39impl BuiltIn for Object {
40 const NAME: &'static str = "Object";
41
42 fn attribute() -> Attribute {
43 Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE
44 }
45
46 fn init(context: &mut Context) -> (&'static str, JsValue, Attribute) {
47 let _timer = BoaProfiler::global().start_event(Self::NAME, "init");
48
49 let object = ConstructorBuilder::with_standard_object(
50 context,
51 Self::constructor,
52 context.standard_objects().object_object().clone(),
53 )
54 .name(Self::NAME)
55 .length(Self::LENGTH)
56 .inherit(JsValue::null())
57 .method(Self::has_own_property, "hasOwnProperty", 0)
58 .method(Self::property_is_enumerable, "propertyIsEnumerable", 0)
59 .method(Self::to_string, "toString", 0)
60 .method(Self::value_of, "valueOf", 0)
61 .method(Self::is_prototype_of, "isPrototypeOf", 0)
62 .static_method(Self::create, "create", 2)
63 .static_method(Self::set_prototype_of, "setPrototypeOf", 2)
64 .static_method(Self::get_prototype_of, "getPrototypeOf", 1)
65 .static_method(Self::define_property, "defineProperty", 3)
66 .static_method(Self::define_properties, "defineProperties", 2)
67 .static_method(Self::assign, "assign", 2)
68 .static_method(Self::is, "is", 2)
69 .static_method(Self::keys, "keys", 1)
70 .static_method(Self::values, "values", 1)
71 .static_method(Self::entries, "entries", 1)
72 .static_method(Self::seal, "seal", 1)
73 .static_method(Self::is_sealed, "isSealed", 1)
74 .static_method(Self::freeze, "freeze", 1)
75 .static_method(Self::is_frozen, "isFrozen", 1)
76 .static_method(Self::prevent_extensions, "preventExtensions", 1)
77 .static_method(Self::is_extensible, "isExtensible", 1)
78 .static_method(
79 Self::get_own_property_descriptor,
80 "getOwnPropertyDescriptor",
81 2,
82 )
83 .static_method(
84 Self::get_own_property_descriptors,
85 "getOwnPropertyDescriptors",
86 1,
87 )
88 .build();
89
90 (Self::NAME, object.into(), Self::attribute())
91 }
92}
93
94impl Object {
95 const LENGTH: usize = 1;
96
97 fn constructor(
98 new_target: &JsValue,
99 args: &[JsValue],
100 context: &mut Context,
101 ) -> JsResult<JsValue> {
102 if !new_target.is_undefined() {
103 let prototype = get_prototype_from_constructor(
104 new_target,
105 StandardObjects::object_object,
106 context,
107 )?;
108 let object = JsValue::new_object(context);
109
110 object
111 .as_object()
112 .expect("this should be an object")
113 .set_prototype_instance(prototype.into());
114 return Ok(object);
115 }
116 if let Some(arg) = args.get(0) {
117 if !arg.is_null_or_undefined() {
118 return Ok(arg.to_object(context)?.into());
119 }
120 }
121 Ok(JsValue::new_object(context))
122 }
123
124 /// `Object.create( proto, [propertiesObject] )`
125 ///
126 /// Creates a new object from the provided prototype.
127 ///
128 /// More information:
129 /// - [ECMAScript reference][spec]
130 /// - [MDN documentation][mdn]
131 ///
132 /// [spec]: https://tc39.es/ecma262/#sec-object.create
133 /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/create
134 pub fn create(_: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {
135 let prototype = args.get_or_undefined(0);
136 let properties = args.get_or_undefined(1);
137
138 let obj = match prototype {
139 JsValue::Object(_) | JsValue::Null => JsObject::new(BuiltinObject::with_prototype(
140 prototype.clone(),
141 ObjectData::ordinary(),
142 )),
143 _ => {
144 return context.throw_type_error(format!(
145 "Object prototype may only be an Object or null: {}",
146 prototype.display()
147 ))
148 }
149 };
150
151 if !properties.is_undefined() {
152 object_define_properties(&obj, properties.clone(), context)?;
153 return Ok(obj.into());
154 }
155
156 Ok(obj.into())
157 }
158
159 /// `Object.getOwnPropertyDescriptor( object, property )`
160 ///
161 /// Returns an object describing the configuration of a specific property on a given object.
162 ///
163 /// More information:
164 /// - [ECMAScript reference][spec]
165 /// - [MDN documentation][mdn]
166 ///
167 /// [spec]: https://tc39.es/ecma262/#sec-object.getownpropertydescriptor
168 /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/getOwnPropertyDescriptor
169 pub fn get_own_property_descriptor(
170 _: &JsValue,
171 args: &[JsValue],
172 context: &mut Context,
173 ) -> JsResult<JsValue> {
174 let object = args.get_or_undefined(0).to_object(context)?;
175 if let Some(key) = args.get(1) {
176 let key = key.to_property_key(context)?;
177
178 if let Some(desc) = object.__get_own_property__(&key, context)? {
179 return Ok(Self::from_property_descriptor(desc, context));
180 }
181 }
182
183 Ok(JsValue::undefined())
184 }
185
186 /// `Object.getOwnPropertyDescriptors( object )`
187 ///
188 /// Returns all own property descriptors of a given object.
189 ///
190 /// More information:
191 /// - [ECMAScript reference][spec]
192 /// - [MDN documentation][mdn]
193 ///
194 /// [spec]: https://tc39.es/ecma262/#sec-object.getownpropertydescriptors
195 /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/getOwnPropertyDescriptors
196 pub fn get_own_property_descriptors(
197 _: &JsValue,
198 args: &[JsValue],
199 context: &mut Context,
200 ) -> JsResult<JsValue> {
201 let object = args
202 .get(0)
203 .unwrap_or(&JsValue::undefined())
204 .to_object(context)?;
205 let descriptors = context.construct_object();
206
207 for key in object.borrow().properties().keys() {
208 let descriptor = {
209 let desc = object
210 .__get_own_property__(&key, context)?
211 .expect("Expected property to be on object.");
212 Self::from_property_descriptor(desc, context)
213 };
214
215 if !descriptor.is_undefined() {
216 descriptors.borrow_mut().insert(
217 key,
218 PropertyDescriptor::builder()
219 .value(descriptor)
220 .writable(true)
221 .enumerable(true)
222 .configurable(true),
223 );
224 }
225 }
226
227 Ok(JsValue::Object(descriptors))
228 }
229
230 /// The abstract operation `FromPropertyDescriptor`.
231 ///
232 /// [ECMAScript reference][spec]
233 ///
234 /// [spec]: https://tc39.es/ecma262/#sec-frompropertydescriptor
235 fn from_property_descriptor(desc: PropertyDescriptor, context: &mut Context) -> JsValue {
236 let mut descriptor = ObjectInitializer::new(context);
237
238 // TODO: use CreateDataPropertyOrThrow
239
240 match desc.kind() {
241 DescriptorKind::Data { value, writable } => {
242 if let Some(value) = value {
243 descriptor.property("value", value.clone(), Attribute::all());
244 }
245 if let Some(writable) = writable {
246 descriptor.property("writable", *writable, Attribute::all());
247 }
248 }
249 DescriptorKind::Accessor { get, set } => {
250 if let Some(get) = get {
251 descriptor.property("get", get.clone(), Attribute::all());
252 }
253 if let Some(set) = set {
254 descriptor.property("set", set.clone(), Attribute::all());
255 }
256 }
257 _ => {}
258 }
259
260 if let Some(enumerable) = desc.enumerable() {
261 descriptor.property("enumerable", enumerable, Attribute::all());
262 }
263
264 if let Some(configurable) = desc.configurable() {
265 descriptor.property("configurable", configurable, Attribute::all());
266 }
267
268 descriptor.build().into()
269 }
270
271 /// Uses the SameValue algorithm to check equality of objects
272 pub fn is(_: &JsValue, args: &[JsValue], _: &mut Context) -> JsResult<JsValue> {
273 let x = args.get_or_undefined(0);
274 let y = args.get_or_undefined(1);
275
276 Ok(JsValue::same_value(x, y).into())
277 }
278
279 /// Get the `prototype` of an object.
280 pub fn get_prototype_of(_: &JsValue, args: &[JsValue], ctx: &mut Context) -> JsResult<JsValue> {
281 if args.is_empty() {
282 return ctx.throw_type_error(
283 "Object.getPrototypeOf: At least 1 argument required, but only 0 passed",
284 );
285 }
286
287 // 1. Let obj be ? ToObject(O).
288 let obj = args[0].clone().to_object(ctx)?;
289
290 // 2. Return ? obj.[[GetPrototypeOf]]().
291 Ok(obj.prototype_instance())
292 }
293
294 /// Set the `prototype` of an object.
295 ///
296 /// [More information][spec]
297 ///
298 /// [spec]: https://tc39.es/ecma262/#sec-object.setprototypeof
299 pub fn set_prototype_of(_: &JsValue, args: &[JsValue], ctx: &mut Context) -> JsResult<JsValue> {
300 if args.len() < 2 {
301 return ctx.throw_type_error(format!(
302 "Object.setPrototypeOf: At least 2 arguments required, but only {} passed",
303 args.len()
304 ));
305 }
306
307 // 1. Set O to ? RequireObjectCoercible(O).
308 let obj = args
309 .get(0)
310 .cloned()
311 .unwrap_or_default()
312 .require_object_coercible(ctx)?
313 .clone();
314
315 // 2. If Type(proto) is neither Object nor Null, throw a TypeError exception.
316 let proto = args.get_or_undefined(1);
317 if !matches!(proto.get_type(), Type::Object | Type::Null) {
318 return ctx.throw_type_error(format!(
319 "expected an object or null, got {}",
320 proto.type_of()
321 ));
322 }
323
324 // 3. If Type(O) is not Object, return O.
325 if !obj.is_object() {
326 return Ok(obj);
327 }
328
329 // 4. Let status be ? O.[[SetPrototypeOf]](proto).
330 let status = obj
331 .as_object()
332 .expect("obj was not an object")
333 .__set_prototype_of__(proto.clone(), ctx)?;
334
335 // 5. If status is false, throw a TypeError exception.
336 if !status {
337 return ctx.throw_type_error("can't set prototype of this object");
338 }
339
340 // 6. Return O.
341 Ok(obj)
342 }
343
344 /// `Object.prototype.isPrototypeOf( proto )`
345 ///
346 /// Check whether or not an object exists within another object's prototype chain.
347 ///
348 /// More information:
349 /// - [ECMAScript reference][spec]
350 /// - [MDN documentation][mdn]
351 ///
352 /// [spec]: https://tc39.es/ecma262/#sec-object.prototype.isprototypeof
353 /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/isPrototypeOf
354 pub fn is_prototype_of(
355 this: &JsValue,
356 args: &[JsValue],
357 context: &mut Context,
358 ) -> JsResult<JsValue> {
359 let v = args.get_or_undefined(0);
360 if !v.is_object() {
361 return Ok(JsValue::new(false));
362 }
363 let mut v = v.clone();
364 let o = JsValue::new(this.to_object(context)?);
365 loop {
366 v = Self::get_prototype_of(this, &[v], context)?;
367 if v.is_null() {
368 return Ok(JsValue::new(false));
369 }
370 if JsValue::same_value(&o, &v) {
371 return Ok(JsValue::new(true));
372 }
373 }
374 }
375
376 /// Define a property in an object
377 pub fn define_property(
378 _: &JsValue,
379 args: &[JsValue],
380 context: &mut Context,
381 ) -> JsResult<JsValue> {
382 let object = args.get_or_undefined(0);
383 if let Some(object) = object.as_object() {
384 let key = args
385 .get(1)
386 .unwrap_or(&JsValue::Undefined)
387 .to_property_key(context)?;
388 let desc = args
389 .get(2)
390 .unwrap_or(&JsValue::Undefined)
391 .to_property_descriptor(context)?;
392
393 object.define_property_or_throw(key, desc, context)?;
394
395 Ok(object.into())
396 } else {
397 context.throw_type_error("Object.defineProperty called on non-object")
398 }
399 }
400
401 /// `Object.defineProperties( proto, [propertiesObject] )`
402 ///
403 /// Creates or update own properties to the object
404 ///
405 /// More information:
406 /// - [ECMAScript reference][spec]
407 /// - [MDN documentation][mdn]
408 ///
409 /// [spec]: https://tc39.es/ecma262/#sec-object.defineproperties
410 /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperties
411 pub fn define_properties(
412 _: &JsValue,
413 args: &[JsValue],
414 context: &mut Context,
415 ) -> JsResult<JsValue> {
416 let arg = args.get_or_undefined(0);
417 let arg_obj = arg.as_object();
418 if let Some(obj) = arg_obj {
419 let props = args.get_or_undefined(1);
420 object_define_properties(&obj, props.clone(), context)?;
421 Ok(arg.clone())
422 } else {
423 context.throw_type_error("Expected an object")
424 }
425 }
426
427 /// `Object.prototype.valueOf()`
428 ///
429 /// More information:
430 /// - [ECMAScript reference][spec]
431 /// - [MDN documentation][mdn]
432 ///
433 /// [spec]: https://tc39.es/ecma262/#sec-object.prototype.valueof
434 /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/valueOf
435 pub fn value_of(this: &JsValue, _: &[JsValue], context: &mut Context) -> JsResult<JsValue> {
436 // 1. Return ? ToObject(this value).
437 Ok(this.to_object(context)?.into())
438 }
439
440 /// `Object.prototype.toString()`
441 ///
442 /// This method returns a string representing the object.
443 ///
444 /// More information:
445 /// - [ECMAScript reference][spec]
446 /// - [MDN documentation][mdn]
447 ///
448 /// [spec]: https://tc39.es/ecma262/#sec-object.prototype.tostring
449 /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/toString
450 #[allow(clippy::wrong_self_convention)]
451 pub fn to_string(this: &JsValue, _: &[JsValue], context: &mut Context) -> JsResult<JsValue> {
452 // 1. If the this value is undefined, return "[object Undefined]".
453 if this.is_undefined() {
454 return Ok("[object Undefined]".into());
455 }
456 // 2. If the this value is null, return "[object Null]".
457 if this.is_null() {
458 return Ok("[object Null]".into());
459 }
460 // 3. Let O be ! ToObject(this value).
461 let o = this.to_object(context)?;
462 // TODO: 4. Let isArray be ? IsArray(O).
463 // TODO: 5. If isArray is true, let builtinTag be "Array".
464
465 // 6. Else if O has a [[ParameterMap]] internal slot, let builtinTag be "Arguments".
466 // 7. Else if O has a [[Call]] internal method, let builtinTag be "Function".
467 // 8. Else if O has an [[ErrorData]] internal slot, let builtinTag be "Error".
468 // 9. Else if O has a [[BooleanData]] internal slot, let builtinTag be "Boolean".
469 // 10. Else if O has a [[NumberData]] internal slot, let builtinTag be "Number".
470 // 11. Else if O has a [[StringData]] internal slot, let builtinTag be "String".
471 // 12. Else if O has a [[DateValue]] internal slot, let builtinTag be "Date".
472 // 13. Else if O has a [[RegExpMatcher]] internal slot, let builtinTag be "RegExp".
473 // 14. Else, let builtinTag be "Object".
474 let builtin_tag = {
475 let o = o.borrow();
476 match o.kind() {
477 ObjectKind::Array => "Array",
478 // TODO: Arguments Exotic Objects are currently not supported
479 ObjectKind::Function(_) => "Function",
480 ObjectKind::Error => "Error",
481 ObjectKind::Boolean(_) => "Boolean",
482 ObjectKind::Number(_) => "Number",
483 ObjectKind::String(_) => "String",
484 ObjectKind::Date(_) => "Date",
485 ObjectKind::RegExp(_) => "RegExp",
486 _ => "Object",
487 }
488 };
489
490 // 15. Let tag be ? Get(O, @@toStringTag).
491 let tag = o.get(WellKnownSymbols::to_string_tag(), context)?;
492
493 // 16. If Type(tag) is not String, set tag to builtinTag.
494 let tag_str = tag.as_string().map(|s| s.as_str()).unwrap_or(builtin_tag);
495
496 // 17. Return the string-concatenation of "[object ", tag, and "]".
497 Ok(format!("[object {}]", tag_str).into())
498 }
499
500 /// `Object.prototype.hasOwnPrototype( property )`
501 ///
502 /// The method returns a boolean indicating whether the object has the specified property
503 /// as its own property (as opposed to inheriting it).
504 ///
505 /// More information:
506 /// - [ECMAScript reference][spec]
507 /// - [MDN documentation][mdn]
508 ///
509 /// [spec]: https://tc39.es/ecma262/#sec-object.prototype.hasownproperty
510 /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/hasOwnProperty
511 pub fn has_own_property(
512 this: &JsValue,
513 args: &[JsValue],
514 context: &mut Context,
515 ) -> JsResult<JsValue> {
516 let key = args
517 .get(0)
518 .unwrap_or(&JsValue::undefined())
519 .to_property_key(context)?;
520 let object = this.to_object(context)?;
521
522 Ok(object.has_own_property(key, context)?.into())
523 }
524
525 /// `Object.prototype.propertyIsEnumerable( property )`
526 ///
527 /// This method returns a Boolean indicating whether the specified property is
528 /// enumerable and is the object's own property.
529 ///
530 /// More information:
531 /// - [ECMAScript reference][spec]
532 /// - [MDN documentation][mdn]
533 ///
534 /// [spec]: https://tc39.es/ecma262/#sec-object.prototype.propertyisenumerable
535 /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/propertyIsEnumerable
536 pub fn property_is_enumerable(
537 this: &JsValue,
538 args: &[JsValue],
539 context: &mut Context,
540 ) -> JsResult<JsValue> {
541 let key = match args.get(0) {
542 None => return Ok(JsValue::new(false)),
543 Some(key) => key,
544 };
545
546 let key = key.to_property_key(context)?;
547 let own_property = this
548 .to_object(context)?
549 .__get_own_property__(&key, context)?;
550
551 Ok(own_property.map_or(JsValue::new(false), |own_prop| {
552 JsValue::new(own_prop.enumerable())
553 }))
554 }
555
556 /// `Object.assign( target, ...sources )`
557 ///
558 /// This method copies all enumerable own properties from one or more
559 /// source objects to a target object. It returns the target object.
560 ///
561 /// More information:
562 /// - [ECMAScript reference][spec]
563 /// - [MDN documentation][mdn]
564 ///
565 /// [spec]: https://tc39.es/ecma262/#sec-object.assign
566 /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign
567 pub fn assign(_: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {
568 // 1. Let to be ? ToObject(target).
569 let to = args.get_or_undefined(0).to_object(context)?;
570
571 // 2. If only one argument was passed, return to.
572 if args.len() == 1 {
573 return Ok(to.into());
574 }
575
576 // 3. For each element nextSource of sources, do
577 for source in &args[1..] {
578 // 3.a. If nextSource is neither undefined nor null, then
579 if !source.is_null_or_undefined() {
580 // 3.a.i. Let from be ! ToObject(nextSource).
581 let from = source.to_object(context).unwrap();
582 // 3.a.ii. Let keys be ? from.[[OwnPropertyKeys]]().
583 let keys = from.__own_property_keys__(context)?;
584 // 3.a.iii. For each element nextKey of keys, do
585 for key in keys {
586 // 3.a.iii.1. Let desc be ? from.[[GetOwnProperty]](nextKey).
587 if let Some(desc) = from.__get_own_property__(&key, context)? {
588 // 3.a.iii.2. If desc is not undefined and desc.[[Enumerable]] is true, then
589 if desc.expect_enumerable() {
590 // 3.a.iii.2.a. Let propValue be ? Get(from, nextKey).
591 let property = from.get(key.clone(), context)?;
592 // 3.a.iii.2.b. Perform ? Set(to, nextKey, propValue, true).
593 to.set(key, property, true, context)?;
594 }
595 }
596 }
597 }
598 }
599
600 // 4. Return to.
601 Ok(to.into())
602 }
603
604 /// `Object.keys( target )`
605 ///
606 /// This method returns an array of a given object's own enumerable
607 /// property names, iterated in the same order that a normal loop would.
608 ///
609 /// More information:
610 /// - [ECMAScript reference][spec]
611 /// - [MDN documentation][mdn]
612 ///
613 /// [spec]: https://tc39.es/ecma262/#sec-object.keys
614 /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/keys
615 pub fn keys(_: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {
616 // 1. Let obj be ? ToObject(target).
617 let obj = args
618 .get(0)
619 .cloned()
620 .unwrap_or_default()
621 .to_object(context)?;
622
623 // 2. Let nameList be ? EnumerableOwnPropertyNames(obj, key).
624 let name_list = obj.enumerable_own_property_names(PropertyNameKind::Key, context)?;
625
626 // 3. Return CreateArrayFromList(nameList).
627 let result = Array::create_array_from_list(name_list, context);
628
629 Ok(result.into())
630 }
631
632 /// `Object.values( target )`
633 ///
634 /// More information:
635 /// - [ECMAScript reference][spec]
636 /// - [MDN documentation][mdn]
637 ///
638 /// [spec]: https://tc39.es/ecma262/#sec-object.values
639 /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/values
640 pub fn values(_: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {
641 // 1. Let obj be ? ToObject(target).
642 let obj = args
643 .get(0)
644 .cloned()
645 .unwrap_or_default()
646 .to_object(context)?;
647
648 // 2. Let nameList be ? EnumerableOwnPropertyNames(obj, value).
649 let name_list = obj.enumerable_own_property_names(PropertyNameKind::Value, context)?;
650
651 // 3. Return CreateArrayFromList(nameList).
652 let result = Array::create_array_from_list(name_list, context);
653
654 Ok(result.into())
655 }
656
657 /// `Object.entries( target )`
658 ///
659 /// This method returns an array of a given object's own enumerable string-keyed property [key, value] pairs.
660 /// This is the same as iterating with a for...in loop,
661 /// except that a for...in loop enumerates properties in the prototype chain as well).
662 ///
663 /// More information:
664 /// - [ECMAScript reference][spec]
665 /// - [MDN documentation][mdn]
666 ///
667 /// [spec]: https://tc39.es/ecma262/#sec-object.entries
668 /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/entries
669 pub fn entries(_: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {
670 // 1. Let obj be ? ToObject(target).
671 let obj = args
672 .get(0)
673 .cloned()
674 .unwrap_or_default()
675 .to_object(context)?;
676
677 // 2. Let nameList be ? EnumerableOwnPropertyNames(obj, key+value).
678 let name_list =
679 obj.enumerable_own_property_names(PropertyNameKind::KeyAndValue, context)?;
680
681 // 3. Return CreateArrayFromList(nameList).
682 let result = Array::create_array_from_list(name_list, context);
683
684 Ok(result.into())
685 }
686
687 /// `Object.seal( target )`
688 ///
689 /// More information:
690 /// - [ECMAScript reference][spec]
691 /// - [MDN documentation][mdn]
692 ///
693 /// [spec]: https://tc39.es/ecma262/#sec-object.seal
694 /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/seal
695 pub fn seal(_: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {
696 let o = args.get_or_undefined(0);
697
698 if let Some(o) = o.as_object() {
699 // 2. Let status be ? SetIntegrityLevel(O, sealed).
700 let status = o.set_integrity_level(IntegrityLevel::Sealed, context)?;
701 // 3. If status is false, throw a TypeError exception.
702 if !status {
703 return context.throw_type_error("cannot seal object");
704 }
705 }
706 // 1. If Type(O) is not Object, return O.
707 // 4. Return O.
708 Ok(o.clone())
709 }
710
711 /// `Object.isSealed( target )`
712 ///
713 /// More information:
714 /// - [ECMAScript reference][spec]
715 /// - [MDN documentation][mdn]
716 ///
717 /// [spec]: https://tc39.es/ecma262/#sec-object.issealed
718 /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/isSealed
719 pub fn is_sealed(_: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {
720 let o = args.get_or_undefined(0);
721
722 // 1. If Type(O) is not Object, return true.
723 // 2. Return ? TestIntegrityLevel(O, sealed).
724 if let Some(o) = o.as_object() {
725 Ok(o.test_integrity_level(IntegrityLevel::Sealed, context)?
726 .into())
727 } else {
728 Ok(JsValue::new(true))
729 }
730 }
731
732 /// `Object.freeze( target )`
733 ///
734 /// More information:
735 /// - [ECMAScript reference][spec]
736 /// - [MDN documentation][mdn]
737 ///
738 /// [spec]: https://tc39.es/ecma262/#sec-object.freeze
739 /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/freeze
740 pub fn freeze(_: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {
741 let o = args.get_or_undefined(0);
742
743 if let Some(o) = o.as_object() {
744 // 2. Let status be ? SetIntegrityLevel(O, frozen).
745 let status = o.set_integrity_level(IntegrityLevel::Frozen, context)?;
746 // 3. If status is false, throw a TypeError exception.
747 if !status {
748 return context.throw_type_error("cannot freeze object");
749 }
750 }
751 // 1. If Type(O) is not Object, return O.
752 // 4. Return O.
753 Ok(o.clone())
754 }
755
756 /// `Object.isFrozen( target )`
757 ///
758 /// More information:
759 /// - [ECMAScript reference][spec]
760 /// - [MDN documentation][mdn]
761 ///
762 /// [spec]: https://tc39.es/ecma262/#sec-object.isfrozen
763 /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/isFrozen
764 pub fn is_frozen(_: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {
765 let o = args.get_or_undefined(0);
766
767 // 1. If Type(O) is not Object, return true.
768 // 2. Return ? TestIntegrityLevel(O, frozen).
769 if let Some(o) = o.as_object() {
770 Ok(o.test_integrity_level(IntegrityLevel::Frozen, context)?
771 .into())
772 } else {
773 Ok(JsValue::new(true))
774 }
775 }
776
777 /// `Object.preventExtensions( target )`
778 ///
779 /// More information:
780 /// - [ECMAScript reference][spec]
781 /// - [MDN documentation][mdn]
782 ///
783 /// [spec]: https://tc39.es/ecma262/#sec-object.preventextensions
784 /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/preventExtensions
785 pub fn prevent_extensions(
786 _: &JsValue,
787 args: &[JsValue],
788 context: &mut Context,
789 ) -> JsResult<JsValue> {
790 let o = args.get_or_undefined(0);
791
792 if let Some(o) = o.as_object() {
793 // 2. Let status be ? O.[[PreventExtensions]]().
794 let status = o.__prevent_extensions__(context)?;
795 // 3. If status is false, throw a TypeError exception.
796 if !status {
797 return context.throw_type_error("cannot prevent extensions");
798 }
799 }
800 // 1. If Type(O) is not Object, return O.
801 // 4. Return O.
802 Ok(o.clone())
803 }
804
805 /// `Object.isExtensible( target )`
806 ///
807 /// More information:
808 /// - [ECMAScript reference][spec]
809 /// - [MDN documentation][mdn]
810 ///
811 /// [spec]: https://tc39.es/ecma262/#sec-object.isextensible
812 /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/isExtensible
813 pub fn is_extensible(
814 _: &JsValue,
815 args: &[JsValue],
816 context: &mut Context,
817 ) -> JsResult<JsValue> {
818 let o = args.get_or_undefined(0);
819 // 1. If Type(O) is not Object, return false.
820 if let Some(o) = o.as_object() {
821 // 2. Return ? IsExtensible(O).
822 Ok(o.is_extensible(context)?.into())
823 } else {
824 Ok(JsValue::new(false))
825 }
826 }
827}
828
829/// The abstract operation ObjectDefineProperties
830///
831/// More information:
832/// - [ECMAScript reference][spec]
833///
834/// [spec]: https://tc39.es/ecma262/#sec-object.defineproperties
835#[inline]
836fn object_define_properties(
837 object: &JsObject,
838 props: JsValue,
839 context: &mut Context,
840) -> JsResult<()> {
841 // 1. Assert: Type(O) is Object.
842 // 2. Let props be ? ToObject(Properties).
843 let props = &props.to_object(context)?;
844
845 // 3. Let keys be ? props.[[OwnPropertyKeys]]().
846 let keys = props.__own_property_keys__(context)?;
847
848 // 4. Let descriptors be a new empty List.
849 let mut descriptors: Vec<(PropertyKey, PropertyDescriptor)> = Vec::new();
850
851 // 5. For each element nextKey of keys, do
852 for next_key in keys {
853 // a. Let propDesc be ? props.[[GetOwnProperty]](nextKey).
854 // b. If propDesc is not undefined and propDesc.[[Enumerable]] is true, then
855 if let Some(prop_desc) = props.__get_own_property__(&next_key, context)? {
856 if prop_desc.expect_enumerable() {
857 // i. Let descObj be ? Get(props, nextKey).
858 let desc_obj = props.get(next_key.clone(), context)?;
859
860 // ii. Let desc be ? ToPropertyDescriptor(descObj).
861 let desc = desc_obj.to_property_descriptor(context)?;
862
863 // iii. Append the pair (a two element List) consisting of nextKey and desc to the end of descriptors.
864 descriptors.push((next_key, desc));
865 }
866 }
867 }
868
869 // 6. For each element pair of descriptors, do
870 // a. Let P be the first element of pair.
871 // b. Let desc be the second element of pair.
872 for (p, d) in descriptors {
873 // c. Perform ? DefinePropertyOrThrow(O, P, desc).
874 object.define_property_or_throw(p, d, context)?;
875 }
876
877 // 7. Return O.
878 Ok(())
879}