@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.
@@ -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& prop_val = obj->storage[i];
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& key = obj->shape->property_names[i];
73
- const auto& prop_val = obj->storage[i];
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 << ": " << to_log_string(prop_val, visited, depth + 1);
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& key = obj->shape->property_names[i];
102
- const auto& prop_val = obj->storage[i];
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 << ": " << to_log_string(prop_val, visited, depth + 1);
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 !val.as_string()->value.empty();
102
- case JsType::Undefined:
103
- return false;
99
+ return is_truthy(val.as_string()->value);
100
+ case JsType::Boolean:
101
+ return val.as_boolean();
104
102
  case JsType::Null:
105
- return false;
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
  }