diff --git a/Libraries/LibJS/Interpreter.cpp b/Libraries/LibJS/Interpreter.cpp index b8dbad53c3..910751cf7e 100644 --- a/Libraries/LibJS/Interpreter.cpp +++ b/Libraries/LibJS/Interpreter.cpp @@ -28,6 +28,7 @@ #include #include #include +#include #include #include #include @@ -55,6 +56,7 @@ Interpreter::Interpreter() m_error_prototype = heap().allocate(); m_date_prototype = heap().allocate(); m_number_prototype = heap().allocate(); + m_boolean_prototype = heap().allocate(); } Interpreter::~Interpreter() @@ -180,6 +182,7 @@ void Interpreter::gather_roots(Badge, HashTable& roots) roots.set(m_date_prototype); roots.set(m_function_prototype); roots.set(m_number_prototype); + roots.set(m_boolean_prototype); roots.set(m_exception); diff --git a/Libraries/LibJS/Interpreter.h b/Libraries/LibJS/Interpreter.h index e0b28391e9..4d79948279 100644 --- a/Libraries/LibJS/Interpreter.h +++ b/Libraries/LibJS/Interpreter.h @@ -143,6 +143,7 @@ public: Object* date_prototype() { return m_date_prototype; } Object* function_prototype() { return m_function_prototype; } Object* number_prototype() { return m_number_prototype; } + Object* boolean_prototype() { return m_boolean_prototype; } Exception* exception() { return m_exception; } void clear_exception() { m_exception = nullptr; } @@ -181,6 +182,7 @@ private: Object* m_date_prototype { nullptr }; Object* m_function_prototype { nullptr }; Object* m_number_prototype { nullptr }; + Object* m_boolean_prototype { nullptr }; Exception* m_exception { nullptr }; diff --git a/Libraries/LibJS/Makefile b/Libraries/LibJS/Makefile index 611cfdefcd..75352c9742 100644 --- a/Libraries/LibJS/Makefile +++ b/Libraries/LibJS/Makefile @@ -9,6 +9,9 @@ OBJS = \ Runtime/Array.o \ Runtime/ArrayConstructor.o \ Runtime/ArrayPrototype.o \ + Runtime/BooleanConstructor.o \ + Runtime/BooleanObject.o \ + Runtime/BooleanPrototype.o \ Runtime/Cell.o \ Runtime/ConsoleObject.o \ Runtime/Date.o \ diff --git a/Libraries/LibJS/Runtime/BooleanConstructor.cpp b/Libraries/LibJS/Runtime/BooleanConstructor.cpp new file mode 100644 index 0000000000..6d756d5865 --- /dev/null +++ b/Libraries/LibJS/Runtime/BooleanConstructor.cpp @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2020, Jack Karamanian + * 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 +#include +#include +#include +#include + +namespace JS { +BooleanConstructor::BooleanConstructor() +{ + put("prototype", Value(interpreter().boolean_prototype())); + put("length", Value(1)); +} + +BooleanConstructor::~BooleanConstructor() {} + +Value BooleanConstructor::call(Interpreter& interpreter) +{ + return Value(interpreter.argument(0).to_boolean()); +} + +Value BooleanConstructor::construct(Interpreter& interpreter) +{ + auto bool_object = interpreter.heap().allocate(interpreter.argument(0).to_boolean()); + return Value(bool_object); +} +} diff --git a/Libraries/LibJS/Runtime/BooleanConstructor.h b/Libraries/LibJS/Runtime/BooleanConstructor.h new file mode 100644 index 0000000000..05fa5a5b63 --- /dev/null +++ b/Libraries/LibJS/Runtime/BooleanConstructor.h @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2020, Jack Karamanian + * 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 + +namespace JS { +class BooleanConstructor final : public NativeFunction { +public: + BooleanConstructor(); + virtual ~BooleanConstructor() 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 "BooleanConstructor"; } +}; +} diff --git a/Libraries/LibJS/Runtime/BooleanObject.cpp b/Libraries/LibJS/Runtime/BooleanObject.cpp new file mode 100644 index 0000000000..4b30001de7 --- /dev/null +++ b/Libraries/LibJS/Runtime/BooleanObject.cpp @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2020, Jack Karamanian + * 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 +#include + +namespace JS { +BooleanObject::BooleanObject(bool value) + : m_value(value) +{ + set_prototype(interpreter().boolean_prototype()); +} + +BooleanObject::~BooleanObject() +{ +} +} diff --git a/Libraries/LibJS/Runtime/BooleanObject.h b/Libraries/LibJS/Runtime/BooleanObject.h new file mode 100644 index 0000000000..a27a129f61 --- /dev/null +++ b/Libraries/LibJS/Runtime/BooleanObject.h @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2020, Jack Karamanian + * 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 + +namespace JS { +class BooleanObject : public Object { +public: + explicit BooleanObject(bool); + virtual ~BooleanObject() override; + + virtual Value value_of() const override + { + return Value(m_value); + } + +private: + virtual const char* class_name() const override { return "BooleanObject"; } + virtual bool is_boolean() const override { return true; } + bool m_value { false }; +}; +} diff --git a/Libraries/LibJS/Runtime/BooleanPrototype.cpp b/Libraries/LibJS/Runtime/BooleanPrototype.cpp new file mode 100644 index 0000000000..c7423dd492 --- /dev/null +++ b/Libraries/LibJS/Runtime/BooleanPrototype.cpp @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2020, Jack Karamanian + * 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 +#include +#include +#include + +namespace JS { +BooleanPrototype::BooleanPrototype() + : BooleanObject(false) +{ + put_native_function("toString", to_string); + put_native_function("valueOf", value_of); +} + +BooleanPrototype::~BooleanPrototype() {} + +Value BooleanPrototype::to_string(Interpreter& interpreter) +{ + auto this_object = interpreter.this_value(); + if (this_object.is_boolean()) { + return js_string(interpreter.heap(), this_object.as_bool() ? "true" : "false"); + } + if (!this_object.is_object() || !this_object.as_object().is_boolean()) { + interpreter.throw_exception("TypeError", "Not a Boolean"); + return {}; + } + + bool bool_value = static_cast(this_object.as_object()).value_of().as_bool(); + return js_string(interpreter.heap(), bool_value ? "true" : "false"); +} + +Value BooleanPrototype::value_of(Interpreter& interpreter) +{ + auto this_object = interpreter.this_value(); + if (this_object.is_boolean()) { + return this_object; + } + if (!this_object.is_object() || !this_object.as_object().is_boolean()) { + interpreter.throw_exception("TypeError", "Not a Boolean"); + return {}; + } + + return static_cast(this_object.as_object()).value_of(); +} +} diff --git a/Libraries/LibJS/Runtime/BooleanPrototype.h b/Libraries/LibJS/Runtime/BooleanPrototype.h new file mode 100644 index 0000000000..4050767839 --- /dev/null +++ b/Libraries/LibJS/Runtime/BooleanPrototype.h @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2020, Jack Karamanian + * 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 + +namespace JS { +class BooleanPrototype final : public BooleanObject { +public: + BooleanPrototype(); + virtual ~BooleanPrototype() override; + +private: + virtual const char* class_name() const override { return "BooleanPrototype"; } + + static Value to_string(Interpreter&); + static Value value_of(Interpreter&); +}; +} diff --git a/Libraries/LibJS/Runtime/GlobalObject.cpp b/Libraries/LibJS/Runtime/GlobalObject.cpp index af85902425..4bda346d81 100644 --- a/Libraries/LibJS/Runtime/GlobalObject.cpp +++ b/Libraries/LibJS/Runtime/GlobalObject.cpp @@ -29,6 +29,7 @@ #include #include #include +#include #include #include #include @@ -58,6 +59,7 @@ GlobalObject::GlobalObject() put("Math", heap().allocate()); put("Object", heap().allocate()); put("Array", heap().allocate()); + put("Boolean", heap().allocate()); } GlobalObject::~GlobalObject() diff --git a/Libraries/LibJS/Runtime/Object.h b/Libraries/LibJS/Runtime/Object.h index 8772e6b1e2..7b9c8dcdfb 100644 --- a/Libraries/LibJS/Runtime/Object.h +++ b/Libraries/LibJS/Runtime/Object.h @@ -59,6 +59,7 @@ public: void put_native_property(const FlyString& property_name, AK::Function getter, AK::Function setter); virtual bool is_array() const { return false; } + virtual bool is_boolean() const { return false; } virtual bool is_date() const { return false; } virtual bool is_error() const { return false; } virtual bool is_function() const { return false; } diff --git a/Libraries/LibJS/Runtime/Value.cpp b/Libraries/LibJS/Runtime/Value.cpp index 6e5d11c3ba..fa489a65b7 100644 --- a/Libraries/LibJS/Runtime/Value.cpp +++ b/Libraries/LibJS/Runtime/Value.cpp @@ -29,6 +29,7 @@ #include #include #include +#include #include #include #include @@ -110,6 +111,9 @@ Object* Value::to_object(Heap& heap) const if (is_number()) return heap.allocate(m_value.as_double); + if (is_boolean()) + return heap.allocate(m_value.as_bool); + if (is_null() || is_undefined()) { heap.interpreter().throw_exception("TypeError", "ToObject on null or undefined."); return nullptr; diff --git a/Libraries/LibJS/Tests/Boolean.js b/Libraries/LibJS/Tests/Boolean.js new file mode 100644 index 0000000000..d6f7f89d25 --- /dev/null +++ b/Libraries/LibJS/Tests/Boolean.js @@ -0,0 +1,35 @@ +try { + assert(Boolean.length === 1); + assert(typeof new Boolean() === "object"); + assert(new Boolean().valueOf() === false); + + var foo = new Boolean(true); + var bar = new Boolean(true); + + assert(foo !== bar); + assert(foo.valueOf() === bar.valueOf()); + + assert(new Boolean(true).toString() === "true"); + assert(new Boolean(false).toString() === "false"); + + assert(typeof Boolean() === "boolean"); + assert(typeof Boolean(true) === "boolean"); + + assert(Boolean() === false); + assert(Boolean(false) === false); + assert(Boolean(null) === false); + assert(Boolean(undefined) === false); + assert(Boolean(NaN) === false); + assert(Boolean("") === false); + assert(Boolean(0.0) === false); + assert(Boolean(-0.0) === false); + assert(Boolean(true) === true); + assert(Boolean("0") === true); + assert(Boolean({}) === true); + assert(Boolean([]) === true); + assert(Boolean(1)) === true; + + console.log("PASS"); +} catch (err) { + console.log("FAIL: " + err); +} diff --git a/Libraries/LibJS/Tests/Boolean.prototype.js b/Libraries/LibJS/Tests/Boolean.prototype.js new file mode 100644 index 0000000000..929a38172d --- /dev/null +++ b/Libraries/LibJS/Tests/Boolean.prototype.js @@ -0,0 +1,8 @@ +try { + assert(typeof Boolean.prototype === "object"); + assert(Boolean.prototype.valueOf() === false); + + console.log("PASS"); +} catch (err) { + console.log("FAIL: " + err); +} diff --git a/Libraries/LibJS/Tests/Boolean.prototype.toString.js b/Libraries/LibJS/Tests/Boolean.prototype.toString.js new file mode 100644 index 0000000000..5ecc51bd67 --- /dev/null +++ b/Libraries/LibJS/Tests/Boolean.prototype.toString.js @@ -0,0 +1,23 @@ +try { + var foo = true; + assert(foo.toString() === "true"); + assert(true.toString() === "true"); + + assert(Boolean.prototype.toString.call(true) === "true"); + assert(Boolean.prototype.toString.call(false) === "false"); + + let error = null; + try { + Boolean.prototype.toString.call("foo"); + } catch (err) { + error = err; + } + + assert(error instanceof Error); + assert(error.name === "TypeError"); + assert(error.message === "Not a Boolean"); + + console.log("PASS"); +} catch (err) { + console.log("FAIL: " + err); +} diff --git a/Libraries/LibJS/Tests/Boolean.prototype.valueOf.js b/Libraries/LibJS/Tests/Boolean.prototype.valueOf.js new file mode 100644 index 0000000000..acb4c5dde6 --- /dev/null +++ b/Libraries/LibJS/Tests/Boolean.prototype.valueOf.js @@ -0,0 +1,23 @@ +try { + var foo = true; + assert(foo.valueOf() === true); + assert(true.valueOf() === true); + + assert(Boolean.prototype.valueOf.call(true) === true); + assert(Boolean.prototype.valueOf.call(false) === false); + + let error = null; + try { + Boolean.prototype.valueOf.call("foo"); + } catch (err) { + error = err; + } + + assert(error instanceof Error); + assert(error.name === "TypeError"); + assert(error.message === "Not a Boolean"); + + console.log("PASS"); +} catch (err) { + console.log("FAIL: " + err); +}