From 66db3421ecb2b7dad4f2f4058b773752a2f283b6 Mon Sep 17 00:00:00 2001 From: Iban Eguia Date: Sat, 2 Jan 2021 21:36:43 +0100 Subject: [PATCH 1/6] Fixed some panics, Object.setPrototypeOf and Object.getPrototypeOf are almost spec compliant --- boa/src/builtins/object/mod.rs | 64 ++++++++++++++++++++++++++++------ boa/src/object/gcobject.rs | 2 +- boa/src/object/mod.rs | 20 +++++++++-- boa/src/value/display.rs | 3 +- 4 files changed, 74 insertions(+), 15 deletions(-) diff --git a/boa/src/builtins/object/mod.rs b/boa/src/builtins/object/mod.rs index 1219d547858..de74d1f4474 100644 --- a/boa/src/builtins/object/mod.rs +++ b/boa/src/builtins/object/mod.rs @@ -19,7 +19,7 @@ use crate::{ property::Attribute, property::DataDescriptor, property::PropertyDescriptor, - value::{same_value, Value}, + value::{same_value, Type, Value}, BoaProfiler, Context, Result, }; @@ -243,18 +243,62 @@ impl Object { } /// Get the `prototype` of an object. - pub fn get_prototype_of(_: &Value, args: &[Value], _: &mut Context) -> Result { - let obj = args.get(0).expect("Cannot get object"); - Ok(obj - .as_object() - .map_or_else(Value::undefined, |object| object.prototype_instance())) + pub fn get_prototype_of(_: &Value, args: &[Value], ctx: &mut Context) -> Result { + if args.is_empty() { + return ctx.throw_type_error( + "Object.getPrototypeOf: At least 1 argument required, but only 0 passed", + ); + } + + // 1. Let obj be ? ToObject(O). + let obj = args[0].clone().to_object(ctx)?; + + // 2. Return ? obj.[[GetPrototypeOf]](). + Ok(obj.prototype_instance()) } /// Set the `prototype` of an object. - pub fn set_prototype_of(_: &Value, args: &[Value], _: &mut Context) -> Result { - let obj = args.get(0).expect("Cannot get object").clone(); - let proto = args.get(1).expect("Cannot get object").clone(); - obj.as_object().unwrap().set_prototype_instance(proto); + /// + /// [More information][spec] + /// + /// [spec]: https://tc39.es/ecma262/#sec-object.setprototypeof + pub fn set_prototype_of(_: &Value, args: &[Value], ctx: &mut Context) -> Result { + if args.len() < 2 { + return ctx.throw_type_error(format!( + "Object.setPrototypeOf: At least 2 arguments required, but only {} passed", + args.len() + )); + } + + // 1. Set O to ? RequireObjectCoercible(O). + let obj = args.get(0).cloned().unwrap_or_default(); // TODO: RequireObjectCoercible + + // 2. If Type(proto) is neither Object nor Null, throw a TypeError exception. + let proto = args.get(1).cloned().unwrap_or_default(); + if !matches!(proto.get_type(), Type::Object | Type::Null) { + return ctx.throw_type_error(format!( + "expected an object or null, got {}", + proto.get_type().as_str() + )); + } + + // 3. If Type(O) is not Object, return O. + if obj.get_type() != Type::Object { + return Ok(obj); + } + + // 4. Let status be ? O.[[SetPrototypeOf]](proto). + let status = obj + .as_object() + .expect("obj was not an object") + .set_prototype_instance(proto); + + // 5. If status is false, throw a TypeError exception. + if !status { + return ctx.throw_type_error("can't set prototype of this object"); + } + + // 6. Return O. Ok(obj) } diff --git a/boa/src/object/gcobject.rs b/boa/src/object/gcobject.rs index 9a49c8f18c0..d9b92ea7228 100644 --- a/boa/src/object/gcobject.rs +++ b/boa/src/object/gcobject.rs @@ -550,7 +550,7 @@ impl GcObject { /// or if th prototype is not an object or undefined. #[inline] #[track_caller] - pub fn set_prototype_instance(&mut self, prototype: Value) { + pub fn set_prototype_instance(&mut self, prototype: Value) -> bool { self.borrow_mut().set_prototype_instance(prototype) } diff --git a/boa/src/object/mod.rs b/boa/src/object/mod.rs index f34e68ec470..94b0aa2b38e 100644 --- a/boa/src/object/mod.rs +++ b/boa/src/object/mod.rs @@ -12,7 +12,7 @@ use crate::{ context::StandardConstructor, gc::{Finalize, Trace}, property::{Attribute, DataDescriptor, PropertyDescriptor, PropertyKey}, - value::{RcBigInt, RcString, RcSymbol, Value}, + value::{same_value, RcBigInt, RcString, RcSymbol, Value}, BoaProfiler, Context, }; use rustc_hash::FxHashMap; @@ -485,9 +485,23 @@ impl Object { #[track_caller] #[inline] - pub fn set_prototype_instance(&mut self, prototype: Value) { + /// Sets the prototype instance of the object. + /// + /// [More information][spec] + /// + /// [spec]: https://tc39.es/ecma262/#sec-invariants-of-the-essential-internal-methods + pub fn set_prototype_instance(&mut self, prototype: Value) -> bool { assert!(prototype.is_null() || prototype.is_object()); - self.prototype = prototype + if self.extensible { + self.prototype = prototype; + true + } else if same_value(&prototype, &self.prototype) { + // unless V is the SameValue as the target's observed [[GetPrototypeOf]] value. + true + } else { + // If target is non-extensible, [[SetPrototypeOf]] must return false + false + } } /// Similar to `Value::new_object`, but you can pass a prototype to create from, plus a kind diff --git a/boa/src/value/display.rs b/boa/src/value/display.rs index a161e4f56e9..d554c9292e5 100644 --- a/boa/src/value/display.rs +++ b/boa/src/value/display.rs @@ -110,7 +110,8 @@ pub(crate) fn log_string_from(x: &Value, print_internals: bool, print_children: .unwrap() .value() .as_number() - .unwrap() as i32; + .map(|n| n as i32) + .unwrap_or_default(); if print_children { if len == 0 { From 400e9eb71cc4a35bbbda7c2f0af76838f58791b2 Mon Sep 17 00:00:00 2001 From: Iban Eguia Date: Sat, 2 Jan 2021 21:43:29 +0100 Subject: [PATCH 2/6] Small documentation fix --- boa/src/object/mod.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/boa/src/object/mod.rs b/boa/src/object/mod.rs index 94b0aa2b38e..8536ef98b6a 100644 --- a/boa/src/object/mod.rs +++ b/boa/src/object/mod.rs @@ -483,13 +483,13 @@ impl Object { &self.prototype } - #[track_caller] - #[inline] /// Sets the prototype instance of the object. /// /// [More information][spec] /// /// [spec]: https://tc39.es/ecma262/#sec-invariants-of-the-essential-internal-methods + #[inline] + #[track_caller] pub fn set_prototype_instance(&mut self, prototype: Value) -> bool { assert!(prototype.is_null() || prototype.is_object()); if self.extensible { From 6fe279ff08e616ca38cd5a1867d1169454d06d56 Mon Sep 17 00:00:00 2001 From: Iban Eguia Date: Sat, 2 Jan 2021 22:30:15 +0100 Subject: [PATCH 3/6] Fixed clippy lint --- boa/src/object/mod.rs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/boa/src/object/mod.rs b/boa/src/object/mod.rs index 8536ef98b6a..58a7bc997cc 100644 --- a/boa/src/object/mod.rs +++ b/boa/src/object/mod.rs @@ -495,12 +495,10 @@ impl Object { if self.extensible { self.prototype = prototype; true - } else if same_value(&prototype, &self.prototype) { - // unless V is the SameValue as the target's observed [[GetPrototypeOf]] value. - true } else { // If target is non-extensible, [[SetPrototypeOf]] must return false - false + // unless V is the SameValue as the target's observed [[GetPrototypeOf]] value. + same_value(&prototype, &self.prototype) } } From 49d8f028867ba06d7c14fc65e72a535f4f4c495f Mon Sep 17 00:00:00 2001 From: Iban Eguia Date: Sat, 2 Jan 2021 22:34:37 +0100 Subject: [PATCH 4/6] Added `RequireObjectCoercible` --- boa/src/builtins/object/mod.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/boa/src/builtins/object/mod.rs b/boa/src/builtins/object/mod.rs index de74d1f4474..10672906d89 100644 --- a/boa/src/builtins/object/mod.rs +++ b/boa/src/builtins/object/mod.rs @@ -271,7 +271,11 @@ impl Object { } // 1. Set O to ? RequireObjectCoercible(O). - let obj = args.get(0).cloned().unwrap_or_default(); // TODO: RequireObjectCoercible + let obj = args + .get(0) + .cloned() + .unwrap_or_default() + .require_object_coercible(ctx); // 2. If Type(proto) is neither Object nor Null, throw a TypeError exception. let proto = args.get(1).cloned().unwrap_or_default(); From 21fc349d2bbb72c26fdd9825250b55da128feb14 Mon Sep 17 00:00:00 2001 From: Iban Eguia Date: Sun, 3 Jan 2021 11:04:42 +0100 Subject: [PATCH 5/6] Fixed compilation error --- boa/src/builtins/object/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/boa/src/builtins/object/mod.rs b/boa/src/builtins/object/mod.rs index 10672906d89..8d268707025 100644 --- a/boa/src/builtins/object/mod.rs +++ b/boa/src/builtins/object/mod.rs @@ -275,7 +275,7 @@ impl Object { .get(0) .cloned() .unwrap_or_default() - .require_object_coercible(ctx); + .require_object_coercible(ctx)?.clone(); // 2. If Type(proto) is neither Object nor Null, throw a TypeError exception. let proto = args.get(1).cloned().unwrap_or_default(); From 4fb28db3455765ac22733e71eb7c52de379c4512 Mon Sep 17 00:00:00 2001 From: Iban Eguia Date: Sun, 3 Jan 2021 11:14:39 +0100 Subject: [PATCH 6/6] cargo fmt --- boa/src/builtins/object/mod.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/boa/src/builtins/object/mod.rs b/boa/src/builtins/object/mod.rs index 8d268707025..a42369329a1 100644 --- a/boa/src/builtins/object/mod.rs +++ b/boa/src/builtins/object/mod.rs @@ -275,7 +275,8 @@ impl Object { .get(0) .cloned() .unwrap_or_default() - .require_object_coercible(ctx)?.clone(); + .require_object_coercible(ctx)? + .clone(); // 2. If Type(proto) is neither Object nor Null, throw a TypeError exception. let proto = args.get(1).cloned().unwrap_or_default();