diff options
Diffstat (limited to 'Libraries/LibJS')
-rw-r--r-- | Libraries/LibJS/AST.cpp | 2 | ||||
-rw-r--r-- | Libraries/LibJS/Interpreter.cpp | 3 | ||||
-rw-r--r-- | Libraries/LibJS/Interpreter.h | 2 | ||||
-rw-r--r-- | Libraries/LibJS/Makefile | 2 | ||||
-rw-r--r-- | Libraries/LibJS/Runtime/Function.cpp | 6 | ||||
-rw-r--r-- | Libraries/LibJS/Runtime/FunctionConstructor.cpp | 77 | ||||
-rw-r--r-- | Libraries/LibJS/Runtime/FunctionConstructor.h | 46 | ||||
-rw-r--r-- | Libraries/LibJS/Runtime/FunctionPrototype.cpp | 123 | ||||
-rw-r--r-- | Libraries/LibJS/Runtime/FunctionPrototype.h | 47 | ||||
-rw-r--r-- | Libraries/LibJS/Runtime/GlobalObject.cpp | 2 | ||||
-rw-r--r-- | Libraries/LibJS/Runtime/ScriptFunction.cpp | 1 | ||||
-rw-r--r-- | Libraries/LibJS/Tests/Function.js | 29 | ||||
-rw-r--r-- | Libraries/LibJS/Tests/Function.prototype.apply.js | 53 | ||||
-rw-r--r-- | Libraries/LibJS/Tests/Function.prototype.call.js | 53 | ||||
-rw-r--r-- | Libraries/LibJS/Tests/Function.prototype.toString.js | 21 |
15 files changed, 463 insertions, 4 deletions
diff --git a/Libraries/LibJS/AST.cpp b/Libraries/LibJS/AST.cpp index 8d44ca0b9b..d3685b7118 100644 --- a/Libraries/LibJS/AST.cpp +++ b/Libraries/LibJS/AST.cpp @@ -333,6 +333,8 @@ Value UnaryExpression::execute(Interpreter& interpreter) const case Value::Type::String: return js_string(interpreter, "string"); case Value::Type::Object: + if (lhs_result.as_object().is_function()) + return js_string(interpreter, "function"); return js_string(interpreter, "object"); case Value::Type::Boolean: return js_string(interpreter, "boolean"); diff --git a/Libraries/LibJS/Interpreter.cpp b/Libraries/LibJS/Interpreter.cpp index 6350fdd0ae..241bb673ee 100644 --- a/Libraries/LibJS/Interpreter.cpp +++ b/Libraries/LibJS/Interpreter.cpp @@ -31,6 +31,7 @@ #include <LibJS/Runtime/DatePrototype.h> #include <LibJS/Runtime/Error.h> #include <LibJS/Runtime/ErrorPrototype.h> +#include <LibJS/Runtime/FunctionPrototype.h> #include <LibJS/Runtime/GlobalObject.h> #include <LibJS/Runtime/NativeFunction.h> #include <LibJS/Runtime/Object.h> @@ -47,6 +48,7 @@ Interpreter::Interpreter() m_empty_object_shape = heap().allocate<Shape>(); m_object_prototype = heap().allocate<ObjectPrototype>(); + m_function_prototype = heap().allocate<FunctionPrototype>(); m_string_prototype = heap().allocate<StringPrototype>(); m_array_prototype = heap().allocate<ArrayPrototype>(); m_error_prototype = heap().allocate<ErrorPrototype>(); @@ -171,6 +173,7 @@ void Interpreter::gather_roots(Badge<Heap>, HashTable<Cell*>& roots) roots.set(m_array_prototype); roots.set(m_error_prototype); roots.set(m_date_prototype); + roots.set(m_function_prototype); roots.set(m_exception); diff --git a/Libraries/LibJS/Interpreter.h b/Libraries/LibJS/Interpreter.h index ab589e8e3c..af79de6b5e 100644 --- a/Libraries/LibJS/Interpreter.h +++ b/Libraries/LibJS/Interpreter.h @@ -136,6 +136,7 @@ public: Object* array_prototype() { return m_array_prototype; } Object* error_prototype() { return m_error_prototype; } Object* date_prototype() { return m_date_prototype; } + Object* function_prototype() { return m_function_prototype; } Exception* exception() { return m_exception; } void clear_exception() { m_exception = nullptr; } @@ -168,6 +169,7 @@ private: Object* m_array_prototype { nullptr }; Object* m_error_prototype { nullptr }; Object* m_date_prototype { nullptr }; + Object* m_function_prototype { nullptr }; Exception* m_exception { nullptr }; diff --git a/Libraries/LibJS/Makefile b/Libraries/LibJS/Makefile index 1a18967fc1..ff059321fb 100644 --- a/Libraries/LibJS/Makefile +++ b/Libraries/LibJS/Makefile @@ -18,6 +18,8 @@ OBJS = \ Runtime/ErrorPrototype.o \ Runtime/Exception.o \ Runtime/Function.o \ + Runtime/FunctionConstructor.o \ + Runtime/FunctionPrototype.o \ Runtime/GlobalObject.o \ Runtime/MathObject.o \ Runtime/NativeFunction.o \ diff --git a/Libraries/LibJS/Runtime/Function.cpp b/Libraries/LibJS/Runtime/Function.cpp index c810237e8b..fe72c6668c 100644 --- a/Libraries/LibJS/Runtime/Function.cpp +++ b/Libraries/LibJS/Runtime/Function.cpp @@ -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() diff --git a/Libraries/LibJS/Runtime/FunctionConstructor.cpp b/Libraries/LibJS/Runtime/FunctionConstructor.cpp new file mode 100644 index 0000000000..6a005f8324 --- /dev/null +++ b/Libraries/LibJS/Runtime/FunctionConstructor.cpp @@ -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); +} + +} diff --git a/Libraries/LibJS/Runtime/FunctionConstructor.h b/Libraries/LibJS/Runtime/FunctionConstructor.h new file mode 100644 index 0000000000..2a891be6ae --- /dev/null +++ b/Libraries/LibJS/Runtime/FunctionConstructor.h @@ -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"; } +}; + +} diff --git a/Libraries/LibJS/Runtime/FunctionPrototype.cpp b/Libraries/LibJS/Runtime/FunctionPrototype.cpp new file mode 100644 index 0000000000..a0a976181d --- /dev/null +++ b/Libraries/LibJS/Runtime/FunctionPrototype.cpp @@ -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); +} + +} diff --git a/Libraries/LibJS/Runtime/FunctionPrototype.h b/Libraries/LibJS/Runtime/FunctionPrototype.h new file mode 100644 index 0000000000..3248d7bb3b --- /dev/null +++ b/Libraries/LibJS/Runtime/FunctionPrototype.h @@ -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&); +}; + +} diff --git a/Libraries/LibJS/Runtime/GlobalObject.cpp b/Libraries/LibJS/Runtime/GlobalObject.cpp index 452877f78d..881b6800ff 100644 --- a/Libraries/LibJS/Runtime/GlobalObject.cpp +++ b/Libraries/LibJS/Runtime/GlobalObject.cpp @@ -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>()); } diff --git a/Libraries/LibJS/Runtime/ScriptFunction.cpp b/Libraries/LibJS/Runtime/ScriptFunction.cpp index 8ae8543472..1a397c96a3 100644 --- a/Libraries/LibJS/Runtime/ScriptFunction.cpp +++ b/Libraries/LibJS/Runtime/ScriptFunction.cpp @@ -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); } diff --git a/Libraries/LibJS/Tests/Function.js b/Libraries/LibJS/Tests/Function.js new file mode 100644 index 0000000000..5618aa5219 --- /dev/null +++ b/Libraries/LibJS/Tests/Function.js @@ -0,0 +1,29 @@ +function assert(x) { if (!x) throw 1; } + +try { + assert(Function.length === 1); + assert(Function.prototype.length === 0); + assert(typeof Function() === "function"); + assert(typeof new Function() === "function"); + assert(Function()() === undefined); + assert(new Function()() === undefined); + assert(Function("return 42")() === 42); + assert(new Function("return 42")() === 42); + assert(new Function("foo", "return foo")(42) === 42); + assert(new Function("foo,bar", "return foo + bar")(1, 2) === 3); + assert(new Function("foo", "bar", "return foo + bar")(1, 2) === 3); + assert(new Function("foo", "bar,baz", "return foo + bar + baz")(1, 2, 3) === 6); + assert(new Function("foo", "bar", "baz", "return foo + bar + baz")(1, 2, 3) === 6); + assert(new Function("foo", "if (foo) { return 42; } else { return 'bar'; }")(true) === 42); + assert(new Function("foo", "if (foo) { return 42; } else { return 'bar'; }")(false) === "bar"); + assert(new Function("return typeof Function()")() === "function"); + // FIXME: This is equivalent to + // (function (x) { return function (y) { return x + y;} })(1)(2) + // and should totally work, but both currently fail with + // Uncaught exception: [ReferenceError]: 'x' not known + // assert(new Function("x", "return function (y) { return x + y };")(1)(2) === 3); + + console.log("PASS"); +} catch (e) { + console.log("FAIL: " + e.message); +} diff --git a/Libraries/LibJS/Tests/Function.prototype.apply.js b/Libraries/LibJS/Tests/Function.prototype.apply.js new file mode 100644 index 0000000000..22d14194ff --- /dev/null +++ b/Libraries/LibJS/Tests/Function.prototype.apply.js @@ -0,0 +1,53 @@ +function assert(x) { if (!x) throw 1; } + +try { + function Foo(arg) { + this.foo = arg; + } + function Bar(arg) { + this.bar = arg; + } + function FooBar(arg) { + Foo.apply(this, [arg]); + Bar.apply(this, [arg]); + } + function FooBarBaz(arg) { + Foo.apply(this, [arg]); + Bar.apply(this, [arg]); + this.baz = arg; + } + + assert(Function.prototype.apply.length === 2); + + var foo = new Foo("test"); + assert(foo.foo === "test"); + assert(foo.bar === undefined); + assert(foo.baz === undefined); + + var bar = new Bar("test"); + assert(bar.foo === undefined); + assert(bar.bar === "test"); + assert(bar.baz === undefined); + + var foobar = new FooBar("test"); + assert(foobar.foo === "test"); + assert(foobar.bar === "test"); + assert(foobar.baz === undefined); + + var foobarbaz = new FooBarBaz("test"); + assert(foobarbaz.foo === "test"); + assert(foobarbaz.bar === "test"); + assert(foobarbaz.baz === "test"); + + assert(Math.abs.apply(null, [-1]) === 1); + + var add = (x, y) => x + y; + assert(add.apply(null, [1, 2]) === 3); + + var multiply = function (x, y) { return x * y; }; + assert(multiply.apply(null, [3, 4]) === 12); + + console.log("PASS"); +} catch (e) { + console.log("FAIL: " + e); +} diff --git a/Libraries/LibJS/Tests/Function.prototype.call.js b/Libraries/LibJS/Tests/Function.prototype.call.js new file mode 100644 index 0000000000..da24707511 --- /dev/null +++ b/Libraries/LibJS/Tests/Function.prototype.call.js @@ -0,0 +1,53 @@ +function assert(x) { if (!x) throw 1; } + +try { + function Foo(arg) { + this.foo = arg; + } + function Bar(arg) { + this.bar = arg; + } + function FooBar(arg) { + Foo.call(this, arg); + Bar.call(this, arg); + } + function FooBarBaz(arg) { + Foo.call(this, arg); + Bar.call(this, arg); + this.baz = arg; + } + + assert(Function.prototype.call.length === 1); + + var foo = new Foo("test"); + assert(foo.foo === "test"); + assert(foo.bar === undefined); + assert(foo.baz === undefined); + + var bar = new Bar("test"); + assert(bar.foo === undefined); + assert(bar.bar === "test"); + assert(bar.baz === undefined); + + var foobar = new FooBar("test"); + assert(foobar.foo === "test"); + assert(foobar.bar === "test"); + assert(foobar.baz === undefined); + + var foobarbaz = new FooBarBaz("test"); + assert(foobarbaz.foo === "test"); + assert(foobarbaz.bar === "test"); + assert(foobarbaz.baz === "test"); + + assert(Math.abs.call(null, -1) === 1); + + var add = (x, y) => x + y; + assert(add.call(null, 1, 2) === 3); + + var multiply = function (x, y) { return x * y; }; + assert(multiply.call(null, 3, 4) === 12); + + console.log("PASS"); +} catch (e) { + console.log("FAIL: " + e); +} diff --git a/Libraries/LibJS/Tests/Function.prototype.toString.js b/Libraries/LibJS/Tests/Function.prototype.toString.js new file mode 100644 index 0000000000..a1fd3c7252 --- /dev/null +++ b/Libraries/LibJS/Tests/Function.prototype.toString.js @@ -0,0 +1,21 @@ +function assert(x) { if (!x) throw 1; } + +try { + assert((function() {}).toString() === "function () {\n ???\n}"); + assert((function(foo) {}).toString() === "function (foo) {\n ???\n}"); + assert((function(foo, bar, baz) {}).toString() === "function (foo, bar, baz) {\n ???\n}"); + assert((function(foo, bar, baz) { + if (foo) { + return baz; + } else if (bar) { + return foo; + } + return bar + 42; + }).toString() === "function (foo, bar, baz) {\n ???\n}"); + assert(console.log.toString() === "function () {\n [NativeFunction]\n}"); + assert(Function.toString() === "function () {\n [FunctionConstructor]\n}"); + + console.log("PASS"); +} catch (e) { + console.log("FAIL: " + e); +} |