@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
@@ -1,734 +1,658 @@
1
- #pragma once
2
-
3
- #include <cassert>
4
- #include <cstdint>
5
- #include <cstring>
6
- #include <limits>
7
- #include <new>
8
- #include <sstream>
9
- #include <iomanip>
10
- #include <type_traits>
11
- #include <memory>
12
- #include <utility>
13
- #include <string>
14
- #include <unordered_map>
15
- #include <vector>
16
- #include <functional>
17
- #include <cmath>
18
-
19
- #include "types.hpp"
20
- #include "values/non_values.hpp"
21
- #include "values/object.hpp"
22
- #include "values/array.hpp"
23
- #include "values/function.hpp"
24
- #include "error.hpp"
25
- #include "descriptors.hpp"
26
-
27
- namespace jspp
28
- {
29
- enum class JsType : uint8_t
30
- {
31
- Undefined = 0,
32
- Null = 1,
33
- Uninitialized = 2,
34
- Boolean = 3,
35
- Number = 4,
36
- String = 5,
37
- Object = 6,
38
- Array = 7,
39
- Function = 8,
40
- DataDescriptor = 9,
41
- AccessorDescriptor = 10,
42
- };
43
-
44
- // Tagged storage with a union for payload
45
- struct TaggedValue
46
- {
47
- JsType type;
48
- union
49
- {
50
- JsUndefined undefined;
51
- JsNull null;
52
- JsUninitialized uninitialized;
53
- bool boolean;
54
- double number;
55
- std::unique_ptr<std::string> str;
56
- std::shared_ptr<JsObject> object;
57
- std::shared_ptr<JsArray> array;
58
- std::shared_ptr<JsFunction> function;
59
- std::shared_ptr<DataDescriptor> data_desc;
60
- std::shared_ptr<AccessorDescriptor> accessor_desc;
61
- };
62
-
63
- TaggedValue() noexcept : type(JsType::Undefined), undefined{} {}
64
- ~TaggedValue() {}
65
- };
66
-
67
- class AnyValue
68
- {
69
- private:
70
- TaggedValue storage;
71
-
72
- void destroy_value() noexcept
73
- {
74
- switch (storage.type)
75
- {
76
- case JsType::String:
77
- storage.str.~unique_ptr();
78
- break;
79
- case JsType::Object:
80
- storage.object.~shared_ptr();
81
- break;
82
- case JsType::Array:
83
- storage.array.~shared_ptr();
84
- break;
85
- case JsType::Function:
86
- storage.function.~shared_ptr();
87
- break;
88
- case JsType::DataDescriptor:
89
- storage.data_desc.~shared_ptr();
90
- break;
91
- case JsType::AccessorDescriptor:
92
- storage.accessor_desc.~shared_ptr();
93
- break;
94
- default:
95
- break;
96
- }
97
- }
98
-
99
- void reset_to_undefined() noexcept
100
- {
101
- destroy_value();
102
- storage.type = JsType::Undefined;
103
- storage.undefined = JsUndefined{};
104
- }
105
-
106
- void move_from(AnyValue &other) noexcept
107
- {
108
- storage.type = other.storage.type;
109
- switch (other.storage.type)
110
- {
111
- case JsType::Undefined:
112
- storage.undefined = JsUndefined{};
113
- break;
114
- case JsType::Null:
115
- storage.null = JsNull{};
116
- break;
117
- case JsType::Uninitialized:
118
- storage.uninitialized = JsUninitialized{};
119
- break;
120
- case JsType::Boolean:
121
- storage.boolean = other.storage.boolean;
122
- break;
123
- case JsType::Number:
124
- storage.number = other.storage.number;
125
- break;
126
- case JsType::String:
127
- new (&storage.str) std::unique_ptr<std::string>(std::move(other.storage.str));
128
- break;
129
- case JsType::Object:
130
- new (&storage.object) std::shared_ptr<JsObject>(std::move(other.storage.object));
131
- break;
132
- case JsType::Array:
133
- new (&storage.array) std::shared_ptr<JsArray>(std::move(other.storage.array));
134
- break;
135
- case JsType::Function:
136
- new (&storage.function) std::shared_ptr<JsFunction>(std::move(other.storage.function));
137
- break;
138
- case JsType::DataDescriptor:
139
- new (&storage.data_desc) std::shared_ptr<DataDescriptor>(std::move(other.storage.data_desc));
140
- break;
141
- case JsType::AccessorDescriptor:
142
- new (&storage.accessor_desc) std::shared_ptr<AccessorDescriptor>(std::move(other.storage.accessor_desc));
143
- break;
144
- }
145
- }
146
-
147
- void copy_from(const AnyValue &other)
148
- {
149
- storage.type = other.storage.type;
150
- switch (other.storage.type)
151
- {
152
- case JsType::Undefined:
153
- storage.undefined = JsUndefined{};
154
- break;
155
- case JsType::Null:
156
- storage.null = JsNull{};
157
- break;
158
- case JsType::Uninitialized:
159
- storage.uninitialized = JsUninitialized{};
160
- break;
161
- case JsType::Boolean:
162
- storage.boolean = other.storage.boolean;
163
- break;
164
- case JsType::Number:
165
- storage.number = other.storage.number;
166
- break;
167
- case JsType::String:
168
- new (&storage.str) std::unique_ptr<std::string>(std::make_unique<std::string>(*other.storage.str));
169
- break;
170
- case JsType::Object:
171
- new (&storage.object) std::shared_ptr<JsObject>(other.storage.object); // shallow copy
172
- break;
173
- case JsType::Array:
174
- new (&storage.array) std::shared_ptr<JsArray>(other.storage.array); // shallow copy
175
- break;
176
- case JsType::Function:
177
- new (&storage.function) std::shared_ptr<JsFunction>(other.storage.function); // shallow copy
178
- break;
179
- case JsType::DataDescriptor:
180
- new (&storage.data_desc) std::shared_ptr<DataDescriptor>(other.storage.data_desc); // shallow copy
181
- break;
182
- case JsType::AccessorDescriptor:
183
- new (&storage.accessor_desc) std::shared_ptr<AccessorDescriptor>(other.storage.accessor_desc); // shallow copy
184
- break;
185
- }
186
- }
187
-
188
- public:
189
- // default ctor (Undefined)
190
- AnyValue() noexcept
191
- {
192
- storage.type = JsType::Undefined;
193
- storage.undefined = JsUndefined{};
194
- }
195
-
196
- // 1. Destructor
197
- ~AnyValue() noexcept
198
- {
199
- destroy_value();
200
- }
201
-
202
- // 2. Copy Constructor (deep copy)
203
- AnyValue(const AnyValue &other)
204
- {
205
- copy_from(other);
206
- }
207
-
208
- // 3. Copy Assignment Operator
209
- AnyValue &operator=(const AnyValue &other)
210
- {
211
- if (this != &other)
212
- {
213
- destroy_value();
214
- copy_from(other);
215
- }
216
- return *this;
217
- }
218
-
219
- // 4. Move Constructor
220
- AnyValue(AnyValue &&other) noexcept
221
- {
222
- storage.type = JsType::Undefined;
223
- storage.undefined = JsUndefined{};
224
- move_from(other);
225
- other.reset_to_undefined();
226
- }
227
-
228
- // 5. Move Assignment Operator
229
- AnyValue &operator=(AnyValue &&other) noexcept
230
- {
231
- if (this != &other)
232
- {
233
- destroy_value();
234
- move_from(other);
235
- other.reset_to_undefined();
236
- }
237
- return *this;
238
- }
239
-
240
- friend void swap(AnyValue &a, AnyValue &b) noexcept
241
- {
242
- AnyValue tmp(std::move(a));
243
- a = std::move(b);
244
- b = std::move(tmp);
245
- }
246
-
247
- // factories -------------------------------------------------------
248
- static AnyValue make_number(double d) noexcept
249
- {
250
- AnyValue v;
251
- v.storage.type = JsType::Number;
252
- v.storage.number = d;
253
- return v;
254
- }
255
- static AnyValue make_nan() noexcept
256
- {
257
- AnyValue v;
258
- v.storage.type = JsType::Number;
259
- v.storage.number = std::numeric_limits<double>::quiet_NaN();
260
- return v;
261
- }
262
- static AnyValue make_uninitialized() noexcept
263
- {
264
- AnyValue v;
265
- v.storage.type = JsType::Uninitialized;
266
- v.storage.uninitialized = JsUninitialized{};
267
- return v;
268
- }
269
- static AnyValue make_undefined() noexcept
270
- {
271
- AnyValue v;
272
- v.storage.type = JsType::Undefined;
273
- v.storage.undefined = JsUndefined{};
274
- return v;
275
- }
276
- static AnyValue make_null() noexcept
277
- {
278
- AnyValue v;
279
- v.storage.type = JsType::Null;
280
- v.storage.null = JsNull{};
281
- return v;
282
- }
283
- static AnyValue make_boolean(bool b) noexcept
284
- {
285
- AnyValue v;
286
- v.storage.type = JsType::Boolean;
287
- v.storage.boolean = b;
288
- return v;
289
- }
290
- static AnyValue make_string(const std::string &raw_s) noexcept
291
- {
292
- AnyValue v;
293
- v.storage.type = JsType::String;
294
- new (&v.storage.str) std::unique_ptr<std::string>(std::make_unique<std::string>(raw_s));
295
- return v;
296
- }
297
- static AnyValue make_object(const std::unordered_map<std::string, AnyValue> &props) noexcept
298
- {
299
- AnyValue v;
300
- v.storage.type = JsType::Object;
301
- new (&v.storage.object) std::shared_ptr<JsObject>(std::make_shared<JsObject>(props));
302
- return v;
303
- }
304
- static AnyValue make_array(const std::vector<std::optional<AnyValue>> &dense) noexcept
305
- {
306
- AnyValue v;
307
- v.storage.type = JsType::Array;
308
- new (&v.storage.array) std::shared_ptr<JsArray>(std::make_shared<JsArray>(dense));
309
- return v;
310
- }
311
- static AnyValue make_function(const std::function<AnyValue(const std::vector<AnyValue> &)> &call, const std::string &name) noexcept
312
- {
313
- AnyValue v;
314
- v.storage.type = JsType::Function;
315
- new (&v.storage.function) std::shared_ptr<JsFunction>(std::make_shared<JsFunction>(call, name));
316
- return v;
317
- }
318
- static AnyValue make_data_descriptor(const AnyValue &value, bool writable, bool enumerable, bool configurable) noexcept
319
- {
320
- AnyValue v;
321
- v.storage.type = JsType::DataDescriptor;
322
- new (&v.storage.data_desc) std::shared_ptr<DataDescriptor>(std::make_shared<DataDescriptor>(std::make_shared<AnyValue>(value), writable, enumerable, configurable));
323
- return v;
324
- }
325
- static AnyValue make_accessor_descriptor(const std::optional<std::function<AnyValue(const std::vector<AnyValue> &)>> &get,
326
- const std::optional<std::function<AnyValue(const std::vector<AnyValue> &)>> &set,
327
- bool enumerable,
328
- bool configurable) noexcept
329
- {
330
- AnyValue v;
331
- v.storage.type = JsType::AccessorDescriptor;
332
- new (&v.storage.accessor_desc) std::shared_ptr<AccessorDescriptor>(std::make_shared<AccessorDescriptor>(get, set, enumerable, configurable));
333
- return v;
334
- }
335
-
336
- // property resolution helpers ---------------------------------------
337
- static AnyValue resolve_property_for_read(const AnyValue &val) noexcept
338
- {
339
- switch (val.storage.type)
340
- {
341
- case JsType::DataDescriptor:
342
- {
343
- return *(val.storage.data_desc->value);
344
- }
345
- case JsType::AccessorDescriptor:
346
- {
347
- if (val.storage.accessor_desc->get.has_value())
348
- return val.storage.accessor_desc->get.value()({});
349
- else
350
- {
351
- static AnyValue undefined = AnyValue{};
352
- return undefined;
353
- }
354
- }
355
- default:
356
- {
357
- return val;
358
- }
359
- }
360
- }
361
- static AnyValue resolve_property_for_write(AnyValue &val, const AnyValue &new_val)
362
- {
363
- switch (val.storage.type)
364
- {
365
- case JsType::DataDescriptor:
366
- {
367
- *(val.storage.data_desc->value) = new_val;
368
- return new_val;
369
- }
370
- case JsType::AccessorDescriptor:
371
- {
372
- if (val.storage.accessor_desc->set.has_value())
373
- {
374
- val.storage.accessor_desc->set.value()({new_val});
375
- return new_val;
376
- }
377
- else
378
- {
379
- throw RuntimeError::make_error("Cannot set property of #<Object> which has only a getter", "TypeError");
380
- }
381
- }
382
- default:
383
- {
384
- val = new_val;
385
- return new_val;
386
- }
387
- }
388
- }
389
-
390
- // type checkers and accessors ---------------------------------------
391
- bool is_number() const noexcept { return storage.type == JsType::Number; }
392
- bool is_string() const noexcept { return storage.type == JsType::String; }
393
- bool is_object() const noexcept { return storage.type == JsType::Object; }
394
- bool is_array() const noexcept { return storage.type == JsType::Array; }
395
- bool is_function() const noexcept { return storage.type == JsType::Function; }
396
- bool is_boolean() const noexcept { return storage.type == JsType::Boolean; }
397
- bool is_null() const noexcept { return storage.type == JsType::Null; }
398
- bool is_undefined() const noexcept { return storage.type == JsType::Undefined; }
399
- bool is_uninitialized() const noexcept { return storage.type == JsType::Uninitialized; }
400
- bool is_data_descriptor() const noexcept { return storage.type == JsType::DataDescriptor; }
401
- bool is_accessor_descriptor() const noexcept { return storage.type == JsType::AccessorDescriptor; }
402
-
403
- // --- TYPE CASTERS
404
- double as_double() const noexcept
405
- {
406
- assert(is_number());
407
- return storage.number;
408
- }
409
- bool as_boolean() const noexcept
410
- {
411
- assert(is_boolean());
412
- return storage.boolean;
413
- }
414
- std::string *as_string() const noexcept
415
- {
416
- assert(is_string());
417
- return storage.str.get();
418
- }
419
- JsObject *as_object() const noexcept
420
- {
421
- assert(is_object());
422
- return storage.object.get();
423
- }
424
- JsArray *as_array() const noexcept
425
- {
426
- assert(is_array());
427
- return storage.array.get();
428
- }
429
- JsFunction *as_function(const std::string &expression = "") const
430
- {
431
- if (is_function())
432
- return storage.function.get();
433
- throw RuntimeError::make_error(expression + " is not a function", "TypeError");
434
- }
435
- DataDescriptor *as_data_descriptor() const noexcept
436
- {
437
- assert(is_data_descriptor());
438
- return storage.data_desc.get();
439
- }
440
- AccessorDescriptor *as_accessor_descriptor() const noexcept
441
- {
442
- assert(is_accessor_descriptor());
443
- return storage.accessor_desc.get();
444
- }
445
-
446
- // --- PROPERTY ACCESS OPERATORS
447
- AnyValue get_own_property(const std::string &key)
448
- {
449
- switch (storage.type)
450
- {
451
- case JsType::Object:
452
- return as_object()->get_property(key);
453
- case JsType::Array:
454
- return as_array()->get_property(key);
455
- case JsType::Function:
456
- return as_function()->get_property(key);
457
- case JsType::String:
458
- {
459
- // Check for prototype methods
460
- auto proto_fn = StringPrototypes::get(key, this->storage.str);
461
- if (proto_fn.has_value())
462
- {
463
- return resolve_property_for_read(proto_fn.value());
464
- }
465
- // Handle character access by string index (e.g., "abc"["1"])
466
- if (JsArray::is_array_index(key))
467
- {
468
- uint32_t idx = static_cast<uint32_t>(std::stoull(key));
469
- return get_own_property(idx);
470
- }
471
- }
472
- case JsType::Undefined:
473
- throw RuntimeError::make_error("Cannot read properties of undefined (reading '" + key + "')", "TypeError");
474
- case JsType::Null:
475
- throw RuntimeError::make_error("Cannot read properties of null (reading '" + key + "')", "TypeError");
476
- default:
477
- return AnyValue::make_undefined();
478
- }
479
- }
480
- AnyValue get_own_property(uint32_t idx) noexcept
481
- {
482
- switch (storage.type)
483
- {
484
- case JsType::Array:
485
- return as_array()->get_property(idx);
486
- case JsType::String: // Handle character access by index (e.g., "abc"[1])
487
- {
488
- if (idx < storage.str->length())
489
- {
490
- return AnyValue::make_string(std::string(1, (*storage.str)[idx]));
491
- }
492
- return AnyValue::make_undefined();
493
- }
494
- default:
495
- return get_own_property(std::to_string(idx));
496
- }
497
- }
498
- AnyValue get_own_property(const AnyValue &key) noexcept
499
- {
500
- if (key.storage.type == JsType::Number && storage.type == JsType::Array)
501
- return storage.array->get_property(key.storage.number);
502
- return get_own_property(key.to_std_string());
503
- }
504
- // for setting values
505
- AnyValue set_own_property(const std::string &key, const AnyValue &value)
506
- {
507
- switch (storage.type)
508
- {
509
- case JsType::Object:
510
- return as_object()->set_property(key, value);
511
- case JsType::Array:
512
- return as_array()->set_property(key, value);
513
- case JsType::Function:
514
- return as_function()->set_property(key, value);
515
- case JsType::Undefined:
516
- throw RuntimeError::make_error("Cannot set properties of undefined (setting '" + key + "')", "TypeError");
517
- case JsType::Null:
518
- throw RuntimeError::make_error("Cannot set properties of null (setting '" + key + "')", "TypeError");
519
- default:
520
- return value;
521
- }
522
- }
523
- AnyValue set_own_property(uint32_t idx, const AnyValue &value)
524
- {
525
- if (storage.type == JsType::Array)
526
- {
527
- return as_array()->set_property(idx, value);
528
- }
529
- return set_own_property(std::to_string(idx), value);
530
- }
531
- AnyValue set_own_property(const AnyValue &key, const AnyValue &value)
532
- {
533
- if (key.storage.type == JsType::Number && storage.type == JsType::Array)
534
- {
535
- return storage.array->set_property(key.storage.number, value);
536
- }
537
- return set_own_property(key.to_std_string(), value);
538
- }
539
-
540
- // --- HELPERS
541
- const bool is_truthy() const noexcept
542
- {
543
- switch (storage.type)
544
- {
545
- case JsType::Boolean:
546
- return storage.boolean;
547
- case JsType::Number:
548
- return storage.number != 0.0;
549
- case JsType::String:
550
- return !storage.str->empty();
551
- default:
552
- return true;
553
- }
554
- }
555
- const bool is_strictly_equal_to(const AnyValue &other) const noexcept
556
- {
557
- if (storage.type == other.storage.type)
558
- {
559
- switch (storage.type)
560
- {
561
- case JsType::Boolean:
562
- return storage.boolean == other.storage.boolean;
563
- case JsType::Number:
564
- return storage.number == other.storage.number;
565
- case JsType::String:
566
- return (*storage.str.get() == *other.storage.str.get());
567
- case JsType::Array:
568
- return (storage.array == other.storage.array);
569
- case JsType::Object:
570
- return (storage.object == other.storage.object);
571
- case JsType::Function:
572
- return (storage.function == other.storage.function);
573
- default:
574
- return true;
575
- }
576
- }
577
- return false;
578
- }
579
- const bool is_equal_to(const AnyValue &other) const noexcept
580
- {
581
- // Implements JavaScript's Abstract Equality Comparison Algorithm (==)
582
- // Step 1: If types are the same, use strict equality (===)
583
- if (storage.type == other.storage.type)
584
- {
585
- return is_strictly_equal_to(other);
586
- }
587
- // Steps 2 & 3: null == undefined
588
- if ((is_null() && other.is_undefined()) || (is_undefined() && other.is_null()))
589
- {
590
- return true;
591
- }
592
- // Step 4 & 5: number == string
593
- if (is_number() && other.is_string())
594
- {
595
- double num_this = this->as_double();
596
- double num_other;
597
- try
598
- {
599
- const std::string &s = *other.as_string();
600
- // JS considers empty string or whitespace-only string to be 0
601
- if (s.empty() || std::all_of(s.begin(), s.end(), [](unsigned char c)
602
- { return std::isspace(c); }))
603
- {
604
- num_other = 0.0;
605
- }
606
- else
607
- {
608
- size_t pos;
609
- num_other = std::stod(s, &pos);
610
- // Check if the entire string was consumed, allowing for trailing whitespace
611
- while (pos < s.length() && std::isspace(static_cast<unsigned char>(s[pos])))
612
- {
613
- pos++;
614
- }
615
- if (pos != s.length())
616
- {
617
- num_other = std::numeric_limits<double>::quiet_NaN();
618
- }
619
- }
620
- }
621
- catch (...)
622
- {
623
- num_other = std::numeric_limits<double>::quiet_NaN();
624
- }
625
- return num_this == num_other;
626
- }
627
- if (is_string() && other.is_number())
628
- {
629
- // Delegate to the other operand to avoid code duplication
630
- return other.is_equal_to(*this);
631
- }
632
- // Step 6 & 7: boolean == any
633
- if (is_boolean())
634
- {
635
- // Convert boolean to number and re-compare
636
- return AnyValue::make_number(as_boolean() ? 1.0 : 0.0).is_equal_to(other);
637
- }
638
- if (other.is_boolean())
639
- {
640
- // Convert boolean to number and re-compare
641
- return is_equal_to(AnyValue::make_number(other.as_boolean() ? 1.0 : 0.0));
642
- }
643
- // Step 8 & 9: object == (string or number)
644
- if ((other.is_object() || other.is_array() || other.is_function()) && (is_string() || is_number()))
645
- {
646
- // Delegate to the other operand to avoid code duplication
647
- return other.is_equal_to(*this);
648
- }
649
- if ((is_object() || is_array() || is_function()) && (other.is_string() || other.is_number()))
650
- {
651
- // Convert object to primitive (string) and re-compare.
652
- // This is a simplification of JS's ToPrimitive which would try valueOf() first.
653
- return AnyValue::make_string(to_std_string()).is_equal_to(other);
654
- }
655
- // Step 10: All other cases (e.g., object == null) are false.
656
- return false;
657
- }
658
- const AnyValue is_strictly_equal_to_primitive(const AnyValue &other) const noexcept
659
- {
660
- return AnyValue::make_boolean(is_strictly_equal_to(other));
661
- }
662
- const AnyValue is_equal_to_primitive(const AnyValue &other) const noexcept
663
- {
664
- return AnyValue::make_boolean(is_equal_to(other));
665
- }
666
- const AnyValue not_strictly_equal_to_primitive(const AnyValue &other) const noexcept
667
- {
668
- return AnyValue::make_boolean(!is_strictly_equal_to(other));
669
- }
670
- const AnyValue not_equal_to_primitive(const AnyValue &other) const noexcept
671
- {
672
- return AnyValue::make_boolean(!is_equal_to(other));
673
- }
674
- const std::string to_std_string() const noexcept
675
- {
676
- switch (storage.type)
677
- {
678
- case JsType::Undefined:
679
- return "undefined";
680
- case JsType::Null:
681
- return "null";
682
- case JsType::Boolean:
683
- return storage.boolean ? "true" : "false";
684
- case JsType::String:
685
- return *storage.str.get();
686
- case JsType::Object:
687
- return storage.object->to_std_string();
688
- case JsType::Array:
689
- return storage.array->to_std_string();
690
- case JsType::Function:
691
- return storage.function->to_std_string();
692
- case JsType::DataDescriptor:
693
- return storage.data_desc->value->to_std_string();
694
- case JsType::AccessorDescriptor:
695
- {
696
- if (storage.accessor_desc->get.has_value())
697
- return storage.accessor_desc->get.value()({}).to_std_string();
698
- else
699
- return "undefined";
700
- }
701
- case JsType::Number:
702
- {
703
- if (std::isnan(storage.number))
704
- {
705
- return "NaN";
706
- }
707
- if (std::abs(storage.number) >= 1e21 || (std::abs(storage.number) > 0 && std::abs(storage.number) < 1e-6))
708
- {
709
- std::ostringstream oss;
710
- oss << std::scientific << std::setprecision(4) << storage.number;
711
- return oss.str();
712
- }
713
- else
714
- {
715
- std::ostringstream oss;
716
- oss << std::setprecision(6) << std::fixed << storage.number;
717
- std::string s = oss.str();
718
- s.erase(s.find_last_not_of('0') + 1, std::string::npos);
719
- if (!s.empty() && s.back() == '.')
720
- {
721
- s.pop_back();
722
- }
723
- return s;
724
- }
725
- }
726
- // Uninitialized and default should not be reached under normal circumstances
727
- case JsType::Uninitialized:
728
- return "<uninitialized>";
729
- default:
730
- return "";
731
- }
732
- }
733
- };
734
- }
1
+ #pragma once
2
+
3
+ #include <cassert>
4
+ #include <cstdint>
5
+ #include <cstring>
6
+ #include <limits>
7
+ #include <new>
8
+ #include <sstream>
9
+ #include <iomanip>
10
+ #include <type_traits>
11
+ #include <memory>
12
+ #include <utility>
13
+ #include <string>
14
+ #include <map>
15
+ #include <vector>
16
+ #include <functional>
17
+ #include <cmath>
18
+ #include <optional>
19
+ #include <coroutine>
20
+
21
+ #include "types.hpp"
22
+ #include "values/non_values.hpp"
23
+ #include "values/object.hpp"
24
+ #include "values/array.hpp"
25
+ #include "values/function.hpp"
26
+ #include "values/iterator.hpp"
27
+ #include "values/promise.hpp"
28
+ #include "values/symbol.hpp"
29
+ #include "values/string.hpp"
30
+ #include "exception.hpp"
31
+ #include "values/descriptors.hpp"
32
+ #include "utils/well_known_symbols.hpp"
33
+
34
+ namespace jspp
35
+ {
36
+ enum class JsType : uint8_t
37
+ {
38
+ Undefined = 0,
39
+ Null = 1,
40
+ Uninitialized = 2,
41
+ Boolean = 3,
42
+ Number = 4,
43
+ String = 5,
44
+ Object = 6,
45
+ Array = 7,
46
+ Function = 8,
47
+ Iterator = 9,
48
+ Symbol = 10,
49
+ Promise = 11,
50
+ DataDescriptor = 12,
51
+ AccessorDescriptor = 13,
52
+ };
53
+
54
+ // Tagged storage with a union for payload
55
+ struct TaggedValue
56
+ {
57
+ JsType type;
58
+ union
59
+ {
60
+ JsUndefined undefined;
61
+ JsNull null;
62
+ JsUninitialized uninitialized;
63
+ bool boolean;
64
+ double number;
65
+ std::shared_ptr<JsString> str;
66
+ std::shared_ptr<JsObject> object;
67
+ std::shared_ptr<JsArray> array;
68
+ std::shared_ptr<JsFunction> function;
69
+ std::shared_ptr<JsIterator<AnyValue>> iterator;
70
+ std::shared_ptr<JsSymbol> symbol;
71
+ std::shared_ptr<JsPromise> promise;
72
+ std::shared_ptr<DataDescriptor> data_desc;
73
+ std::shared_ptr<AccessorDescriptor> accessor_desc;
74
+ };
75
+
76
+ TaggedValue() noexcept : type(JsType::Undefined), undefined{} {}
77
+ ~TaggedValue() {}
78
+ };
79
+
80
+ class AnyValue
81
+ {
82
+ private:
83
+ TaggedValue storage;
84
+
85
+ void destroy_value() noexcept
86
+ {
87
+ switch (storage.type)
88
+ {
89
+ case JsType::String:
90
+ storage.str.~shared_ptr();
91
+ break;
92
+ case JsType::Object:
93
+ storage.object.~shared_ptr();
94
+ break;
95
+ case JsType::Array:
96
+ storage.array.~shared_ptr();
97
+ break;
98
+ case JsType::Function:
99
+ storage.function.~shared_ptr();
100
+ break;
101
+ case JsType::Iterator:
102
+ storage.iterator.~shared_ptr();
103
+ break;
104
+ case JsType::Symbol:
105
+ storage.symbol.~shared_ptr();
106
+ break;
107
+ case JsType::Promise:
108
+ storage.promise.~shared_ptr();
109
+ break;
110
+ case JsType::DataDescriptor:
111
+ storage.data_desc.~shared_ptr();
112
+ break;
113
+ case JsType::AccessorDescriptor:
114
+ storage.accessor_desc.~shared_ptr();
115
+ break;
116
+ default:
117
+ break;
118
+ }
119
+ }
120
+
121
+ void reset_to_undefined() noexcept
122
+ {
123
+ destroy_value();
124
+ storage.type = JsType::Undefined;
125
+ storage.undefined = JsUndefined{};
126
+ }
127
+
128
+ void move_from(AnyValue &other) noexcept
129
+ {
130
+ storage.type = other.storage.type;
131
+ switch (other.storage.type)
132
+ {
133
+ case JsType::Undefined:
134
+ storage.undefined = JsUndefined{};
135
+ break;
136
+ case JsType::Null:
137
+ storage.null = JsNull{};
138
+ break;
139
+ case JsType::Uninitialized:
140
+ storage.uninitialized = JsUninitialized{};
141
+ break;
142
+ case JsType::Boolean:
143
+ storage.boolean = other.storage.boolean;
144
+ break;
145
+ case JsType::Number:
146
+ storage.number = other.storage.number;
147
+ break;
148
+ case JsType::String:
149
+ new (&storage.str) std::shared_ptr<JsString>(std::move(other.storage.str));
150
+ break;
151
+ case JsType::Object:
152
+ new (&storage.object) std::shared_ptr<JsObject>(std::move(other.storage.object));
153
+ break;
154
+ case JsType::Array:
155
+ new (&storage.array) std::shared_ptr<JsArray>(std::move(other.storage.array));
156
+ break;
157
+ case JsType::Function:
158
+ new (&storage.function) std::shared_ptr<JsFunction>(std::move(other.storage.function));
159
+ break;
160
+ case JsType::Iterator:
161
+ new (&storage.iterator) std::shared_ptr<JsIterator<AnyValue>>(std::move(other.storage.iterator));
162
+ break;
163
+ case JsType::Symbol:
164
+ new (&storage.symbol) std::shared_ptr<JsSymbol>(std::move(other.storage.symbol));
165
+ break;
166
+ case JsType::Promise:
167
+ new (&storage.promise) std::shared_ptr<JsPromise>(std::move(other.storage.promise));
168
+ break;
169
+ case JsType::DataDescriptor:
170
+ new (&storage.data_desc) std::shared_ptr<DataDescriptor>(std::move(other.storage.data_desc));
171
+ break;
172
+ case JsType::AccessorDescriptor:
173
+ new (&storage.accessor_desc) std::shared_ptr<AccessorDescriptor>(std::move(other.storage.accessor_desc));
174
+ break;
175
+ }
176
+ }
177
+
178
+ void copy_from(const AnyValue &other)
179
+ {
180
+ storage.type = other.storage.type;
181
+ switch (other.storage.type)
182
+ {
183
+ case JsType::Undefined:
184
+ storage.undefined = JsUndefined{};
185
+ break;
186
+ case JsType::Null:
187
+ storage.null = JsNull{};
188
+ break;
189
+ case JsType::Uninitialized:
190
+ storage.uninitialized = JsUninitialized{};
191
+ break;
192
+ case JsType::Boolean:
193
+ storage.boolean = other.storage.boolean;
194
+ break;
195
+ case JsType::Number:
196
+ storage.number = other.storage.number;
197
+ break;
198
+ case JsType::String:
199
+ new (&storage.str) std::shared_ptr<JsString>(std::make_shared<JsString>(*other.storage.str));
200
+ break;
201
+ case JsType::Object:
202
+ new (&storage.object) std::shared_ptr<JsObject>(other.storage.object); // shallow copy
203
+ break;
204
+ case JsType::Array:
205
+ new (&storage.array) std::shared_ptr<JsArray>(other.storage.array); // shallow copy
206
+ break;
207
+ case JsType::Function:
208
+ new (&storage.function) std::shared_ptr<JsFunction>(other.storage.function); // shallow copy
209
+ break;
210
+ case JsType::Iterator:
211
+ new (&storage.iterator) std::shared_ptr<JsIterator<AnyValue>>(other.storage.iterator); // shallow copy
212
+ break;
213
+ case JsType::Symbol:
214
+ new (&storage.symbol) std::shared_ptr<JsSymbol>(other.storage.symbol); // shallow copy (shared)
215
+ break;
216
+ case JsType::Promise:
217
+ new (&storage.promise) std::shared_ptr<JsPromise>(other.storage.promise); // shallow copy
218
+ break;
219
+ case JsType::DataDescriptor:
220
+ new (&storage.data_desc) std::shared_ptr<DataDescriptor>(other.storage.data_desc); // shallow copy
221
+ break;
222
+ case JsType::AccessorDescriptor:
223
+ new (&storage.accessor_desc) std::shared_ptr<AccessorDescriptor>(other.storage.accessor_desc); // shallow copy
224
+ break;
225
+ }
226
+ }
227
+
228
+ public:
229
+ // default ctor (Undefined)
230
+ AnyValue() noexcept
231
+ {
232
+ storage.type = JsType::Undefined;
233
+ storage.undefined = JsUndefined{};
234
+ }
235
+
236
+ // 1. Destructor
237
+ ~AnyValue() noexcept
238
+ {
239
+ destroy_value();
240
+ }
241
+
242
+ // 2. Copy Constructor (deep copy)
243
+ AnyValue(const AnyValue &other)
244
+ {
245
+ copy_from(other);
246
+ }
247
+
248
+ // 3. Copy Assignment Operator
249
+ AnyValue &operator=(const AnyValue &other)
250
+ {
251
+ if (this != &other)
252
+ {
253
+ destroy_value();
254
+ copy_from(other);
255
+ }
256
+ return *this;
257
+ }
258
+
259
+ // 4. Move Constructor
260
+ AnyValue(AnyValue &&other) noexcept
261
+ {
262
+ storage.type = JsType::Undefined;
263
+ storage.undefined = JsUndefined{};
264
+ move_from(other);
265
+ other.reset_to_undefined();
266
+ }
267
+
268
+ // 5. Move Assignment Operator
269
+ AnyValue &operator=(AnyValue &&other) noexcept
270
+ {
271
+ if (this != &other)
272
+ {
273
+ destroy_value();
274
+ move_from(other);
275
+ other.reset_to_undefined();
276
+ }
277
+ return *this;
278
+ }
279
+
280
+ friend void swap(AnyValue &a, AnyValue &b) noexcept
281
+ {
282
+ AnyValue tmp(std::move(a));
283
+ a = std::move(b);
284
+ b = std::move(tmp);
285
+ }
286
+
287
+ // factories -------------------------------------------------------
288
+ static AnyValue make_number(double d) noexcept
289
+ {
290
+ AnyValue v;
291
+ v.storage.type = JsType::Number;
292
+ v.storage.number = d;
293
+ return v;
294
+ }
295
+ static AnyValue make_nan() noexcept
296
+ {
297
+ AnyValue v;
298
+ v.storage.type = JsType::Number;
299
+ v.storage.number = std::numeric_limits<double>::quiet_NaN();
300
+ return v;
301
+ }
302
+ static AnyValue make_uninitialized() noexcept
303
+ {
304
+ AnyValue v;
305
+ v.storage.type = JsType::Uninitialized;
306
+ v.storage.uninitialized = JsUninitialized{};
307
+ return v;
308
+ }
309
+ static AnyValue make_undefined() noexcept
310
+ {
311
+ AnyValue v;
312
+ v.storage.type = JsType::Undefined;
313
+ v.storage.undefined = JsUndefined{};
314
+ return v;
315
+ }
316
+ static AnyValue make_null() noexcept
317
+ {
318
+ AnyValue v;
319
+ v.storage.type = JsType::Null;
320
+ v.storage.null = JsNull{};
321
+ return v;
322
+ }
323
+ static AnyValue make_boolean(bool b) noexcept
324
+ {
325
+ AnyValue v;
326
+ v.storage.type = JsType::Boolean;
327
+ v.storage.boolean = b;
328
+ return v;
329
+ }
330
+ static AnyValue make_string(const std::string &raw_s) noexcept
331
+ {
332
+ AnyValue v;
333
+ v.storage.type = JsType::String;
334
+ new (&v.storage.str) std::shared_ptr<JsString>(std::make_shared<JsString>(raw_s));
335
+ return v;
336
+ }
337
+ static AnyValue make_object(const std::map<std::string, AnyValue> &props) noexcept
338
+ {
339
+ AnyValue v;
340
+ v.storage.type = JsType::Object;
341
+ new (&v.storage.object) std::shared_ptr<JsObject>(std::make_shared<JsObject>(props));
342
+ return v;
343
+ }
344
+ static AnyValue make_object_with_proto(const std::map<std::string, AnyValue> &props, const AnyValue &proto) noexcept
345
+ {
346
+ AnyValue v;
347
+ v.storage.type = JsType::Object;
348
+ auto protoPtr = std::make_shared<AnyValue>(proto);
349
+ new (&v.storage.object) std::shared_ptr<JsObject>(std::make_shared<JsObject>(props, protoPtr));
350
+ return v;
351
+ }
352
+ static AnyValue make_array(const std::vector<std::optional<AnyValue>> &dense) noexcept
353
+ {
354
+ AnyValue v;
355
+ v.storage.type = JsType::Array;
356
+ new (&v.storage.array) std::shared_ptr<JsArray>(std::make_shared<JsArray>(dense));
357
+ return v;
358
+ }
359
+ static AnyValue make_array(std::vector<std::optional<AnyValue>> &&dense) noexcept
360
+ {
361
+ AnyValue v;
362
+ v.storage.type = JsType::Array;
363
+ new (&v.storage.array) std::shared_ptr<JsArray>(std::make_shared<JsArray>(std::move(dense)));
364
+ return v;
365
+ }
366
+ static AnyValue make_function(const JsFunctionCallable &call, const std::string &name) noexcept
367
+ {
368
+ AnyValue v;
369
+ v.storage.type = JsType::Function;
370
+ new (&v.storage.function) std::shared_ptr<JsFunction>(std::make_shared<JsFunction>(call, name));
371
+
372
+ auto proto = make_object({});
373
+ proto.set_own_property("constructor", AnyValue::make_data_descriptor(v, true, false, false));
374
+ v.set_own_property("prototype", AnyValue::make_data_descriptor(proto, false, false, false));
375
+
376
+ return v;
377
+ }
378
+ static AnyValue make_class(const JsFunctionCallable &call, const std::string &name) noexcept
379
+ {
380
+ AnyValue v;
381
+ v.storage.type = JsType::Function;
382
+ // use Constructor A with is_cls = true
383
+ new (&v.storage.function) std::shared_ptr<JsFunction>(std::make_shared<JsFunction>(call, name, std::unordered_map<std::string, AnyValue>{}, true));
384
+
385
+ auto proto = make_object({});
386
+ proto.set_own_property("constructor", AnyValue::make_data_descriptor(v, true, false, false));
387
+ v.set_own_property("prototype", AnyValue::make_data_descriptor(proto, false, false, false));
388
+
389
+ return v;
390
+ }
391
+ static AnyValue make_generator(const JsFunctionCallable &call, const std::string &name) noexcept
392
+ {
393
+ AnyValue v;
394
+ v.storage.type = JsType::Function;
395
+ // use Constructor B with is_gen = true
396
+ new (&v.storage.function) std::shared_ptr<JsFunction>(std::make_shared<JsFunction>(call, true, name));
397
+
398
+ auto proto = make_object({});
399
+ proto.set_own_property("constructor", AnyValue::make_data_descriptor(v, true, false, false));
400
+ v.set_own_property("prototype", AnyValue::make_data_descriptor(proto, false, false, false));
401
+
402
+ return v;
403
+ }
404
+ static AnyValue make_async_function(const JsFunctionCallable &call, const std::string &name) noexcept
405
+ {
406
+ AnyValue v;
407
+ v.storage.type = JsType::Function;
408
+ // use Constructor C with is_async_func = true
409
+ new (&v.storage.function) std::shared_ptr<JsFunction>(std::make_shared<JsFunction>(call, false, true, name));
410
+
411
+ auto proto = make_object({});
412
+ proto.set_own_property("constructor", AnyValue::make_data_descriptor(v, true, false, false));
413
+ v.set_own_property("prototype", AnyValue::make_data_descriptor(proto, false, false, false));
414
+
415
+ return v;
416
+ }
417
+ static AnyValue make_symbol(const std::string &description = "") noexcept
418
+ {
419
+ AnyValue v;
420
+ v.storage.type = JsType::Symbol;
421
+ new (&v.storage.symbol) std::shared_ptr<JsSymbol>(std::make_shared<JsSymbol>(description));
422
+ return v;
423
+ }
424
+ static AnyValue make_promise(const JsPromise &promise) noexcept
425
+ {
426
+ AnyValue v;
427
+ v.storage.type = JsType::Promise;
428
+ new (&v.storage.promise) std::shared_ptr<JsPromise>(std::make_shared<JsPromise>(promise));
429
+ return v;
430
+ }
431
+ static AnyValue make_data_descriptor(const AnyValue &value, bool writable, bool enumerable, bool configurable) noexcept
432
+ {
433
+ AnyValue v;
434
+ v.storage.type = JsType::DataDescriptor;
435
+ new (&v.storage.data_desc) std::shared_ptr<DataDescriptor>(std::make_shared<DataDescriptor>(std::make_shared<AnyValue>(value), writable, enumerable, configurable));
436
+ return v;
437
+ }
438
+ static AnyValue make_accessor_descriptor(const std::optional<std::function<AnyValue(const AnyValue &, const std::vector<AnyValue> &)>> &get,
439
+ const std::optional<std::function<AnyValue(const AnyValue &, const std::vector<AnyValue> &)>> &set,
440
+ bool enumerable,
441
+ bool configurable) noexcept
442
+ {
443
+ AnyValue v;
444
+ v.storage.type = JsType::AccessorDescriptor;
445
+ new (&v.storage.accessor_desc) std::shared_ptr<AccessorDescriptor>(std::make_shared<AccessorDescriptor>(get, set, enumerable, configurable));
446
+ return v;
447
+ }
448
+
449
+ static AnyValue from_symbol(std::shared_ptr<JsSymbol> sym) noexcept
450
+ {
451
+ AnyValue v;
452
+ v.storage.type = JsType::Symbol;
453
+ new (&v.storage.symbol) std::shared_ptr<JsSymbol>(std::move(sym));
454
+ return v;
455
+ }
456
+ static AnyValue from_string(std::shared_ptr<JsString> str) noexcept
457
+ {
458
+ AnyValue v;
459
+ v.storage.type = JsType::String;
460
+ new (&v.storage.str) std::shared_ptr<JsString>(std::move(str));
461
+ return v;
462
+ }
463
+ static AnyValue from_iterator(JsIterator<AnyValue> &&iterator) noexcept
464
+ {
465
+ AnyValue v;
466
+ v.storage.type = JsType::Iterator;
467
+ new (&v.storage.iterator) std::shared_ptr<JsIterator<AnyValue>>(std::make_shared<JsIterator<AnyValue>>(std::move(iterator)));
468
+ return v;
469
+ }
470
+ static AnyValue from_iterator_ref(JsIterator<AnyValue> *iterator) noexcept
471
+ {
472
+ AnyValue v;
473
+ v.storage.type = JsType::Iterator;
474
+ new (&v.storage.iterator) std::shared_ptr<JsIterator<AnyValue>>(iterator, [](JsIterator<AnyValue> *) {});
475
+ return v;
476
+ }
477
+
478
+ // PROPERTY RESOLUTION HELPERS ---------------------------------------
479
+ static AnyValue resolve_property_for_read(const AnyValue &val, const AnyValue &thisVal, const std::string &propName) noexcept
480
+ {
481
+ switch (val.storage.type)
482
+ {
483
+ case JsType::DataDescriptor:
484
+ {
485
+ return *(val.storage.data_desc->value);
486
+ }
487
+ case JsType::AccessorDescriptor:
488
+ {
489
+ if (val.storage.accessor_desc->get.has_value())
490
+ return val.storage.accessor_desc->get.value()(thisVal, {});
491
+ else
492
+ {
493
+ static AnyValue undefined = AnyValue{};
494
+ return undefined;
495
+ }
496
+ }
497
+ default:
498
+ {
499
+ return val;
500
+ }
501
+ }
502
+ }
503
+ static AnyValue resolve_property_for_write(AnyValue &val, const AnyValue &thisVal, const AnyValue &new_val, const std::string &propName)
504
+ {
505
+ switch (val.storage.type)
506
+ {
507
+ case JsType::DataDescriptor:
508
+ {
509
+ if (val.storage.data_desc->writable)
510
+ {
511
+ *(val.storage.data_desc->value) = new_val;
512
+ return new_val;
513
+ }
514
+ else
515
+ {
516
+ throw Exception::make_exception("Cannot assign to read only property '" + propName + "' of object '#<Object>'", "TypeError");
517
+ }
518
+ }
519
+ case JsType::AccessorDescriptor:
520
+ {
521
+ if (val.storage.accessor_desc->set.has_value())
522
+ {
523
+ val.storage.accessor_desc->set.value()(thisVal, {new_val});
524
+ return new_val;
525
+ }
526
+ else
527
+ {
528
+ throw Exception::make_exception("Cannot set property of #<Object> which has only a getter", "TypeError");
529
+ }
530
+ }
531
+ default:
532
+ {
533
+ val = new_val;
534
+ return new_val;
535
+ }
536
+ }
537
+ }
538
+
539
+ // TYPE CHECKERS AND ACCESSORS ---------------------------------------
540
+ JsType get_type() const noexcept { return storage.type; }
541
+ bool is_number() const noexcept { return storage.type == JsType::Number; }
542
+ bool is_string() const noexcept { return storage.type == JsType::String; }
543
+ bool is_object() const noexcept { return storage.type == JsType::Object; }
544
+ bool is_array() const noexcept { return storage.type == JsType::Array; }
545
+ bool is_function() const noexcept { return storage.type == JsType::Function; }
546
+ bool is_iterator() const noexcept { return storage.type == JsType::Iterator; }
547
+ bool is_boolean() const noexcept { return storage.type == JsType::Boolean; }
548
+ bool is_symbol() const noexcept { return storage.type == JsType::Symbol; }
549
+ bool is_promise() const noexcept { return storage.type == JsType::Promise; }
550
+ bool is_null() const noexcept { return storage.type == JsType::Null; }
551
+ bool is_undefined() const noexcept { return storage.type == JsType::Undefined; }
552
+ bool is_uninitialized() const noexcept { return storage.type == JsType::Uninitialized; }
553
+ bool is_data_descriptor() const noexcept { return storage.type == JsType::DataDescriptor; }
554
+ bool is_accessor_descriptor() const noexcept { return storage.type == JsType::AccessorDescriptor; }
555
+ bool is_generator() const noexcept { return storage.type == JsType::Function && storage.function->is_generator; }
556
+
557
+ // --- TYPE CASTERS
558
+ double as_double() const noexcept
559
+ {
560
+ assert(is_number());
561
+ return storage.number;
562
+ }
563
+ bool as_boolean() const noexcept
564
+ {
565
+ assert(is_boolean());
566
+ return storage.boolean;
567
+ }
568
+ JsString *as_string() const noexcept
569
+ {
570
+ assert(is_string());
571
+ return storage.str.get();
572
+ }
573
+ JsObject *as_object() const noexcept
574
+ {
575
+ assert(is_object());
576
+ return storage.object.get();
577
+ }
578
+ JsArray *as_array() const noexcept
579
+ {
580
+ assert(is_array());
581
+ return storage.array.get();
582
+ }
583
+ JsFunction *as_function(const std::optional<std::string> &expression = std::nullopt) const
584
+ {
585
+ if (is_function())
586
+ return storage.function.get();
587
+ throw Exception::make_exception(expression.value_or(to_std_string()) + " is not a function", "TypeError");
588
+ }
589
+ JsSymbol *as_symbol() const noexcept
590
+ {
591
+ assert(is_symbol());
592
+ return storage.symbol.get();
593
+ }
594
+ JsPromise *as_promise() const noexcept
595
+ {
596
+ assert(is_promise());
597
+ return storage.promise.get();
598
+ }
599
+ std::shared_ptr<JsIterator<AnyValue>> as_iterator() const
600
+ {
601
+ assert(is_iterator());
602
+ return storage.iterator; // Returns the shared_ptr, incrementing ref count
603
+ }
604
+ DataDescriptor *as_data_descriptor() const noexcept
605
+ {
606
+ assert(is_data_descriptor());
607
+ return storage.data_desc.get();
608
+ }
609
+ AccessorDescriptor *as_accessor_descriptor() const noexcept
610
+ {
611
+ assert(is_accessor_descriptor());
612
+ return storage.accessor_desc.get();
613
+ }
614
+
615
+ // --- CO_AWAIT Operator ---
616
+ auto operator co_await() const;
617
+
618
+ // --- PROPERTY ACCESS OPERATORS
619
+ AnyValue get_own_property(const std::string &key) const;
620
+ AnyValue get_own_property(uint32_t idx) const noexcept;
621
+ AnyValue get_own_property(const AnyValue &key) const noexcept;
622
+ // for getting values with a specific receiver (used in inheritance chains)
623
+ AnyValue get_property_with_receiver(const std::string &key, const AnyValue &receiver) const;
624
+ // for setting values
625
+ AnyValue set_own_property(const std::string &key, const AnyValue &value) const;
626
+ AnyValue set_own_property(uint32_t idx, const AnyValue &value) const;
627
+ AnyValue set_own_property(const AnyValue &key, const AnyValue &value) const;
628
+
629
+ // --- DEFINERS (Object.defineProperty semantics)
630
+ void define_data_property(const std::string &key, const AnyValue &value);
631
+ void define_data_property(const AnyValue &key, const AnyValue &value);
632
+ void define_data_property(const std::string &key, const AnyValue &value, bool writable, bool enumerable, bool configurable);
633
+ void define_getter(const std::string &key, const AnyValue &getter);
634
+ void define_getter(const AnyValue &key, const AnyValue &getter);
635
+ void define_setter(const std::string &key, const AnyValue &setter);
636
+ void define_setter(const AnyValue &key, const AnyValue &setter);
637
+
638
+ // --- HELPERS
639
+ const bool is_truthy() const noexcept;
640
+ const bool is_strictly_equal_to_primitive(const AnyValue &other) const noexcept;
641
+ const bool is_equal_to_primitive(const AnyValue &other) const noexcept;
642
+
643
+ const AnyValue is_strictly_equal_to(const AnyValue &other) const noexcept;
644
+ const AnyValue is_equal_to(const AnyValue &other) const noexcept;
645
+ const AnyValue not_strictly_equal_to(const AnyValue &other) const noexcept;
646
+ const AnyValue not_equal_to(const AnyValue &other) const noexcept;
647
+
648
+ const AnyValue construct(const std::vector<AnyValue> &args) const;
649
+ void set_prototype(const AnyValue &proto);
650
+ const std::string to_std_string() const noexcept;
651
+ };
652
+
653
+ // Inline implementation of operator co_await
654
+ inline auto AnyValue::operator co_await() const
655
+ {
656
+ return AnyValueAwaiter{*this};
657
+ }
658
+ }