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:
parent
c209ea1985
commit
7c4e53f31e
25 changed files with 145 additions and 102 deletions
|
@ -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({});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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())
|
||||||
|
|
|
@ -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());
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -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');
|
||||||
|
|
|
@ -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();
|
||||||
|
|
|
@ -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>());
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)));
|
||||||
}
|
}
|
||||||
|
|
|
@ -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; }
|
||||||
|
|
|
@ -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 {};
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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&);
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -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];
|
||||||
|
|
|
@ -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; }
|
||||||
|
|
|
@ -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();
|
||||||
|
|
|
@ -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();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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 };
|
||||||
|
|
|
@ -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());
|
||||||
}
|
}
|
||||||
|
|
|
@ -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();
|
||||||
|
|
|
@ -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();
|
||||||
|
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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()!
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue