1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-05-31 10:38:11 +00:00

LibJS: Rework how native functions are called to improve |this| value

Native functions now only get the Interpreter& as an argument. They can
then extract |this| along with any indexed arguments it wants from it.

This forces functions that want |this| to actually deal with calling
interpreter.this_value().to_object(), and dealing with the possibility
of a non-object |this|.

This is still not great but let's keep massaging it forward.
This commit is contained in:
Andreas Kling 2020-03-28 22:48:35 +01:00
parent c209ea1985
commit 7c4e53f31e
25 changed files with 145 additions and 102 deletions

View file

@ -99,7 +99,7 @@ Value CallExpression::execute(Interpreter& interpreter) const
} }
} }
auto result = function->call(interpreter, call_frame.arguments); auto result = function->call(interpreter);
interpreter.pop_call_frame(); interpreter.pop_call_frame();
if (is_new_expression()) { if (is_new_expression()) {
@ -554,10 +554,10 @@ Value AssignmentExpression::execute(Interpreter& interpreter) const
}; };
} else if (m_lhs->is_member_expression()) { } else if (m_lhs->is_member_expression()) {
commit = [&](Value value) { commit = [&](Value value) {
auto object = static_cast<const MemberExpression&>(*m_lhs).object().execute(interpreter).to_object(interpreter.heap()); if (auto* object = static_cast<const MemberExpression&>(*m_lhs).object().execute(interpreter).to_object(interpreter.heap())) {
ASSERT(object.is_object()); auto property_name = static_cast<const MemberExpression&>(*m_lhs).computed_property_name(interpreter);
auto property_name = static_cast<const MemberExpression&>(*m_lhs).computed_property_name(interpreter); object->put(property_name, value);
object.as_object()->put(property_name, value); }
}; };
} else { } else {
ASSERT_NOT_REACHED(); ASSERT_NOT_REACHED();
@ -751,11 +751,10 @@ FlyString MemberExpression::computed_property_name(Interpreter& interpreter) con
Value MemberExpression::execute(Interpreter& interpreter) const Value MemberExpression::execute(Interpreter& interpreter) const
{ {
auto object_result = m_object->execute(interpreter).to_object(interpreter.heap()); auto* object_result = m_object->execute(interpreter).to_object(interpreter.heap());
if (interpreter.exception()) if (interpreter.exception())
return {}; return {};
ASSERT(object_result.is_object()); auto result = object_result->get(computed_property_name(interpreter));
auto result = object_result.as_object()->get(computed_property_name(interpreter));
return result.value_or({}); return result.value_or({});
} }

View file

@ -190,7 +190,7 @@ Value Interpreter::call(Function* function, Value this_value, const Vector<Value
auto& call_frame = push_call_frame(); auto& call_frame = push_call_frame();
call_frame.this_value = this_value; call_frame.this_value = this_value;
call_frame.arguments = arguments; call_frame.arguments = arguments;
auto result = function->call(*this, call_frame.arguments); auto result = function->call(*this);
pop_call_frame(); pop_call_frame();
return result; return result;
} }

View file

@ -96,6 +96,8 @@ public:
return m_call_stack.last(); return m_call_stack.last();
} }
void pop_call_frame() { m_call_stack.take_last(); } void pop_call_frame() { m_call_stack.take_last(); }
const CallFrame& call_frame() { return m_call_stack.last(); }
Value this_value() const Value this_value() const
{ {
if (m_call_stack.is_empty()) if (m_call_stack.is_empty())

View file

@ -35,24 +35,30 @@ namespace JS {
ArrayPrototype::ArrayPrototype() ArrayPrototype::ArrayPrototype()
{ {
put_native_function("shift", [](Object* this_object, const Vector<Value>&) -> Value { put_native_function("shift", [](Interpreter& interpreter) -> Value {
ASSERT(this_object); auto* this_object = interpreter.this_value().to_object(interpreter.heap());
if (!this_object)
return {};
ASSERT(this_object->is_array()); ASSERT(this_object->is_array());
return static_cast<Array*>(this_object)->shift(); return static_cast<Array*>(this_object)->shift();
}); });
put_native_function("pop", [](Object* this_object, const Vector<Value>&) -> Value { put_native_function("pop", [](Interpreter& interpreter) -> Value {
ASSERT(this_object); auto* this_object = interpreter.this_value().to_object(interpreter.heap());
if (!this_object)
return {};
ASSERT(this_object->is_array()); ASSERT(this_object->is_array());
return static_cast<Array*>(this_object)->pop(); return static_cast<Array*>(this_object)->pop();
}); });
put_native_function("push", [](Object* this_object, const Vector<Value>& arguments) -> Value { put_native_function("push", [](Interpreter& interpreter) -> Value {
if (arguments.is_empty()) auto* this_object = interpreter.this_value().to_object(interpreter.heap());
return js_undefined(); if (!this_object)
ASSERT(this_object); return {};
ASSERT(this_object->is_array()); ASSERT(this_object->is_array());
static_cast<Array*>(this_object)->push(arguments[0]); if (interpreter.call_frame().arguments.is_empty())
return js_undefined();
static_cast<Array*>(this_object)->push(interpreter.call_frame().arguments[0]);
return Value(static_cast<const Array*>(this_object)->length()); return Value(static_cast<const Array*>(this_object)->length());
}); });
} }

View file

@ -24,8 +24,9 @@
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/ */
#include <AK/Function.h>
#include <AK/FlyString.h> #include <AK/FlyString.h>
#include <AK/Function.h>
#include <LibJS/Interpreter.h>
#include <LibJS/Runtime/ConsoleObject.h> #include <LibJS/Runtime/ConsoleObject.h>
#include <stdio.h> #include <stdio.h>
@ -33,10 +34,10 @@ namespace JS {
ConsoleObject::ConsoleObject() ConsoleObject::ConsoleObject()
{ {
put_native_function("log", [](Object*, const Vector<Value>& arguments) -> Value { put_native_function("log", [](Interpreter& interpreter) -> Value {
for (size_t i = 0; i < arguments.size(); ++i) { for (size_t i = 0; i < interpreter.call_frame().arguments.size(); ++i) {
printf("%s", arguments[i].to_string().characters()); printf("%s", interpreter.call_frame().arguments[i].to_string().characters());
if (i != arguments.size() - 1) if (i != interpreter.call_frame().arguments.size() - 1)
putchar(' '); putchar(' ');
} }
putchar('\n'); putchar('\n');

View file

@ -35,7 +35,7 @@ class Function : public Object {
public: public:
virtual ~Function(); virtual ~Function();
virtual Value call(Interpreter&, const Vector<Value>&) = 0; virtual Value call(Interpreter&) = 0;
protected: protected:
Function(); Function();

View file

@ -14,15 +14,15 @@ namespace JS {
GlobalObject::GlobalObject() GlobalObject::GlobalObject()
{ {
put("console", heap().allocate<ConsoleObject>()); put("console", heap().allocate<ConsoleObject>());
put_native_function("gc", [](Object* this_object, Vector<Value>) -> Value { put_native_function("gc", [](Interpreter& interpreter) -> Value {
dbg() << "Forced garbage collection requested!"; dbg() << "Forced garbage collection requested!";
this_object->heap().collect_garbage(); interpreter.heap().collect_garbage();
return js_undefined(); return js_undefined();
}); });
put_native_function("isNaN", [](Object*, Vector<Value> arguments) -> Value { put_native_function("isNaN", [](Interpreter& interpreter) -> Value {
if (arguments.size() < 1) if (interpreter.call_frame().arguments.size() < 1)
return js_undefined(); return js_undefined();
return Value(arguments[0].to_number().is_nan()); return Value(interpreter.call_frame().arguments[0].to_number().is_nan());
}); });
put("Math", heap().allocate<MathObject>()); put("Math", heap().allocate<MathObject>());
put("Object", heap().allocate<ObjectConstructor>()); put("Object", heap().allocate<ObjectConstructor>());

View file

@ -32,7 +32,7 @@ namespace JS {
MathObject::MathObject() MathObject::MathObject()
{ {
put_native_function("random", [](Object*, const Vector<Value>&) { put_native_function("random", [](Interpreter&) {
#ifdef __serenity__ #ifdef __serenity__
double r = (double)arc4random() / (double)UINT32_MAX; double r = (double)arc4random() / (double)UINT32_MAX;
#else #else

View file

@ -30,7 +30,7 @@
namespace JS { namespace JS {
NativeFunction::NativeFunction(AK::Function<Value(Object*, const Vector<Value>&)> native_function) NativeFunction::NativeFunction(AK::Function<Value(Interpreter&)> native_function)
: m_native_function(move(native_function)) : m_native_function(move(native_function))
{ {
} }
@ -39,14 +39,9 @@ NativeFunction::~NativeFunction()
{ {
} }
Value NativeFunction::call(Interpreter& interpreter, const Vector<Value>& arguments) Value NativeFunction::call(Interpreter& interpreter)
{ {
auto this_value = interpreter.this_value(); return m_native_function(interpreter);
// FIXME: Why are we here with a non-object 'this'?
Object* this_object = nullptr;
if (this_value.is_object())
this_object = this_value.as_object();
return m_native_function(this_object, arguments);
} }
} }

View file

@ -33,10 +33,10 @@ namespace JS {
class NativeFunction : public Function { class NativeFunction : public Function {
public: public:
explicit NativeFunction(AK::Function<Value(Object*, const Vector<Value>&)>); explicit NativeFunction(AK::Function<Value(Interpreter&)>);
virtual ~NativeFunction() override; virtual ~NativeFunction() override;
virtual Value call(Interpreter&, const Vector<Value>&) override; virtual Value call(Interpreter&) override;
protected: protected:
NativeFunction() {} NativeFunction() {}
@ -45,7 +45,7 @@ private:
virtual bool is_native_function() const override { return true; } virtual bool is_native_function() const override { return true; }
virtual const char* class_name() const override { return "NativeFunction"; } virtual const char* class_name() const override { return "NativeFunction"; }
AK::Function<Value(Object*, const Vector<Value>&)> m_native_function; AK::Function<Value(Interpreter&)> m_native_function;
}; };
} }

View file

@ -102,7 +102,7 @@ void Object::put(const FlyString& property_name, Value value)
put_own_property(*this, property_name, value); put_own_property(*this, property_name, value);
} }
void Object::put_native_function(const FlyString& property_name, AK::Function<Value(Object*, Vector<Value>)> native_function) void Object::put_native_function(const FlyString& property_name, AK::Function<Value(Interpreter&)> native_function)
{ {
put(property_name, heap().allocate<NativeFunction>(move(native_function))); put(property_name, heap().allocate<NativeFunction>(move(native_function)));
} }

View file

@ -46,7 +46,7 @@ public:
virtual Optional<Value> get_own_property(const Object& this_object, const FlyString& property_name) const; virtual Optional<Value> get_own_property(const Object& this_object, const FlyString& property_name) const;
virtual bool put_own_property(Object& this_object, const FlyString& property_name, Value); virtual bool put_own_property(Object& this_object, const FlyString& property_name, Value);
void put_native_function(const FlyString& property_name, AK::Function<Value(Object*, Vector<Value>)>); void put_native_function(const FlyString& property_name, AK::Function<Value(Interpreter&)>);
void put_native_property(const FlyString& property_name, AK::Function<Value(Object*)> getter, AK::Function<void(Object*, Value)> setter); void put_native_property(const FlyString& property_name, AK::Function<Value(Object*)> getter, AK::Function<void(Object*, Value)> setter);
virtual bool is_error() const { return false; } virtual bool is_error() const { return false; }

View file

@ -35,39 +35,40 @@ ObjectConstructor::ObjectConstructor()
{ {
put("prototype", interpreter().object_prototype()); put("prototype", interpreter().object_prototype());
put_native_function("getPrototypeOf", [this](Object*, const Vector<Value>& arguments) -> Value { put_native_function("getPrototypeOf", get_prototype_of);
if (arguments.size() < 1) put_native_function("setPrototypeOf", set_prototype_of);
return {};
auto object = arguments[0].to_object(heap());
if (interpreter().exception())
return {};
if (!object.is_object())
return {};
return object.as_object()->prototype();
});
put_native_function("setPrototypeOf", [this](Object*, const Vector<Value>& arguments) -> Value {
if (arguments.size() < 2)
return {};
auto object = arguments[0].to_object(heap());
if (interpreter().exception())
return {};
if (!object.is_object())
return {};
if (!arguments[1].is_object())
return {};
const_cast<Object*>(object.as_object())->set_prototype(const_cast<Object*>(arguments[1].as_object()));
return {};
});
} }
ObjectConstructor::~ObjectConstructor() ObjectConstructor::~ObjectConstructor()
{ {
} }
Value ObjectConstructor::call(Interpreter& interpreter, const Vector<Value>&) Value ObjectConstructor::call(Interpreter& interpreter)
{ {
return interpreter.heap().allocate<Object>(); return interpreter.heap().allocate<Object>();
} }
Value ObjectConstructor::get_prototype_of(Interpreter& interpreter)
{
if (interpreter.call_frame().arguments.size() < 1)
return {};
auto* object = interpreter.call_frame().arguments[0].to_object(interpreter.heap());
if (interpreter.exception())
return {};
return object->prototype();
}
Value ObjectConstructor::set_prototype_of(Interpreter& interpreter)
{
if (interpreter.call_frame().arguments.size() < 2)
return {};
if (!interpreter.call_frame().arguments[1].is_object())
return {};
auto* object = interpreter.call_frame().arguments[0].to_object(interpreter.heap());
if (interpreter.exception())
return {};
object->set_prototype(const_cast<Object*>(interpreter.call_frame().arguments[1].as_object()));
return {};
}
} }

View file

@ -35,10 +35,13 @@ public:
ObjectConstructor(); ObjectConstructor();
virtual ~ObjectConstructor() override; virtual ~ObjectConstructor() override;
virtual Value call(Interpreter&, const Vector<Value>&) override; virtual Value call(Interpreter&) override;
private: private:
virtual const char* class_name() const override { return "ObjectConstructor"; } virtual const char* class_name() const override { return "ObjectConstructor"; }
static Value get_prototype_of(Interpreter&);
static Value set_prototype_of(Interpreter&);
}; };
} }

View file

@ -37,21 +37,26 @@ ObjectPrototype::ObjectPrototype()
{ {
set_prototype(nullptr); set_prototype(nullptr);
put_native_function("hasOwnProperty", [](Object* this_object, const Vector<Value>& arguments) -> Value { put_native_function("hasOwnProperty", [](Interpreter& interpreter) -> Value {
ASSERT(this_object); auto* this_object = interpreter.this_value().to_object(interpreter.heap());
if (arguments.is_empty()) if (!this_object)
return {};
if (interpreter.call_frame().arguments.is_empty())
return js_undefined(); return js_undefined();
return Value(this_object->has_own_property(arguments[0].to_string())); return Value(this_object->has_own_property(interpreter.call_frame().arguments[0].to_string()));
}); });
put_native_function("toString", [](Object* this_object, Vector<Value>) -> Value { put_native_function("toString", [](Interpreter& interpreter) -> Value {
ASSERT(this_object); auto* this_object = interpreter.this_value().to_object(interpreter.heap());
if (!this_object)
return {};
return Value(this_object->to_string()); return Value(this_object->to_string());
}); });
put_native_function("valueOf", [](Object* this_object, Vector<Value>) -> Value { put_native_function("valueOf", [](Interpreter& interpreter) -> Value {
ASSERT(this_object); auto* this_object = interpreter.this_value().to_object(interpreter.heap());
if (!this_object)
return {};
return this_object->value_of(); return this_object->value_of();
}); });
} }

View file

@ -41,8 +41,9 @@ ScriptFunction::~ScriptFunction()
{ {
} }
Value ScriptFunction::call(Interpreter& interpreter, const Vector<Value>& argument_values) Value ScriptFunction::call(Interpreter& interpreter)
{ {
auto& argument_values = interpreter.call_frame().arguments;
Vector<Argument> arguments; Vector<Argument> arguments;
for (size_t i = 0; i < m_parameters.size(); ++i) { for (size_t i = 0; i < m_parameters.size(); ++i) {
auto name = parameters()[i]; auto name = parameters()[i];

View file

@ -38,7 +38,7 @@ public:
const ScopeNode& body() const { return m_body; } const ScopeNode& body() const { return m_body; }
const Vector<FlyString>& parameters() const { return m_parameters; }; const Vector<FlyString>& parameters() const { return m_parameters; };
virtual Value call(Interpreter&, const Vector<Value>&) override; virtual Value call(Interpreter&) override;
private: private:
virtual bool is_script_function() const final { return true; } virtual bool is_script_function() const final { return true; }

View file

@ -44,23 +44,28 @@ StringPrototype::StringPrototype()
return Value((i32) static_cast<const StringObject*>(this_object)->primitive_string()->string().length()); return Value((i32) static_cast<const StringObject*>(this_object)->primitive_string()->string().length());
}, },
nullptr); nullptr);
put_native_function("charAt", [](Object* this_object, const Vector<Value>& arguments) -> Value { put_native_function("charAt", [](Interpreter& interpreter) -> Value {
ASSERT(this_object); auto* this_object = interpreter.this_value().to_object(interpreter.heap());
if (!this_object)
return {};
i32 index = 0; i32 index = 0;
if (!arguments.is_empty()) if (!interpreter.call_frame().arguments.is_empty())
index = arguments[0].to_i32(); index = interpreter.call_frame().arguments[0].to_i32();
ASSERT(this_object->is_string_object()); ASSERT(this_object->is_string_object());
auto underlying_string = static_cast<const StringObject*>(this_object)->primitive_string()->string(); auto underlying_string = static_cast<const StringObject*>(this_object)->primitive_string()->string();
if (index < 0 || index >= static_cast<i32>(underlying_string.length())) if (index < 0 || index >= static_cast<i32>(underlying_string.length()))
return js_string(this_object->heap(), String::empty()); return js_string(this_object->heap(), String::empty());
return js_string(this_object->heap(), underlying_string.substring(index, 1)); return js_string(this_object->heap(), underlying_string.substring(index, 1));
}); });
put_native_function("repeat", [](Object* this_object, const Vector<Value>& arguments) -> Value { put_native_function("repeat", [](Interpreter& interpreter) -> Value {
auto* this_object = interpreter.this_value().to_object(interpreter.heap());
if (!this_object)
return {};
ASSERT(this_object->is_string_object()); ASSERT(this_object->is_string_object());
if (arguments.is_empty()) if (interpreter.call_frame().arguments.is_empty())
return js_string(this_object->heap(), String::empty()); return js_string(this_object->heap(), String::empty());
i32 count = 0; i32 count = 0;
count = arguments[0].to_i32(); count = interpreter.call_frame().arguments[0].to_i32();
if (count < 0) { if (count < 0) {
// FIXME: throw RangeError // FIXME: throw RangeError
return js_undefined(); return js_undefined();

View file

@ -27,6 +27,8 @@
#include <AK/FlyString.h> #include <AK/FlyString.h>
#include <AK/String.h> #include <AK/String.h>
#include <LibJS/Heap/Heap.h> #include <LibJS/Heap/Heap.h>
#include <LibJS/Interpreter.h>
#include <LibJS/Runtime/Error.h>
#include <LibJS/Runtime/Object.h> #include <LibJS/Runtime/Object.h>
#include <LibJS/Runtime/PrimitiveString.h> #include <LibJS/Runtime/PrimitiveString.h>
#include <LibJS/Runtime/StringObject.h> #include <LibJS/Runtime/StringObject.h>
@ -86,7 +88,7 @@ bool Value::to_boolean() const
} }
} }
Value Value::to_object(Heap& heap) const Object* Value::to_object(Heap& heap) const
{ {
if (is_object()) if (is_object())
return const_cast<Object*>(as_object()); return const_cast<Object*>(as_object());
@ -94,6 +96,11 @@ Value Value::to_object(Heap& heap) const
if (is_string()) if (is_string())
return heap.allocate<StringObject>(m_value.as_string); return heap.allocate<StringObject>(m_value.as_string);
if (is_null() || is_undefined()) {
heap.interpreter().throw_exception<Error>("TypeError", "ToObject on null or undefined.");
return nullptr;
}
ASSERT_NOT_REACHED(); ASSERT_NOT_REACHED();
} }

View file

@ -144,7 +144,7 @@ public:
Value to_number() const; Value to_number() const;
i32 to_i32() const; i32 to_i32() const;
Value to_object(Heap&) const; Object* to_object(Heap&) const;
private: private:
Type m_type { Type::Undefined }; Type m_type { Type::Undefined };

View file

@ -24,8 +24,9 @@
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/ */
#include <AK/Function.h>
#include <AK/FlyString.h> #include <AK/FlyString.h>
#include <AK/Function.h>
#include <LibJS/Interpreter.h>
#include <LibJS/Runtime/PrimitiveString.h> #include <LibJS/Runtime/PrimitiveString.h>
#include <LibJS/Runtime/Value.h> #include <LibJS/Runtime/Value.h>
#include <LibWeb/Bindings/CanvasRenderingContext2DWrapper.h> #include <LibWeb/Bindings/CanvasRenderingContext2DWrapper.h>
@ -50,7 +51,8 @@ CanvasRenderingContext2DWrapper::CanvasRenderingContext2DWrapper(CanvasRendering
[this](JS::Object*, JS::Value value) { [this](JS::Object*, JS::Value value) {
m_impl->set_fill_style(value.to_string()); m_impl->set_fill_style(value.to_string());
}); });
put_native_function("fillRect", [this](JS::Object*, const Vector<JS::Value>& arguments) { put_native_function("fillRect", [this](JS::Interpreter& interpreter) {
auto& arguments = interpreter.call_frame().arguments;
if (arguments.size() >= 4) { if (arguments.size() >= 4) {
m_impl->fill_rect(arguments[0].to_i32(), arguments[1].to_i32(), arguments[2].to_i32(), arguments[3].to_i32()); m_impl->fill_rect(arguments[0].to_i32(), arguments[1].to_i32(), arguments[2].to_i32(), arguments[3].to_i32());
} }

View file

@ -25,6 +25,7 @@
*/ */
#include <AK/FlyString.h> #include <AK/FlyString.h>
#include <LibJS/Interpreter.h>
#include <LibJS/Runtime/PrimitiveString.h> #include <LibJS/Runtime/PrimitiveString.h>
#include <LibJS/Runtime/Value.h> #include <LibJS/Runtime/Value.h>
#include <LibWeb/Bindings/DocumentWrapper.h> #include <LibWeb/Bindings/DocumentWrapper.h>
@ -37,7 +38,8 @@ namespace Bindings {
DocumentWrapper::DocumentWrapper(Document& document) DocumentWrapper::DocumentWrapper(Document& document)
: NodeWrapper(document) : NodeWrapper(document)
{ {
put_native_function("getElementById", [this](JS::Object*, const Vector<JS::Value>& arguments) -> JS::Value { put_native_function("getElementById", [this](JS::Interpreter& interpreter) -> JS::Value {
auto& arguments = interpreter.call_frame().arguments;
if (arguments.is_empty()) if (arguments.is_empty())
return JS::js_null(); return JS::js_null();
auto id = arguments[0].to_string(); auto id = arguments[0].to_string();

View file

@ -24,8 +24,9 @@
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/ */
#include <AK/Function.h>
#include <AK/FlyString.h> #include <AK/FlyString.h>
#include <AK/Function.h>
#include <LibJS/Interpreter.h>
#include <LibJS/Runtime/Function.h> #include <LibJS/Runtime/Function.h>
#include <LibWeb/Bindings/EventListenerWrapper.h> #include <LibWeb/Bindings/EventListenerWrapper.h>
#include <LibWeb/Bindings/EventTargetWrapper.h> #include <LibWeb/Bindings/EventTargetWrapper.h>
@ -38,7 +39,12 @@ namespace Bindings {
EventTargetWrapper::EventTargetWrapper(EventTarget& impl) EventTargetWrapper::EventTargetWrapper(EventTarget& impl)
: m_impl(impl) : m_impl(impl)
{ {
put_native_function("addEventListener", [](Object* this_object, const Vector<JS::Value>& arguments) { put_native_function("addEventListener", [](JS::Interpreter& interpreter) -> JS::Value {
auto* this_object = interpreter.this_value().to_object(interpreter.heap());
if (!this_object)
return {};
auto& arguments = interpreter.call_frame().arguments;
if (arguments.size() < 2) if (arguments.size() < 2)
return JS::js_undefined(); return JS::js_undefined();

View file

@ -24,8 +24,9 @@
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/ */
#include <AK/Function.h>
#include <AK/FlyString.h> #include <AK/FlyString.h>
#include <AK/Function.h>
#include <LibJS/Interpreter.h>
#include <LibJS/Runtime/PrimitiveString.h> #include <LibJS/Runtime/PrimitiveString.h>
#include <LibJS/Runtime/Value.h> #include <LibJS/Runtime/Value.h>
#include <LibWeb/Bindings/CanvasRenderingContext2DWrapper.h> #include <LibWeb/Bindings/CanvasRenderingContext2DWrapper.h>
@ -39,7 +40,8 @@ namespace Bindings {
HTMLCanvasElementWrapper::HTMLCanvasElementWrapper(HTMLCanvasElement& element) HTMLCanvasElementWrapper::HTMLCanvasElementWrapper(HTMLCanvasElement& element)
: ElementWrapper(element) : ElementWrapper(element)
{ {
put_native_function("getContext", [this](JS::Object*, const Vector<JS::Value>& arguments) -> JS::Value { put_native_function("getContext", [this](JS::Interpreter& interpreter) -> JS::Value {
auto& arguments = interpreter.call_frame().arguments;
if (arguments.size() >= 1) { if (arguments.size() >= 1) {
auto* context = node().get_context(arguments[0].to_string()); auto* context = node().get_context(arguments[0].to_string());
return wrap(heap(), *context); return wrap(heap(), *context);

View file

@ -338,14 +338,16 @@ JS::Interpreter& Document::interpreter()
if (!m_interpreter) { if (!m_interpreter) {
m_interpreter = make<JS::Interpreter>(); m_interpreter = make<JS::Interpreter>();
m_interpreter->global_object().put_native_function("alert", [](JS::Object*, const Vector<JS::Value>& arguments) -> JS::Value { m_interpreter->global_object().put_native_function("alert", [](JS::Interpreter& interpreter) -> JS::Value {
auto& arguments = interpreter.call_frame().arguments;
if (arguments.size() < 1) if (arguments.size() < 1)
return JS::js_undefined(); return JS::js_undefined();
GUI::MessageBox::show(arguments[0].to_string(), "Alert", GUI::MessageBox::Type::Information); GUI::MessageBox::show(arguments[0].to_string(), "Alert", GUI::MessageBox::Type::Information);
return JS::js_undefined(); return JS::js_undefined();
}); });
m_interpreter->global_object().put_native_function("setInterval", [this](JS::Object*, const Vector<JS::Value>& arguments) -> JS::Value { m_interpreter->global_object().put_native_function("setInterval", [this](JS::Interpreter& interpreter) -> JS::Value {
auto& arguments = interpreter.call_frame().arguments;
if (arguments.size() < 2) if (arguments.size() < 2)
return JS::js_undefined(); return JS::js_undefined();
ASSERT(arguments[0].is_object()); ASSERT(arguments[0].is_object());
@ -355,14 +357,16 @@ JS::Interpreter& Document::interpreter()
// FIXME: This timer should not be leaked! It should also be removable with clearInterval()! // FIXME: This timer should not be leaked! It should also be removable with clearInterval()!
(void)Core::Timer::construct( (void)Core::Timer::construct(
arguments[1].to_i32(), [this, callback] { arguments[1].to_i32(), [this, callback] {
const_cast<JS::Function*>(static_cast<const JS::Function*>(callback.cell()))->call(*m_interpreter, {}); // FIXME: Perform the call through Interpreter so it can set up a call frame!
const_cast<JS::Function*>(static_cast<const JS::Function*>(callback.cell()))->call(*m_interpreter);
}) })
.leak_ref(); .leak_ref();
return JS::js_undefined(); return JS::js_undefined();
}); });
m_interpreter->global_object().put_native_function("requestAnimationFrame", [this](JS::Object*, const Vector<JS::Value>& arguments) -> JS::Value { m_interpreter->global_object().put_native_function("requestAnimationFrame", [this](JS::Interpreter& interpreter) -> JS::Value {
auto& arguments = interpreter.call_frame().arguments;
if (arguments.size() < 1) if (arguments.size() < 1)
return JS::js_undefined(); return JS::js_undefined();
ASSERT(arguments[0].is_object()); ASSERT(arguments[0].is_object());
@ -370,13 +374,15 @@ JS::Interpreter& Document::interpreter()
auto callback = make_handle(const_cast<JS::Object*>(arguments[0].as_object())); auto callback = make_handle(const_cast<JS::Object*>(arguments[0].as_object()));
// FIXME: Don't hand out raw DisplayLink ID's to JavaScript! // FIXME: Don't hand out raw DisplayLink ID's to JavaScript!
i32 link_id = GUI::DisplayLink::register_callback([this, callback](i32 link_id) { i32 link_id = GUI::DisplayLink::register_callback([this, callback](i32 link_id) {
const_cast<JS::Function*>(static_cast<const JS::Function*>(callback.cell()))->call(*m_interpreter, {}); // FIXME: Perform the call through Interpreter so it can set up a call frame!
const_cast<JS::Function*>(static_cast<const JS::Function*>(callback.cell()))->call(*m_interpreter);
GUI::DisplayLink::unregister_callback(link_id); GUI::DisplayLink::unregister_callback(link_id);
}); });
return JS::Value(link_id); return JS::Value(link_id);
}); });
m_interpreter->global_object().put_native_function("cancelAnimationFrame", [](JS::Object*, const Vector<JS::Value>& arguments) -> JS::Value { m_interpreter->global_object().put_native_function("cancelAnimationFrame", [](JS::Interpreter& interpreter) -> JS::Value {
auto& arguments = interpreter.call_frame().arguments;
if (arguments.size() < 1) if (arguments.size() < 1)
return JS::js_undefined(); return JS::js_undefined();
// FIXME: We should not be passing untrusted numbers to DisplayLink::unregistered_callback()! // FIXME: We should not be passing untrusted numbers to DisplayLink::unregistered_callback()!