@ugo-studio/jspp 0.1.4 → 0.1.5

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 (68) hide show
  1. package/dist/analysis/scope.js +17 -0
  2. package/dist/analysis/typeAnalyzer.js +7 -1
  3. package/dist/ast/symbols.js +29 -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 +31 -21
  12. package/dist/core/codegen/declaration-handlers.js +10 -6
  13. package/dist/core/codegen/expression-handlers.js +202 -60
  14. package/dist/core/codegen/function-handlers.js +179 -70
  15. package/dist/core/codegen/helpers.js +107 -17
  16. package/dist/core/codegen/index.js +9 -8
  17. package/dist/core/codegen/literal-handlers.js +15 -6
  18. package/dist/core/codegen/statement-handlers.js +67 -53
  19. package/dist/core/codegen/visitor.js +3 -1
  20. package/package.json +1 -1
  21. package/src/prelude/any_value.hpp +195 -342
  22. package/src/prelude/any_value_access.hpp +78 -30
  23. package/src/prelude/any_value_defines.hpp +74 -35
  24. package/src/prelude/any_value_helpers.hpp +73 -180
  25. package/src/prelude/exception.hpp +1 -0
  26. package/src/prelude/exception_helpers.hpp +4 -4
  27. package/src/prelude/index.hpp +9 -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 +53 -43
  38. package/src/prelude/library/symbol.hpp +45 -57
  39. package/src/prelude/library/timer.hpp +6 -6
  40. package/src/prelude/types.hpp +48 -0
  41. package/src/prelude/utils/access.hpp +182 -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/descriptors.hpp +2 -2
  51. package/src/prelude/values/function.hpp +71 -62
  52. package/src/prelude/values/helpers/array.hpp +64 -28
  53. package/src/prelude/values/helpers/function.hpp +77 -92
  54. package/src/prelude/values/helpers/iterator.hpp +3 -3
  55. package/src/prelude/values/helpers/object.hpp +54 -9
  56. package/src/prelude/values/helpers/promise.hpp +3 -3
  57. package/src/prelude/values/iterator.hpp +1 -1
  58. package/src/prelude/values/object.hpp +10 -3
  59. package/src/prelude/values/promise.hpp +3 -3
  60. package/src/prelude/values/prototypes/array.hpp +851 -12
  61. package/src/prelude/values/prototypes/function.hpp +2 -2
  62. package/src/prelude/values/prototypes/iterator.hpp +5 -5
  63. package/src/prelude/values/prototypes/number.hpp +153 -0
  64. package/src/prelude/values/prototypes/object.hpp +2 -2
  65. package/src/prelude/values/prototypes/promise.hpp +40 -30
  66. package/src/prelude/values/prototypes/string.hpp +28 -28
  67. package/src/prelude/values/prototypes/symbol.hpp +20 -3
  68. package/src/prelude/values/shape.hpp +52 -0
@@ -9,5 +9,17 @@ namespace jspp
9
9
  {
10
10
  // We use a specific prefix "@@" for well-known symbols to distinguish them from user symbols
11
11
  inline std::shared_ptr<JsSymbol> iterator = std::make_shared<JsSymbol>("Symbol.iterator", "@@iterator");
12
+ inline std::shared_ptr<JsSymbol> asyncIterator = std::make_shared<JsSymbol>("Symbol.asyncIterator", "@@asyncIterator");
13
+ inline std::shared_ptr<JsSymbol> hasInstance = std::make_shared<JsSymbol>("Symbol.hasInstance", "@@hasInstance");
14
+ inline std::shared_ptr<JsSymbol> isConcatSpreadable = std::make_shared<JsSymbol>("Symbol.isConcatSpreadable", "@@isConcatSpreadable");
15
+ inline std::shared_ptr<JsSymbol> match = std::make_shared<JsSymbol>("Symbol.match", "@@match");
16
+ inline std::shared_ptr<JsSymbol> matchAll = std::make_shared<JsSymbol>("Symbol.matchAll", "@@matchAll");
17
+ inline std::shared_ptr<JsSymbol> replace = std::make_shared<JsSymbol>("Symbol.replace", "@@replace");
18
+ inline std::shared_ptr<JsSymbol> search = std::make_shared<JsSymbol>("Symbol.search", "@@search");
19
+ inline std::shared_ptr<JsSymbol> species = std::make_shared<JsSymbol>("Symbol.species", "@@species");
20
+ inline std::shared_ptr<JsSymbol> split = std::make_shared<JsSymbol>("Symbol.split", "@@split");
21
+ inline std::shared_ptr<JsSymbol> toPrimitive = std::make_shared<JsSymbol>("Symbol.toPrimitive", "@@toPrimitive");
22
+ inline std::shared_ptr<JsSymbol> toStringTag = std::make_shared<JsSymbol>("Symbol.toStringTag", "@@toStringTag");
23
+ inline std::shared_ptr<JsSymbol> unscopables = std::make_shared<JsSymbol>("Symbol.unscopables", "@@unscopables");
12
24
  }
13
25
  }
@@ -10,18 +10,20 @@ namespace jspp
10
10
 
11
11
  struct JsArray
12
12
  {
13
- std::vector<std::optional<AnyValue>> dense; // dense storage for small/contiguous indices
14
- std::unordered_map<uint32_t, std::optional<AnyValue>> sparse; // sparse indices (very large indices)
15
- std::unordered_map<std::string, AnyValue> props; // non-index string properties
13
+ std::vector<AnyValue> dense; // dense storage for small/contiguous indices
14
+ std::unordered_map<uint32_t, AnyValue> sparse; // sparse indices (very large indices)
15
+ std::unordered_map<std::string, AnyValue> props; // non-index string properties
16
+ std::shared_ptr<AnyValue> proto;
16
17
  uint64_t length = 0;
17
18
 
18
- JsArray() = default;
19
- explicit JsArray(const std::vector<std::optional<AnyValue>> &items) : dense(items), length(items.size()) {}
20
- explicit JsArray(std::vector<std::optional<AnyValue>> &&items) : dense(std::move(items)), length(dense.size()) {}
19
+ JsArray() : proto(nullptr) {}
20
+ explicit JsArray(const std::vector<AnyValue> &items) : dense(items), proto(nullptr), length(items.size()) {}
21
+ explicit JsArray(std::vector<AnyValue> &&items) : dense(std::move(items)), proto(nullptr), length(dense.size()) {}
21
22
 
22
23
  std::string to_std_string() const;
23
24
  JsIterator<AnyValue> get_iterator();
24
25
 
26
+ bool has_property(const std::string &key) const;
25
27
  AnyValue get_property(const std::string &key, const AnyValue &thisVal);
26
28
  AnyValue get_property(uint32_t idx);
27
29
  AnyValue set_property(const std::string &key, const AnyValue &value, const AnyValue &thisVal);
@@ -17,8 +17,8 @@ namespace jspp
17
17
 
18
18
  struct AccessorDescriptor
19
19
  {
20
- std::optional<std::function<AnyValue(const AnyValue &, const std::vector<AnyValue> &)>> get; // getter = function or undefined
21
- std::optional<std::function<AnyValue(const AnyValue &, const std::vector<AnyValue> &)>> set; // setter = function or undefined
20
+ std::optional<std::function<AnyValue(const AnyValue &, std::span<const AnyValue>)>> get; // getter = function or undefined
21
+ std::optional<std::function<AnyValue(const AnyValue &, std::span<const AnyValue>)>> set; // setter = function or undefined
22
22
  bool enumerable = false;
23
23
  bool configurable = true;
24
24
  };
@@ -1,76 +1,85 @@
1
1
  #pragma once
2
2
 
3
3
  #include "types.hpp"
4
- #include <variant> // Ensure variant is included
4
+ #include <variant>
5
+ #include <optional>
5
6
 
6
7
  namespace jspp
7
8
  {
8
- // Forward declaration of AnyValue
9
- class AnyValue;
9
+ // Forward declaration of AnyValue
10
+ class AnyValue;
10
11
 
11
- using JsFunctionCallable = std::variant<std::function<AnyValue(const AnyValue &, const std::vector<AnyValue> &)>, // 0: Normal
12
- std::function<jspp::JsIterator<jspp::AnyValue>(const AnyValue &, const std::vector<jspp::AnyValue> &)>, // 1: Generator
13
- std::function<jspp::JsPromise(const AnyValue &, const std::vector<jspp::AnyValue> &)>>; // 2: Async
12
+ using JsFunctionCallable = std::variant<std::function<AnyValue(const AnyValue &, std::span<const AnyValue>)>, // 0: Normal
13
+ std::function<jspp::JsIterator<jspp::AnyValue>(const AnyValue &, std::span<const AnyValue>)>, // 1: Generator
14
+ std::function<jspp::JsPromise(const AnyValue &, std::span<const AnyValue>)>>; // 2: Async
14
15
 
15
- struct JsFunction
16
+ struct JsFunction
17
+ {
18
+ JsFunctionCallable callable;
19
+ std::optional<std::string> name;
20
+ std::unordered_map<std::string, AnyValue> props;
21
+ std::shared_ptr<AnyValue> proto = nullptr;
22
+ bool is_generator;
23
+ bool is_async;
24
+ bool is_class;
25
+ bool is_constructor;
26
+
27
+ // ---- Constructor A: infer flags ----
28
+ JsFunction(const JsFunctionCallable &c,
29
+ std::optional<std::string> n = std::nullopt,
30
+ std::unordered_map<std::string, AnyValue> p = {},
31
+ bool is_cls = false,
32
+ bool is_ctor = true)
33
+ : callable(c),
34
+ name(std::move(n)),
35
+ props(std::move(p)),
36
+ is_generator(callable.index() == 1),
37
+ is_async(callable.index() == 2),
38
+ is_class(is_cls),
39
+ is_constructor(is_ctor && !is_generator && !is_async) // Generators and asyncs are never constructors
16
40
  {
17
- JsFunctionCallable callable;
18
- std::string name;
19
- std::unordered_map<std::string, AnyValue> props;
20
- std::shared_ptr<AnyValue> proto = nullptr;
21
- bool is_generator;
22
- bool is_async;
23
- bool is_class;
41
+ }
24
42
 
25
- // ---- Constructor A: infer flags ----
26
- JsFunction(const JsFunctionCallable &c,
27
- std::string n = {},
28
- std::unordered_map<std::string, AnyValue> p = {},
29
- bool is_cls = false)
30
- : callable(c),
31
- name(std::move(n)),
32
- props(std::move(p)),
33
- is_generator(callable.index() == 1),
34
- is_async(callable.index() == 2),
35
- is_class(is_cls)
36
- {
37
- }
43
+ // ---- Constructor B: explicit generator flag (backward compat) ----
44
+ JsFunction(const JsFunctionCallable &c,
45
+ bool is_gen,
46
+ std::optional<std::string> n = std::nullopt,
47
+ std::unordered_map<std::string, AnyValue> p = {},
48
+ bool is_cls = false,
49
+ bool is_ctor = true)
50
+ : callable(c),
51
+ name(std::move(n)),
52
+ props(std::move(p)),
53
+ is_generator(is_gen),
54
+ is_async(callable.index() == 2),
55
+ is_class(is_cls),
56
+ is_constructor(is_ctor && !is_gen && !is_async)
57
+ {
58
+ }
38
59
 
39
- // ---- Constructor B: explicit generator flag (backward compat) ----
40
- JsFunction(const JsFunctionCallable &c,
41
- bool is_gen,
42
- std::string n = {},
43
- std::unordered_map<std::string, AnyValue> p = {},
44
- bool is_cls = false)
45
- : callable(c),
46
- name(std::move(n)),
47
- props(std::move(p)),
48
- is_generator(is_gen),
49
- is_async(callable.index() == 2),
50
- is_class(is_cls)
51
- {
52
- }
53
-
54
- // ---- Constructor C: explicit async flag ----
55
- JsFunction(const JsFunctionCallable &c,
56
- bool is_gen,
57
- bool is_async_func,
58
- std::string n = {},
59
- std::unordered_map<std::string, AnyValue> p = {},
60
- bool is_cls = false)
61
- : callable(c),
62
- name(std::move(n)),
63
- props(std::move(p)),
64
- is_generator(is_gen),
65
- is_async(is_async_func),
66
- is_class(is_cls)
67
- {
68
- }
60
+ // ---- Constructor C: explicit async flag ----
61
+ JsFunction(const JsFunctionCallable &c,
62
+ bool is_gen,
63
+ bool is_async_func,
64
+ std::optional<std::string> n = std::nullopt,
65
+ std::unordered_map<std::string, AnyValue> p = {},
66
+ bool is_cls = false,
67
+ bool is_ctor = true)
68
+ : callable(c),
69
+ name(std::move(n)),
70
+ props(std::move(p)),
71
+ is_generator(is_gen),
72
+ is_async(is_async_func),
73
+ is_class(is_cls),
74
+ is_constructor(is_ctor && !is_gen && !is_async_func)
75
+ {
76
+ }
69
77
 
70
- std::string to_std_string() const;
71
- AnyValue call(const AnyValue &thisVal, const std::vector<AnyValue> &args);
78
+ std::string to_std_string() const;
79
+ AnyValue call(const AnyValue &thisVal, std::span<const AnyValue> args);
72
80
 
73
- AnyValue get_property(const std::string &key, const AnyValue &thisVal);
74
- AnyValue set_property(const std::string &key, const AnyValue &value, const AnyValue &thisVal);
75
- };
81
+ bool has_property(const std::string &key) const;
82
+ AnyValue get_property(const std::string &key, const AnyValue &thisVal);
83
+ AnyValue set_property(const std::string &key, const AnyValue &value, const AnyValue &thisVal);
84
+ };
76
85
  }
@@ -16,23 +16,23 @@ std::string jspp::JsArray::to_std_string() const
16
16
  std::string result = "";
17
17
  for (uint64_t i = 0; i < length; ++i)
18
18
  {
19
- std::optional<AnyValue> itemVal;
19
+ const AnyValue *itemPtr = nullptr;
20
20
  if (i < dense.size())
21
21
  {
22
- itemVal = dense[i];
22
+ itemPtr = &dense[i];
23
23
  }
24
24
  else
25
25
  {
26
26
  auto it = sparse.find(static_cast<uint32_t>(i));
27
27
  if (it != sparse.end())
28
28
  {
29
- itemVal = it->second;
29
+ itemPtr = &it->second;
30
30
  }
31
31
  }
32
32
 
33
- if (itemVal.has_value())
33
+ if (itemPtr && !itemPtr->is_uninitialized())
34
34
  {
35
- const auto &item = itemVal.value();
35
+ const auto &item = *itemPtr;
36
36
  if (!item.is_undefined() && !item.is_null())
37
37
  {
38
38
  result += item.to_std_string();
@@ -57,6 +57,32 @@ jspp::JsIterator<jspp::AnyValue> jspp::JsArray::get_iterator()
57
57
  co_return AnyValue::make_undefined();
58
58
  }
59
59
 
60
+ bool jspp::JsArray::has_property(const std::string &key) const
61
+ {
62
+ if (key == "length")
63
+ return true;
64
+ if (is_array_index(key))
65
+ {
66
+ uint32_t idx = static_cast<uint32_t>(std::stoull(key));
67
+ if (idx < dense.size())
68
+ return true;
69
+ if (sparse.find(idx) != sparse.end())
70
+ return true;
71
+ }
72
+ if (props.find(key) != props.end())
73
+ return true;
74
+
75
+ if (proto && !(*proto).is_null() && !(*proto).is_undefined())
76
+ {
77
+ if ((*proto).has_property(key))
78
+ return true;
79
+ }
80
+
81
+ if (ArrayPrototypes::get(key, const_cast<JsArray *>(this)).has_value())
82
+ return true;
83
+ return false;
84
+ }
85
+
60
86
  jspp::AnyValue jspp::JsArray::get_property(const std::string &key, const AnyValue &thisVal)
61
87
  {
62
88
  if (
@@ -71,7 +97,26 @@ jspp::AnyValue jspp::JsArray::get_property(const std::string &key, const AnyValu
71
97
  auto it = props.find(key);
72
98
  if (it == props.end())
73
99
  {
74
- // check prototype
100
+ // check special own properties (length)
101
+ if (key == "length")
102
+ {
103
+ auto proto_it = ArrayPrototypes::get(key, this);
104
+ if (proto_it.has_value())
105
+ {
106
+ return AnyValue::resolve_property_for_read(proto_it.value(), thisVal, key);
107
+ }
108
+ }
109
+
110
+ // check explicit proto chain
111
+ if (proto && !(*proto).is_null() && !(*proto).is_undefined())
112
+ {
113
+ if ((*proto).has_property(key))
114
+ {
115
+ return (*proto).get_property_with_receiver(key, thisVal);
116
+ }
117
+ }
118
+
119
+ // check prototype (implicit Array.prototype)
75
120
  auto proto_it = ArrayPrototypes::get(key, this);
76
121
  if (proto_it.has_value())
77
122
  {
@@ -88,12 +133,14 @@ jspp::AnyValue jspp::JsArray::get_property(uint32_t idx)
88
133
  {
89
134
  if (idx < dense.size())
90
135
  {
91
- return dense[idx].value_or(AnyValue::make_undefined());
136
+ const auto &val = dense[idx];
137
+ return val.is_uninitialized() ? AnyValue::make_undefined() : val;
92
138
  }
93
139
  const auto &it = sparse.find(idx);
94
140
  if (it != sparse.end())
95
141
  {
96
- return it->second.value_or(AnyValue::make_undefined());
142
+ const auto &val = it->second;
143
+ return val.is_uninitialized() ? AnyValue::make_undefined() : val;
97
144
  }
98
145
  return AnyValue::make_undefined();
99
146
  }
@@ -111,6 +158,12 @@ jspp::AnyValue jspp::JsArray::set_property(const std::string &key, const AnyValu
111
158
  {
112
159
  // set prototype property if accessor descriptor
113
160
  auto proto_val_opt = ArrayPrototypes::get(key, this);
161
+ if (!proto_val_opt.has_value() && proto && !(*proto).is_null() && !(*proto).is_undefined())
162
+ {
163
+ // This is a bit simplified, ideally we should call get_property on proto to check descriptors
164
+ // For now, let's assume if it's not in ArrayPrototypes, it might be in the explicit proto chain
165
+ }
166
+
114
167
  if (proto_val_opt.has_value())
115
168
  {
116
169
  auto proto_value = proto_val_opt.value();
@@ -147,35 +200,18 @@ jspp::AnyValue jspp::JsArray::set_property(uint32_t idx, const AnyValue &value)
147
200
  const uint32_t DENSE_GROW_THRESHOLD = 1024;
148
201
  if (idx < dense.size())
149
202
  {
150
- if (!dense[idx].has_value())
151
- {
152
- dense[idx] = AnyValue::make_undefined();
153
- }
154
203
  dense[idx] = value;
155
204
  return value;
156
205
  }
157
206
  else if (idx <= dense.size() + DENSE_GROW_THRESHOLD)
158
207
  {
159
- dense.resize(idx + 1);
208
+ dense.resize(idx + 1, AnyValue::make_uninitialized());
160
209
  dense[idx] = value;
161
210
  return value;
162
211
  }
163
212
  else
164
213
  {
165
- auto it = sparse.find(idx);
166
- if (it != sparse.end())
167
- {
168
- if (!it->second.has_value())
169
- {
170
- it->second = AnyValue::make_undefined();
171
- }
172
- it->second = value;
173
- return value;
174
- }
175
- else
176
- {
177
- sparse[idx] = value;
178
- return value;
179
- }
214
+ sparse[idx] = value;
215
+ return value;
180
216
  }
181
217
  }
@@ -1,125 +1,110 @@
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)
13
+ std::string JsFunction::to_std_string() const
16
14
  {
17
- return "function* " + name + "() { [native code] }";
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] }";
18
19
  }
19
- return "function " + name + "() { [native code] }";
20
- }
21
20
 
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))
21
+ AnyValue JsFunction::call(const AnyValue &thisVal, std::span<const AnyValue> args)
33
22
  {
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())
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))
73
32
  {
74
- return AnyValue::resolve_property_for_write(proto_value, thisVal, value, key);
33
+ return AnyValue::make_promise((*func)(thisVal, args));
75
34
  }
76
- if (proto_value.is_data_descriptor() && !proto_value.as_data_descriptor()->writable)
35
+ else
77
36
  {
78
- return AnyValue::resolve_property_for_write(proto_value, thisVal, value, key);
37
+ return AnyValue::make_undefined();
79
38
  }
80
39
  }
81
40
 
82
- // set own property
83
- auto it = props.find(key);
84
- if (it != props.end())
41
+ bool JsFunction::has_property(const std::string &key) const
85
42
  {
86
- return AnyValue::resolve_property_for_write(it->second, thisVal, value, key);
87
- }
88
- else
89
- {
90
- props[key] = value;
91
- return value;
43
+ if (props.find(key) != props.end())
44
+ return true;
45
+ if (proto && !(*proto).is_null() && !(*proto).is_undefined())
46
+ {
47
+ if ((*proto).has_property(key))
48
+ return true;
49
+ }
50
+ if (FunctionPrototypes::get(key, const_cast<JsFunction *>(this)).has_value())
51
+ return true;
52
+ return false;
92
53
  }
93
- }
94
54
 
95
- // AnyValue::construct implementation
96
- const jspp::AnyValue jspp::AnyValue::construct(const std::vector<AnyValue> &args) const
97
- {
98
- if (!is_function())
55
+ AnyValue JsFunction::get_property(const std::string &key, const AnyValue &thisVal)
99
56
  {
100
- throw Exception::make_exception(to_std_string() + " is not a constructor", "TypeError");
101
- }
57
+ auto it = props.find(key);
58
+ if (it == props.end())
59
+ {
60
+ // check explicit proto chain (e.g. for classes extending other classes)
61
+ if (proto && !(*proto).is_null() && !(*proto).is_undefined())
62
+ {
63
+ if ((*proto).has_property(key))
64
+ {
65
+ return (*proto).get_property_with_receiver(key, thisVal);
66
+ }
67
+ }
102
68
 
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({});
69
+ // check prototype (implicit Function.prototype)
70
+ auto proto_it = FunctionPrototypes::get(key, this);
71
+ if (proto_it.has_value())
72
+ {
73
+ return AnyValue::resolve_property_for_read(proto_it.value(), thisVal, key);
74
+ }
75
+ // not found
76
+ return AnyValue::make_undefined();
77
+ }
78
+ return AnyValue::resolve_property_for_read(it->second, thisVal, key);
110
79
  }
111
80
 
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())
81
+ AnyValue JsFunction::set_property(const std::string &key, const AnyValue &value, const AnyValue &thisVal)
121
82
  {
122
- return result;
83
+ // set prototype property if accessor descriptor
84
+ auto proto_it = FunctionPrototypes::get(key, this);
85
+ if (proto_it.has_value())
86
+ {
87
+ auto proto_value = proto_it.value();
88
+ if (proto_value.is_accessor_descriptor())
89
+ {
90
+ return AnyValue::resolve_property_for_write(proto_value, thisVal, value, key);
91
+ }
92
+ if (proto_value.is_data_descriptor() && !proto_value.as_data_descriptor()->writable)
93
+ {
94
+ return AnyValue::resolve_property_for_write(proto_value, thisVal, value, key);
95
+ }
96
+ }
97
+
98
+ // set own property
99
+ auto it = props.find(key);
100
+ if (it != props.end())
101
+ {
102
+ return AnyValue::resolve_property_for_write(it->second, thisVal, value, key);
103
+ }
104
+ else
105
+ {
106
+ props[key] = value;
107
+ return value;
108
+ }
123
109
  }
124
- return instance;
125
- }
110
+ }
@@ -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
  }