@ugo-studio/jspp 0.2.4 → 0.2.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +51 -36
- package/dist/analysis/scope.js +7 -0
- package/dist/analysis/typeAnalyzer.js +58 -5
- package/dist/ast/symbols.js +58 -15
- package/dist/cli/args.js +59 -0
- package/dist/cli/colors.js +9 -0
- package/dist/cli/file-utils.js +20 -0
- package/dist/cli/index.js +160 -0
- package/dist/cli/spinner.js +55 -0
- package/dist/cli.js +1 -1
- package/dist/core/codegen/control-flow-handlers.js +139 -51
- package/dist/core/codegen/declaration-handlers.js +45 -12
- package/dist/core/codegen/expression-handlers.js +139 -48
- package/dist/core/codegen/function-handlers.js +53 -59
- package/dist/core/codegen/helpers.js +196 -32
- package/dist/core/codegen/index.js +15 -9
- package/dist/core/codegen/statement-handlers.js +178 -63
- package/dist/core/codegen/visitor.js +22 -2
- package/dist/core/constants.js +16 -0
- package/dist/core/error.js +58 -0
- package/dist/core/parser.js +2 -2
- package/dist/index.js +6 -3
- package/package.json +3 -3
- package/src/prelude/scheduler.hpp +144 -144
- package/src/prelude/utils/access.hpp +2 -2
- package/src/prelude/utils/log_any_value/object.hpp +12 -10
- package/src/prelude/utils/log_any_value/primitives.hpp +7 -0
- package/src/prelude/utils/operators.hpp +4 -6
- package/src/prelude/values/prototypes/function.hpp +18 -0
|
@@ -1,145 +1,145 @@
|
|
|
1
|
-
#pragma once
|
|
2
|
-
#include <deque>
|
|
3
|
-
#include <functional>
|
|
4
|
-
#include <chrono>
|
|
5
|
-
#include <queue>
|
|
6
|
-
#include <unordered_set>
|
|
7
|
-
#include <vector>
|
|
8
|
-
#include <thread>
|
|
9
|
-
#include <iostream>
|
|
10
|
-
|
|
11
|
-
namespace jspp {
|
|
12
|
-
class Scheduler {
|
|
13
|
-
public:
|
|
14
|
-
using Task = std::function<void()>;
|
|
15
|
-
using TimePoint = std::chrono::steady_clock::time_point;
|
|
16
|
-
|
|
17
|
-
struct Timer {
|
|
18
|
-
size_t id;
|
|
19
|
-
TimePoint next_run;
|
|
20
|
-
std::chrono::milliseconds interval; // 0 if not repeating
|
|
21
|
-
Task task;
|
|
22
|
-
|
|
23
|
-
// Min-heap priority queue (smallest time at top)
|
|
24
|
-
bool operator>(const Timer& other) const {
|
|
25
|
-
return next_run > other.next_run;
|
|
26
|
-
}
|
|
27
|
-
};
|
|
28
|
-
|
|
29
|
-
static Scheduler& instance() {
|
|
30
|
-
static Scheduler s;
|
|
31
|
-
return s;
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
void enqueue(Task task) {
|
|
35
|
-
tasks.push_back(std::move(task));
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
size_t set_timeout(Task task, size_t delay_ms) {
|
|
39
|
-
return schedule_timer(std::move(task), delay_ms, false);
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
size_t set_interval(Task task, size_t delay_ms) {
|
|
43
|
-
return schedule_timer(std::move(task), delay_ms, true);
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
void clear_timer(size_t id) {
|
|
47
|
-
cancelled_timers.insert(id);
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
void run() {
|
|
51
|
-
while (true) {
|
|
52
|
-
bool has_work = false;
|
|
53
|
-
|
|
54
|
-
// 1. Process all immediate tasks (microtask/task queue)
|
|
55
|
-
while (!tasks.empty()) {
|
|
56
|
-
Task task = tasks.front();
|
|
57
|
-
tasks.pop_front();
|
|
58
|
-
task();
|
|
59
|
-
has_work = true;
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
// 2. Process timers
|
|
63
|
-
auto now = std::chrono::steady_clock::now();
|
|
64
|
-
while (!timers.empty()) {
|
|
65
|
-
// Peek top
|
|
66
|
-
const auto& top = timers.top();
|
|
67
|
-
|
|
68
|
-
// Cleanup cancelled timers lazily
|
|
69
|
-
if (cancelled_timers.count(top.id)) {
|
|
70
|
-
cancelled_timers.erase(top.id);
|
|
71
|
-
timers.pop();
|
|
72
|
-
continue;
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
if (top.next_run <= now) {
|
|
76
|
-
// Timer is ready
|
|
77
|
-
Timer t = top;
|
|
78
|
-
timers.pop();
|
|
79
|
-
|
|
80
|
-
// Execute task
|
|
81
|
-
t.task();
|
|
82
|
-
has_work = true;
|
|
83
|
-
|
|
84
|
-
// Reschedule if interval and not cancelled during execution
|
|
85
|
-
if (t.interval.count() > 0 && cancelled_timers.find(t.id) == cancelled_timers.end()) {
|
|
86
|
-
// Drift-safe(ish) scheduling: run next interval relative to now
|
|
87
|
-
// Note: JS allows drift.
|
|
88
|
-
t.next_run = std::chrono::steady_clock::now() + t.interval;
|
|
89
|
-
timers.push(t);
|
|
90
|
-
}
|
|
91
|
-
} else {
|
|
92
|
-
break; // Next timer is in the future
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
// 3. Exit or Wait
|
|
97
|
-
if (!has_work) {
|
|
98
|
-
if (tasks.empty() && timers.empty()) {
|
|
99
|
-
break; // No pending work, exit event loop
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
if (!timers.empty()) {
|
|
103
|
-
auto next_time = timers.top().next_run;
|
|
104
|
-
std::this_thread::sleep_until(next_time);
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
bool has_tasks() const {
|
|
111
|
-
return !tasks.empty() || !timers.empty();
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
private:
|
|
115
|
-
std::deque<Task> tasks;
|
|
116
|
-
std::priority_queue<Timer, std::vector<Timer>, std::greater<Timer>> timers;
|
|
117
|
-
std::unordered_set<size_t> cancelled_timers;
|
|
118
|
-
size_t next_timer_id = 1;
|
|
119
|
-
const size_t MAX_TIMER_ID = 2147483647; // 2^31 - 1
|
|
120
|
-
|
|
121
|
-
size_t schedule_timer(Task task, size_t delay_ms, bool repeat) {
|
|
122
|
-
size_t id = next_timer_id++;
|
|
123
|
-
if (next_timer_id > MAX_TIMER_ID) {
|
|
124
|
-
next_timer_id = 1;
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
// If we wrap around, ensure we don't accidentally treat this new ID as cancelled
|
|
128
|
-
// (in case an old timer with this ID is still lingering in the 'cancelled' set)
|
|
129
|
-
if (cancelled_timers.count(id)) {
|
|
130
|
-
cancelled_timers.erase(id);
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
auto now = std::chrono::steady_clock::now();
|
|
134
|
-
|
|
135
|
-
Timer t;
|
|
136
|
-
t.id = id;
|
|
137
|
-
t.next_run = now + std::chrono::milliseconds(delay_ms);
|
|
138
|
-
t.interval = repeat ? std::chrono::milliseconds(delay_ms) : std::chrono::milliseconds(0);
|
|
139
|
-
t.task = std::move(task);
|
|
140
|
-
|
|
141
|
-
timers.push(t);
|
|
142
|
-
return id;
|
|
143
|
-
}
|
|
144
|
-
};
|
|
1
|
+
#pragma once
|
|
2
|
+
#include <deque>
|
|
3
|
+
#include <functional>
|
|
4
|
+
#include <chrono>
|
|
5
|
+
#include <queue>
|
|
6
|
+
#include <unordered_set>
|
|
7
|
+
#include <vector>
|
|
8
|
+
#include <thread>
|
|
9
|
+
#include <iostream>
|
|
10
|
+
|
|
11
|
+
namespace jspp {
|
|
12
|
+
class Scheduler {
|
|
13
|
+
public:
|
|
14
|
+
using Task = std::function<void()>;
|
|
15
|
+
using TimePoint = std::chrono::steady_clock::time_point;
|
|
16
|
+
|
|
17
|
+
struct Timer {
|
|
18
|
+
size_t id;
|
|
19
|
+
TimePoint next_run;
|
|
20
|
+
std::chrono::milliseconds interval; // 0 if not repeating
|
|
21
|
+
Task task;
|
|
22
|
+
|
|
23
|
+
// Min-heap priority queue (smallest time at top)
|
|
24
|
+
bool operator>(const Timer& other) const {
|
|
25
|
+
return next_run > other.next_run;
|
|
26
|
+
}
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
static Scheduler& instance() {
|
|
30
|
+
static Scheduler s;
|
|
31
|
+
return s;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
void enqueue(Task task) {
|
|
35
|
+
tasks.push_back(std::move(task));
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
size_t set_timeout(Task task, size_t delay_ms) {
|
|
39
|
+
return schedule_timer(std::move(task), delay_ms, false);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
size_t set_interval(Task task, size_t delay_ms) {
|
|
43
|
+
return schedule_timer(std::move(task), delay_ms, true);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
void clear_timer(size_t id) {
|
|
47
|
+
cancelled_timers.insert(id);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
void run() {
|
|
51
|
+
while (true) {
|
|
52
|
+
bool has_work = false;
|
|
53
|
+
|
|
54
|
+
// 1. Process all immediate tasks (microtask/task queue)
|
|
55
|
+
while (!tasks.empty()) {
|
|
56
|
+
Task task = tasks.front();
|
|
57
|
+
tasks.pop_front();
|
|
58
|
+
task();
|
|
59
|
+
has_work = true;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// 2. Process timers
|
|
63
|
+
auto now = std::chrono::steady_clock::now();
|
|
64
|
+
while (!timers.empty()) {
|
|
65
|
+
// Peek top
|
|
66
|
+
const auto& top = timers.top();
|
|
67
|
+
|
|
68
|
+
// Cleanup cancelled timers lazily
|
|
69
|
+
if (cancelled_timers.count(top.id)) {
|
|
70
|
+
cancelled_timers.erase(top.id);
|
|
71
|
+
timers.pop();
|
|
72
|
+
continue;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
if (top.next_run <= now) {
|
|
76
|
+
// Timer is ready
|
|
77
|
+
Timer t = top;
|
|
78
|
+
timers.pop();
|
|
79
|
+
|
|
80
|
+
// Execute task
|
|
81
|
+
t.task();
|
|
82
|
+
has_work = true;
|
|
83
|
+
|
|
84
|
+
// Reschedule if interval and not cancelled during execution
|
|
85
|
+
if (t.interval.count() > 0 && cancelled_timers.find(t.id) == cancelled_timers.end()) {
|
|
86
|
+
// Drift-safe(ish) scheduling: run next interval relative to now
|
|
87
|
+
// Note: JS allows drift.
|
|
88
|
+
t.next_run = std::chrono::steady_clock::now() + t.interval;
|
|
89
|
+
timers.push(t);
|
|
90
|
+
}
|
|
91
|
+
} else {
|
|
92
|
+
break; // Next timer is in the future
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// 3. Exit or Wait
|
|
97
|
+
if (!has_work) {
|
|
98
|
+
if (tasks.empty() && timers.empty()) {
|
|
99
|
+
break; // No pending work, exit event loop
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
if (!timers.empty()) {
|
|
103
|
+
auto next_time = timers.top().next_run;
|
|
104
|
+
std::this_thread::sleep_until(next_time);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
bool has_tasks() const {
|
|
111
|
+
return !tasks.empty() || !timers.empty();
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
private:
|
|
115
|
+
std::deque<Task> tasks;
|
|
116
|
+
std::priority_queue<Timer, std::vector<Timer>, std::greater<Timer>> timers;
|
|
117
|
+
std::unordered_set<size_t> cancelled_timers;
|
|
118
|
+
size_t next_timer_id = 1;
|
|
119
|
+
const size_t MAX_TIMER_ID = 2147483647; // 2^31 - 1
|
|
120
|
+
|
|
121
|
+
size_t schedule_timer(Task task, size_t delay_ms, bool repeat) {
|
|
122
|
+
size_t id = next_timer_id++;
|
|
123
|
+
if (next_timer_id > MAX_TIMER_ID) {
|
|
124
|
+
next_timer_id = 1;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// If we wrap around, ensure we don't accidentally treat this new ID as cancelled
|
|
128
|
+
// (in case an old timer with this ID is still lingering in the 'cancelled' set)
|
|
129
|
+
if (cancelled_timers.count(id)) {
|
|
130
|
+
cancelled_timers.erase(id);
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
auto now = std::chrono::steady_clock::now();
|
|
134
|
+
|
|
135
|
+
Timer t;
|
|
136
|
+
t.id = id;
|
|
137
|
+
t.next_run = now + std::chrono::milliseconds(delay_ms);
|
|
138
|
+
t.interval = repeat ? std::chrono::milliseconds(delay_ms) : std::chrono::milliseconds(0);
|
|
139
|
+
t.task = std::move(task);
|
|
140
|
+
|
|
141
|
+
timers.push(t);
|
|
142
|
+
return id;
|
|
143
|
+
}
|
|
144
|
+
};
|
|
145
145
|
}
|
|
@@ -231,7 +231,7 @@ namespace jspp
|
|
|
231
231
|
{
|
|
232
232
|
throw jspp::Exception::make_exception("Function has non-object prototype in instanceof check", "TypeError");
|
|
233
233
|
}
|
|
234
|
-
|
|
234
|
+
|
|
235
235
|
AnyValue current = lhs;
|
|
236
236
|
|
|
237
237
|
while (true)
|
|
@@ -253,7 +253,7 @@ namespace jspp
|
|
|
253
253
|
{
|
|
254
254
|
break;
|
|
255
255
|
}
|
|
256
|
-
|
|
256
|
+
|
|
257
257
|
if (proto.is_null() || proto.is_undefined())
|
|
258
258
|
break;
|
|
259
259
|
if (is_strictly_equal_to_primitive(proto, targetProto))
|
|
@@ -22,7 +22,7 @@ namespace jspp
|
|
|
22
22
|
|
|
23
23
|
for (size_t i = 0; i < obj->storage.size(); ++i)
|
|
24
24
|
{
|
|
25
|
-
const auto&
|
|
25
|
+
const auto &prop_val = obj->storage[i];
|
|
26
26
|
if (!is_enumerable_property(prop_val))
|
|
27
27
|
{
|
|
28
28
|
prop_count--;
|
|
@@ -69,8 +69,8 @@ namespace jspp
|
|
|
69
69
|
size_t current_prop = 0;
|
|
70
70
|
for (size_t i = 0; i < obj->shape->property_names.size(); ++i)
|
|
71
71
|
{
|
|
72
|
-
const auto&
|
|
73
|
-
const auto&
|
|
72
|
+
const auto &key = obj->shape->property_names[i];
|
|
73
|
+
const auto &prop_val = obj->storage[i];
|
|
74
74
|
|
|
75
75
|
if (!is_enumerable_property(prop_val))
|
|
76
76
|
continue;
|
|
@@ -81,9 +81,10 @@ namespace jspp
|
|
|
81
81
|
}
|
|
82
82
|
else
|
|
83
83
|
{
|
|
84
|
-
ss << "\"" << key << "\"";
|
|
84
|
+
ss << Color::GREEN << "\"" << key << "\"" << Color::RESET;
|
|
85
85
|
}
|
|
86
|
-
ss << ": " <<
|
|
86
|
+
ss << Color::BRIGHT_BLACK << ": " << Color::RESET;
|
|
87
|
+
ss << to_log_string(prop_val, visited, depth + 1);
|
|
87
88
|
if (++current_prop < prop_count)
|
|
88
89
|
ss << Color::BRIGHT_BLACK << ", " << Color::RESET;
|
|
89
90
|
}
|
|
@@ -98,12 +99,12 @@ namespace jspp
|
|
|
98
99
|
size_t props_shown = 0;
|
|
99
100
|
for (size_t i = 0; i < obj->shape->property_names.size(); ++i)
|
|
100
101
|
{
|
|
101
|
-
const auto&
|
|
102
|
-
const auto&
|
|
102
|
+
const auto &key = obj->shape->property_names[i];
|
|
103
|
+
const auto &prop_val = obj->storage[i];
|
|
103
104
|
|
|
104
105
|
if (props_shown >= MAX_OBJECT_PROPS)
|
|
105
106
|
break;
|
|
106
|
-
|
|
107
|
+
|
|
107
108
|
if (!is_enumerable_property(prop_val))
|
|
108
109
|
continue;
|
|
109
110
|
|
|
@@ -117,9 +118,10 @@ namespace jspp
|
|
|
117
118
|
}
|
|
118
119
|
else
|
|
119
120
|
{
|
|
120
|
-
ss << "\"" << key << "\"";
|
|
121
|
+
ss << Color::GREEN << "\"" << key << "\"" << Color::RESET;
|
|
121
122
|
}
|
|
122
|
-
ss << ": " <<
|
|
123
|
+
ss << Color::BRIGHT_BLACK << ": " << Color::RESET;
|
|
124
|
+
ss << to_log_string(prop_val, visited, depth + 1);
|
|
123
125
|
props_shown++;
|
|
124
126
|
}
|
|
125
127
|
if (prop_count > MAX_OBJECT_PROPS)
|
|
@@ -27,7 +27,14 @@ namespace jspp
|
|
|
27
27
|
if (val.is_symbol())
|
|
28
28
|
return Color::BLUE + val.to_std_string() + Color::RESET;
|
|
29
29
|
if (val.is_accessor_descriptor())
|
|
30
|
+
{
|
|
31
|
+
auto desc = val.as_accessor_descriptor();
|
|
32
|
+
if (desc->get.has_value() && !desc->set.has_value())
|
|
33
|
+
return Color::BLUE + std::string("[Getter]") + Color::RESET;
|
|
34
|
+
if (!desc->get.has_value() && desc->set.has_value())
|
|
35
|
+
return Color::BLUE + std::string("[Setter]") + Color::RESET;
|
|
30
36
|
return Color::BLUE + std::string("[Getter/Setter]") + Color::RESET;
|
|
37
|
+
}
|
|
31
38
|
|
|
32
39
|
if (val.is_string())
|
|
33
40
|
{
|
|
@@ -93,16 +93,14 @@ namespace jspp
|
|
|
93
93
|
{
|
|
94
94
|
switch (val.get_type())
|
|
95
95
|
{
|
|
96
|
-
case JsType::Boolean:
|
|
97
|
-
return val.as_boolean();
|
|
98
96
|
case JsType::Number:
|
|
99
97
|
return is_truthy(val.as_double());
|
|
100
98
|
case JsType::String:
|
|
101
|
-
return
|
|
102
|
-
case JsType::
|
|
103
|
-
return
|
|
99
|
+
return is_truthy(val.as_string()->value);
|
|
100
|
+
case JsType::Boolean:
|
|
101
|
+
return val.as_boolean();
|
|
104
102
|
case JsType::Null:
|
|
105
|
-
|
|
103
|
+
case JsType::Undefined:
|
|
106
104
|
case JsType::Uninitialized:
|
|
107
105
|
return false;
|
|
108
106
|
default:
|
|
@@ -20,6 +20,24 @@ namespace jspp
|
|
|
20
20
|
key);
|
|
21
21
|
}
|
|
22
22
|
|
|
23
|
+
// --- call() method ---
|
|
24
|
+
if (key == "call")
|
|
25
|
+
{
|
|
26
|
+
return AnyValue::make_function([](const AnyValue &thisVal, std::span<const AnyValue> args) -> AnyValue
|
|
27
|
+
{
|
|
28
|
+
AnyValue thisArg = Constants::UNDEFINED;
|
|
29
|
+
std::span<const AnyValue> fnArgs;
|
|
30
|
+
|
|
31
|
+
if (!args.empty())
|
|
32
|
+
{
|
|
33
|
+
thisArg = args[0];
|
|
34
|
+
fnArgs = args.subspan(1);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
return thisVal.call(thisArg, fnArgs);
|
|
38
|
+
}, key);
|
|
39
|
+
}
|
|
40
|
+
|
|
23
41
|
return std::nullopt;
|
|
24
42
|
}
|
|
25
43
|
}
|