@ugo-studio/jspp 0.1.4 → 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.
Files changed (71) hide show
  1. package/dist/analysis/scope.js +17 -0
  2. package/dist/analysis/typeAnalyzer.js +7 -1
  3. package/dist/ast/symbols.js +32 -0
  4. package/dist/ast/types.js +0 -6
  5. package/dist/cli-utils/args.js +57 -0
  6. package/dist/cli-utils/colors.js +9 -0
  7. package/dist/cli-utils/file-utils.js +20 -0
  8. package/dist/cli-utils/spinner.js +55 -0
  9. package/dist/cli.js +105 -30
  10. package/dist/core/codegen/class-handlers.js +10 -6
  11. package/dist/core/codegen/control-flow-handlers.js +57 -28
  12. package/dist/core/codegen/declaration-handlers.js +10 -6
  13. package/dist/core/codegen/expression-handlers.js +206 -61
  14. package/dist/core/codegen/function-handlers.js +203 -76
  15. package/dist/core/codegen/helpers.js +125 -28
  16. package/dist/core/codegen/index.js +23 -15
  17. package/dist/core/codegen/literal-handlers.js +15 -6
  18. package/dist/core/codegen/statement-handlers.js +282 -84
  19. package/dist/core/codegen/visitor.js +3 -1
  20. package/package.json +1 -1
  21. package/src/prelude/any_value.hpp +221 -342
  22. package/src/prelude/any_value_access.hpp +168 -81
  23. package/src/prelude/any_value_defines.hpp +74 -35
  24. package/src/prelude/any_value_helpers.hpp +75 -180
  25. package/src/prelude/exception.hpp +1 -0
  26. package/src/prelude/exception_helpers.hpp +4 -4
  27. package/src/prelude/index.hpp +12 -2
  28. package/src/prelude/library/array.hpp +190 -0
  29. package/src/prelude/library/console.hpp +6 -5
  30. package/src/prelude/library/error.hpp +10 -8
  31. package/src/prelude/library/function.hpp +10 -0
  32. package/src/prelude/library/global.hpp +20 -0
  33. package/src/prelude/library/math.hpp +308 -0
  34. package/src/prelude/library/object.hpp +288 -0
  35. package/src/prelude/library/performance.hpp +1 -1
  36. package/src/prelude/library/process.hpp +39 -0
  37. package/src/prelude/library/promise.hpp +57 -55
  38. package/src/prelude/library/symbol.hpp +45 -57
  39. package/src/prelude/library/timer.hpp +6 -6
  40. package/src/prelude/types.hpp +54 -0
  41. package/src/prelude/utils/access.hpp +215 -11
  42. package/src/prelude/utils/assignment_operators.hpp +99 -0
  43. package/src/prelude/utils/log_any_value/array.hpp +8 -8
  44. package/src/prelude/utils/log_any_value/function.hpp +6 -4
  45. package/src/prelude/utils/log_any_value/object.hpp +41 -24
  46. package/src/prelude/utils/log_any_value/primitives.hpp +3 -1
  47. package/src/prelude/utils/operators.hpp +750 -274
  48. package/src/prelude/utils/well_known_symbols.hpp +12 -0
  49. package/src/prelude/values/array.hpp +8 -6
  50. package/src/prelude/values/async_iterator.hpp +79 -0
  51. package/src/prelude/values/descriptors.hpp +2 -2
  52. package/src/prelude/values/function.hpp +72 -62
  53. package/src/prelude/values/helpers/array.hpp +64 -28
  54. package/src/prelude/values/helpers/async_iterator.hpp +275 -0
  55. package/src/prelude/values/helpers/function.hpp +81 -92
  56. package/src/prelude/values/helpers/iterator.hpp +3 -3
  57. package/src/prelude/values/helpers/object.hpp +54 -9
  58. package/src/prelude/values/helpers/promise.hpp +13 -6
  59. package/src/prelude/values/iterator.hpp +1 -1
  60. package/src/prelude/values/object.hpp +10 -3
  61. package/src/prelude/values/promise.hpp +7 -11
  62. package/src/prelude/values/prototypes/array.hpp +851 -12
  63. package/src/prelude/values/prototypes/async_iterator.hpp +50 -0
  64. package/src/prelude/values/prototypes/function.hpp +2 -2
  65. package/src/prelude/values/prototypes/iterator.hpp +5 -5
  66. package/src/prelude/values/prototypes/number.hpp +153 -0
  67. package/src/prelude/values/prototypes/object.hpp +2 -2
  68. package/src/prelude/values/prototypes/promise.hpp +40 -30
  69. package/src/prelude/values/prototypes/string.hpp +28 -28
  70. package/src/prelude/values/prototypes/symbol.hpp +20 -3
  71. package/src/prelude/values/shape.hpp +52 -0
@@ -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
+ }
@@ -1,125 +1,114 @@
1
1
  #pragma once
2
2
 
3
3
  #include <variant>
4
+ #include <optional>
4
5
 
5
6
  #include "types.hpp"
6
7
  #include "values/function.hpp"
7
8
  #include "any_value.hpp"
8
9
  #include "values/prototypes/function.hpp"
9
10
 
10
- std::string jspp::JsFunction::to_std_string() const
11
+ namespace jspp
11
12
  {
12
- if (is_async) {
13
- return "async function " + name + "() { [native code] }";
14
- }
15
- if (is_generator)
16
- {
17
- return "function* " + name + "() { [native code] }";
18
- }
19
- return "function " + name + "() { [native code] }";
20
- }
21
-
22
- jspp::AnyValue jspp::JsFunction::call(const AnyValue &thisVal, const std::vector<AnyValue> &args)
23
- {
24
- if (std::function<AnyValue(const AnyValue &, const std::vector<AnyValue> &)> *func = std::get_if<0>(&callable))
25
- {
26
- return (*func)(thisVal, args);
27
- }
28
- else if (std::function<jspp::JsIterator<jspp::AnyValue>(const AnyValue &, const std::vector<jspp::AnyValue> &)> *func = std::get_if<1>(&callable))
29
- {
30
- return AnyValue::from_iterator((*func)(thisVal, args));
31
- }
32
- else if (std::function<jspp::JsPromise(const AnyValue &, const std::vector<jspp::AnyValue> &)> *func = std::get_if<2>(&callable))
33
- {
34
- return AnyValue::make_promise((*func)(thisVal, args));
35
- }
36
- else
13
+ std::string JsFunction::to_std_string() const
37
14
  {
38
- return AnyValue::make_undefined();
15
+ std::string type_part = this->is_async ? "async function" : this->is_generator ? "function*"
16
+ : "function";
17
+ std::string name_part = this->name.value_or("");
18
+ return type_part + " " + name_part + "() { [native code] }";
39
19
  }
40
- }
41
20
 
42
- jspp::AnyValue jspp::JsFunction::get_property(const std::string &key, const AnyValue &thisVal)
43
- {
44
- auto it = props.find(key);
45
- if (it == props.end())
21
+ AnyValue JsFunction::call(const AnyValue &thisVal, std::span<const AnyValue> args)
46
22
  {
47
- // check explicit proto chain (e.g. for classes extending other classes)
48
- if (proto && !(*proto).is_null() && !(*proto).is_undefined())
23
+ if (std::function<AnyValue(const AnyValue &, std::span<const AnyValue>)> *func = std::get_if<0>(&callable))
49
24
  {
50
- return (*proto).get_property_with_receiver(key, thisVal);
25
+ return (*func)(thisVal, args);
51
26
  }
52
-
53
- // check prototype (implicit Function.prototype)
54
- auto proto_it = FunctionPrototypes::get(key, this);
55
- if (proto_it.has_value())
27
+ else if (std::function<jspp::JsIterator<jspp::AnyValue>(const AnyValue &, std::span<const AnyValue>)> *func = std::get_if<1>(&callable))
56
28
  {
57
- return AnyValue::resolve_property_for_read(proto_it.value(), thisVal, key);
29
+ return AnyValue::from_iterator((*func)(thisVal, args));
58
30
  }
59
- // not found
60
- return AnyValue::make_undefined();
61
- }
62
- return AnyValue::resolve_property_for_read(it->second, thisVal, key);
63
- }
64
-
65
- jspp::AnyValue jspp::JsFunction::set_property(const std::string &key, const AnyValue &value, const AnyValue &thisVal)
66
- {
67
- // set prototype property if accessor descriptor
68
- auto proto_it = FunctionPrototypes::get(key, this);
69
- if (proto_it.has_value())
70
- {
71
- auto proto_value = proto_it.value();
72
- if (proto_value.is_accessor_descriptor())
31
+ else if (std::function<jspp::JsPromise(const AnyValue &, std::span<const AnyValue>)> *func = std::get_if<2>(&callable))
32
+ {
33
+ return AnyValue::make_promise((*func)(thisVal, args));
34
+ }
35
+ else if (std::function<jspp::JsAsyncIterator<jspp::AnyValue>(const AnyValue &, std::span<const AnyValue>)> *func = std::get_if<3>(&callable))
73
36
  {
74
- return AnyValue::resolve_property_for_write(proto_value, thisVal, value, key);
37
+ return AnyValue::from_async_iterator((*func)(thisVal, args));
75
38
  }
76
- if (proto_value.is_data_descriptor() && !proto_value.as_data_descriptor()->writable)
39
+ else
77
40
  {
78
- return AnyValue::resolve_property_for_write(proto_value, thisVal, value, key);
41
+ return AnyValue::make_undefined();
79
42
  }
80
43
  }
81
44
 
82
- // set own property
83
- auto it = props.find(key);
84
- if (it != props.end())
45
+ bool JsFunction::has_property(const std::string &key) const
85
46
  {
86
- return AnyValue::resolve_property_for_write(it->second, thisVal, value, key);
87
- }
88
- else
89
- {
90
- props[key] = value;
91
- return value;
47
+ if (props.find(key) != props.end())
48
+ return true;
49
+ if (proto && !(*proto).is_null() && !(*proto).is_undefined())
50
+ {
51
+ if ((*proto).has_property(key))
52
+ return true;
53
+ }
54
+ if (FunctionPrototypes::get(key, const_cast<JsFunction *>(this)).has_value())
55
+ return true;
56
+ return false;
92
57
  }
93
- }
94
58
 
95
- // AnyValue::construct implementation
96
- const jspp::AnyValue jspp::AnyValue::construct(const std::vector<AnyValue> &args) const
97
- {
98
- if (!is_function())
59
+ AnyValue JsFunction::get_property(const std::string &key, const AnyValue &thisVal)
99
60
  {
100
- throw Exception::make_exception(to_std_string() + " is not a constructor", "TypeError");
101
- }
61
+ auto it = props.find(key);
62
+ if (it == props.end())
63
+ {
64
+ // check explicit proto chain (e.g. for classes extending other classes)
65
+ if (proto && !(*proto).is_null() && !(*proto).is_undefined())
66
+ {
67
+ if ((*proto).has_property(key))
68
+ {
69
+ return (*proto).get_property_with_receiver(key, thisVal);
70
+ }
71
+ }
102
72
 
103
- // 1. Get prototype
104
- AnyValue proto = get_own_property("prototype");
105
- // If prototype is not an object, default to a plain object (which ideally inherits from Object.prototype)
106
- // Here we just make a plain object.
107
- if (!proto.is_object())
108
- {
109
- proto = AnyValue::make_object({});
73
+ // check prototype (implicit Function.prototype)
74
+ auto proto_it = FunctionPrototypes::get(key, this);
75
+ if (proto_it.has_value())
76
+ {
77
+ return AnyValue::resolve_property_for_read(proto_it.value(), thisVal, key);
78
+ }
79
+ // not found
80
+ return AnyValue::make_undefined();
81
+ }
82
+ return AnyValue::resolve_property_for_read(it->second, thisVal, key);
110
83
  }
111
84
 
112
- // 2. Create instance
113
- AnyValue instance = AnyValue::make_object_with_proto({}, proto);
114
-
115
- // 3. Call function
116
- // We pass 'instance' as 'this'
117
- AnyValue result = as_function()->call(instance, args);
118
-
119
- // 4. Return result if object, else instance
120
- if (result.is_object() || result.is_function() || result.is_array() || result.is_promise())
85
+ AnyValue JsFunction::set_property(const std::string &key, const AnyValue &value, const AnyValue &thisVal)
121
86
  {
122
- return result;
87
+ // set prototype property if accessor descriptor
88
+ auto proto_it = FunctionPrototypes::get(key, this);
89
+ if (proto_it.has_value())
90
+ {
91
+ auto proto_value = proto_it.value();
92
+ if (proto_value.is_accessor_descriptor())
93
+ {
94
+ return AnyValue::resolve_property_for_write(proto_value, thisVal, value, key);
95
+ }
96
+ if (proto_value.is_data_descriptor() && !proto_value.as_data_descriptor()->writable)
97
+ {
98
+ return AnyValue::resolve_property_for_write(proto_value, thisVal, value, key);
99
+ }
100
+ }
101
+
102
+ // set own property
103
+ auto it = props.find(key);
104
+ if (it != props.end())
105
+ {
106
+ return AnyValue::resolve_property_for_write(it->second, thisVal, value, key);
107
+ }
108
+ else
109
+ {
110
+ props[key] = value;
111
+ return value;
112
+ }
123
113
  }
124
- return instance;
125
- }
114
+ }
@@ -35,9 +35,9 @@ jspp::JsIterator<T>::NextResult jspp::JsIterator<T>::next(const T &val)
35
35
  }
36
36
 
37
37
  template <typename T>
38
- std::vector<std::optional<T>> jspp::JsIterator<T>::to_vector()
38
+ std::vector<T> jspp::JsIterator<T>::to_vector()
39
39
  {
40
- std::vector<std::optional<AnyValue>> result;
40
+ std::vector<T> result;
41
41
  while (true)
42
42
  {
43
43
  auto next = this->next();
@@ -45,7 +45,7 @@ std::vector<std::optional<T>> jspp::JsIterator<T>::to_vector()
45
45
  {
46
46
  break;
47
47
  }
48
- result.push_back(next.value);
48
+ result.push_back(next.value.value_or(AnyValue::make_undefined()));
49
49
  }
50
50
  return result;
51
51
  }
@@ -4,24 +4,66 @@
4
4
  #include "values/object.hpp"
5
5
  #include "any_value.hpp"
6
6
 
7
+ namespace jspp {
8
+ JsObject::JsObject() : shape(Shape::empty_shape()), proto(nullptr) {}
9
+
10
+ JsObject::JsObject(std::initializer_list<std::pair<std::string, AnyValue>> p, std::shared_ptr<AnyValue> pr) : proto(pr) {
11
+ shape = Shape::empty_shape();
12
+ storage.reserve(p.size());
13
+ for (const auto& pair : p) {
14
+ shape = shape->transition(pair.first);
15
+ storage.push_back(pair.second);
16
+ }
17
+ }
18
+
19
+ JsObject::JsObject(const std::map<std::string, AnyValue> &p, std::shared_ptr<AnyValue> pr) : proto(pr) {
20
+ shape = Shape::empty_shape();
21
+ storage.reserve(p.size());
22
+ for (const auto& pair : p) {
23
+ shape = shape->transition(pair.first);
24
+ storage.push_back(pair.second);
25
+ }
26
+ }
27
+ }
28
+
7
29
  std::string jspp::JsObject::to_std_string() const
8
30
  {
9
31
  return "[Object Object]";
10
32
  }
11
33
 
34
+ bool jspp::JsObject::has_property(const std::string &key) const
35
+ {
36
+ if (deleted_keys.count(key)) return false;
37
+
38
+ if (shape->get_offset(key).has_value())
39
+ return true;
40
+ if (proto && !(*proto).is_null() && !(*proto).is_undefined())
41
+ {
42
+ if ((*proto).has_property(key))
43
+ return true;
44
+ }
45
+ if (ObjectPrototypes::get(key, const_cast<JsObject *>(this)).has_value())
46
+ return true;
47
+ return false;
48
+ }
49
+
12
50
  jspp::AnyValue jspp::JsObject::get_property(const std::string &key, const AnyValue &thisVal)
13
51
  {
14
- auto it = props.find(key);
15
- if (it == props.end())
52
+ if (deleted_keys.count(key)) return AnyValue::make_undefined();
53
+
54
+ auto offset = shape->get_offset(key);
55
+ if (!offset.has_value())
16
56
  {
17
57
  // check prototype chain
18
58
  if (proto && !(*proto).is_null() && !(*proto).is_undefined())
19
59
  {
20
- return (*proto).get_property_with_receiver(key, thisVal);
60
+ if ((*proto).has_property(key))
61
+ {
62
+ return (*proto).get_property_with_receiver(key, thisVal);
63
+ }
21
64
  }
22
65
 
23
66
  // check built-in prototype methods (Object.prototype)
24
- // ideally these should be on the root prototype object, but for now we keep this fallback
25
67
  auto proto_it = ObjectPrototypes::get(key, this);
26
68
  if (proto_it.has_value())
27
69
  {
@@ -30,7 +72,7 @@ jspp::AnyValue jspp::JsObject::get_property(const std::string &key, const AnyVal
30
72
  // not found
31
73
  return AnyValue::make_undefined();
32
74
  }
33
- return AnyValue::resolve_property_for_read(it->second, thisVal, key);
75
+ return AnyValue::resolve_property_for_read(storage[offset.value()], thisVal, key);
34
76
  }
35
77
 
36
78
  jspp::AnyValue jspp::JsObject::set_property(const std::string &key, const AnyValue &value, const AnyValue &thisVal)
@@ -51,14 +93,17 @@ jspp::AnyValue jspp::JsObject::set_property(const std::string &key, const AnyVal
51
93
  }
52
94
 
53
95
  // set own property
54
- auto it = props.find(key);
55
- if (it != props.end())
96
+ if (deleted_keys.count(key)) deleted_keys.erase(key);
97
+
98
+ auto offset = shape->get_offset(key);
99
+ if (offset.has_value())
56
100
  {
57
- return AnyValue::resolve_property_for_write(it->second, thisVal, value, key);
101
+ return AnyValue::resolve_property_for_write(storage[offset.value()], thisVal, value, key);
58
102
  }
59
103
  else
60
104
  {
61
- props[key] = value;
105
+ shape = shape->transition(key);
106
+ storage.push_back(value);
62
107
  return value;
63
108
  }
64
109
  }
@@ -24,13 +24,13 @@ namespace jspp {
24
24
  auto weak_state = std::weak_ptr<PromiseState>(state);
25
25
 
26
26
  p->then(
27
- [weak_state](AnyValue v) {
27
+ [weak_state](const AnyValue& v) {
28
28
  if (auto s = weak_state.lock()) {
29
29
  JsPromise localP; localP.state = s;
30
30
  localP.resolve(v);
31
31
  }
32
32
  },
33
- [weak_state](AnyValue r) {
33
+ [weak_state](const AnyValue& r) {
34
34
  if (auto s = weak_state.lock()) {
35
35
  JsPromise localP; localP.state = s;
36
36
  localP.reject(r);
@@ -71,7 +71,7 @@ namespace jspp {
71
71
  }
72
72
  }
73
73
 
74
- inline void JsPromise::then(std::function<void(AnyValue)> onFulfilled, std::function<void(AnyValue)> onRejected) {
74
+ inline void JsPromise::then(std::function<void(const AnyValue&)> onFulfilled, std::function<void(const AnyValue&)> onRejected) {
75
75
  if (state->status == PromiseStatus::Fulfilled) {
76
76
  if (onFulfilled) {
77
77
  AnyValue val = *(state->result);
@@ -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();
@@ -89,7 +89,7 @@ namespace jspp
89
89
 
90
90
  std::string to_std_string() const;
91
91
  NextResult next(const T &val = T());
92
- std::vector<std::optional<T>> to_vector();
92
+ std::vector<T> to_vector();
93
93
  AnyValue get_property(const std::string &key, const AnyValue &thisVal);
94
94
  AnyValue set_property(const std::string &key, const AnyValue &value, const AnyValue &thisVal);
95
95
  };