Skip to content

Commit

Permalink
wip
Browse files Browse the repository at this point in the history
  • Loading branch information
sleepy-monax committed Dec 18, 2024
1 parent 1eda808 commit ebbe011
Show file tree
Hide file tree
Showing 14 changed files with 612 additions and 31 deletions.
1 change: 1 addition & 0 deletions index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
console.log('Hello, world!');
8 changes: 8 additions & 0 deletions src/libs/karm-base/string.h
Original file line number Diff line number Diff line change
Expand Up @@ -334,4 +334,12 @@ inline constexpr Karm::Str operator""s(char const *buf, usize len) {
return {buf, len};
}

inline constexpr Karm::_Str<Karm::Utf8> operator""_s8(char const *buf, usize len) {
return {buf, len};
}

inline constexpr Karm::_Str<Karm::Utf16> operator""_s16(char16_t const *buf, usize len) {
return {reinterpret_cast<u16 const *>(buf), len};
}

#pragma clang diagnostic pop
2 changes: 1 addition & 1 deletion src/libs/karm-io/fmt.h
Original file line number Diff line number Diff line change
Expand Up @@ -635,7 +635,7 @@ struct Formatter<Res<T, E>> {
}
}

Res<usize> format(Io::TextWriter &writer, Res<T> const &val) {
Res<usize> format(Io::TextWriter &writer, Res<T, E> const &val) {
if (val)
return _fmtOk.format(writer, val.unwrap());
return _fmtErr.format(writer, val.none());
Expand Down
14 changes: 14 additions & 0 deletions src/web/vaev-script/agent.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
#pragma once

#include <karm-base/res.h>

#include "gc.h"

namespace Vaev::Script {

// https://tc39.es/ecma262/#agent
struct Agent {
Gc::Gc &gc;
};

} // namespace Vaev::Script
29 changes: 0 additions & 29 deletions src/web/vaev-script/cli/main.cpp

This file was deleted.

77 changes: 77 additions & 0 deletions src/web/vaev-script/completion.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
#pragma once

#include <karm-base/res.h>

#include "value.h"

namespace Vaev::Script {

// https://tc39.es/ecma262/#sec-completion-record-specification-type
struct [[nodiscard]] Completion {
enum struct _Type {
NORMAL,
BREAK,
CONTINUE,
RETURN,
THROW,
};

using enum _Type;

_Type type = _Type::NORMAL;
Value value = UNDEFINED;
String target = u""_s16;

static Completion normal(Value value) {
return {NORMAL, value};
}

static Completion break_(String target) {
return {BREAK, UNDEFINED, target};
}

static Completion continue_(String target) {
return {CONTINUE, UNDEFINED, target};
}

static Completion return_(Value value) {
return {RETURN, value};
}

static Completion throw_(Value value) {
return {THROW, value};
}

bool operator==(_Type t) const {
return type == t;
}

void repr(Io::Emit &e) const {
switch (type) {
case NORMAL:
e("normal({})", value);
break;

case BREAK:
e("break({})", target);
break;

case CONTINUE:
e("continue({})", target);
break;

case RETURN:
e("return({})", value);
break;

case THROW:
e("throw({})", value);
break;
}
}
};

template <typename T>
using Except = Res<T, Completion>;

} // namespace Vaev::Script
48 changes: 48 additions & 0 deletions src/web/vaev-script/gc.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
#pragma once

#include <karm-base/base.h>
#include <karm-base/panic.h>
#include <karm-io/emit.h>

namespace Karm::Gc {

template <typename T>
struct Ref {
T *_ptr = nullptr;

Ref() = default;

Ref(T *ptr) : _ptr{ptr} {
if (not _ptr)
panic("null pointer");
}

T const *operator->() const {
return _ptr;
}

T *operator->() {
return _ptr;
}

T const &operator*() const {
return *_ptr;
}

T &operator*() {
return *_ptr;
}

void repr(Io::Emit &e) const {
e("{}", *_ptr);
}
};

struct Gc {
template <typename T, typename... Args>
Ref<T> alloc(Args &&...args) {
return new T{std::forward<Args>(args)...};
}
};

} // namespace Karm::Gc
25 changes: 25 additions & 0 deletions src/web/vaev-script/main/main.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
#include <karm-sys/entry.h>
#include <vaev-script/object.h>
#include <vaev-script/realm.h>

using namespace Vaev;

Async::Task<> entryPointAsync(Sys::Context &) {
Gc::Gc gc;

auto agent = gc.alloc<Script::Agent>(gc);
auto realm = gc.alloc<Script::Realm>(agent);

(void)realm->initializeHostDefinedRealm(agent);

auto object1 = Script::Object::create(*agent);
object1->defineOwnProperty(Script::PropertyKey::from(u"foo"_s16), {.value = 42.});

auto object2 = Script::Object::create(*agent, {.prototype = object1});

auto res = object2->get(Script::PropertyKey::from(u"foo"_s16), object2);

Sys::print("object2.foo = {}\n", res);

co_return Ok();
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"$schema": "https://schemas.cute.engineering/stable/cutekit.manifest.component.v1",
"id": "vaev-script.cli",
"id": "vaev-script.main",
"type": "exe",
"description": "ECMAScript engine testing tool",
"requires": [
Expand Down
167 changes: 167 additions & 0 deletions src/web/vaev-script/object.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,167 @@
#pragma once

#include <karm-base/vec.h>

#include "agent.h"
#include "ops.h"
#include "value.h"

namespace Vaev::Script {

// https://tc39.es/ecma262/#property-key

struct PropertyKey {
using _Store = Union<String, Symbol, u64>;

_Store store;

static PropertyKey from(String str) {
return {str};
}

static PropertyKey from(Symbol sym) {
return {sym};
}

static PropertyKey from(u64 num) {
return {num};
}
};

// https://tc39.es/ecma262/#sec-property-attributes
struct PropertyDescriptor {
Value value = UNDEFINED;
Boolean writable = false;
Value get = UNDEFINED;
Value set = UNDEFINED;
Boolean enumerable = false;
Boolean configurable = false;

// https://tc39.es/ecma262/#sec-isaccessordescriptor
Boolean isAccessorDescriptor() const {
// 1. If Desc is undefined, return false.

// 2. If Desc has a [[Get]] or [[Set]] field, return true.
if (get != UNDEFINED or set != UNDEFINED)
return true;

// 3. Return false.
return false;
}

// https://tc39.es/ecma262/#sec-isdatadescriptor
Boolean isDataDescriptor() const {
// 1. If Desc is undefined, return false.

// 2. If Desc has a [[Value]] field, return true.
if (value != UNDEFINED)
return true;

// 3. If Desc has a [[Writable]] field, return true.
if (writable)
return true;

// 4. Return false.
return false;
}
};

// https://tc39.es/ecma262/#sec-object-type

struct _ObjectCreateArgs {
Opt<Gc::Ref<Object>> prototype = NONE;
};

struct Object {
Agent &agent;

static Gc::Ref<Object> create(Agent &agent, _ObjectCreateArgs args = {});

// https://tc39.es/ecma262/#table-essential-internal-methods
Opt<Gc::Ref<Object>> getPrototypeOf() const;

Boolean setPrototypeOf(Gc::Ref<Object> proto);

Boolean isExtensible() const;

Boolean preventExtensions();

Union<PropertyDescriptor, Undefined> getOwnProperty(PropertyKey key) const;

// https://tc39.es/ecma262/#sec-ordinarydefineownproperty
void ordinaryDefineOwnProperty() {
// 1. Let current be ? O.[[GetOwnProperty]](P).
// 2. Let extensible be ? IsExtensible(O).
// 3. Return ValidateAndApplyPropertyDescriptor(O, P, extensible, Desc, current).
}

// https://tc39.es/ecma262/#sec-ordinary-object-internal-methods-and-internal-slots-defineownproperty-p-desc
Except<Boolean> defineOwnProperty(PropertyKey key, PropertyDescriptor desc) {
// 1. Return ? OrdinaryDefineOwnProperty(O, P, Desc).
return ordinaryDefineOwnProperty(key, desc);
}

Boolean hasProperty(PropertyKey key) const;

// https://tc39.es/ecma262/#sec-ordinary-object-internal-methods-and-internal-slots-get-p-receiver
Except<Value> get(PropertyKey key, Value receiver) const {
// 1. Return ? OrdinaryGet(O, P, Receiver).
return ordinaryGet(key, receiver);
}

// https://tc39.es/ecma262/#sec-ordinaryget
Except<Value> ordinaryGet(PropertyKey key, Value receiver) const {
// 1. Let desc be ? O.[[GetOwnProperty]](P).
auto maybeDesc = getOwnProperty(key);

// 2. If desc is undefined, then
if (maybeDesc == UNDEFINED) {
// a. Let parent be ? O.[[GetPrototypeOf]]().
auto parent = getPrototypeOf();

// b. If parent is null, return undefined.
if (parent == NONE)
return Ok(UNDEFINED);

// c. Return ? parent.[[Get]](P, Receiver).
return (*parent)->get(key, receiver);
}

auto &desc = maybeDesc.unwrap<PropertyDescriptor>();

// 3. If IsDataDescriptor(desc) is true, return desc.[[Value]].
if (desc.isDataDescriptor())
return Ok(desc.value);

// 4. Assert: IsAccessorDescriptor(desc) is true.
if (not desc.isAccessorDescriptor())
panic("expected accessor descriptor");

// 5. Let getter be desc.[[Get]].
auto getter = desc.get;

// 6. If getter is undefined, return undefined.
if (getter == UNDEFINED)
return Ok(UNDEFINED);

// 7. Return ? Call(getter, Receiver).
return Script::call(agent, getter, receiver);
}

Boolean set(PropertyKey key, Value value, Value receiver);

Boolean delete_(PropertyKey key) const;

Vec<PropertyKey> ownPropertyKeys() const;

// https://tc39.es/ecma262/#table-additional-essential-internal-methods-of-function-objects
Except<Value> call(Gc::Ref<Object> thisArg, Slice<Value> args) const;

Value construct(Slice<Value> args, Gc::Ref<Object> newTarget) const;

void repr(Io::Emit &e) const {
e("[object Object]");
}
};

} // namespace Vaev::Script
Loading

0 comments on commit ebbe011

Please sign in to comment.