JSSpecCompiler: Pave a way for representing compile-time objects

This commit is contained in:
Dan Klishch 2024-03-09 00:51:37 -05:00 committed by Andrew Kaster
parent 3077e516a2
commit d99d66e358
12 changed files with 418 additions and 0 deletions

View file

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

View file

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

View file

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

View file

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

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

View file

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

View file

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

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

View file

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

View file

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

View file

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

View file

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