1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-07-25 04:17:35 +00:00

LibJS: Add Function() and Function.prototype

This commit is contained in:
Linus Groh 2020-04-04 14:34:31 +01:00 committed by Andreas Kling
parent 4d931b524d
commit 2944039d6b
15 changed files with 463 additions and 4 deletions

View file

@ -24,16 +24,14 @@
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <AK/FlyString.h>
#include <LibJS/Heap/Heap.h>
#include <LibJS/Interpreter.h>
#include <LibJS/Runtime/Function.h>
#include <LibJS/Runtime/Value.h>
namespace JS {
Function::Function()
{
put("prototype", heap().allocate<Object>());
set_prototype(interpreter().function_prototype());
}
Function::~Function()

View file

@ -0,0 +1,77 @@
/*
* Copyright (c) 2020, Linus Groh <mail@linusgroh.de>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <AK/StringBuilder.h>
#include <LibJS/AST.h>
#include <LibJS/Interpreter.h>
#include <LibJS/Parser.h>
#include <LibJS/Runtime/Error.h>
#include <LibJS/Runtime/FunctionConstructor.h>
#include <LibJS/Runtime/ScriptFunction.h>
namespace JS {
FunctionConstructor::FunctionConstructor()
{
put("prototype", interpreter().function_prototype());
put("length", Value(1));
}
FunctionConstructor::~FunctionConstructor()
{
}
Value FunctionConstructor::call(Interpreter& interpreter)
{
return construct(interpreter);
}
Value FunctionConstructor::construct(Interpreter& interpreter)
{
String parameters_source = "";
String body_source = "";
if (interpreter.argument_count() == 1)
body_source = interpreter.argument(0).to_string();
if (interpreter.argument_count() > 1) {
Vector<String> parameters;
for (size_t i = 0; i < interpreter.argument_count() - 1; ++i)
parameters.append(interpreter.argument(i).to_string());
StringBuilder parameters_builder;
parameters_builder.join(',', parameters);
parameters_source = parameters_builder.build();
body_source = interpreter.argument(interpreter.argument_count() - 1).to_string();
}
auto source = String::format("function (%s) { %s }", parameters_source.characters(), body_source.characters());
auto parser = Parser(Lexer(source));
auto function_expression = parser.parse_function_node<FunctionExpression>();
if (parser.has_errors()) {
// FIXME: The parser should expose parsing error strings rather than just fprintf()'ing them
return interpreter.heap().allocate<Error>("SyntaxError", "");
}
return function_expression->execute(interpreter);
}
}

View file

@ -0,0 +1,46 @@
/*
* Copyright (c) 2020, Linus Groh <mail@linusgroh.de>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#pragma once
#include <LibJS/Runtime/NativeFunction.h>
namespace JS {
class FunctionConstructor final : public NativeFunction {
public:
FunctionConstructor();
virtual ~FunctionConstructor() override;
virtual Value call(Interpreter&) override;
virtual Value construct(Interpreter&) override;
private:
virtual bool has_constructor() const override { return true; }
virtual const char* class_name() const override { return "FunctionConstructor"; }
};
}

View file

@ -0,0 +1,123 @@
/*
* Copyright (c) 2020, Linus Groh <mail@linusgroh.de>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <AK/Function.h>
#include <AK/StringBuilder.h>
#include <LibJS/AST.h>
#include <LibJS/Interpreter.h>
#include <LibJS/Runtime/Error.h>
#include <LibJS/Runtime/Function.h>
#include <LibJS/Runtime/FunctionPrototype.h>
#include <LibJS/Runtime/ScriptFunction.h>
namespace JS {
FunctionPrototype::FunctionPrototype()
{
put_native_function("apply", apply, 2);
put_native_function("bind", bind, 1);
put_native_function("call", call, 1);
put_native_function("toString", to_string);
put("length", Value(0));
}
FunctionPrototype::~FunctionPrototype()
{
}
Value FunctionPrototype::apply(Interpreter& interpreter)
{
auto* this_object = interpreter.this_value().to_object(interpreter.heap());
if (!this_object)
return {};
if (!this_object->is_function())
return interpreter.throw_exception<Error>("TypeError", "Not a Function object");
auto function = static_cast<Function*>(this_object);
auto this_arg = interpreter.argument(0);
auto arg_array = interpreter.argument(1);
if (arg_array.is_null() || arg_array.is_undefined())
return interpreter.call(function, this_arg);
if (!arg_array.is_object())
return interpreter.throw_exception<Error>("TypeError", "argument array must be an object");
size_t length = 0;
auto length_property = arg_array.as_object().get("length");
if (length_property.has_value())
length = length_property.value().to_number().to_i32();
Vector<Value> arguments;
for (size_t i = 0; i < length; ++i)
arguments.append(arg_array.as_object().get(String::number(i)).value_or(js_undefined()));
return interpreter.call(function, this_arg, arguments);
}
Value FunctionPrototype::bind(Interpreter& interpreter)
{
auto* this_object = interpreter.this_value().to_object(interpreter.heap());
if (!this_object)
return {};
// FIXME: Implement me :^)
ASSERT_NOT_REACHED();
}
Value FunctionPrototype::call(Interpreter& interpreter)
{
auto* this_object = interpreter.this_value().to_object(interpreter.heap());
if (!this_object)
return {};
if (!this_object->is_function())
return interpreter.throw_exception<Error>("TypeError", "Not a Function object");
auto function = static_cast<Function*>(this_object);
auto this_arg = interpreter.argument(0);
Vector<Value> arguments;
if (interpreter.argument_count() > 1) {
for (size_t i = 1; i < interpreter.argument_count(); ++i)
arguments.append(interpreter.argument(i));
}
return interpreter.call(function, this_arg, arguments);
}
Value FunctionPrototype::to_string(Interpreter& interpreter)
{
auto* this_object = interpreter.this_value().to_object(interpreter.heap());
if (!this_object)
return {};
if (!this_object->is_function())
return interpreter.throw_exception<Error>("TypeError", "Not a Function object");
// FIXME: Functions should be able to know their name, if any
if (this_object->is_native_function()) {
auto function_source = String::format("function () {\n [%s]\n}", this_object->class_name());
return js_string(interpreter, function_source);
}
auto parameters = static_cast<ScriptFunction*>(this_object)->parameters();
StringBuilder parameters_builder;
parameters_builder.join(", ", parameters);
// FIXME: ASTNodes should be able to dump themselves to source strings - something like this:
// auto& body = static_cast<ScriptFunction*>(this_object)->body();
// auto body_source = body.to_source();
auto function_source = String::format("function (%s) {\n %s\n}", parameters_builder.build().characters(), "???");
return js_string(interpreter, function_source);
}
}

View file

@ -0,0 +1,47 @@
/*
* Copyright (c) 2020, Linus Groh <mail@linusgroh.de>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#pragma once
#include <LibJS/Runtime/Object.h>
namespace JS {
class FunctionPrototype final : public Object {
public:
FunctionPrototype();
virtual ~FunctionPrototype() override;
private:
virtual const char* class_name() const override { return "FunctionPrototype"; }
static Value apply(Interpreter&);
static Value bind(Interpreter&);
static Value call(Interpreter&);
static Value to_string(Interpreter&);
};
}

View file

@ -5,6 +5,7 @@
#include <LibJS/Runtime/ConsoleObject.h>
#include <LibJS/Runtime/DateConstructor.h>
#include <LibJS/Runtime/ErrorConstructor.h>
#include <LibJS/Runtime/FunctionConstructor.h>
#include <LibJS/Runtime/GlobalObject.h>
#include <LibJS/Runtime/MathObject.h>
#include <LibJS/Runtime/NativeFunction.h>
@ -26,6 +27,7 @@ GlobalObject::GlobalObject()
put("console", heap().allocate<ConsoleObject>());
put("Date", heap().allocate<DateConstructor>());
put("Error", heap().allocate<ErrorConstructor>());
put("Function", heap().allocate<FunctionConstructor>());
put("Math", heap().allocate<MathObject>());
put("Object", heap().allocate<ObjectConstructor>());
}

View file

@ -37,6 +37,7 @@ ScriptFunction::ScriptFunction(const ScopeNode& body, Vector<FlyString> paramete
: m_body(body)
, m_parameters(move(parameters))
{
put("prototype", heap().allocate<Object>());
put_native_property("length", length_getter, length_setter);
}