diff --git a/Meta/Lagom/Tools/CodeGenerators/JSSpecCompiler/CMakeLists.txt b/Meta/Lagom/Tools/CodeGenerators/JSSpecCompiler/CMakeLists.txt index f8e228e3a61..6f7a12170ff 100644 --- a/Meta/Lagom/Tools/CodeGenerators/JSSpecCompiler/CMakeLists.txt +++ b/Meta/Lagom/Tools/CodeGenerators/JSSpecCompiler/CMakeLists.txt @@ -22,6 +22,9 @@ set(SOURCES Parser/SpecificationParsingStep.cpp Parser/TextParser.cpp Parser/XMLUtils.cpp + Runtime/Object.cpp + Runtime/ObjectType.cpp + Runtime/Realm.cpp DiagnosticEngine.cpp Function.cpp main.cpp diff --git a/Meta/Lagom/Tools/CodeGenerators/JSSpecCompiler/Forward.h b/Meta/Lagom/Tools/CodeGenerators/JSSpecCompiler/Forward.h index 90fb84713e4..13b1fe6b9bd 100644 --- a/Meta/Lagom/Tools/CodeGenerators/JSSpecCompiler/Forward.h +++ b/Meta/Lagom/Tools/CodeGenerators/JSSpecCompiler/Forward.h @@ -69,6 +69,13 @@ class SpecificationFunction; class SpecificationClause; class Specification; +namespace Runtime { +class Cell; +class Object; +class ObjectType; +class Realm; +} + // DiagnosticEngine.h struct LogicalLocation; struct Location; diff --git a/Meta/Lagom/Tools/CodeGenerators/JSSpecCompiler/Function.cpp b/Meta/Lagom/Tools/CodeGenerators/JSSpecCompiler/Function.cpp index 5917a8bb483..b5618358f37 100644 --- a/Meta/Lagom/Tools/CodeGenerators/JSSpecCompiler/Function.cpp +++ b/Meta/Lagom/Tools/CodeGenerators/JSSpecCompiler/Function.cpp @@ -7,11 +7,13 @@ #include "Function.h" #include "AST/AST.h" #include "Compiler/ControlFlowGraph.h" +#include "Runtime/Realm.h" namespace JSSpecCompiler { TranslationUnit::TranslationUnit(StringView filename) : m_filename(filename) + , m_realm(make(m_diagnostic_engine)) { } diff --git a/Meta/Lagom/Tools/CodeGenerators/JSSpecCompiler/Function.h b/Meta/Lagom/Tools/CodeGenerators/JSSpecCompiler/Function.h index 147c573c4a3..28bca950798 100644 --- a/Meta/Lagom/Tools/CodeGenerators/JSSpecCompiler/Function.h +++ b/Meta/Lagom/Tools/CodeGenerators/JSSpecCompiler/Function.h @@ -7,6 +7,7 @@ #pragma once #include +#include #include #include #include @@ -31,6 +32,8 @@ public: EnumeratorRef get_node_for_enumerator_value(StringView value); + Runtime::Realm* realm() const { return m_realm; } + private: StringView m_filename; DiagnosticEngine m_diagnostic_engine; @@ -38,6 +41,8 @@ private: Vector> m_declarations_owner; HashMap m_abstract_operation_index; HashMap m_enumerator_nodes; + + NonnullOwnPtr m_realm; }; struct FunctionArgument { diff --git a/Meta/Lagom/Tools/CodeGenerators/JSSpecCompiler/Printer.h b/Meta/Lagom/Tools/CodeGenerators/JSSpecCompiler/Printer.h new file mode 100644 index 00000000000..e296fa4e6d1 --- /dev/null +++ b/Meta/Lagom/Tools/CodeGenerators/JSSpecCompiler/Printer.h @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2024, Dan Klishch + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include +#include + +namespace JSSpecCompiler { + +class Printer { +public: + template + void block(Func&& func, StringView start = "{"sv, StringView end = "}"sv) + { + formatln("{}", start); + ++indent_level; + func(); + --indent_level; + format("{}", end); + } + + template + void format(AK::CheckedFormatString&& fmtstr, Parameters const&... parameters) + { + if (builder.string_view().ends_with('\n')) + builder.append_repeated(' ', indent_level * 4); + builder.appendff(move(fmtstr), forward(parameters)...); + } + + template + void formatln(AK::CheckedFormatString&& fmtstr, Parameters const&... parameters) + { + format(move(fmtstr), forward(parameters)...); + builder.append("\n"sv); + } + + StringView view() const { return builder.string_view(); } + +private: + StringBuilder builder; + size_t indent_level = 0; +}; + +} diff --git a/Meta/Lagom/Tools/CodeGenerators/JSSpecCompiler/Runtime/Cell.h b/Meta/Lagom/Tools/CodeGenerators/JSSpecCompiler/Runtime/Cell.h new file mode 100644 index 00000000000..a4db70705e7 --- /dev/null +++ b/Meta/Lagom/Tools/CodeGenerators/JSSpecCompiler/Runtime/Cell.h @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2024, Dan Klishch + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include "Forward.h" +#include "Printer.h" + +namespace JSSpecCompiler::Runtime { + +class Cell { +public: + virtual ~Cell() { } + + virtual StringView type_name() const = 0; + + void dump(Printer& printer) const + { + // FIXME: Handle cyclic references. + return do_dump(printer); + } + +protected: + virtual void do_dump(Printer& printer) const = 0; +}; + +} diff --git a/Meta/Lagom/Tools/CodeGenerators/JSSpecCompiler/Runtime/Object.cpp b/Meta/Lagom/Tools/CodeGenerators/JSSpecCompiler/Runtime/Object.cpp new file mode 100644 index 00000000000..a266736347b --- /dev/null +++ b/Meta/Lagom/Tools/CodeGenerators/JSSpecCompiler/Runtime/Object.cpp @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2024, Dan Klishch + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include "Runtime/Object.h" +#include "Function.h" + +namespace JSSpecCompiler::Runtime { + +Optional Property::get_data_property_or_diagnose(Realm* realm, QualifiedName name, Location current_location) +{ + if (!has()) { + realm->diag().error(current_location, + "{} must be a data property", name.to_string()); + realm->diag().note(location(), + "defined as an accessor property here"); + return {}; + } + return get(); +} + +static StringView well_known_symbol_to_sv(WellKnownSymbol symbol) +{ + static Array string_value = { +#define STRING_VALUE(enum_name, spec_name) "@@" #spec_name##sv, + ENUMERATE_WELL_KNOWN_SYMBOLS(STRING_VALUE) +#undef STRING_VALUE + }; + return string_value[to_underlying(symbol)]; +} + +void Object::do_dump(Printer& printer) const +{ + printer.block([&] { + for (auto const& [key, value] : m_properties) { + key.visit( + [&](Slot const& slot) { printer.format("[[{}]]", slot.key); }, + [&](StringPropertyKey const& string_property) { printer.format("{}", string_property.key); }, + [&](WellKnownSymbol const& symbol) { printer.format("{}", well_known_symbol_to_sv(symbol)); }); + printer.format(": "); + value.visit( + [&](DataProperty const& data) { + printer.format( + "[{}{}{}] ", + data.is_configurable ? "c" : "", + data.is_enumerable ? "e" : "", + data.is_writable ? "w" : ""); + data.value->dump(printer); + }, + [&](AccessorProperty const& accessor) { + printer.format( + "[{}{}] AccessorProperty", + accessor.is_configurable ? "c" : "", + accessor.is_enumerable ? "e" : ""); + printer.block([&] { + if (accessor.getter.has_value()) + printer.formatln("get: {},", accessor.getter.value()->name()); + if (accessor.setter.has_value()) + printer.formatln("set: {},", accessor.setter.value()->name()); + }); + }); + printer.formatln(","); + } + }); +} + +} diff --git a/Meta/Lagom/Tools/CodeGenerators/JSSpecCompiler/Runtime/Object.h b/Meta/Lagom/Tools/CodeGenerators/JSSpecCompiler/Runtime/Object.h new file mode 100644 index 00000000000..371b7bb56e9 --- /dev/null +++ b/Meta/Lagom/Tools/CodeGenerators/JSSpecCompiler/Runtime/Object.h @@ -0,0 +1,151 @@ +/* + * Copyright (c) 2024, Dan Klishch + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include + +#include "DiagnosticEngine.h" +#include "Function.h" +#include "Runtime/ObjectType.h" + +namespace JSSpecCompiler::Runtime { + +struct Slot { + bool operator==(Slot const&) const = default; + + FlyString key; +}; + +struct StringPropertyKey { + bool operator==(StringPropertyKey const&) const = default; + + FlyString key; +}; + +#define ENUMERATE_WELL_KNOWN_SYMBOLS(F) \ + F(InstanceType, _instanceType) \ + F(ToStringTag, toStringTag) + +enum class WellKnownSymbol { +#define ID(enum_name, spec_name) enum_name, + ENUMERATE_WELL_KNOWN_SYMBOLS(ID) +#undef ID +}; + +class PropertyKey : public Variant { +public: + using Variant::Variant; +}; + +struct DataProperty { + template + bool is() const + { + return ::is(value); + } + + template + T* as() const + { + return verify_cast(value); + } + + template + Optional get_or_diagnose(Realm* realm, QualifiedName name, Location location) + { + if (!is()) { + realm->diag().error(location, + "{} must be a {}", name.to_string(), T::TYPE_NAME); + realm->diag().note(this->location, + "set to {} here", value->type_name()); + return {}; + } + return verify_cast(value); + } + + Cell* value; + Location location; + + bool is_writable = true; + bool is_enumerable = false; + bool is_configurable = true; +}; + +struct AccessorProperty { + Optional getter; + Optional setter; + Location location; + + bool is_enumerable = false; + bool is_configurable = true; +}; + +class Property : public Variant { +public: + using Variant::Variant; + + Location location() const + { + return visit([&](auto const& value) { return value.location; }); + } + + Optional get_data_property_or_diagnose(Realm* realm, QualifiedName name, Location location); +}; + +class Object : public Runtime::Cell { +public: + static constexpr StringView TYPE_NAME = "object"sv; + + static Object* create(Realm* realm) + { + return realm->adopt_cell(new Object {}); + } + + StringView type_name() const override { return TYPE_NAME; } + + auto& type() { return m_type; } + auto& properties() { return m_properties; } + + bool has(PropertyKey const& key) const + { + return m_properties.contains(key); + } + + Property& get(PropertyKey const& key) + { + return m_properties.get(key).value(); + } + + void set(PropertyKey const& key, Property&& property) + { + auto insertion_result = m_properties.set(key, move(property)); + VERIFY(insertion_result == HashSetResult::InsertedNewEntry); + } + +protected: + void do_dump(Printer& printer) const override; + +private: + Object() = default; + + Optional m_type; + HashMap m_properties; +}; + +} + +template<> +struct AK::Traits : public DefaultTraits { + static unsigned hash(JSSpecCompiler::Runtime::PropertyKey const& key) + { + using namespace JSSpecCompiler::Runtime; + return key.visit( + [](Slot const& slot) { return pair_int_hash(1, slot.key.hash()); }, + [](StringPropertyKey const& string_key) { return pair_int_hash(2, string_key.key.hash()); }, + [](WellKnownSymbol const& symbol) { return pair_int_hash(3, to_underlying(symbol)); }); + } +}; diff --git a/Meta/Lagom/Tools/CodeGenerators/JSSpecCompiler/Runtime/ObjectType.cpp b/Meta/Lagom/Tools/CodeGenerators/JSSpecCompiler/Runtime/ObjectType.cpp new file mode 100644 index 00000000000..53d510dd12c --- /dev/null +++ b/Meta/Lagom/Tools/CodeGenerators/JSSpecCompiler/Runtime/ObjectType.cpp @@ -0,0 +1,16 @@ +/* + * Copyright (c) 2024, Dan Klishch + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include "Runtime/ObjectType.h" + +namespace JSSpecCompiler::Runtime { + +void ObjectType::do_dump(Printer& printer) const +{ + printer.format("ObjectType"); +} + +} diff --git a/Meta/Lagom/Tools/CodeGenerators/JSSpecCompiler/Runtime/ObjectType.h b/Meta/Lagom/Tools/CodeGenerators/JSSpecCompiler/Runtime/ObjectType.h new file mode 100644 index 00000000000..e6a0dd5b668 --- /dev/null +++ b/Meta/Lagom/Tools/CodeGenerators/JSSpecCompiler/Runtime/ObjectType.h @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2024, Dan Klishch + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include "Runtime/Realm.h" + +namespace JSSpecCompiler::Runtime { + +class ObjectType : public Cell { +public: + static constexpr StringView TYPE_NAME = "type"sv; + + static ObjectType* create(Realm* realm) + { + return realm->adopt_cell(new ObjectType {}); + } + + StringView type_name() const override { return TYPE_NAME; } + +protected: + void do_dump(Printer& printer) const override; + +private: + ObjectType() = default; +}; + +} diff --git a/Meta/Lagom/Tools/CodeGenerators/JSSpecCompiler/Runtime/Realm.cpp b/Meta/Lagom/Tools/CodeGenerators/JSSpecCompiler/Runtime/Realm.cpp new file mode 100644 index 00000000000..6f04059fcd3 --- /dev/null +++ b/Meta/Lagom/Tools/CodeGenerators/JSSpecCompiler/Runtime/Realm.cpp @@ -0,0 +1,18 @@ +/* + * Copyright (c) 2024, Dan Klishch + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include "Runtime/Realm.h" +#include "Runtime/Object.h" + +namespace JSSpecCompiler::Runtime { + +Realm::Realm(DiagnosticEngine& diag) + : m_diag(diag) + , m_global_object(Object::create(this)) +{ +} + +} diff --git a/Meta/Lagom/Tools/CodeGenerators/JSSpecCompiler/Runtime/Realm.h b/Meta/Lagom/Tools/CodeGenerators/JSSpecCompiler/Runtime/Realm.h new file mode 100644 index 00000000000..f9b65bbd1f1 --- /dev/null +++ b/Meta/Lagom/Tools/CodeGenerators/JSSpecCompiler/Runtime/Realm.h @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2024, Dan Klishch + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include +#include + +#include "Runtime/Cell.h" + +namespace JSSpecCompiler::Runtime { + +class Realm { +public: + Realm(DiagnosticEngine& diag); + + Runtime::Object* global_object() { return m_global_object; } + + template + T* adopt_cell(T* cell) + { + m_cells.append(NonnullOwnPtr { NonnullOwnPtr::AdoptTag::Adopt, *cell }); + return cell; + } + + DiagnosticEngine& diag() { return m_diag; } + +private: + DiagnosticEngine& m_diag; + Vector> m_cells; + + Runtime::Object* m_global_object; +}; + +}