@ugo-studio/jspp 0.1.2 → 0.1.4

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 (72) hide show
  1. package/README.md +5 -3
  2. package/dist/analysis/scope.js +38 -15
  3. package/dist/analysis/typeAnalyzer.js +257 -23
  4. package/dist/ast/types.js +6 -0
  5. package/dist/cli.js +3 -4
  6. package/dist/core/codegen/class-handlers.js +127 -0
  7. package/dist/core/codegen/control-flow-handlers.js +464 -0
  8. package/dist/core/codegen/declaration-handlers.js +31 -14
  9. package/dist/core/codegen/expression-handlers.js +432 -116
  10. package/dist/core/codegen/function-handlers.js +110 -13
  11. package/dist/core/codegen/helpers.js +76 -8
  12. package/dist/core/codegen/index.js +18 -5
  13. package/dist/core/codegen/literal-handlers.js +3 -0
  14. package/dist/core/codegen/statement-handlers.js +152 -186
  15. package/dist/core/codegen/visitor.js +35 -3
  16. package/package.json +3 -3
  17. package/src/prelude/any_value.hpp +658 -734
  18. package/src/prelude/any_value_access.hpp +103 -0
  19. package/src/prelude/any_value_defines.hpp +151 -0
  20. package/src/prelude/any_value_helpers.hpp +246 -0
  21. package/src/prelude/exception.hpp +31 -0
  22. package/src/prelude/exception_helpers.hpp +49 -0
  23. package/src/prelude/index.hpp +35 -12
  24. package/src/prelude/library/console.hpp +20 -20
  25. package/src/prelude/library/error.hpp +111 -0
  26. package/src/prelude/library/global.hpp +15 -4
  27. package/src/prelude/library/performance.hpp +25 -0
  28. package/src/prelude/library/promise.hpp +121 -0
  29. package/src/prelude/library/symbol.hpp +60 -4
  30. package/src/prelude/library/timer.hpp +92 -0
  31. package/src/prelude/scheduler.hpp +145 -0
  32. package/src/prelude/types.hpp +33 -6
  33. package/src/prelude/utils/access.hpp +174 -0
  34. package/src/prelude/utils/log_any_value/array.hpp +245 -0
  35. package/src/prelude/utils/log_any_value/config.hpp +32 -0
  36. package/src/prelude/utils/log_any_value/function.hpp +37 -0
  37. package/src/prelude/utils/log_any_value/fwd.hpp +15 -0
  38. package/src/prelude/utils/log_any_value/helpers.hpp +62 -0
  39. package/src/prelude/utils/log_any_value/log_any_value.hpp +94 -0
  40. package/src/prelude/utils/log_any_value/object.hpp +119 -0
  41. package/src/prelude/utils/log_any_value/primitives.hpp +41 -0
  42. package/src/prelude/{operators.hpp → utils/operators.hpp} +31 -12
  43. package/src/prelude/utils/well_known_symbols.hpp +13 -0
  44. package/src/prelude/values/array.hpp +5 -2
  45. package/src/prelude/{descriptors.hpp → values/descriptors.hpp} +2 -2
  46. package/src/prelude/values/function.hpp +76 -19
  47. package/src/prelude/values/{operators → helpers}/array.hpp +30 -14
  48. package/src/prelude/values/helpers/function.hpp +125 -0
  49. package/src/prelude/values/helpers/iterator.hpp +107 -0
  50. package/src/prelude/values/helpers/object.hpp +64 -0
  51. package/src/prelude/values/helpers/promise.hpp +181 -0
  52. package/src/prelude/values/helpers/string.hpp +50 -0
  53. package/src/prelude/values/helpers/symbol.hpp +23 -0
  54. package/src/prelude/values/iterator.hpp +96 -0
  55. package/src/prelude/values/object.hpp +8 -3
  56. package/src/prelude/values/promise.hpp +73 -0
  57. package/src/prelude/values/prototypes/array.hpp +23 -16
  58. package/src/prelude/values/prototypes/function.hpp +26 -0
  59. package/src/prelude/values/prototypes/iterator.hpp +58 -0
  60. package/src/prelude/values/prototypes/object.hpp +26 -0
  61. package/src/prelude/values/prototypes/promise.hpp +124 -0
  62. package/src/prelude/values/prototypes/string.hpp +366 -357
  63. package/src/prelude/values/prototypes/symbol.hpp +41 -0
  64. package/src/prelude/values/string.hpp +25 -0
  65. package/src/prelude/values/symbol.hpp +102 -0
  66. package/src/prelude/access.hpp +0 -86
  67. package/src/prelude/error.hpp +0 -31
  68. package/src/prelude/error_helpers.hpp +0 -59
  69. package/src/prelude/log_string.hpp +0 -403
  70. package/src/prelude/values/operators/function.hpp +0 -34
  71. package/src/prelude/values/operators/object.hpp +0 -34
  72. package/src/prelude/well_known_symbols.hpp +0 -10
@@ -2,7 +2,7 @@
2
2
 
3
3
  #include "types.hpp"
4
4
  #include "values/array.hpp"
5
- #include "error.hpp"
5
+ #include "exception.hpp"
6
6
  #include "any_value.hpp"
7
7
  #include "values/prototypes/array.hpp"
8
8
 
@@ -47,7 +47,17 @@ std::string jspp::JsArray::to_std_string() const
47
47
  return result;
48
48
  }
49
49
 
50
- jspp::AnyValue jspp::JsArray::get_property(const std::string &key)
50
+ jspp::JsIterator<jspp::AnyValue> jspp::JsArray::get_iterator()
51
+ {
52
+ for (uint64_t idx = 0; idx < length; ++idx)
53
+ {
54
+ co_yield get_property(static_cast<uint32_t>(idx));
55
+ }
56
+
57
+ co_return AnyValue::make_undefined();
58
+ }
59
+
60
+ jspp::AnyValue jspp::JsArray::get_property(const std::string &key, const AnyValue &thisVal)
51
61
  {
52
62
  if (
53
63
  !key.empty() && std::isdigit(static_cast<unsigned char>(key[0])) // Quick check: if the first character is not a digit, it can't be a standard index.
@@ -65,12 +75,12 @@ jspp::AnyValue jspp::JsArray::get_property(const std::string &key)
65
75
  auto proto_it = ArrayPrototypes::get(key, this);
66
76
  if (proto_it.has_value())
67
77
  {
68
- return AnyValue::resolve_property_for_read(proto_it.value());
78
+ return AnyValue::resolve_property_for_read(proto_it.value(), thisVal, key);
69
79
  }
70
80
  // not found
71
81
  return AnyValue::make_undefined();
72
82
  }
73
- return AnyValue::resolve_property_for_read(it->second);
83
+ return AnyValue::resolve_property_for_read(it->second, thisVal, key);
74
84
  }
75
85
  }
76
86
 
@@ -78,17 +88,17 @@ jspp::AnyValue jspp::JsArray::get_property(uint32_t idx)
78
88
  {
79
89
  if (idx < dense.size())
80
90
  {
81
- return AnyValue::resolve_property_for_read(dense[idx].value_or(AnyValue::make_undefined()));
91
+ return dense[idx].value_or(AnyValue::make_undefined());
82
92
  }
83
93
  const auto &it = sparse.find(idx);
84
94
  if (it != sparse.end())
85
95
  {
86
- return AnyValue::resolve_property_for_read(it->second.value_or(AnyValue::make_undefined()));
96
+ return it->second.value_or(AnyValue::make_undefined());
87
97
  }
88
98
  return AnyValue::make_undefined();
89
99
  }
90
100
 
91
- jspp::AnyValue jspp::JsArray::set_property(const std::string &key, const AnyValue &value)
101
+ jspp::AnyValue jspp::JsArray::set_property(const std::string &key, const AnyValue &value, const AnyValue &thisVal)
92
102
  {
93
103
  if (
94
104
  !key.empty() && std::isdigit(static_cast<unsigned char>(key[0])) // Quick check: if the first character is not a digit, it can't be a standard index.
@@ -100,13 +110,17 @@ jspp::AnyValue jspp::JsArray::set_property(const std::string &key, const AnyValu
100
110
  else
101
111
  {
102
112
  // set prototype property if accessor descriptor
103
- auto proto_it = ArrayPrototypes::get(key, this);
104
- if (proto_it.has_value())
113
+ auto proto_val_opt = ArrayPrototypes::get(key, this);
114
+ if (proto_val_opt.has_value())
105
115
  {
106
- auto proto_value = proto_it.value();
116
+ auto proto_value = proto_val_opt.value();
107
117
  if (proto_value.is_accessor_descriptor())
108
118
  {
109
- return AnyValue::resolve_property_for_write(proto_it.value(), value);
119
+ return AnyValue::resolve_property_for_write(proto_value, thisVal, value, key);
120
+ }
121
+ if (proto_value.is_data_descriptor() && !proto_value.as_data_descriptor()->writable)
122
+ {
123
+ return AnyValue::resolve_property_for_write(proto_value, thisVal, value, key);
110
124
  }
111
125
  }
112
126
 
@@ -114,7 +128,7 @@ jspp::AnyValue jspp::JsArray::set_property(const std::string &key, const AnyValu
114
128
  auto it = props.find(key);
115
129
  if (it != props.end())
116
130
  {
117
- return AnyValue::resolve_property_for_write(it->second, value);
131
+ return AnyValue::resolve_property_for_write(it->second, thisVal, value, key);
118
132
  }
119
133
  else
120
134
  {
@@ -137,7 +151,8 @@ jspp::AnyValue jspp::JsArray::set_property(uint32_t idx, const AnyValue &value)
137
151
  {
138
152
  dense[idx] = AnyValue::make_undefined();
139
153
  }
140
- return AnyValue::resolve_property_for_write(dense[idx].value(), value);
154
+ dense[idx] = value;
155
+ return value;
141
156
  }
142
157
  else if (idx <= dense.size() + DENSE_GROW_THRESHOLD)
143
158
  {
@@ -154,7 +169,8 @@ jspp::AnyValue jspp::JsArray::set_property(uint32_t idx, const AnyValue &value)
154
169
  {
155
170
  it->second = AnyValue::make_undefined();
156
171
  }
157
- return AnyValue::resolve_property_for_write(it->second.value(), value);
172
+ it->second = value;
173
+ return value;
158
174
  }
159
175
  else
160
176
  {
@@ -0,0 +1,125 @@
1
+ #pragma once
2
+
3
+ #include <variant>
4
+
5
+ #include "types.hpp"
6
+ #include "values/function.hpp"
7
+ #include "any_value.hpp"
8
+ #include "values/prototypes/function.hpp"
9
+
10
+ std::string jspp::JsFunction::to_std_string() const
11
+ {
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
37
+ {
38
+ return AnyValue::make_undefined();
39
+ }
40
+ }
41
+
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())
46
+ {
47
+ // check explicit proto chain (e.g. for classes extending other classes)
48
+ if (proto && !(*proto).is_null() && !(*proto).is_undefined())
49
+ {
50
+ return (*proto).get_property_with_receiver(key, thisVal);
51
+ }
52
+
53
+ // check prototype (implicit Function.prototype)
54
+ auto proto_it = FunctionPrototypes::get(key, this);
55
+ if (proto_it.has_value())
56
+ {
57
+ return AnyValue::resolve_property_for_read(proto_it.value(), thisVal, key);
58
+ }
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())
73
+ {
74
+ return AnyValue::resolve_property_for_write(proto_value, thisVal, value, key);
75
+ }
76
+ if (proto_value.is_data_descriptor() && !proto_value.as_data_descriptor()->writable)
77
+ {
78
+ return AnyValue::resolve_property_for_write(proto_value, thisVal, value, key);
79
+ }
80
+ }
81
+
82
+ // set own property
83
+ auto it = props.find(key);
84
+ if (it != props.end())
85
+ {
86
+ return AnyValue::resolve_property_for_write(it->second, thisVal, value, key);
87
+ }
88
+ else
89
+ {
90
+ props[key] = value;
91
+ return value;
92
+ }
93
+ }
94
+
95
+ // AnyValue::construct implementation
96
+ const jspp::AnyValue jspp::AnyValue::construct(const std::vector<AnyValue> &args) const
97
+ {
98
+ if (!is_function())
99
+ {
100
+ throw Exception::make_exception(to_std_string() + " is not a constructor", "TypeError");
101
+ }
102
+
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({});
110
+ }
111
+
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())
121
+ {
122
+ return result;
123
+ }
124
+ return instance;
125
+ }
@@ -0,0 +1,107 @@
1
+ #pragma once
2
+
3
+ #include "types.hpp"
4
+ #include "values/iterator.hpp"
5
+ #include "any_value.hpp"
6
+
7
+ template <typename T>
8
+ std::string jspp::JsIterator<T>::to_std_string() const
9
+ {
10
+ return "[object Generator]";
11
+ }
12
+
13
+ template <typename T>
14
+ jspp::JsIterator<T>::NextResult jspp::JsIterator<T>::next(const T &val)
15
+ {
16
+ // If the generator is already finished or invalid, return {undefined, true}
17
+ if (!handle || handle.done())
18
+ return {std::nullopt, true};
19
+
20
+ handle.promise().input_value = val;
21
+
22
+ // Resume execution until next co_yield or co_return
23
+ handle.resume();
24
+
25
+ if (handle.promise().exception_)
26
+ {
27
+ std::rethrow_exception(handle.promise().exception_);
28
+ }
29
+
30
+ // If handle.done() is TRUE, we hit co_return (value: X, done: true)
31
+ // If handle.done() is FALSE, we hit co_yield (value: X, done: false)
32
+ bool is_done = handle.done();
33
+
34
+ return {handle.promise().current_value, is_done};
35
+ }
36
+
37
+ template <typename T>
38
+ std::vector<std::optional<T>> jspp::JsIterator<T>::to_vector()
39
+ {
40
+ std::vector<std::optional<AnyValue>> result;
41
+ while (true)
42
+ {
43
+ auto next = this->next();
44
+ if (next.done)
45
+ {
46
+ break;
47
+ }
48
+ result.push_back(next.value);
49
+ }
50
+ return result;
51
+ }
52
+
53
+ template <typename T>
54
+ jspp::AnyValue jspp::JsIterator<T>::get_property(const std::string &key, const AnyValue &thisVal)
55
+ {
56
+ auto it = props.find(key);
57
+ if (it == props.end())
58
+ {
59
+ // check prototype
60
+ if constexpr (std::is_same_v<T, AnyValue>)
61
+ {
62
+ auto proto_it = IteratorPrototypes::get(key, this);
63
+ if (proto_it.has_value())
64
+ {
65
+ return AnyValue::resolve_property_for_read(proto_it.value(), thisVal, key);
66
+ }
67
+ }
68
+ // prototype not found
69
+ return jspp::AnyValue::make_undefined();
70
+ }
71
+
72
+ return jspp::AnyValue::resolve_property_for_read(it->second, thisVal, key);
73
+ }
74
+
75
+ template <typename T>
76
+ jspp::AnyValue jspp::JsIterator<T>::set_property(const std::string &key, const AnyValue &value, const AnyValue &thisVal)
77
+ {
78
+ // set prototype property if accessor descriptor
79
+ if constexpr (std::is_same_v<T, AnyValue>)
80
+ {
81
+ auto proto_it = IteratorPrototypes::get(key, this);
82
+ if (proto_it.has_value())
83
+ {
84
+ auto proto_value = proto_it.value();
85
+ if (proto_value.is_accessor_descriptor())
86
+ {
87
+ return AnyValue::resolve_property_for_write(proto_value, thisVal, value, key);
88
+ }
89
+ if (proto_value.is_data_descriptor() && !proto_value.as_data_descriptor()->writable)
90
+ {
91
+ return AnyValue::resolve_property_for_write(proto_value, thisVal, value, key);
92
+ }
93
+ }
94
+ }
95
+
96
+ // set own property
97
+ auto it = props.find(key);
98
+ if (it != props.end())
99
+ {
100
+ return jspp::AnyValue::resolve_property_for_write(it->second, thisVal, value, key);
101
+ }
102
+ else
103
+ {
104
+ props[key] = value;
105
+ return value;
106
+ }
107
+ }
@@ -0,0 +1,64 @@
1
+ #pragma once
2
+
3
+ #include "types.hpp"
4
+ #include "values/object.hpp"
5
+ #include "any_value.hpp"
6
+
7
+ std::string jspp::JsObject::to_std_string() const
8
+ {
9
+ return "[Object Object]";
10
+ }
11
+
12
+ jspp::AnyValue jspp::JsObject::get_property(const std::string &key, const AnyValue &thisVal)
13
+ {
14
+ auto it = props.find(key);
15
+ if (it == props.end())
16
+ {
17
+ // check prototype chain
18
+ if (proto && !(*proto).is_null() && !(*proto).is_undefined())
19
+ {
20
+ return (*proto).get_property_with_receiver(key, thisVal);
21
+ }
22
+
23
+ // 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
+ auto proto_it = ObjectPrototypes::get(key, this);
26
+ if (proto_it.has_value())
27
+ {
28
+ return AnyValue::resolve_property_for_read(proto_it.value(), thisVal, key);
29
+ }
30
+ // not found
31
+ return AnyValue::make_undefined();
32
+ }
33
+ return AnyValue::resolve_property_for_read(it->second, thisVal, key);
34
+ }
35
+
36
+ jspp::AnyValue jspp::JsObject::set_property(const std::string &key, const AnyValue &value, const AnyValue &thisVal)
37
+ {
38
+ // set prototype property if accessor descriptor
39
+ auto proto_it = ObjectPrototypes::get(key, this);
40
+ if (proto_it.has_value())
41
+ {
42
+ auto proto_value = proto_it.value();
43
+ if (proto_value.is_accessor_descriptor())
44
+ {
45
+ return AnyValue::resolve_property_for_write(proto_value, thisVal, value, key);
46
+ }
47
+ if (proto_value.is_data_descriptor() && !proto_value.as_data_descriptor()->writable)
48
+ {
49
+ return AnyValue::resolve_property_for_write(proto_value, thisVal, value, key);
50
+ }
51
+ }
52
+
53
+ // set own property
54
+ auto it = props.find(key);
55
+ if (it != props.end())
56
+ {
57
+ return AnyValue::resolve_property_for_write(it->second, thisVal, value, key);
58
+ }
59
+ else
60
+ {
61
+ props[key] = value;
62
+ return value;
63
+ }
64
+ }
@@ -0,0 +1,181 @@
1
+ #pragma once
2
+
3
+ #include "types.hpp"
4
+ #include "values/promise.hpp"
5
+ #include "any_value.hpp"
6
+ #include "values/prototypes/promise.hpp"
7
+
8
+ namespace jspp {
9
+
10
+ inline PromiseState::PromiseState() : result(std::make_shared<AnyValue>(AnyValue::make_undefined())) {}
11
+
12
+ inline JsPromise::JsPromise() : state(std::make_shared<PromiseState>()) {}
13
+
14
+ inline void JsPromise::resolve(const AnyValue& value) {
15
+ if (state->status != PromiseStatus::Pending) return;
16
+
17
+ if (value.is_promise()) {
18
+ auto p = value.as_promise();
19
+ if (p->state == state) {
20
+ reject(AnyValue::make_string("TypeError: Chaining cycle detected for promise"));
21
+ return;
22
+ }
23
+
24
+ auto weak_state = std::weak_ptr<PromiseState>(state);
25
+
26
+ p->then(
27
+ [weak_state](AnyValue v) {
28
+ if (auto s = weak_state.lock()) {
29
+ JsPromise localP; localP.state = s;
30
+ localP.resolve(v);
31
+ }
32
+ },
33
+ [weak_state](AnyValue r) {
34
+ if (auto s = weak_state.lock()) {
35
+ JsPromise localP; localP.state = s;
36
+ localP.reject(r);
37
+ }
38
+ }
39
+ );
40
+ return;
41
+ }
42
+
43
+ state->status = PromiseStatus::Fulfilled;
44
+ *(state->result) = value;
45
+
46
+ // Schedule callbacks
47
+ auto callbacks = state->onFulfilled;
48
+ state->onFulfilled.clear();
49
+ state->onRejected.clear();
50
+
51
+ for (auto& cb : callbacks) {
52
+ jspp::Scheduler::instance().enqueue([cb, value]() {
53
+ cb(value);
54
+ });
55
+ }
56
+ }
57
+
58
+ inline void JsPromise::reject(const AnyValue& reason) {
59
+ if (state->status != PromiseStatus::Pending) return;
60
+ state->status = PromiseStatus::Rejected;
61
+ *(state->result) = reason;
62
+
63
+ auto callbacks = state->onRejected;
64
+ state->onFulfilled.clear();
65
+ state->onRejected.clear();
66
+
67
+ for (auto& cb : callbacks) {
68
+ jspp::Scheduler::instance().enqueue([cb, reason]() {
69
+ cb(reason);
70
+ });
71
+ }
72
+ }
73
+
74
+ inline void JsPromise::then(std::function<void(AnyValue)> onFulfilled, std::function<void(AnyValue)> onRejected) {
75
+ if (state->status == PromiseStatus::Fulfilled) {
76
+ if (onFulfilled) {
77
+ AnyValue val = *(state->result);
78
+ jspp::Scheduler::instance().enqueue([onFulfilled, val]() {
79
+ onFulfilled(val);
80
+ });
81
+ }
82
+ } else if (state->status == PromiseStatus::Rejected) {
83
+ if (onRejected) {
84
+ AnyValue val = *(state->result);
85
+ jspp::Scheduler::instance().enqueue([onRejected, val]() {
86
+ onRejected(val);
87
+ });
88
+ }
89
+ } else {
90
+ if (onFulfilled) state->onFulfilled.push_back(onFulfilled);
91
+ if (onRejected) state->onRejected.push_back(onRejected);
92
+ }
93
+ }
94
+
95
+ inline std::string JsPromise::to_std_string() const {
96
+ return "[object Promise]";
97
+ }
98
+
99
+ inline AnyValue JsPromise::get_property(const std::string& key, const AnyValue& thisVal) {
100
+ // Prototype lookup
101
+ auto proto_it = PromisePrototypes::get(key, const_cast<JsPromise*>(this));
102
+ if (proto_it.has_value()) {
103
+ return AnyValue::resolve_property_for_read(proto_it.value(), thisVal, key);
104
+ }
105
+
106
+ auto it = props.find(key);
107
+ if (it != props.end()) {
108
+ return AnyValue::resolve_property_for_read(it->second, thisVal, key);
109
+ }
110
+ return AnyValue::make_undefined();
111
+ }
112
+
113
+ inline AnyValue JsPromise::set_property(const std::string& key, const AnyValue& value, const AnyValue& thisVal) {
114
+ // Prototype check (if we had setters on prototype)
115
+
116
+ auto it = props.find(key);
117
+ if (it != props.end()) {
118
+ return AnyValue::resolve_property_for_write(it->second, thisVal, value, key);
119
+ } else {
120
+ props[key] = value;
121
+ return value;
122
+ }
123
+ }
124
+
125
+ // --- Coroutine Methods ---
126
+
127
+ inline void JsPromisePromiseType::return_value(const AnyValue& val) {
128
+ promise.resolve(val);
129
+ }
130
+
131
+ inline void JsPromisePromiseType::unhandled_exception() {
132
+ try {
133
+ throw;
134
+ } catch (const Exception& e) {
135
+ promise.reject(*(e.data));
136
+ } catch (const std::exception& e) {
137
+ promise.reject(AnyValue::make_string(e.what()));
138
+ } catch (...) {
139
+ promise.reject(AnyValue::make_string("Unknown exception"));
140
+ }
141
+ }
142
+
143
+ inline auto JsPromisePromiseType::await_transform(const AnyValue& value) {
144
+ return AnyValueAwaiter{value};
145
+ }
146
+
147
+ // --- AnyValueAwaiter ---
148
+
149
+ 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.
152
+ return false;
153
+ }
154
+
155
+ inline void AnyValueAwaiter::await_suspend(std::coroutine_handle<> h) {
156
+ if (!value.is_promise()) {
157
+ h.resume();
158
+ return;
159
+ }
160
+ auto p = value.as_promise();
161
+
162
+ // Attach resume to promise resolution.
163
+ // Since we are using the Scheduler in .then(), this will be queued.
164
+ p->then(
165
+ [h](AnyValue v) mutable { h.resume(); },
166
+ [h](AnyValue e) mutable { h.resume(); }
167
+ );
168
+ }
169
+
170
+ inline AnyValue AnyValueAwaiter::await_resume() {
171
+ if (!value.is_promise()) return value;
172
+ auto p = value.as_promise();
173
+ if (p->state->status == PromiseStatus::Fulfilled) {
174
+ return *(p->state->result);
175
+ } else {
176
+ // Throw exception to be caught by try/catch in coroutine
177
+ throw Exception(*(p->state->result));
178
+ }
179
+ }
180
+
181
+ }
@@ -0,0 +1,50 @@
1
+ #pragma once
2
+
3
+ #include "types.hpp"
4
+ #include "values/array.hpp"
5
+ #include "values/string.hpp"
6
+ #include "exception.hpp"
7
+ #include "any_value.hpp"
8
+ #include "values/prototypes/string.hpp"
9
+
10
+ std::string jspp::JsString::to_std_string() const
11
+ {
12
+ return value;
13
+ }
14
+
15
+ jspp::JsIterator<jspp::AnyValue> jspp::JsString::get_iterator()
16
+ {
17
+ size_t strLength = value.length();
18
+ for (size_t idx = 0; idx < strLength; idx++)
19
+ {
20
+ co_yield AnyValue::make_string(std::string(1, value[idx]));
21
+ }
22
+ co_return AnyValue::make_undefined();
23
+ }
24
+
25
+ jspp::AnyValue jspp::JsString::get_property(const std::string &key, const AnyValue &thisVal)
26
+ {
27
+ // Check for prototype methods
28
+ auto proto_fn = StringPrototypes::get(key, this);
29
+ if (proto_fn.has_value())
30
+ {
31
+ return AnyValue::resolve_property_for_read(proto_fn.value(), thisVal, key);
32
+ }
33
+ // Handle character access by string index (e.g., "abc"["1"])
34
+ if (JsArray::is_array_index(key))
35
+ {
36
+ uint32_t idx = static_cast<uint32_t>(std::stoull(key));
37
+ return get_property(idx);
38
+ }
39
+ // not found
40
+ return AnyValue::make_undefined();
41
+ }
42
+
43
+ jspp::AnyValue jspp::JsString::get_property(uint32_t idx)
44
+ {
45
+ if (idx < value.length())
46
+ {
47
+ return AnyValue::make_string(std::string(1, value[idx]));
48
+ }
49
+ return AnyValue::make_undefined();
50
+ }
@@ -0,0 +1,23 @@
1
+ #pragma once
2
+
3
+ #include "types.hpp"
4
+ #include "values/symbol.hpp"
5
+ #include "any_value.hpp"
6
+ #include "values/prototypes/symbol.hpp"
7
+
8
+ std::string jspp::JsSymbol::to_std_string() const
9
+ {
10
+ return "Symbol(" + description + ")";
11
+ }
12
+
13
+ jspp::AnyValue jspp::JsSymbol::get_property(const std::string &key, const AnyValue &thisVal)
14
+ {
15
+ // check prototype
16
+ auto proto_it = SymbolPrototypes::get(key, this);
17
+ if (proto_it.has_value())
18
+ {
19
+ return AnyValue::resolve_property_for_read(proto_it.value(), thisVal, key);
20
+ }
21
+ // not found
22
+ return AnyValue::make_undefined();
23
+ }