mirror of
https://github.com/SerenityOS/serenity
synced 2024-10-06 16:09:30 +00:00
JSSpecCompiler: Pave a way for representing compile-time objects
This commit is contained in:
parent
3077e516a2
commit
d99d66e358
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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<Runtime::Realm>(m_diagnostic_engine))
|
||||
{
|
||||
}
|
||||
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
#pragma once
|
||||
|
||||
#include <AK/HashMap.h>
|
||||
#include <AK/NonnullOwnPtr.h>
|
||||
#include <AK/RefCounted.h>
|
||||
#include <AK/RefPtr.h>
|
||||
#include <AK/StringView.h>
|
||||
|
@ -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<NonnullRefPtr<FunctionDeclaration>> m_declarations_owner;
|
||||
HashMap<FlyString, FunctionDeclarationRef> m_abstract_operation_index;
|
||||
HashMap<StringView, EnumeratorRef> m_enumerator_nodes;
|
||||
|
||||
NonnullOwnPtr<Runtime::Realm> m_realm;
|
||||
};
|
||||
|
||||
struct FunctionArgument {
|
||||
|
|
48
Meta/Lagom/Tools/CodeGenerators/JSSpecCompiler/Printer.h
Normal file
48
Meta/Lagom/Tools/CodeGenerators/JSSpecCompiler/Printer.h
Normal file
|
@ -0,0 +1,48 @@
|
|||
/*
|
||||
* Copyright (c) 2024, Dan Klishch <danilklishch@gmail.com>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AK/StringBuilder.h>
|
||||
#include <AK/TemporaryChange.h>
|
||||
|
||||
namespace JSSpecCompiler {
|
||||
|
||||
class Printer {
|
||||
public:
|
||||
template<typename Func>
|
||||
void block(Func&& func, StringView start = "{"sv, StringView end = "}"sv)
|
||||
{
|
||||
formatln("{}", start);
|
||||
++indent_level;
|
||||
func();
|
||||
--indent_level;
|
||||
format("{}", end);
|
||||
}
|
||||
|
||||
template<typename... Parameters>
|
||||
void format(AK::CheckedFormatString<Parameters...>&& fmtstr, Parameters const&... parameters)
|
||||
{
|
||||
if (builder.string_view().ends_with('\n'))
|
||||
builder.append_repeated(' ', indent_level * 4);
|
||||
builder.appendff(move(fmtstr), forward<Parameters const&>(parameters)...);
|
||||
}
|
||||
|
||||
template<typename... Parameters>
|
||||
void formatln(AK::CheckedFormatString<Parameters...>&& fmtstr, Parameters const&... parameters)
|
||||
{
|
||||
format(move(fmtstr), forward<Parameters const&>(parameters)...);
|
||||
builder.append("\n"sv);
|
||||
}
|
||||
|
||||
StringView view() const { return builder.string_view(); }
|
||||
|
||||
private:
|
||||
StringBuilder builder;
|
||||
size_t indent_level = 0;
|
||||
};
|
||||
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
/*
|
||||
* Copyright (c) 2024, Dan Klishch <danilklishch@gmail.com>
|
||||
*
|
||||
* 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;
|
||||
};
|
||||
|
||||
}
|
|
@ -0,0 +1,69 @@
|
|||
/*
|
||||
* Copyright (c) 2024, Dan Klishch <danilklishch@gmail.com>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include "Runtime/Object.h"
|
||||
#include "Function.h"
|
||||
|
||||
namespace JSSpecCompiler::Runtime {
|
||||
|
||||
Optional<DataProperty&> Property::get_data_property_or_diagnose(Realm* realm, QualifiedName name, Location current_location)
|
||||
{
|
||||
if (!has<DataProperty>()) {
|
||||
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<DataProperty>();
|
||||
}
|
||||
|
||||
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(",");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
}
|
151
Meta/Lagom/Tools/CodeGenerators/JSSpecCompiler/Runtime/Object.h
Normal file
151
Meta/Lagom/Tools/CodeGenerators/JSSpecCompiler/Runtime/Object.h
Normal file
|
@ -0,0 +1,151 @@
|
|||
/*
|
||||
* Copyright (c) 2024, Dan Klishch <danilklishch@gmail.com>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AK/TypeCasts.h>
|
||||
|
||||
#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<Slot, StringPropertyKey, WellKnownSymbol> {
|
||||
public:
|
||||
using Variant::Variant;
|
||||
};
|
||||
|
||||
struct DataProperty {
|
||||
template<typename T>
|
||||
bool is() const
|
||||
{
|
||||
return ::is<Runtime::Object>(value);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
T* as() const
|
||||
{
|
||||
return verify_cast<T>(value);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
Optional<T*> get_or_diagnose(Realm* realm, QualifiedName name, Location location)
|
||||
{
|
||||
if (!is<T>()) {
|
||||
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<T>(value);
|
||||
}
|
||||
|
||||
Cell* value;
|
||||
Location location;
|
||||
|
||||
bool is_writable = true;
|
||||
bool is_enumerable = false;
|
||||
bool is_configurable = true;
|
||||
};
|
||||
|
||||
struct AccessorProperty {
|
||||
Optional<FunctionDeclarationRef> getter;
|
||||
Optional<FunctionDeclarationRef> setter;
|
||||
Location location;
|
||||
|
||||
bool is_enumerable = false;
|
||||
bool is_configurable = true;
|
||||
};
|
||||
|
||||
class Property : public Variant<DataProperty, AccessorProperty> {
|
||||
public:
|
||||
using Variant::Variant;
|
||||
|
||||
Location location() const
|
||||
{
|
||||
return visit([&](auto const& value) { return value.location; });
|
||||
}
|
||||
|
||||
Optional<DataProperty&> 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<ObjectType*> m_type;
|
||||
HashMap<PropertyKey, Property> m_properties;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
template<>
|
||||
struct AK::Traits<JSSpecCompiler::Runtime::PropertyKey> : public DefaultTraits<JSSpecCompiler::Runtime::PropertyKey> {
|
||||
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)); });
|
||||
}
|
||||
};
|
|
@ -0,0 +1,16 @@
|
|||
/*
|
||||
* Copyright (c) 2024, Dan Klishch <danilklishch@gmail.com>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include "Runtime/ObjectType.h"
|
||||
|
||||
namespace JSSpecCompiler::Runtime {
|
||||
|
||||
void ObjectType::do_dump(Printer& printer) const
|
||||
{
|
||||
printer.format("ObjectType");
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,31 @@
|
|||
/*
|
||||
* Copyright (c) 2024, Dan Klishch <danilklishch@gmail.com>
|
||||
*
|
||||
* 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;
|
||||
};
|
||||
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
/*
|
||||
* Copyright (c) 2024, Dan Klishch <danilklishch@gmail.com>
|
||||
*
|
||||
* 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))
|
||||
{
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
/*
|
||||
* Copyright (c) 2024, Dan Klishch <danilklishch@gmail.com>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AK/NonnullOwnPtr.h>
|
||||
#include <AK/Vector.h>
|
||||
|
||||
#include "Runtime/Cell.h"
|
||||
|
||||
namespace JSSpecCompiler::Runtime {
|
||||
|
||||
class Realm {
|
||||
public:
|
||||
Realm(DiagnosticEngine& diag);
|
||||
|
||||
Runtime::Object* global_object() { return m_global_object; }
|
||||
|
||||
template<typename T>
|
||||
T* adopt_cell(T* cell)
|
||||
{
|
||||
m_cells.append(NonnullOwnPtr<T> { NonnullOwnPtr<T>::AdoptTag::Adopt, *cell });
|
||||
return cell;
|
||||
}
|
||||
|
||||
DiagnosticEngine& diag() { return m_diag; }
|
||||
|
||||
private:
|
||||
DiagnosticEngine& m_diag;
|
||||
Vector<NonnullOwnPtr<Runtime::Cell>> m_cells;
|
||||
|
||||
Runtime::Object* m_global_object;
|
||||
};
|
||||
|
||||
}
|
Loading…
Reference in a new issue