@ugo-studio/jspp 0.1.5 → 0.1.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.
@@ -0,0 +1,275 @@
1
+ #pragma once
2
+
3
+ #include "types.hpp"
4
+ #include "values/async_iterator.hpp"
5
+ #include "any_value.hpp"
6
+ #include "values/prototypes/async_iterator.hpp"
7
+
8
+ // --- JsAsyncIterator methods ---
9
+
10
+ template <typename T>
11
+ std::string jspp::JsAsyncIterator<T>::to_std_string() const
12
+ {
13
+ return "[object AsyncGenerator]";
14
+ }
15
+
16
+ template <typename T>
17
+ jspp::AnyValue jspp::JsAsyncIterator<T>::get_property(const std::string &key, const AnyValue &thisVal)
18
+ {
19
+ auto it = props.find(key);
20
+ if (it == props.end())
21
+ {
22
+ if constexpr (std::is_same_v<T, AnyValue>)
23
+ {
24
+ auto proto_it = AsyncIteratorPrototypes::get(key, this);
25
+ if (proto_it.has_value())
26
+ {
27
+ return AnyValue::resolve_property_for_read(proto_it.value(), thisVal, key);
28
+ }
29
+ }
30
+ return AnyValue::make_undefined();
31
+ }
32
+ return AnyValue::resolve_property_for_read(it->second, thisVal, key);
33
+ }
34
+
35
+ template <typename T>
36
+ jspp::AnyValue jspp::JsAsyncIterator<T>::set_property(const std::string &key, const AnyValue &value, const AnyValue &thisVal)
37
+ {
38
+ if constexpr (std::is_same_v<T, AnyValue>)
39
+ {
40
+ auto proto_it = AsyncIteratorPrototypes::get(key, this);
41
+ if (proto_it.has_value())
42
+ {
43
+ auto proto_value = proto_it.value();
44
+ if (proto_value.is_accessor_descriptor())
45
+ {
46
+ return AnyValue::resolve_property_for_write(proto_value, thisVal, value, key);
47
+ }
48
+ if (proto_value.is_data_descriptor() && !proto_value.as_data_descriptor()->writable)
49
+ {
50
+ return AnyValue::resolve_property_for_write(proto_value, thisVal, value, key);
51
+ }
52
+ }
53
+ }
54
+
55
+ auto it = props.find(key);
56
+ if (it != props.end())
57
+ {
58
+ return jspp::AnyValue::resolve_property_for_write(it->second, thisVal, value, key);
59
+ }
60
+ else
61
+ {
62
+ props[key] = value;
63
+ return value;
64
+ }
65
+ }
66
+
67
+ template <typename T>
68
+ void jspp::JsAsyncIterator<T>::resume_next()
69
+ {
70
+ if (!handle || handle.done())
71
+ return;
72
+ auto &p = handle.promise();
73
+ if (p.is_awaiting || p.is_running)
74
+ return;
75
+ if (p.pending_calls.empty())
76
+ return;
77
+
78
+ p.is_running = true;
79
+
80
+ auto &next_call = p.pending_calls.front();
81
+ p.current_input = next_call.second;
82
+
83
+ handle.resume();
84
+
85
+ p.is_running = false;
86
+
87
+ // After yield/return, if more calls are pending, handle them.
88
+ if (!p.pending_calls.empty() && !p.is_awaiting && !handle.done())
89
+ {
90
+ Scheduler::instance().enqueue([this]()
91
+ { this->resume_next(); });
92
+ }
93
+ }
94
+
95
+ template <typename T>
96
+ jspp::JsPromise jspp::JsAsyncIterator<T>::next(const T &val)
97
+ {
98
+ JsPromise p;
99
+ if (handle)
100
+ {
101
+ if (handle.done())
102
+ {
103
+ p.resolve(AnyValue::make_object({{"value", Constants::UNDEFINED}, {"done", Constants::TRUE}}));
104
+ }
105
+ else
106
+ {
107
+ handle.promise().pending_calls.push({p, val});
108
+ resume_next();
109
+ }
110
+ }
111
+ else
112
+ {
113
+ p.resolve(AnyValue::make_object({{"value", Constants::UNDEFINED}, {"done", Constants::TRUE}}));
114
+ }
115
+ return p;
116
+ }
117
+
118
+ // --- JsAsyncIterator::promise_type methods ---
119
+
120
+ template <typename T>
121
+ template <typename From>
122
+ auto jspp::JsAsyncIterator<T>::promise_type::yield_value(From &&from)
123
+ {
124
+ if (!pending_calls.empty())
125
+ {
126
+ auto call = pending_calls.front();
127
+ pending_calls.pop();
128
+ AnyValue result = AnyValue::make_object({{"value", std::forward<From>(from)}, {"done", AnyValue::make_boolean(false)}});
129
+ call.first.resolve(result);
130
+ }
131
+
132
+ struct YieldAwaiter
133
+ {
134
+ promise_type &p;
135
+ bool await_ready() { return false; }
136
+ void await_suspend(std::coroutine_handle<promise_type> h)
137
+ {
138
+ // Suspended at yield.
139
+ }
140
+ T await_resume() { return p.current_input; }
141
+ };
142
+ return YieldAwaiter{*this};
143
+ }
144
+
145
+ template <typename T>
146
+ template <typename From>
147
+ void jspp::JsAsyncIterator<T>::promise_type::return_value(From &&from)
148
+ {
149
+ if (!pending_calls.empty())
150
+ {
151
+ auto call = pending_calls.front();
152
+ pending_calls.pop();
153
+ AnyValue result = AnyValue::make_object({{"value", std::forward<From>(from)}, {"done", Constants::TRUE}});
154
+ call.first.resolve(result);
155
+ }
156
+
157
+ while (!pending_calls.empty())
158
+ {
159
+ auto call = pending_calls.front();
160
+ pending_calls.pop();
161
+ AnyValue result = AnyValue::make_object({{"value", Constants::UNDEFINED}, {"done", Constants::TRUE}});
162
+ call.first.resolve(result);
163
+ }
164
+ }
165
+
166
+ template <typename T>
167
+ void jspp::JsAsyncIterator<T>::promise_type::fail_all(const AnyValue &reason)
168
+ {
169
+ while (!pending_calls.empty())
170
+ {
171
+ auto call = pending_calls.front();
172
+ pending_calls.pop();
173
+ call.first.reject(reason);
174
+ }
175
+ }
176
+
177
+ template <typename T>
178
+ void jspp::JsAsyncIterator<T>::promise_type::unhandled_exception()
179
+ {
180
+ try
181
+ {
182
+ std::rethrow_exception(std::current_exception());
183
+ }
184
+ catch (const Exception &e)
185
+ {
186
+ fail_all(*e.data);
187
+ }
188
+ catch (const std::exception &e)
189
+ {
190
+ fail_all(AnyValue::make_string(e.what()));
191
+ }
192
+ catch (...)
193
+ {
194
+ fail_all(AnyValue::make_string("Unknown error in async generator"));
195
+ }
196
+ }
197
+
198
+ template <typename T>
199
+ auto jspp::JsAsyncIterator<T>::promise_type::await_transform(AnyValue value)
200
+ {
201
+ is_awaiting = true;
202
+ struct AsyncIterAwaiter
203
+ {
204
+ AnyValueAwaiter base_awaiter;
205
+ promise_type &p_ref;
206
+
207
+ bool await_ready() { return base_awaiter.await_ready(); }
208
+ void await_suspend(std::coroutine_handle<promise_type> h)
209
+ {
210
+ if (!base_awaiter.value.is_promise())
211
+ {
212
+ jspp::Scheduler::instance().enqueue([h]() mutable
213
+ {
214
+ auto &pr = h.promise();
215
+ pr.is_awaiting = false;
216
+ pr.is_running = true;
217
+ h.resume();
218
+ pr.is_running = false;
219
+
220
+ if (!h.done() && !pr.is_awaiting && !pr.pending_calls.empty())
221
+ {
222
+ while (!h.done() && !pr.is_awaiting && !pr.pending_calls.empty())
223
+ {
224
+ pr.is_running = true;
225
+ pr.current_input = pr.pending_calls.front().second;
226
+ h.resume();
227
+ pr.is_running = false;
228
+ }
229
+ } });
230
+ return;
231
+ }
232
+ auto p = base_awaiter.value.as_promise();
233
+ p->then(
234
+ [h](AnyValue v) mutable
235
+ {
236
+ auto &pr = h.promise();
237
+ pr.is_awaiting = false;
238
+ pr.is_running = true;
239
+ h.resume();
240
+ pr.is_running = false;
241
+
242
+ // After resume, if we suspended at a yield and have more calls, loop.
243
+ if (!h.done() && !pr.is_awaiting && !pr.pending_calls.empty())
244
+ {
245
+ // We need to call resume_next, but we don't have the iterator.
246
+ // Actually, the loop in resume_next handles this.
247
+ // But wait, who calls resume_next?
248
+ // If we are here, we are in a microtask.
249
+ // Let's just manually continue if needed.
250
+ while (!h.done() && !pr.is_awaiting && !pr.pending_calls.empty())
251
+ {
252
+ pr.is_running = true;
253
+ pr.current_input = pr.pending_calls.front().second;
254
+ h.resume();
255
+ pr.is_running = false;
256
+ }
257
+ }
258
+ },
259
+ [h](AnyValue e) mutable
260
+ {
261
+ auto &pr = h.promise();
262
+ pr.is_awaiting = false;
263
+ pr.is_running = true;
264
+ h.resume();
265
+ pr.is_running = false;
266
+ // Errors handled via await_resume/exception throw
267
+ });
268
+ }
269
+ AnyValue await_resume()
270
+ {
271
+ return base_awaiter.await_resume();
272
+ }
273
+ };
274
+ return AsyncIterAwaiter{AnyValueAwaiter{std::move(value)}, *this};
275
+ }
@@ -32,6 +32,10 @@ namespace jspp
32
32
  {
33
33
  return AnyValue::make_promise((*func)(thisVal, args));
34
34
  }
35
+ else if (std::function<jspp::JsAsyncIterator<jspp::AnyValue>(const AnyValue &, std::span<const AnyValue>)> *func = std::get_if<3>(&callable))
36
+ {
37
+ return AnyValue::from_async_iterator((*func)(thisVal, args));
38
+ }
35
39
  else
36
40
  {
37
41
  return AnyValue::make_undefined();
@@ -92,6 +92,10 @@ namespace jspp {
92
92
  }
93
93
  }
94
94
 
95
+ inline auto JsPromise::operator co_await() const {
96
+ return AnyValueAwaiter{AnyValue::make_promise(*this)};
97
+ }
98
+
95
99
  inline std::string JsPromise::to_std_string() const {
96
100
  return "[object Promise]";
97
101
  }
@@ -144,17 +148,20 @@ namespace jspp {
144
148
  return AnyValueAwaiter{value};
145
149
  }
146
150
 
151
+ inline auto JsPromisePromiseType::await_transform(const JsPromise& value) {
152
+ return AnyValueAwaiter{AnyValue::make_promise(value)};
153
+ }
154
+
147
155
  // --- AnyValueAwaiter ---
148
156
 
149
157
  inline bool AnyValueAwaiter::await_ready() {
150
- if (!value.is_promise()) return true; // Non-promise values are ready immediately (for now)
151
- // Always suspend for promises to ensure microtask interleaving, even if already resolved.
158
+ // Always suspend to ensure microtask interleaving, even if already resolved or not a promise.
152
159
  return false;
153
160
  }
154
161
 
155
162
  inline void AnyValueAwaiter::await_suspend(std::coroutine_handle<> h) {
156
163
  if (!value.is_promise()) {
157
- h.resume();
164
+ jspp::Scheduler::instance().enqueue([h]() mutable { h.resume(); });
158
165
  return;
159
166
  }
160
167
  auto p = value.as_promise();
@@ -46,6 +46,8 @@ namespace jspp
46
46
  std::string to_std_string() const;
47
47
  AnyValue get_property(const std::string& key, const AnyValue& thisVal);
48
48
  AnyValue set_property(const std::string& key, const AnyValue& value, const AnyValue& thisVal);
49
+
50
+ auto operator co_await() const;
49
51
  };
50
52
 
51
53
  struct JsPromisePromiseType {
@@ -61,13 +63,7 @@ namespace jspp
61
63
 
62
64
  // await_transform for AnyValue
63
65
  auto await_transform(const AnyValue& value);
64
- };
65
-
66
- // Awaiter for AnyValue
67
- struct AnyValueAwaiter {
68
- const AnyValue& value; // Reference to the value being awaited
69
- bool await_ready();
70
- void await_suspend(std::coroutine_handle<> h);
71
- AnyValue await_resume();
66
+ // await_transform for JsPromise
67
+ auto await_transform(const JsPromise& value);
72
68
  };
73
69
  }
@@ -0,0 +1,50 @@
1
+ #pragma once
2
+
3
+ #include "types.hpp"
4
+ #include "values/async_iterator.hpp"
5
+ #include "any_value.hpp"
6
+ #include "exception.hpp"
7
+ #include "utils/operators.hpp"
8
+
9
+ namespace jspp
10
+ {
11
+ namespace AsyncIteratorPrototypes
12
+ {
13
+ inline std::optional<AnyValue> get(const std::string &key, JsAsyncIterator<AnyValue> *self)
14
+ {
15
+ // --- toString() method ---
16
+ if (key == "toString" || key == WellKnownSymbols::toStringTag->key)
17
+ {
18
+ return AnyValue::make_function([self](const AnyValue &thisVal, std::span<const AnyValue>) -> AnyValue
19
+ { return AnyValue::make_string(self->to_std_string()); },
20
+ key);
21
+ }
22
+ // --- [Symbol.asyncIterator]() method ---
23
+ // For async iterators, the async iterator is itself (similar to sync iterators)
24
+ if (key == WellKnownSymbols::asyncIterator->key)
25
+ {
26
+ // We return 'this'. Since we can't easily create a shared_ptr from raw pointer,
27
+ // we rely on the context to hold the reference or implement better shared_from_this strategy.
28
+ // For now, assume it works like Iterator.
29
+ return AnyValue::make_function([self](const AnyValue &thisVal, std::span<const AnyValue>) -> AnyValue
30
+ {
31
+ // This is slightly dangerous as we create a new shared_ptr from raw.
32
+ // TODO: fix lifetime management
33
+ return thisVal; },
34
+ key);
35
+ }
36
+ // --- next() method ---
37
+ if (key == "next")
38
+ {
39
+ return AnyValue::make_function([self](const AnyValue &thisVal, std::span<const AnyValue> args) -> AnyValue
40
+ {
41
+ AnyValue val = args.empty() ? AnyValue::make_undefined() : args[0];
42
+ auto res = self->next(val);
43
+ return AnyValue::make_promise(res); },
44
+ key);
45
+ }
46
+
47
+ return std::nullopt;
48
+ }
49
+ }
50
+ }