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

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.
This commit is contained in:
Linus Groh 2022-03-29 20:36:22 +01:00
parent e7eb6241c2
commit 1e01a85cdf
9 changed files with 343 additions and 0 deletions

View file

@ -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

View file

@ -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();
}
}

View file

@ -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);
};
}

View file

@ -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();
}
}

View file

@ -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);
};
}

View file

@ -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();
}
}

View file

@ -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);
};
}

View file

@ -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");
}
}

View file

@ -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; }
};
}