LibJS: Explicitly pass a "Function& new_target" to Function::construct

This allows the proxy handler to pass the proper new.target to construct
handlers.
This commit is contained in:
Matthew Olsson 2020-06-25 15:30:58 -07:00 committed by Andreas Kling
parent 19411e22d0
commit bda39ef7ab
38 changed files with 63 additions and 50 deletions

View file

@ -268,7 +268,7 @@ Value Interpreter::construct(Function& function, Function& new_target, Optional<
// If we are a Derived constructor, |this| has not been constructed before super is called.
Value this_value = function.constructor_kind() == Function::ConstructorKind::Base ? new_object : Value {};
call_frame.this_value = this_value;
auto result = function.construct(*this);
auto result = function.construct(*this, new_target);
this_value = current_environment()->get_this_binding();
pop_call_frame();

View file

@ -79,7 +79,7 @@ Value ArrayConstructor::call(Interpreter& interpreter)
return array;
}
Value ArrayConstructor::construct(Interpreter& interpreter)
Value ArrayConstructor::construct(Interpreter& interpreter, Function&)
{
return call(interpreter);
}

View file

@ -39,7 +39,7 @@ public:
virtual ~ArrayConstructor() override;
virtual Value call(Interpreter&) override;
virtual Value construct(Interpreter&) override;
virtual Value construct(Interpreter&, Function& new_target) override;
private:
virtual bool has_constructor() const override { return true; }

View file

@ -72,7 +72,7 @@ Value BigIntConstructor::call(Interpreter& interpreter)
return bigint;
}
Value BigIntConstructor::construct(Interpreter& interpreter)
Value BigIntConstructor::construct(Interpreter& interpreter, Function&)
{
interpreter.throw_exception<TypeError>(ErrorType::NotAConstructor, "BigInt");
return {};

View file

@ -39,7 +39,7 @@ public:
virtual ~BigIntConstructor() override;
virtual Value call(Interpreter&) override;
virtual Value construct(Interpreter&) override;
virtual Value construct(Interpreter&, Function& new_target) override;
private:
virtual bool has_constructor() const override { return true; }

View file

@ -54,7 +54,7 @@ Value BooleanConstructor::call(Interpreter& interpreter)
return Value(interpreter.argument(0).to_boolean());
}
Value BooleanConstructor::construct(Interpreter& interpreter)
Value BooleanConstructor::construct(Interpreter& interpreter, Function&)
{
return BooleanObject::create(global_object(), interpreter.argument(0).to_boolean());
}

View file

@ -39,7 +39,7 @@ public:
virtual ~BooleanConstructor() override;
virtual Value call(Interpreter&) override;
virtual Value construct(Interpreter&) override;
virtual Value construct(Interpreter&, Function& new_target) override;
private:
virtual bool has_constructor() const override { return true; }

View file

@ -54,14 +54,14 @@ Value BoundFunction::call(Interpreter& interpreter)
return m_target_function->call(interpreter);
}
Value BoundFunction::construct(Interpreter& interpreter)
Value BoundFunction::construct(Interpreter& interpreter, Function& new_target)
{
if (auto this_value = interpreter.this_value(global_object()); m_constructor_prototype && this_value.is_object()) {
this_value.as_object().set_prototype(m_constructor_prototype);
if (interpreter.exception())
return {};
}
return m_target_function->construct(interpreter);
return m_target_function->construct(interpreter, new_target);
}
LexicalEnvironment* BoundFunction::create_environment()

View file

@ -40,7 +40,7 @@ public:
virtual Value call(Interpreter& interpreter) override;
virtual Value construct(Interpreter& interpreter) override;
virtual Value construct(Interpreter&, Function& new_target) override;
virtual LexicalEnvironment* create_environment() override;

View file

@ -54,13 +54,13 @@ DateConstructor::~DateConstructor()
Value DateConstructor::call(Interpreter& interpreter)
{
auto date = construct(interpreter);
auto date = construct(interpreter, *this);
if (!date.is_object())
return {};
return js_string(interpreter, static_cast<Date&>(date.as_object()).string());
}
Value DateConstructor::construct(Interpreter&)
Value DateConstructor::construct(Interpreter&, Function&)
{
// TODO: Support args
struct timeval tv;

View file

@ -39,7 +39,7 @@ public:
virtual ~DateConstructor() override;
virtual Value call(Interpreter&) override;
virtual Value construct(Interpreter&) override;
virtual Value construct(Interpreter&, Function& new_target) override;
private:
virtual bool has_constructor() const override { return true; }

View file

@ -49,10 +49,10 @@ ErrorConstructor::~ErrorConstructor()
Value ErrorConstructor::call(Interpreter& interpreter)
{
return construct(interpreter);
return construct(interpreter, *this);
}
Value ErrorConstructor::construct(Interpreter& interpreter)
Value ErrorConstructor::construct(Interpreter& interpreter, Function&)
{
String message = "";
if (!interpreter.call_frame().arguments.is_empty() && !interpreter.call_frame().arguments[0].is_undefined()) {
@ -77,9 +77,9 @@ Value ErrorConstructor::construct(Interpreter& interpreter)
ConstructorName::~ConstructorName() { } \
Value ConstructorName::call(Interpreter& interpreter) \
{ \
return construct(interpreter); \
return construct(interpreter, *this); \
} \
Value ConstructorName::construct(Interpreter& interpreter) \
Value ConstructorName::construct(Interpreter& interpreter, Function&) \
{ \
String message = ""; \
if (!interpreter.call_frame().arguments.is_empty() && !interpreter.call_frame().arguments[0].is_undefined()) { \

View file

@ -40,7 +40,7 @@ public:
virtual ~ErrorConstructor() override;
virtual Value call(Interpreter&) override;
virtual Value construct(Interpreter&) override;
virtual Value construct(Interpreter&, Function& new_target) override;
private:
virtual bool has_constructor() const override { return true; }
@ -55,7 +55,7 @@ private:
virtual void initialize(Interpreter&, GlobalObject&) override; \
virtual ~ConstructorName() override; \
virtual Value call(Interpreter&) override; \
virtual Value construct(Interpreter&) override; \
virtual Value construct(Interpreter&, Function& new_target) override; \
\
private: \
virtual bool has_constructor() const override { return true; } \

View file

@ -68,7 +68,7 @@
"Object prototype must not be %s on a super property access") \
M(ObjectPrototypeWrongType, "Prototype must be an object or null") \
M(ProxyCallWithNew, "Proxy must be called with the 'new' operator") \
M(ProxyConstructBadReturnType, "Proxy handler's construct trap violates invariant: must return" \
M(ProxyConstructBadReturnType, "Proxy handler's construct trap violates invariant: must return " \
"an object") \
M(ProxyConstructorBadType, "Expected %s argument of Proxy constructor to be object, got %s") \
M(ProxyDefinePropExistingConfigurable, "Proxy handler's defineProperty trap violates " \

View file

@ -44,7 +44,7 @@ public:
virtual void initialize(Interpreter&, GlobalObject&) override { }
virtual Value call(Interpreter&) = 0;
virtual Value construct(Interpreter&) = 0;
virtual Value construct(Interpreter&, Function& new_target) = 0;
virtual const FlyString& name() const = 0;
virtual LexicalEnvironment* create_environment() = 0;

View file

@ -53,10 +53,10 @@ FunctionConstructor::~FunctionConstructor()
Value FunctionConstructor::call(Interpreter& interpreter)
{
return construct(interpreter);
return construct(interpreter, *this);
}
Value FunctionConstructor::construct(Interpreter& interpreter)
Value FunctionConstructor::construct(Interpreter& interpreter, Function&)
{
String parameters_source = "";
String body_source = "";

View file

@ -39,7 +39,7 @@ public:
virtual ~FunctionConstructor() override;
virtual Value call(Interpreter&) override;
virtual Value construct(Interpreter&) override;
virtual Value construct(Interpreter&, Function& new_target) override;
private:
virtual bool has_constructor() const override { return true; }

View file

@ -63,7 +63,7 @@ Value NativeFunction::call(Interpreter& interpreter)
return m_native_function(interpreter, global_object());
}
Value NativeFunction::construct(Interpreter&)
Value NativeFunction::construct(Interpreter&, Function&)
{
return {};
}

View file

@ -42,7 +42,7 @@ public:
virtual ~NativeFunction() override;
virtual Value call(Interpreter&) override;
virtual Value construct(Interpreter&) override;
virtual Value construct(Interpreter&, Function& new_target) override;
virtual const FlyString& name() const override { return m_name; };
virtual bool has_constructor() const { return false; }

View file

@ -72,7 +72,7 @@ Value NumberConstructor::call(Interpreter& interpreter)
return interpreter.argument(0).to_number(interpreter);
}
Value NumberConstructor::construct(Interpreter& interpreter)
Value NumberConstructor::construct(Interpreter& interpreter, Function&)
{
double number = 0;
if (interpreter.argument_count()) {

View file

@ -39,7 +39,7 @@ public:
virtual ~NumberConstructor() override;
virtual Value call(Interpreter&) override;
virtual Value construct(Interpreter&) override;
virtual Value construct(Interpreter&, Function& new_target) override;
private:
virtual bool has_constructor() const override { return true; }

View file

@ -69,7 +69,7 @@ Value ObjectConstructor::call(Interpreter& interpreter)
return Object::create_empty(interpreter, global_object());
}
Value ObjectConstructor::construct(Interpreter& interpreter)
Value ObjectConstructor::construct(Interpreter& interpreter, Function&)
{
return call(interpreter);
}

View file

@ -39,7 +39,7 @@ public:
virtual ~ObjectConstructor() override;
virtual Value call(Interpreter&) override;
virtual Value construct(Interpreter&) override;
virtual Value construct(Interpreter&, Function& new_target) override;
private:
virtual bool has_constructor() const override { return true; }

View file

@ -54,7 +54,7 @@ Value ProxyConstructor::call(Interpreter& interpreter)
return interpreter.throw_exception<TypeError>(ErrorType::ProxyCallWithNew);
}
Value ProxyConstructor::construct(Interpreter& interpreter)
Value ProxyConstructor::construct(Interpreter& interpreter, Function&)
{
if (interpreter.argument_count() < 2)
return interpreter.throw_exception<TypeError>(ErrorType::ProxyTwoArguments);

View file

@ -39,7 +39,7 @@ public:
virtual ~ProxyConstructor() override;
virtual Value call(Interpreter&) override;
virtual Value construct(Interpreter&) override;
virtual Value construct(Interpreter&, Function& new_target) override;
private:
virtual bool has_constructor() const override { return true; }

View file

@ -501,7 +501,7 @@ Value ProxyObject::call(Interpreter& interpreter) {
return interpreter.call(trap.as_function(), Value(&m_handler), move(arguments));
}
Value ProxyObject::construct(Interpreter& interpreter) {
Value ProxyObject::construct(Interpreter& interpreter, Function& new_target) {
if (!is_function())
return interpreter.throw_exception<TypeError>(ErrorType::NotAConstructor, Value(this).to_string_without_side_effects().characters());
@ -513,7 +513,7 @@ Value ProxyObject::construct(Interpreter& interpreter) {
if (interpreter.exception())
return {};
if (trap.is_empty() || trap.is_undefined() || trap.is_null())
return static_cast<Function&>(m_target).construct(interpreter);
return static_cast<Function&>(m_target).construct(interpreter, new_target);
if (!trap.is_function())
return interpreter.throw_exception<TypeError>(ErrorType::ProxyInvalidTrap, "construct");
@ -524,9 +524,7 @@ Value ProxyObject::construct(Interpreter& interpreter) {
arguments_array->indexed_properties().append(argument);
});
arguments.values().append(arguments_array);
// FIXME: We need access to the actual newTarget property here. This is just
// a quick fix
arguments.values().append(Value(this));
arguments.values().append(Value(&new_target));
auto result = interpreter.call(trap.as_function(), Value(&m_handler), move(arguments));
if (!result.is_object())
return interpreter.throw_exception<TypeError>(ErrorType::ProxyConstructBadReturnType);

View file

@ -40,7 +40,7 @@ public:
virtual ~ProxyObject() override;
virtual Value call(Interpreter&) override;
virtual Value construct(Interpreter&) override;
virtual Value construct(Interpreter&, Function& new_target) override;
virtual const FlyString& name() const override;
virtual LexicalEnvironment* create_environment() override;

View file

@ -50,10 +50,10 @@ RegExpConstructor::~RegExpConstructor()
Value RegExpConstructor::call(Interpreter& interpreter)
{
return construct(interpreter);
return construct(interpreter, *this);
}
Value RegExpConstructor::construct(Interpreter& interpreter)
Value RegExpConstructor::construct(Interpreter& interpreter, Function&)
{
if (!interpreter.argument_count())
return RegExpObject::create(global_object(), "(?:)", "");

View file

@ -39,7 +39,7 @@ public:
virtual ~RegExpConstructor() override;
virtual Value call(Interpreter&) override;
virtual Value construct(Interpreter&) override;
virtual Value construct(Interpreter&, Function& new_target) override;
private:
virtual bool has_constructor() const override { return true; }

View file

@ -133,7 +133,7 @@ Value ScriptFunction::call(Interpreter& interpreter)
return interpreter.run(global_object(), m_body, arguments, ScopeType::Function);
}
Value ScriptFunction::construct(Interpreter& interpreter)
Value ScriptFunction::construct(Interpreter& interpreter, Function&)
{
if (m_is_arrow_function)
return interpreter.throw_exception<TypeError>(ErrorType::NotAConstructor, m_name.characters());

View file

@ -45,7 +45,7 @@ public:
const Vector<FunctionNode::Parameter>& parameters() const { return m_parameters; };
virtual Value call(Interpreter&) override;
virtual Value construct(Interpreter&) override;
virtual Value construct(Interpreter&, Function& new_target) override;
virtual const FlyString& name() const override { return m_name; };
void set_name(const FlyString& name) { m_name = name; };

View file

@ -65,7 +65,7 @@ Value StringConstructor::call(Interpreter& interpreter)
return string;
}
Value StringConstructor::construct(Interpreter& interpreter)
Value StringConstructor::construct(Interpreter& interpreter, Function&)
{
PrimitiveString* primitive_string = nullptr;
if (!interpreter.argument_count())

View file

@ -39,7 +39,7 @@ public:
virtual ~StringConstructor() override;
virtual Value call(Interpreter&) override;
virtual Value construct(Interpreter&) override;
virtual Value construct(Interpreter&, Function& new_target) override;
private:
virtual bool has_constructor() const override { return true; }

View file

@ -74,7 +74,7 @@ Value SymbolConstructor::call(Interpreter& interpreter)
return js_symbol(interpreter, interpreter.argument(0).to_string(interpreter), false);
}
Value SymbolConstructor::construct(Interpreter& interpreter)
Value SymbolConstructor::construct(Interpreter& interpreter, Function&)
{
interpreter.throw_exception<TypeError>(ErrorType::NotAConstructor, "Symbol");
return {};

View file

@ -39,7 +39,7 @@ public:
virtual ~SymbolConstructor() override;
virtual Value call(Interpreter&) override;
virtual Value construct(Interpreter&) override;
virtual Value construct(Interpreter&, Function& new_target) override;
private:
virtual bool has_constructor() const override { return true; }

View file

@ -27,6 +27,21 @@ try {
assert(new p(15).x === 15);
assert(new p(15, true).x === 30);
let p;
function theNewTarget() {};
const handler = {
construct(target, arguments, newTarget) {
assert(target === f);
assert(newTarget === theNewTarget);
if (arguments[1])
return Reflect.construct(target, [arguments[0] * 2], newTarget);
return Reflect.construct(target, arguments, newTarget);
},
};
p = new Proxy(f, handler);
Reflect.construct(p, [15], theNewTarget);
// Invariants
[{}, [], new Proxy({}, {})].forEach(item => {
assertThrowsError(() => {

View file

@ -59,10 +59,10 @@ XMLHttpRequestConstructor::~XMLHttpRequestConstructor()
JS::Value XMLHttpRequestConstructor::call(JS::Interpreter& interpreter)
{
return construct(interpreter);
return construct(interpreter, *this);
}
JS::Value XMLHttpRequestConstructor::construct(JS::Interpreter& interpreter)
JS::Value XMLHttpRequestConstructor::construct(JS::Interpreter& interpreter, Function&)
{
auto& window = static_cast<WindowObject&>(global_object());
return interpreter.heap().allocate<XMLHttpRequestWrapper>(window, window, XMLHttpRequest::create(window.impl()));

View file

@ -38,7 +38,7 @@ public:
virtual ~XMLHttpRequestConstructor() override;
virtual JS::Value call(JS::Interpreter&) override;
virtual JS::Value construct(JS::Interpreter&) override;
virtual JS::Value construct(JS::Interpreter& interpreter, Function& new_target) override;
private:
virtual bool has_constructor() const override { return true; }