@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.
- package/dist/analysis/scope.js +17 -0
- package/dist/analysis/typeAnalyzer.js +7 -1
- package/dist/ast/symbols.js +32 -0
- package/dist/ast/types.js +0 -6
- package/dist/cli-utils/args.js +57 -0
- package/dist/cli-utils/colors.js +9 -0
- package/dist/cli-utils/file-utils.js +20 -0
- package/dist/cli-utils/spinner.js +55 -0
- package/dist/cli.js +105 -30
- package/dist/core/codegen/class-handlers.js +10 -6
- package/dist/core/codegen/control-flow-handlers.js +57 -28
- package/dist/core/codegen/declaration-handlers.js +10 -6
- package/dist/core/codegen/expression-handlers.js +206 -61
- package/dist/core/codegen/function-handlers.js +203 -76
- package/dist/core/codegen/helpers.js +125 -28
- package/dist/core/codegen/index.js +23 -15
- package/dist/core/codegen/literal-handlers.js +15 -6
- package/dist/core/codegen/statement-handlers.js +282 -84
- package/dist/core/codegen/visitor.js +3 -1
- package/package.json +1 -1
- package/src/prelude/any_value.hpp +221 -342
- package/src/prelude/any_value_access.hpp +168 -81
- package/src/prelude/any_value_defines.hpp +74 -35
- package/src/prelude/any_value_helpers.hpp +75 -180
- package/src/prelude/exception.hpp +1 -0
- package/src/prelude/exception_helpers.hpp +4 -4
- package/src/prelude/index.hpp +12 -2
- package/src/prelude/library/array.hpp +190 -0
- package/src/prelude/library/console.hpp +6 -5
- package/src/prelude/library/error.hpp +10 -8
- package/src/prelude/library/function.hpp +10 -0
- package/src/prelude/library/global.hpp +20 -0
- package/src/prelude/library/math.hpp +308 -0
- package/src/prelude/library/object.hpp +288 -0
- package/src/prelude/library/performance.hpp +1 -1
- package/src/prelude/library/process.hpp +39 -0
- package/src/prelude/library/promise.hpp +57 -55
- package/src/prelude/library/symbol.hpp +45 -57
- package/src/prelude/library/timer.hpp +6 -6
- package/src/prelude/types.hpp +54 -0
- package/src/prelude/utils/access.hpp +215 -11
- package/src/prelude/utils/assignment_operators.hpp +99 -0
- package/src/prelude/utils/log_any_value/array.hpp +8 -8
- package/src/prelude/utils/log_any_value/function.hpp +6 -4
- package/src/prelude/utils/log_any_value/object.hpp +41 -24
- package/src/prelude/utils/log_any_value/primitives.hpp +3 -1
- package/src/prelude/utils/operators.hpp +750 -274
- package/src/prelude/utils/well_known_symbols.hpp +12 -0
- package/src/prelude/values/array.hpp +8 -6
- package/src/prelude/values/async_iterator.hpp +79 -0
- package/src/prelude/values/descriptors.hpp +2 -2
- package/src/prelude/values/function.hpp +72 -62
- package/src/prelude/values/helpers/array.hpp +64 -28
- package/src/prelude/values/helpers/async_iterator.hpp +275 -0
- package/src/prelude/values/helpers/function.hpp +81 -92
- package/src/prelude/values/helpers/iterator.hpp +3 -3
- package/src/prelude/values/helpers/object.hpp +54 -9
- package/src/prelude/values/helpers/promise.hpp +13 -6
- package/src/prelude/values/iterator.hpp +1 -1
- package/src/prelude/values/object.hpp +10 -3
- package/src/prelude/values/promise.hpp +7 -11
- package/src/prelude/values/prototypes/array.hpp +851 -12
- package/src/prelude/values/prototypes/async_iterator.hpp +50 -0
- package/src/prelude/values/prototypes/function.hpp +2 -2
- package/src/prelude/values/prototypes/iterator.hpp +5 -5
- package/src/prelude/values/prototypes/number.hpp +153 -0
- package/src/prelude/values/prototypes/object.hpp +2 -2
- package/src/prelude/values/prototypes/promise.hpp +40 -30
- package/src/prelude/values/prototypes/string.hpp +28 -28
- package/src/prelude/values/prototypes/symbol.hpp +20 -3
- 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
|
-
|
|
11
|
+
namespace jspp
|
|
11
12
|
{
|
|
12
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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 (*
|
|
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::
|
|
29
|
+
return AnyValue::from_iterator((*func)(thisVal, args));
|
|
58
30
|
}
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
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::
|
|
37
|
+
return AnyValue::from_async_iterator((*func)(thisVal, args));
|
|
75
38
|
}
|
|
76
|
-
|
|
39
|
+
else
|
|
77
40
|
{
|
|
78
|
-
return AnyValue::
|
|
41
|
+
return AnyValue::make_undefined();
|
|
79
42
|
}
|
|
80
43
|
}
|
|
81
44
|
|
|
82
|
-
|
|
83
|
-
auto it = props.find(key);
|
|
84
|
-
if (it != props.end())
|
|
45
|
+
bool JsFunction::has_property(const std::string &key) const
|
|
85
46
|
{
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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<
|
|
38
|
+
std::vector<T> jspp::JsIterator<T>::to_vector()
|
|
39
39
|
{
|
|
40
|
-
std::vector<
|
|
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
|
-
|
|
15
|
-
|
|
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
|
-
|
|
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(
|
|
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
|
-
|
|
55
|
-
|
|
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(
|
|
101
|
+
return AnyValue::resolve_property_for_write(storage[offset.value()], thisVal, value, key);
|
|
58
102
|
}
|
|
59
103
|
else
|
|
60
104
|
{
|
|
61
|
-
|
|
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
|
-
|
|
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<
|
|
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
|
};
|