From ff25a8ad026aba645332e3cfeca355b566cb3c63 Mon Sep 17 00:00:00 2001 From: Halid Odat Date: Mon, 5 Oct 2020 23:52:38 +0200 Subject: [PATCH] Make `Function.prototype` a function (#802) --- boa/src/builtins/function/mod.rs | 16 +++++++-- boa/src/builtins/function/tests.rs | 53 ++++++++++++++++++++++++++++++ boa/src/object/mod.rs | 23 +++++++++++++ 3 files changed, 90 insertions(+), 2 deletions(-) diff --git a/boa/src/builtins/function/mod.rs b/boa/src/builtins/function/mod.rs index de6278a147..f7e26bdca8 100644 --- a/boa/src/builtins/function/mod.rs +++ b/boa/src/builtins/function/mod.rs @@ -14,7 +14,7 @@ use crate::{ builtins::{Array, BuiltIn}, environment::lexical_environment::Environment, - object::{ConstructorBuilder, Object, ObjectData, PROTOTYPE}, + object::{ConstructorBuilder, FunctionBuilder, Object, ObjectData, PROTOTYPE}, property::{Attribute, Property}, syntax::ast::node::{FormalParameter, RcStatementList}, BoaProfiler, Context, Result, Value, @@ -301,13 +301,17 @@ pub struct BuiltInFunctionObject; impl BuiltInFunctionObject { pub const LENGTH: usize = 1; - fn constructor(this: &Value, _args: &[Value], _context: &mut Context) -> Result { + fn constructor(this: &Value, _: &[Value], _: &mut Context) -> Result { this.set_data(ObjectData::Function(Function::BuiltIn( BuiltInFunction(|_, _, _| Ok(Value::undefined())), FunctionFlags::CALLABLE | FunctionFlags::CONSTRUCTABLE, ))); Ok(this.clone()) } + + fn prototype(_: &Value, _: &[Value], _: &mut Context) -> Result { + Ok(Value::undefined()) + } } impl BuiltIn for BuiltInFunctionObject { @@ -320,6 +324,14 @@ impl BuiltIn for BuiltInFunctionObject { fn init(context: &mut Context) -> (&'static str, Value, Attribute) { let _timer = BoaProfiler::global().start_event("function", "init"); + let function_prototype = context.standard_objects().function_object().prototype(); + FunctionBuilder::new(context, Self::prototype) + .name("") + .length(0) + .callable(true) + .constructable(false) + .build_function_prototype(&function_prototype); + let function_object = ConstructorBuilder::with_standard_object( context, Self::constructor, diff --git a/boa/src/builtins/function/tests.rs b/boa/src/builtins/function/tests.rs index eaacbf4193..1bcc7f417c 100644 --- a/boa/src/builtins/function/tests.rs +++ b/boa/src/builtins/function/tests.rs @@ -61,3 +61,56 @@ fn self_mutating_function_when_constructing() { 3 ); } + +#[test] +fn call_function_prototype() { + let mut engine = Context::new(); + let func = r#" + Function.prototype() + "#; + let value = forward_val(&mut engine, func).unwrap(); + assert!(value.is_undefined()); +} + +#[test] +fn call_function_prototype_with_arguments() { + let mut engine = Context::new(); + let func = r#" + Function.prototype(1, "", new String("")) + "#; + let value = forward_val(&mut engine, func).unwrap(); + assert!(value.is_undefined()); +} + +#[test] +fn call_function_prototype_with_new() { + let mut engine = Context::new(); + let func = r#" + new Function.prototype() + "#; + let value = forward_val(&mut engine, func); + assert!(value.is_err()); +} + +#[test] +fn function_prototype_name() { + let mut engine = Context::new(); + let func = r#" + Function.prototype.name + "#; + let value = forward_val(&mut engine, func).unwrap(); + assert!(value.is_string()); + assert!(value.as_string().unwrap().is_empty()); +} + +#[test] +#[allow(clippy::float_cmp)] +fn function_prototype_length() { + let mut engine = Context::new(); + let func = r#" + Function.prototype.length + "#; + let value = forward_val(&mut engine, func).unwrap(); + assert!(value.is_number()); + assert_eq!(value.as_number().unwrap(), 0.0); +} diff --git a/boa/src/object/mod.rs b/boa/src/object/mod.rs index e6fa412a0c..655ac5161e 100644 --- a/boa/src/object/mod.rs +++ b/boa/src/object/mod.rs @@ -670,6 +670,29 @@ impl<'context> FunctionBuilder<'context> { GcObject::new(function) } + + /// Initializes the `Function.prototype` function object. + pub(crate) fn build_function_prototype(&mut self, object: &GcObject) { + let mut object = object.borrow_mut(); + object.data = ObjectData::Function(Function::BuiltIn( + self.function, + FunctionFlags::from_parameters(self.callable, self.constructable), + )); + object.set_prototype_instance( + self.context + .standard_objects() + .object_object() + .prototype() + .into(), + ); + let attribute = Attribute::READONLY | Attribute::NON_ENUMERABLE | Attribute::PERMANENT; + if let Some(name) = self.name.take() { + object.insert_property("name", name, attribute); + } else { + object.insert_property("name", "", attribute); + } + object.insert_property("length", self.length, attribute); + } } /// Builder for creating objects with properties.