瀏覽代碼

LibJS: Import C++ sources from libjs-test262 :^)

This commit upstreams most of the C++ bits of the LibJS test262 runner
at https://github.com/linusg/libjs-test262/, specifically everything but
the main.cpp file serving as the actual executable.
Since all of these are just regular JS objects, I opted to put them in
LibJS itself, in a new Contrib/ directory like many other projects have
one. Other code that can end up there in the future is the runtime for
esvu, which might even share some functionality with test262's $262
object.

The code has been copied verbatim, and only a small number of changes
have been made:

- Putting everything into the JS::Test262 namespace
- Removing now redundant JS namespace prefixes
- Updating includes to use absolute <LibJS/...> paths
- Updating the SPDX-License-Identifier comments from MIT to BSD-2-Clause

I gained permission to change the license and upstream these changes
from all the major contributors to this code: Ali, Andrew, David, Idan.

The removal of the code from the source repository is here:
https://github.com/linusg/libjs-test262/pull/54

This is only the first step, the goal is to eventually upstream the
actual libjs-test262-runner executable and supporting Python scripts
into SerenityOS as well.
Linus Groh 3 年之前
父節點
當前提交
1e01a85cdf

+ 4 - 0
Userland/Libraries/LibJS/CMakeLists.txt

@@ -15,6 +15,10 @@ set(SOURCES
     Bytecode/Pass/UnifySameBlocks.cpp
     Bytecode/StringTable.cpp
     Console.cpp
+    Contrib/Test262/$262Object.cpp
+    Contrib/Test262/AgentObject.cpp
+    Contrib/Test262/GlobalObject.cpp
+    Contrib/Test262/IsHTMLDDA.cpp
     CyclicModule.cpp
     Heap/BlockAllocator.cpp
     Heap/CellAllocator.cpp

+ 88 - 0
Userland/Libraries/LibJS/Contrib/Test262/$262Object.cpp

@@ -0,0 +1,88 @@
+/*
+ * Copyright (c) 2021, Linus Groh <linusg@serenityos.org>
+ * Copyright (c) 2021, Idan Horowitz <idan.horowitz@serenityos.org>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#include <LibJS/Contrib/Test262/$262Object.h>
+#include <LibJS/Contrib/Test262/AgentObject.h>
+#include <LibJS/Contrib/Test262/GlobalObject.h>
+#include <LibJS/Contrib/Test262/IsHTMLDDA.h>
+#include <LibJS/Heap/Cell.h>
+#include <LibJS/Interpreter.h>
+#include <LibJS/Runtime/ArrayBuffer.h>
+#include <LibJS/Runtime/GlobalObject.h>
+#include <LibJS/Runtime/Object.h>
+#include <LibJS/Script.h>
+
+namespace JS::Test262 {
+
+$262Object::$262Object(JS::GlobalObject& global_object)
+    : Object(Object::ConstructWithoutPrototypeTag::Tag, global_object)
+{
+}
+
+void $262Object::initialize(JS::GlobalObject& global_object)
+{
+    Base::initialize(global_object);
+
+    m_agent = vm().heap().allocate<AgentObject>(global_object, global_object);
+    m_is_htmldda = vm().heap().allocate<IsHTMLDDA>(global_object, global_object);
+
+    u8 attr = Attribute::Writable | Attribute::Configurable;
+    define_native_function("clearKeptObjects", clear_kept_objects, 0, attr);
+    define_native_function("createRealm", create_realm, 0, attr);
+    define_native_function("detachArrayBuffer", detach_array_buffer, 1, attr);
+    define_native_function("evalScript", eval_script, 1, attr);
+
+    define_direct_property("agent", m_agent, attr);
+    define_direct_property("gc", global_object.get_without_side_effects("gc"), attr);
+    define_direct_property("global", &global_object, attr);
+    define_direct_property("IsHTMLDDA", m_is_htmldda, attr);
+}
+
+void $262Object::visit_edges(Cell::Visitor& visitor)
+{
+    Base::visit_edges(visitor);
+    visitor.visit(m_agent);
+    visitor.visit(m_is_htmldda);
+}
+
+JS_DEFINE_NATIVE_FUNCTION($262Object::clear_kept_objects)
+{
+    vm.finish_execution_generation();
+    return js_undefined();
+}
+
+JS_DEFINE_NATIVE_FUNCTION($262Object::create_realm)
+{
+    auto realm = vm.heap().allocate_without_global_object<GlobalObject>();
+    realm->initialize_global_object();
+    return Value(realm->$262());
+}
+
+// 25.1.2.3 DetachArrayBuffer, https://tc39.es/ecma262/#sec-detacharraybuffer
+JS_DEFINE_NATIVE_FUNCTION($262Object::detach_array_buffer)
+{
+    auto array_buffer = vm.argument(0);
+    if (!array_buffer.is_object() || !is<ArrayBuffer>(array_buffer.as_object()))
+        return vm.throw_completion<TypeError>(global_object);
+    auto& array_buffer_object = static_cast<ArrayBuffer&>(array_buffer.as_object());
+    if (!same_value(array_buffer_object.detach_key(), vm.argument(1)))
+        return vm.throw_completion<TypeError>(global_object);
+    array_buffer_object.detach_buffer();
+    return js_null();
+}
+
+JS_DEFINE_NATIVE_FUNCTION($262Object::eval_script)
+{
+    auto source = TRY(vm.argument(0).to_string(global_object));
+    auto script_or_error = Script::parse(source, *vm.current_realm());
+    if (script_or_error.is_error())
+        return vm.throw_completion<SyntaxError>(global_object, script_or_error.error()[0].to_string());
+    TRY(vm.interpreter().run(script_or_error.value()));
+    return js_undefined();
+}
+
+}

+ 36 - 0
Userland/Libraries/LibJS/Contrib/Test262/$262Object.h

@@ -0,0 +1,36 @@
+/*
+ * Copyright (c) 2021, Linus Groh <linusg@serenityos.org>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#pragma once
+
+#include <LibJS/Contrib/Test262/AgentObject.h>
+#include <LibJS/Contrib/Test262/IsHTMLDDA.h>
+#include <LibJS/Runtime/GlobalObject.h>
+#include <LibJS/Runtime/Object.h>
+
+namespace JS::Test262 {
+
+class $262Object final : public Object {
+    JS_OBJECT($262Object, Object);
+
+public:
+    $262Object(JS::GlobalObject&);
+    virtual void initialize(JS::GlobalObject&) override;
+    virtual ~$262Object() override = default;
+
+private:
+    virtual void visit_edges(Visitor&) override;
+
+    AgentObject* m_agent { nullptr };
+    IsHTMLDDA* m_is_htmldda { nullptr };
+
+    JS_DECLARE_NATIVE_FUNCTION(clear_kept_objects);
+    JS_DECLARE_NATIVE_FUNCTION(create_realm);
+    JS_DECLARE_NATIVE_FUNCTION(detach_array_buffer);
+    JS_DECLARE_NATIVE_FUNCTION(eval_script);
+};
+
+}

+ 46 - 0
Userland/Libraries/LibJS/Contrib/Test262/AgentObject.cpp

@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2021-2022, Linus Groh <linusg@serenityos.org>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#include <AK/Time.h>
+#include <LibJS/Contrib/Test262/AgentObject.h>
+#include <LibJS/Runtime/GlobalObject.h>
+#include <LibJS/Runtime/Object.h>
+#include <unistd.h>
+
+namespace JS::Test262 {
+
+AgentObject::AgentObject(JS::GlobalObject& global_object)
+    : Object(Object::ConstructWithoutPrototypeTag::Tag, global_object)
+{
+}
+
+void AgentObject::initialize(JS::GlobalObject& global_object)
+{
+    Base::initialize(global_object);
+
+    u8 attr = Attribute::Writable | Attribute::Configurable;
+    define_native_function("monotonicNow", monotonic_now, 0, attr);
+    define_native_function("sleep", sleep, 1, attr);
+    // TODO: broadcast
+    // TODO: getReport
+    // TODO: start
+}
+
+JS_DEFINE_NATIVE_FUNCTION(AgentObject::monotonic_now)
+{
+    auto time = Time::now_monotonic();
+    auto milliseconds = time.to_milliseconds();
+    return Value(static_cast<double>(milliseconds));
+}
+
+JS_DEFINE_NATIVE_FUNCTION(AgentObject::sleep)
+{
+    auto milliseconds = TRY(vm.argument(0).to_i32(global_object));
+    ::usleep(milliseconds * 1000);
+    return js_undefined();
+}
+
+}

+ 27 - 0
Userland/Libraries/LibJS/Contrib/Test262/AgentObject.h

@@ -0,0 +1,27 @@
+/*
+ * Copyright (c) 2021, Linus Groh <linusg@serenityos.org>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#pragma once
+
+#include <LibJS/Runtime/GlobalObject.h>
+#include <LibJS/Runtime/Object.h>
+
+namespace JS::Test262 {
+
+class AgentObject final : public Object {
+    JS_OBJECT(AgentObject, Object);
+
+public:
+    AgentObject(JS::GlobalObject&);
+    virtual void initialize(JS::GlobalObject&) override;
+    virtual ~AgentObject() override = default;
+
+private:
+    JS_DECLARE_NATIVE_FUNCTION(monotonic_now);
+    JS_DECLARE_NATIVE_FUNCTION(sleep);
+};
+
+}

+ 42 - 0
Userland/Libraries/LibJS/Contrib/Test262/GlobalObject.cpp

@@ -0,0 +1,42 @@
+/*
+ * Copyright (c) 2021, Linus Groh <linusg@serenityos.org>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#include <AK/Format.h>
+#include <LibJS/Contrib/Test262/$262Object.h>
+#include <LibJS/Contrib/Test262/AgentObject.h>
+#include <LibJS/Contrib/Test262/GlobalObject.h>
+#include <LibJS/Heap/Cell.h>
+#include <LibJS/Runtime/GlobalObject.h>
+#include <LibJS/Runtime/VM.h>
+
+namespace JS::Test262 {
+
+void GlobalObject::initialize_global_object()
+{
+    Base::initialize_global_object();
+
+    m_$262 = vm().heap().allocate<$262Object>(*this, *this);
+
+    // https://github.com/tc39/test262/blob/master/INTERPRETING.md#host-defined-functions
+    u8 attr = Attribute::Writable | Attribute::Configurable;
+    define_native_function("print", print, 1, attr);
+    define_direct_property("$262", m_$262, attr);
+}
+
+void GlobalObject::visit_edges(Cell::Visitor& visitor)
+{
+    Base::visit_edges(visitor);
+    visitor.visit(m_$262);
+}
+
+JS_DEFINE_NATIVE_FUNCTION(GlobalObject::print)
+{
+    auto string = TRY(vm.argument(0).to_string(global_object));
+    outln("{}", string);
+    return js_undefined();
+}
+
+}

+ 32 - 0
Userland/Libraries/LibJS/Contrib/Test262/GlobalObject.h

@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2021, Linus Groh <linusg@serenityos.org>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#pragma once
+
+#include <LibJS/Contrib/Test262/$262Object.h>
+#include <LibJS/Runtime/GlobalObject.h>
+
+namespace JS::Test262 {
+
+class GlobalObject final : public JS::GlobalObject {
+    JS_OBJECT(GlobalObject, JS::GlobalObject);
+
+public:
+    GlobalObject() = default;
+    virtual void initialize_global_object() override;
+    virtual ~GlobalObject() override = default;
+
+    $262Object* $262() const { return m_$262; }
+
+private:
+    virtual void visit_edges(Visitor&) override;
+
+    $262Object* m_$262 { nullptr };
+
+    JS_DECLARE_NATIVE_FUNCTION(print);
+};
+
+}

+ 40 - 0
Userland/Libraries/LibJS/Contrib/Test262/IsHTMLDDA.cpp

@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2021, Linus Groh <linusg@serenityos.org>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#include <LibJS/Contrib/Test262/IsHTMLDDA.h>
+#include <LibJS/Runtime/GlobalObject.h>
+
+namespace JS::Test262 {
+
+IsHTMLDDA::IsHTMLDDA(JS::GlobalObject& global_object)
+    // NativeFunction without prototype is currently not possible (only due to the lack of a ctor that supports it)
+    : NativeFunction("IsHTMLDDA", *global_object.function_prototype())
+{
+}
+
+ThrowCompletionOr<Value> IsHTMLDDA::call()
+{
+    auto& vm = this->vm();
+    if (vm.argument_count() == 0)
+        return js_null();
+    if (vm.argument(0).is_string() && vm.argument(0).as_string().string().is_empty())
+        return js_null();
+    // Not sure if this really matters, INTERPRETING.md simply says:
+    // * IsHTMLDDA - (present only in implementations that can provide it) an object that:
+    //   a. has an [[IsHTMLDDA]] internal slot, and
+    //   b. when called with no arguments or with the first argument "" (an empty string) returns null.
+    return js_undefined();
+}
+
+ThrowCompletionOr<Object*> IsHTMLDDA::construct(FunctionObject&)
+{
+    // Not sure if we need to support construction, but ¯\_(ツ)_/¯
+    auto& vm = this->vm();
+    auto& global_object = this->global_object();
+    return vm.throw_completion<TypeError>(global_object, ErrorType::NotAConstructor, "IsHTMLDDA");
+}
+
+}

+ 28 - 0
Userland/Libraries/LibJS/Contrib/Test262/IsHTMLDDA.h

@@ -0,0 +1,28 @@
+/*
+ * Copyright (c) 2021, Linus Groh <linusg@serenityos.org>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#pragma once
+
+#include <LibJS/Runtime/NativeFunction.h>
+
+namespace JS::Test262 {
+
+class IsHTMLDDA final : public NativeFunction {
+    JS_OBJECT(IsHTMLDDA, NativeFunction);
+
+public:
+    explicit IsHTMLDDA(JS::GlobalObject&);
+    virtual ~IsHTMLDDA() override = default;
+
+    virtual ThrowCompletionOr<Value> call() override;
+    virtual ThrowCompletionOr<Object*> construct(FunctionObject& new_target) override;
+
+private:
+    virtual bool has_constructor() const override { return true; }
+    virtual bool is_htmldda() const override { return true; }
+};
+
+}