@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
@@ -7,54 +7,102 @@ jspp::AnyValue jspp::AnyValue::get_own_property(const std::string &key) const
7
7
  {
8
8
  return get_property_with_receiver(key, *this);
9
9
  }
10
- jspp::AnyValue jspp::AnyValue::get_own_property(uint32_t idx) const noexcept
10
+ bool jspp::AnyValue::has_property(const std::string &key) const
11
11
  {
12
- switch (storage.type)
12
+ switch (get_type())
13
13
  {
14
+ case JsType::Object:
15
+ return std::get<std::shared_ptr<JsObject>>(storage)->has_property(key);
14
16
  case JsType::Array:
15
- return storage.array->get_property(idx);
17
+ return std::get<std::shared_ptr<JsArray>>(storage)->has_property(key);
18
+ case JsType::Function:
19
+ return std::get<std::shared_ptr<JsFunction>>(storage)->has_property(key);
20
+ case JsType::Promise:
21
+ // Promises don't have their own props usually, but could.
22
+ return false;
23
+ case JsType::Iterator:
24
+ return false;
25
+ case JsType::Symbol:
26
+ return false;
16
27
  case JsType::String:
17
- return storage.str->get_property(idx);
28
+ if (key == "length")
29
+ return true;
30
+ if (JsArray::is_array_index(key))
31
+ {
32
+ uint32_t idx = static_cast<uint32_t>(std::stoull(key));
33
+ return idx < std::get<std::shared_ptr<JsString>>(storage)->value.length();
34
+ }
35
+ return false;
36
+ case JsType::Number:
37
+ return false;
38
+ case JsType::Uninitialized:
39
+ Exception::throw_uninitialized_reference("#<Object>");
40
+ return false;
41
+ default:
42
+ return false;
43
+ }
44
+ }
45
+ jspp::AnyValue jspp::AnyValue::get_own_property(uint32_t idx) const
46
+ {
47
+ switch (storage.index())
48
+ {
49
+ case 7: // Array
50
+ return std::get<std::shared_ptr<JsArray>>(storage)->get_property(idx);
51
+ case 5: // String
52
+ return std::get<std::shared_ptr<JsString>>(storage)->get_property(idx);
53
+ case 4: // Number
54
+ return get_own_property(std::to_string(idx));
18
55
  default:
19
56
  return get_own_property(std::to_string(idx));
20
57
  }
21
58
  }
22
- jspp::AnyValue jspp::AnyValue::get_own_property(const AnyValue &key) const noexcept
59
+ jspp::AnyValue jspp::AnyValue::get_own_property(const AnyValue &key) const
23
60
  {
24
- if (key.storage.type == JsType::Number && storage.type == JsType::Array)
25
- return storage.array->get_property(key.storage.number);
26
- if (key.storage.type == JsType::Number && storage.type == JsType::String)
27
- return storage.str->get_property(key.storage.number);
61
+ if (key.is_number() && is_array())
62
+ return std::get<std::shared_ptr<JsArray>>(storage)->get_property(key.as_double());
63
+ if (key.is_number() && is_string())
64
+ return std::get<std::shared_ptr<JsString>>(storage)->get_property(key.as_double());
28
65
 
29
66
  // If the key is a Symbol, use its internal key string
30
- if (key.storage.type == JsType::Symbol)
31
- return get_own_property(key.storage.symbol->key);
67
+ if (key.is_symbol())
68
+ return get_own_property(key.as_symbol()->key);
32
69
 
33
70
  return get_own_property(key.to_std_string());
34
71
  }
35
72
 
36
73
  jspp::AnyValue jspp::AnyValue::get_property_with_receiver(const std::string &key, const AnyValue &receiver) const
37
74
  {
38
- switch (storage.type)
75
+ switch (get_type())
39
76
  {
40
77
  case JsType::Object:
41
- return storage.object->get_property(key, receiver);
78
+ return std::get<std::shared_ptr<JsObject>>(storage)->get_property(key, receiver);
42
79
  case JsType::Array:
43
- return storage.array->get_property(key, receiver);
80
+ return std::get<std::shared_ptr<JsArray>>(storage)->get_property(key, receiver);
44
81
  case JsType::Function:
45
- return storage.function->get_property(key, receiver);
82
+ return std::get<std::shared_ptr<JsFunction>>(storage)->get_property(key, receiver);
46
83
  case JsType::Promise:
47
- return storage.promise->get_property(key, receiver);
84
+ return std::get<std::shared_ptr<JsPromise>>(storage)->get_property(key, receiver);
48
85
  case JsType::Iterator:
49
- return storage.iterator->get_property(key, receiver);
86
+ return std::get<std::shared_ptr<JsIterator<AnyValue>>>(storage)->get_property(key, receiver);
50
87
  case JsType::Symbol:
51
- return storage.symbol->get_property(key, receiver);
88
+ return std::get<std::shared_ptr<JsSymbol>>(storage)->get_property(key, receiver);
52
89
  case JsType::String:
53
- return storage.str->get_property(key, receiver);
90
+ return std::get<std::shared_ptr<JsString>>(storage)->get_property(key, receiver);
91
+ case JsType::Number:
92
+ {
93
+ auto proto_it = NumberPrototypes::get(key, std::get<double>(storage));
94
+ if (proto_it.has_value())
95
+ {
96
+ return AnyValue::resolve_property_for_read(proto_it.value(), receiver, key);
97
+ }
98
+ return AnyValue::make_undefined();
99
+ }
54
100
  case JsType::Undefined:
55
101
  throw Exception::make_exception("Cannot read properties of undefined (reading '" + key + "')", "TypeError");
56
102
  case JsType::Null:
57
103
  throw Exception::make_exception("Cannot read properties of null (reading '" + key + "')", "TypeError");
104
+ case JsType::Uninitialized:
105
+ Exception::throw_uninitialized_reference("#<Object>");
58
106
  default:
59
107
  return AnyValue::make_undefined();
60
108
  }
@@ -62,16 +110,16 @@ jspp::AnyValue jspp::AnyValue::get_property_with_receiver(const std::string &key
62
110
 
63
111
  jspp::AnyValue jspp::AnyValue::set_own_property(const std::string &key, const AnyValue &value) const
64
112
  {
65
- switch (storage.type)
113
+ switch (get_type())
66
114
  {
67
115
  case JsType::Object:
68
- return storage.object->set_property(key, value, *this);
116
+ return std::get<std::shared_ptr<JsObject>>(storage)->set_property(key, value, *this);
69
117
  case JsType::Array:
70
- return storage.array->set_property(key, value, *this);
118
+ return std::get<std::shared_ptr<JsArray>>(storage)->set_property(key, value, *this);
71
119
  case JsType::Function:
72
- return storage.function->set_property(key, value, *this);
120
+ return std::get<std::shared_ptr<JsFunction>>(storage)->set_property(key, value, *this);
73
121
  case JsType::Promise:
74
- return storage.promise->set_property(key, value, *this);
122
+ return std::get<std::shared_ptr<JsPromise>>(storage)->set_property(key, value, *this);
75
123
  case JsType::Undefined:
76
124
  throw Exception::make_exception("Cannot set properties of undefined (setting '" + key + "')", "TypeError");
77
125
  case JsType::Null:
@@ -82,22 +130,22 @@ jspp::AnyValue jspp::AnyValue::set_own_property(const std::string &key, const An
82
130
  }
83
131
  jspp::AnyValue jspp::AnyValue::set_own_property(uint32_t idx, const AnyValue &value) const
84
132
  {
85
- if (storage.type == JsType::Array)
133
+ if (is_array())
86
134
  {
87
- return storage.array->set_property(idx, value);
135
+ return std::get<std::shared_ptr<JsArray>>(storage)->set_property(idx, value);
88
136
  }
89
137
  return set_own_property(std::to_string(idx), value);
90
138
  }
91
139
  jspp::AnyValue jspp::AnyValue::set_own_property(const AnyValue &key, const AnyValue &value) const
92
140
  {
93
- if (key.storage.type == JsType::Number && storage.type == JsType::Array)
141
+ if (key.is_number() && is_array())
94
142
  {
95
- return storage.array->set_property(key.storage.number, value);
143
+ return std::get<std::shared_ptr<JsArray>>(storage)->set_property(key.as_double(), value);
96
144
  }
97
145
 
98
146
  // If the key is a Symbol, use its internal key string
99
- if (key.storage.type == JsType::Symbol)
100
- return set_own_property(key.storage.symbol->key, value);
147
+ if (key.is_symbol())
148
+ return set_own_property(key.as_symbol()->key, value);
101
149
 
102
150
  return set_own_property(key.to_std_string(), value);
103
151
  }
@@ -7,16 +7,25 @@
7
7
 
8
8
  namespace jspp
9
9
  {
10
-
11
10
  inline void AnyValue::define_data_property(const std::string &key, const AnyValue &value)
12
11
  {
13
12
  if (is_object())
14
13
  {
15
- storage.object->props[key] = value;
14
+ auto obj = std::get<std::shared_ptr<JsObject>>(storage);
15
+ auto offset = obj->shape->get_offset(key);
16
+ if (offset.has_value())
17
+ {
18
+ obj->storage[offset.value()] = value;
19
+ }
20
+ else
21
+ {
22
+ obj->shape = obj->shape->transition(key);
23
+ obj->storage.push_back(value);
24
+ }
16
25
  }
17
26
  else if (is_function())
18
27
  {
19
- storage.function->props[key] = value;
28
+ std::get<std::shared_ptr<JsFunction>>(storage)->props[key] = value;
20
29
  }
21
30
  }
22
31
 
@@ -37,42 +46,56 @@ namespace jspp
37
46
  {
38
47
  if (is_object())
39
48
  {
40
- auto &props = storage.object->props;
41
- auto it = props.find(key);
42
- if (it != props.end() && it->second.is_accessor_descriptor())
49
+ auto obj = std::get<std::shared_ptr<JsObject>>(storage);
50
+ auto offset = obj->shape->get_offset(key);
51
+
52
+ if (offset.has_value())
43
53
  {
44
- auto desc = it->second.as_accessor_descriptor();
45
- desc->get = [getter](const AnyValue &thisVal, const std::vector<AnyValue> &args) -> AnyValue
54
+ auto &val = obj->storage[offset.value()];
55
+ if (val.is_accessor_descriptor())
46
56
  {
47
- return getter.as_function()->call(thisVal, args);
48
- };
57
+ auto desc = val.as_accessor_descriptor();
58
+ desc->get = [getter](const AnyValue &thisVal, std::span<const AnyValue> args) -> AnyValue
59
+ {
60
+ return getter.call(thisVal, args);
61
+ };
62
+ }
63
+ else
64
+ {
65
+ auto getFunc = [getter](const AnyValue &thisVal, std::span<const AnyValue> args) -> AnyValue
66
+ {
67
+ return getter.call(thisVal, args);
68
+ };
69
+ obj->storage[offset.value()] = AnyValue::make_accessor_descriptor(getFunc, std::nullopt, true, true);
70
+ }
49
71
  }
50
72
  else
51
73
  {
52
- auto getFunc = [getter](const AnyValue &thisVal, const std::vector<AnyValue> &args) -> AnyValue
74
+ auto getFunc = [getter](const AnyValue &thisVal, std::span<const AnyValue> args) -> AnyValue
53
75
  {
54
- return getter.as_function()->call(thisVal, args);
76
+ return getter.call(thisVal, args);
55
77
  };
56
- props[key] = AnyValue::make_accessor_descriptor(getFunc, std::nullopt, true, true);
78
+ obj->shape = obj->shape->transition(key);
79
+ obj->storage.push_back(AnyValue::make_accessor_descriptor(getFunc, std::nullopt, true, true));
57
80
  }
58
81
  }
59
82
  else if (is_function())
60
83
  {
61
- auto &props = storage.function->props;
84
+ auto &props = std::get<std::shared_ptr<JsFunction>>(storage)->props;
62
85
  auto it = props.find(key);
63
86
  if (it != props.end() && it->second.is_accessor_descriptor())
64
87
  {
65
88
  auto desc = it->second.as_accessor_descriptor();
66
- desc->get = [getter](const AnyValue &thisVal, const std::vector<AnyValue> &args) -> AnyValue
89
+ desc->get = [getter](const AnyValue &thisVal, std::span<const AnyValue> args) -> AnyValue
67
90
  {
68
- return getter.as_function()->call(thisVal, args);
91
+ return getter.call(thisVal, args);
69
92
  };
70
93
  }
71
94
  else
72
95
  {
73
- auto getFunc = [getter](const AnyValue &thisVal, const std::vector<AnyValue> &args) -> AnyValue
96
+ auto getFunc = [getter](const AnyValue &thisVal, std::span<const AnyValue> args) -> AnyValue
74
97
  {
75
- return getter.as_function()->call(thisVal, args);
98
+ return getter.call(thisVal, args);
76
99
  };
77
100
  props[key] = AnyValue::make_accessor_descriptor(getFunc, std::nullopt, true, true);
78
101
  }
@@ -91,50 +114,66 @@ namespace jspp
91
114
  {
92
115
  if (is_object())
93
116
  {
94
- auto &props = storage.object->props;
95
- auto it = props.find(key);
96
- if (it != props.end() && it->second.is_accessor_descriptor())
117
+ auto obj = std::get<std::shared_ptr<JsObject>>(storage);
118
+ auto offset = obj->shape->get_offset(key);
119
+
120
+ if (offset.has_value())
97
121
  {
98
- auto desc = it->second.as_accessor_descriptor();
99
- desc->set = [setter](const AnyValue &thisVal, const std::vector<AnyValue> &args) -> AnyValue
122
+ auto &val = obj->storage[offset.value()];
123
+ if (val.is_accessor_descriptor())
100
124
  {
101
- if (args.empty())
102
- return AnyValue::make_undefined();
103
- return setter.as_function()->call(thisVal, args);
104
- };
125
+ auto desc = val.as_accessor_descriptor();
126
+ desc->set = [setter](const AnyValue &thisVal, std::span<const AnyValue> args) -> AnyValue
127
+ {
128
+ if (args.empty())
129
+ return AnyValue::make_undefined();
130
+ return setter.call(thisVal, args);
131
+ };
132
+ }
133
+ else
134
+ {
135
+ auto setFunc = [setter](const AnyValue &thisVal, std::span<const AnyValue> args) -> AnyValue
136
+ {
137
+ if (args.empty())
138
+ return AnyValue::make_undefined();
139
+ return setter.call(thisVal, args);
140
+ };
141
+ obj->storage[offset.value()] = AnyValue::make_accessor_descriptor(std::nullopt, setFunc, true, true);
142
+ }
105
143
  }
106
144
  else
107
145
  {
108
- auto setFunc = [setter](const AnyValue &thisVal, const std::vector<AnyValue> &args) -> AnyValue
146
+ auto setFunc = [setter](const AnyValue &thisVal, std::span<const AnyValue> args) -> AnyValue
109
147
  {
110
148
  if (args.empty())
111
149
  return AnyValue::make_undefined();
112
- return setter.as_function()->call(thisVal, args);
150
+ return setter.call(thisVal, args);
113
151
  };
114
- props[key] = AnyValue::make_accessor_descriptor(std::nullopt, setFunc, true, true);
152
+ obj->shape = obj->shape->transition(key);
153
+ obj->storage.push_back(AnyValue::make_accessor_descriptor(std::nullopt, setFunc, true, true));
115
154
  }
116
155
  }
117
156
  else if (is_function())
118
157
  {
119
- auto &props = storage.function->props;
158
+ auto &props = std::get<std::shared_ptr<JsFunction>>(storage)->props;
120
159
  auto it = props.find(key);
121
160
  if (it != props.end() && it->second.is_accessor_descriptor())
122
161
  {
123
162
  auto desc = it->second.as_accessor_descriptor();
124
- desc->set = [setter](const AnyValue &thisVal, const std::vector<AnyValue> &args) -> AnyValue
163
+ desc->set = [setter](const AnyValue &thisVal, std::span<const AnyValue> args) -> AnyValue
125
164
  {
126
165
  if (args.empty())
127
166
  return AnyValue::make_undefined();
128
- return setter.as_function()->call(thisVal, args);
167
+ return setter.call(thisVal, args);
129
168
  };
130
169
  }
131
170
  else
132
171
  {
133
- auto setFunc = [setter](const AnyValue &thisVal, const std::vector<AnyValue> &args) -> AnyValue
172
+ auto setFunc = [setter](const AnyValue &thisVal, std::span<const AnyValue> args) -> AnyValue
134
173
  {
135
174
  if (args.empty())
136
175
  return AnyValue::make_undefined();
137
- return setter.as_function()->call(thisVal, args);
176
+ return setter.call(thisVal, args);
138
177
  };
139
178
  props[key] = AnyValue::make_accessor_descriptor(std::nullopt, setFunc, true, true);
140
179
  }
@@ -3,217 +3,60 @@
3
3
  #include "types.hpp"
4
4
  #include "any_value.hpp"
5
5
  #include "values/string.hpp"
6
+ #include "exception.hpp"
6
7
 
7
8
  namespace jspp
8
9
  {
9
- const bool AnyValue::is_truthy() const noexcept
10
+ std::string AnyValue::to_std_string() const
10
11
  {
11
- switch (storage.type)
12
- {
13
- case JsType::Boolean:
14
- return storage.boolean;
15
- case JsType::Number:
16
- return storage.number != 0.0;
17
- case JsType::String:
18
- return !storage.str->value.empty();
19
- case JsType::Undefined:
20
- return false;
21
- case JsType::Null:
22
- return false;
23
- case JsType::Uninitialized:
24
- return false;
25
- default:
26
- return true;
27
- }
28
- }
29
-
30
- const bool AnyValue::is_strictly_equal_to_primitive(const AnyValue &other) const noexcept
31
- {
32
- if (storage.type == other.storage.type)
33
- {
34
- switch (storage.type)
35
- {
36
- case JsType::Boolean:
37
- return storage.boolean == other.storage.boolean;
38
- case JsType::Number:
39
- return storage.number == other.storage.number;
40
- case JsType::String:
41
- return (storage.str->value == other.storage.str->value);
42
- case JsType::Array:
43
- return (storage.array == other.storage.array);
44
- case JsType::Object:
45
- return (storage.object == other.storage.object);
46
- case JsType::Function:
47
- return (storage.function == other.storage.function);
48
- case JsType::Iterator:
49
- return (storage.iterator == other.storage.iterator);
50
- case JsType::Promise:
51
- return (storage.promise == other.storage.promise);
52
- case JsType::Symbol:
53
- // Symbols are unique by reference/pointer identity
54
- return (storage.symbol == other.storage.symbol);
55
- case JsType::DataDescriptor:
56
- return storage.data_desc == other.storage.data_desc;
57
- case JsType::AccessorDescriptor:
58
- return storage.accessor_desc == other.storage.accessor_desc;
59
- default:
60
- return true;
61
- }
62
- }
63
- return false;
64
- }
65
- const bool AnyValue::is_equal_to_primitive(const AnyValue &other) const noexcept
66
- {
67
- // Implements JavaScript's Abstract Equality Comparison Algorithm (==)
68
- // Step 1: If types are the same, use strict equality (===)
69
- if (storage.type == other.storage.type)
70
- {
71
- return is_strictly_equal_to_primitive(other);
72
- }
73
- // Steps 2 & 3: null == undefined
74
- if ((is_null() && other.is_undefined()) || (is_undefined() && other.is_null()))
75
- {
76
- return true;
77
- }
78
- // Step 4 & 5: number == string
79
- if (is_number() && other.is_string())
80
- {
81
- double num_this = this->as_double();
82
- double num_other;
83
- try
84
- {
85
- const std::string &s = other.as_string()->value;
86
- // JS considers empty string or whitespace-only string to be 0
87
- if (s.empty() || std::all_of(s.begin(), s.end(), [](unsigned char c)
88
- { return std::isspace(c); }))
89
- {
90
- num_other = 0.0;
91
- }
92
- else
93
- {
94
- size_t pos;
95
- num_other = std::stod(s, &pos);
96
- // Check if the entire string was consumed, allowing for trailing whitespace
97
- while (pos < s.length() && std::isspace(static_cast<unsigned char>(s[pos])))
98
- {
99
- pos++;
100
- }
101
- if (pos != s.length())
102
- {
103
- num_other = std::numeric_limits<double>::quiet_NaN();
104
- }
105
- }
106
- }
107
- catch (...)
108
- {
109
- num_other = std::numeric_limits<double>::quiet_NaN();
110
- }
111
- return num_this == num_other;
112
- }
113
- if (is_string() && other.is_number())
114
- {
115
- // Delegate to the other operand to avoid code duplication
116
- return other.is_equal_to_primitive(*this);
117
- }
118
- // Step 6 & 7: boolean == any
119
- if (is_boolean())
120
- {
121
- // Convert boolean to number and re-compare
122
- return AnyValue::make_number(as_boolean() ? 1.0 : 0.0).is_equal_to_primitive(other);
123
- }
124
- if (other.is_boolean())
125
- {
126
- // Convert boolean to number and re-compare
127
- return is_equal_to_primitive(AnyValue::make_number(other.as_boolean() ? 1.0 : 0.0));
128
- }
129
- // Step 8 & 9: object == (string or number or symbol)
130
- // Simplified: Objects convert to primitives.
131
- if ((is_object() || is_array() || is_function() || is_promise() || is_iterator()) && (other.is_string() || other.is_number() || other.is_symbol()))
132
- {
133
- // Convert object to primitive (string) and re-compare.
134
- // This is a simplification of JS's ToPrimitive.
135
- return AnyValue::make_string(to_std_string()).is_equal_to_primitive(other);
136
- }
137
- if ((other.is_object() || other.is_array() || other.is_function() || other.is_promise() || other.is_iterator()) && (is_string() || is_number() || is_symbol()))
138
- {
139
- return other.is_equal_to_primitive(*this);
140
- }
141
- // Step 10: Datacriptor or accessor descriptor
142
- if (is_data_descriptor() || is_accessor_descriptor())
143
- {
144
- return (*this).is_strictly_equal_to_primitive(other);
145
- }
146
- // Step 11: All other cases (e.g., object == null) are false.
147
- return false;
148
- }
149
-
150
- const AnyValue AnyValue::is_strictly_equal_to(const AnyValue &other) const noexcept
151
- {
152
- return AnyValue::make_boolean(is_strictly_equal_to_primitive(other));
153
- }
154
- const AnyValue AnyValue::is_equal_to(const AnyValue &other) const noexcept
155
- {
156
- return AnyValue::make_boolean(is_equal_to_primitive(other));
157
- }
158
-
159
- const AnyValue AnyValue::not_strictly_equal_to(const AnyValue &other) const noexcept
160
- {
161
- return AnyValue::make_boolean(!is_strictly_equal_to_primitive(other));
162
- }
163
- const AnyValue AnyValue::not_equal_to(const AnyValue &other) const noexcept
164
- {
165
- return AnyValue::make_boolean(!is_equal_to_primitive(other));
166
- }
167
-
168
- const std::string AnyValue::to_std_string() const noexcept
169
- {
170
- switch (storage.type)
12
+ switch (get_type())
171
13
  {
172
14
  case JsType::Undefined:
173
15
  return "undefined";
174
16
  case JsType::Null:
175
17
  return "null";
176
18
  case JsType::Boolean:
177
- return storage.boolean ? "true" : "false";
19
+ return std::get<bool>(storage) ? "true" : "false";
178
20
  case JsType::String:
179
- return storage.str->to_std_string();
21
+ return std::get<std::shared_ptr<JsString>>(storage)->to_std_string();
180
22
  case JsType::Object:
181
- return storage.object->to_std_string();
23
+ return std::get<std::shared_ptr<JsObject>>(storage)->to_std_string();
182
24
  case JsType::Array:
183
- return storage.array->to_std_string();
25
+ return std::get<std::shared_ptr<JsArray>>(storage)->to_std_string();
184
26
  case JsType::Function:
185
- return storage.function->to_std_string();
27
+ return std::get<std::shared_ptr<JsFunction>>(storage)->to_std_string();
186
28
  case JsType::Iterator:
187
- return storage.iterator->to_std_string();
29
+ return std::get<std::shared_ptr<JsIterator<AnyValue>>>(storage)->to_std_string();
188
30
  case JsType::Promise:
189
- return storage.promise->to_std_string();
31
+ return std::get<std::shared_ptr<JsPromise>>(storage)->to_std_string();
190
32
  case JsType::Symbol:
191
- return storage.symbol->to_std_string();
33
+ return std::get<std::shared_ptr<JsSymbol>>(storage)->to_std_string();
192
34
  case JsType::DataDescriptor:
193
- return storage.data_desc->value->to_std_string();
35
+ return std::get<std::shared_ptr<DataDescriptor>>(storage)->value->to_std_string();
194
36
  case JsType::AccessorDescriptor:
195
37
  {
196
- if (storage.accessor_desc->get.has_value())
197
- return storage.accessor_desc->get.value()(*this, {}).to_std_string();
38
+ if (std::get<std::shared_ptr<AccessorDescriptor>>(storage)->get.has_value())
39
+ return std::get<std::shared_ptr<AccessorDescriptor>>(storage)->get.value()(*this, {}).to_std_string();
198
40
  else
199
41
  return "undefined";
200
42
  }
201
43
  case JsType::Number:
202
44
  {
203
- if (std::isnan(storage.number))
45
+ double num = std::get<double>(storage);
46
+ if (std::isnan(num))
204
47
  {
205
48
  return "NaN";
206
49
  }
207
- if (std::abs(storage.number) >= 1e21 || (std::abs(storage.number) > 0 && std::abs(storage.number) < 1e-6))
50
+ if (std::abs(num) >= 1e21 || (std::abs(num) > 0 && std::abs(num) < 1e-6))
208
51
  {
209
52
  std::ostringstream oss;
210
- oss << std::scientific << std::setprecision(4) << storage.number;
53
+ oss << std::scientific << std::setprecision(4) << num;
211
54
  return oss.str();
212
55
  }
213
56
  else
214
57
  {
215
58
  std::ostringstream oss;
216
- oss << std::setprecision(6) << std::fixed << storage.number;
59
+ oss << std::setprecision(6) << std::fixed << num;
217
60
  std::string s = oss.str();
218
61
  s.erase(s.find_last_not_of('0') + 1, std::string::npos);
219
62
  if (!s.empty() && s.back() == '.')
@@ -223,9 +66,8 @@ namespace jspp
223
66
  return s;
224
67
  }
225
68
  }
226
- // Uninitialized and default should not be reached under normal circumstances
227
69
  case JsType::Uninitialized:
228
- return "<uninitialized>";
70
+ Exception::throw_uninitialized_reference("#<Object>");
229
71
  default:
230
72
  return "";
231
73
  }
@@ -235,12 +77,63 @@ namespace jspp
235
77
  {
236
78
  if (is_object())
237
79
  {
238
- storage.object->proto = std::make_shared<AnyValue>(proto);
80
+ std::get<std::shared_ptr<JsObject>>(storage)->proto = std::make_shared<AnyValue>(proto);
81
+ }
82
+ else if (is_array())
83
+ {
84
+ std::get<std::shared_ptr<JsArray>>(storage)->proto = std::make_shared<AnyValue>(proto);
239
85
  }
240
86
  else if (is_function())
241
87
  {
242
- storage.function->proto = std::make_shared<AnyValue>(proto);
88
+ std::get<std::shared_ptr<JsFunction>>(storage)->proto = std::make_shared<AnyValue>(proto);
89
+ }
90
+ else if (is_uninitialized())
91
+ {
92
+ Exception::throw_uninitialized_reference("#<Object>");
93
+ }
94
+ }
95
+
96
+ // AnyValue::call implementation
97
+ const AnyValue AnyValue::call(const AnyValue &thisVal, std::span<const AnyValue> args, const std::optional<std::string> &expr = std::nullopt) const
98
+ {
99
+ if (!is_function())
100
+ {
101
+ throw Exception::make_exception(expr.value_or(to_std_string()) + " is not a function", "TypeError");
102
+ }
103
+ return as_function()->call(thisVal, args); // Convert to function before calling, to avoid an infinite loop
104
+ }
105
+
106
+ // AnyValue::construct implementation
107
+ const AnyValue AnyValue::construct(std::span<const AnyValue> args, const std::optional<std::string> &name) const
108
+ {
109
+ if (!is_function() || !as_function()->is_constructor)
110
+ {
111
+ // std::cerr << "Construct fail: " << name.value_or(to_std_string()) << " is_function=" << is_function() << " is_constructor=" << (is_function() ? as_function()->is_constructor : false) << std::endl;
112
+ throw Exception::make_exception(name.value_or(to_std_string()) + " is not a constructor", "TypeError");
113
+ }
114
+
115
+ // 1. Get prototype
116
+ AnyValue proto = get_own_property("prototype");
117
+ // If prototype is not an object, default to a plain object (which ideally inherits from Object.prototype)
118
+ // Here we just make a plain object.
119
+ if (!proto.is_object())
120
+ {
121
+ proto = AnyValue::make_object({});
122
+ }
123
+
124
+ // 2. Create instance
125
+ AnyValue instance = AnyValue::make_object_with_proto({}, proto);
126
+
127
+ // 3. Call function
128
+ // We pass 'instance' as 'this'
129
+ AnyValue result = call(instance, args);
130
+
131
+ // 4. Return result if object, else instance
132
+ if (result.is_object() || result.is_function() || result.is_array() || result.is_promise())
133
+ {
134
+ return result;
243
135
  }
136
+ return instance;
244
137
  }
245
138
 
246
139
  }