@ugo-studio/jspp 0.1.3 → 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 (69) hide show
  1. package/README.md +2 -2
  2. package/dist/analysis/scope.js +16 -4
  3. package/dist/analysis/typeAnalyzer.js +253 -20
  4. package/dist/ast/types.js +6 -0
  5. package/dist/cli.js +1 -2
  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 +429 -117
  10. package/dist/core/codegen/function-handlers.js +91 -15
  11. package/dist/core/codegen/helpers.js +66 -2
  12. package/dist/core/codegen/index.js +17 -6
  13. package/dist/core/codegen/literal-handlers.js +3 -0
  14. package/dist/core/codegen/statement-handlers.js +133 -204
  15. package/dist/core/codegen/visitor.js +29 -3
  16. package/package.json +3 -3
  17. package/src/prelude/any_value.hpp +658 -634
  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 -225
  21. package/src/prelude/exception.hpp +31 -0
  22. package/src/prelude/exception_helpers.hpp +49 -0
  23. package/src/prelude/index.hpp +18 -9
  24. package/src/prelude/library/console.hpp +13 -13
  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 +2 -2
  28. package/src/prelude/library/promise.hpp +121 -0
  29. package/src/prelude/library/symbol.hpp +3 -4
  30. package/src/prelude/library/timer.hpp +92 -0
  31. package/src/prelude/scheduler.hpp +145 -0
  32. package/src/prelude/types.hpp +10 -1
  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} +29 -10
  43. package/src/prelude/{well_known_symbols.hpp → utils/well_known_symbols.hpp} +0 -1
  44. package/src/prelude/values/array.hpp +3 -2
  45. package/src/prelude/{descriptors.hpp → values/descriptors.hpp} +2 -2
  46. package/src/prelude/values/function.hpp +76 -51
  47. package/src/prelude/values/helpers/array.hpp +20 -11
  48. package/src/prelude/values/helpers/function.hpp +125 -77
  49. package/src/prelude/values/helpers/iterator.hpp +13 -7
  50. package/src/prelude/values/helpers/object.hpp +36 -6
  51. package/src/prelude/values/helpers/promise.hpp +181 -0
  52. package/src/prelude/values/helpers/string.hpp +3 -3
  53. package/src/prelude/values/helpers/symbol.hpp +2 -2
  54. package/src/prelude/values/iterator.hpp +13 -5
  55. package/src/prelude/values/object.hpp +6 -2
  56. package/src/prelude/values/promise.hpp +73 -0
  57. package/src/prelude/values/prototypes/array.hpp +16 -16
  58. package/src/prelude/values/prototypes/function.hpp +4 -4
  59. package/src/prelude/values/prototypes/iterator.hpp +11 -10
  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 +26 -26
  63. package/src/prelude/values/prototypes/symbol.hpp +5 -3
  64. package/src/prelude/values/string.hpp +1 -1
  65. package/src/prelude/values/symbol.hpp +1 -1
  66. package/src/prelude/access.hpp +0 -91
  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 -407
@@ -5,8 +5,8 @@
5
5
  #include "values/non_values.hpp"
6
6
  #include "values/object.hpp"
7
7
  #include "values/function.hpp"
8
- #include "operators.hpp"
9
- #include "log_string.hpp"
8
+ #include "utils/operators.hpp"
9
+ #include "utils/log_any_value/log_any_value.hpp"
10
10
 
11
11
  #include <cmath>
12
12
  #include <sstream>
@@ -14,39 +14,39 @@
14
14
 
15
15
  static std::map<std::string, std::chrono::steady_clock::time_point> timers = {};
16
16
 
17
- auto logFn = jspp::AnyValue::make_function([](const std::vector<jspp::AnyValue> &args)
17
+ auto logFn = jspp::AnyValue::make_function([](const jspp::AnyValue &thisVal, const std::vector<jspp::AnyValue> &args)
18
18
  {
19
19
  for (size_t i = 0; i < args.size(); ++i)
20
20
  {
21
- std::cout << jspp::LogString::to_log_string(args[i]);
21
+ std::cout << jspp::LogAnyValue::to_log_string(args[i]);
22
22
  if (i < args.size() - 1)
23
23
  std::cout << " ";
24
24
  }
25
- std::cout << "\n";
25
+ std::cout << "\n" << std::flush;
26
26
  return jspp::AnyValue::make_undefined(); }, "log");
27
- auto warnFn = jspp::AnyValue::make_function([](const std::vector<jspp::AnyValue> &args)
27
+ auto warnFn = jspp::AnyValue::make_function([](const jspp::AnyValue &thisVal, const std::vector<jspp::AnyValue> &args)
28
28
  {
29
29
  std::cerr << "\033[33m";
30
30
  for (size_t i = 0; i < args.size(); ++i)
31
31
  {
32
- std::cout << jspp::LogString::to_log_string(args[i]);
32
+ std::cout << jspp::LogAnyValue::to_log_string(args[i]);
33
33
  if (i < args.size() - 1)
34
34
  std::cout << " ";
35
35
  }
36
- std::cerr << "\033[0m" << "\n"; // reset
36
+ std::cerr << "\033[0m" << "\n" << std::flush; // reset
37
37
  return jspp::AnyValue::make_undefined(); }, "warn");
38
- auto errorFn = jspp::AnyValue::make_function([](const std::vector<jspp::AnyValue> &args)
38
+ auto errorFn = jspp::AnyValue::make_function([](const jspp::AnyValue &thisVal, const std::vector<jspp::AnyValue> &args)
39
39
  {
40
40
  std::cerr << "\033[31m";
41
41
  for (size_t i = 0; i < args.size(); ++i)
42
42
  {
43
- std::cout << jspp::LogString::to_log_string(args[i]);
43
+ std::cout << jspp::LogAnyValue::to_log_string(args[i]);
44
44
  if (i < args.size() - 1)
45
45
  std::cout << " ";
46
46
  }
47
- std::cerr << "\033[0m" << "\n"; // reset
47
+ std::cerr << "\033[0m" << "\n" << std::flush; // reset
48
48
  return jspp::AnyValue::make_undefined(); }, "error");
49
- auto timeFn = jspp::AnyValue::make_function([](const std::vector<jspp::AnyValue> &args)
49
+ auto timeFn = jspp::AnyValue::make_function([](const jspp::AnyValue &thisVal, const std::vector<jspp::AnyValue> &args)
50
50
  {
51
51
  auto start = std::chrono::steady_clock::now(); // capture immediately
52
52
  auto key_str = args.size() > 0 ? args[0].to_std_string() : "default";
@@ -83,7 +83,7 @@ static auto format_duration = [](double ms) -> std::string
83
83
  return ss.str();
84
84
  };
85
85
 
86
- auto timeEndFn = jspp::AnyValue::make_function([](const std::vector<jspp::AnyValue> &args)
86
+ auto timeEndFn = jspp::AnyValue::make_function([](const jspp::AnyValue &thisVal, const std::vector<jspp::AnyValue> &args)
87
87
  {
88
88
  auto end = std::chrono::steady_clock::now(); // capture immediately
89
89
  auto key_str = args.size() > 0 ? args[0].to_std_string() : "default";
@@ -0,0 +1,111 @@
1
+ #pragma once
2
+
3
+ #include "types.hpp"
4
+ #include "any_value.hpp"
5
+
6
+ // Declare Error variable
7
+ inline jspp::AnyValue Error;
8
+
9
+ // Constructor logic
10
+ inline auto errorConstructor = [](const jspp::AnyValue &thisVal, const std::vector<jspp::AnyValue> &args) -> jspp::AnyValue
11
+ {
12
+ // Access global Error to get prototype
13
+ jspp::AnyValue proto = Error.get_own_property("prototype");
14
+
15
+ jspp::AnyValue target = thisVal;
16
+ bool is_construct_call = false;
17
+
18
+ if (target.is_object())
19
+ {
20
+ auto obj = target.as_object();
21
+ if (obj->proto && (*obj->proto).is_strictly_equal_to_primitive(proto))
22
+ {
23
+ is_construct_call = true;
24
+ }
25
+ }
26
+
27
+ if (!is_construct_call)
28
+ {
29
+ target = jspp::AnyValue::make_object_with_proto({}, proto);
30
+ }
31
+
32
+ std::string message = "";
33
+ if (!args.empty() && !args[0].is_undefined())
34
+ {
35
+ message = args[0].to_std_string();
36
+ }
37
+
38
+ target.define_data_property("message", jspp::AnyValue::make_string(message), true, false, true);
39
+ target.define_data_property("name", jspp::AnyValue::make_string("Error"), true, false, true);
40
+ target.define_data_property("stack", jspp::AnyValue::make_string("Error: " + message + "\n at <unknown>"), true, false, true);
41
+
42
+ if (args.size() > 1 && args[1].is_object())
43
+ {
44
+ jspp::AnyValue cause = args[1].get_own_property("cause");
45
+ if (!cause.is_undefined())
46
+ {
47
+ target.define_data_property("cause", cause, true, false, true);
48
+ }
49
+ }
50
+
51
+ return target;
52
+ };
53
+
54
+ // Static Error.isError(val) implementation
55
+ inline auto isErrorFn = jspp::AnyValue::make_function([](const jspp::AnyValue &thisVal, const std::vector<jspp::AnyValue> &args) -> jspp::AnyValue
56
+ {
57
+ if (args.empty()) return jspp::AnyValue::make_boolean(false);
58
+
59
+ jspp::AnyValue val = args[0];
60
+ if (!val.is_object()) return jspp::AnyValue::make_boolean(false);
61
+
62
+ // Check if it inherits from Error.prototype
63
+ // This is essentially 'instanceof Error'
64
+ jspp::AnyValue proto = Error.get_own_property("prototype");
65
+
66
+ // Walk prototype chain
67
+ if (val.is_object()) {
68
+ auto current = val.as_object()->proto;
69
+ while (current && !(*current).is_null()) {
70
+ if ((*current).is_strictly_equal_to_primitive(proto)) return jspp::AnyValue::make_boolean(true);
71
+ if ((*current).is_object()) current = (*current).as_object()->proto;
72
+ else break;
73
+ }
74
+ }
75
+
76
+ return jspp::AnyValue::make_boolean(false); }, "isError");
77
+
78
+ // toString method for Error.prototype
79
+ inline auto errorToStringFn = jspp::AnyValue::make_function([](const jspp::AnyValue &thisVal, const std::vector<jspp::AnyValue> &args) -> jspp::AnyValue
80
+ {
81
+ std::string name = "Error";
82
+ std::string msg = "";
83
+
84
+ jspp::AnyValue n = thisVal.get_own_property("name");
85
+ if (!n.is_undefined()) name = n.to_std_string();
86
+
87
+ jspp::AnyValue m = thisVal.get_own_property("message");
88
+ if (!m.is_undefined()) msg = m.to_std_string();
89
+
90
+ if (name.empty() && msg.empty()) return jspp::AnyValue::make_string("Error");
91
+ if (name.empty()) return jspp::AnyValue::make_string(msg);
92
+ if (msg.empty()) return jspp::AnyValue::make_string(name);
93
+
94
+ return jspp::AnyValue::make_string(name + ": " + msg); }, "toString");
95
+
96
+ // Initialize Error class and its prototype
97
+ struct ErrorInit
98
+ {
99
+ ErrorInit()
100
+ {
101
+ // Initialize Error class
102
+ Error = jspp::AnyValue::make_class(errorConstructor, "Error");
103
+
104
+ // Define Error.prototype.toString
105
+ auto proto = Error.get_own_property("prototype");
106
+ proto.define_data_property("toString", errorToStringFn, true, false, true);
107
+
108
+ // Define static Error.isError
109
+ Error.define_data_property("isError", isErrorFn,true ,false,true);
110
+ }
111
+ } errorInit;
@@ -1,10 +1,21 @@
1
1
  #pragma once
2
2
 
3
3
  #include "types.hpp"
4
- #include "object.hpp"
5
- #include "console.hpp"
4
+ #include "values/non_values.hpp"
5
+ #include "values/object.hpp"
6
+ #include "values/function.hpp"
7
+ #include "utils/operators.hpp"
6
8
 
7
- inline auto global = jspp::Object::make_object({
9
+ #include "library/promise.hpp"
10
+ #include "library/timer.hpp"
11
+
12
+ inline auto global = jspp::AnyValue::make_object({
8
13
  {"console", console},
14
+ {"performance", performance},
15
+ {"Error", Error},
16
+ {"Promise", Promise},
17
+ {"setTimeout", setTimeout},
18
+ {"clearTimeout", clearTimeout},
19
+ {"setInterval", setInterval},
20
+ {"clearInterval", clearInterval},
9
21
  });
10
- inline auto globalThis = global;
@@ -5,7 +5,7 @@
5
5
  #include "values/non_values.hpp"
6
6
  #include "values/object.hpp"
7
7
  #include "values/function.hpp"
8
- #include "operators.hpp"
8
+ #include "utils/operators.hpp"
9
9
 
10
10
  inline auto performance = jspp::AnyValue::make_object({
11
11
  {"now",
@@ -13,7 +13,7 @@ inline auto performance = jspp::AnyValue::make_object({
13
13
  // [C++14 Feature] Generalized Lambda Capture
14
14
  // We initialize 'startTime' RIGHT HERE inside the [].
15
15
  // It acts like a private variable stored inside this specific function.
16
- [startTime = std::chrono::steady_clock::now()](const std::vector<jspp::AnyValue> &)
16
+ [startTime = std::chrono::steady_clock::now()](const jspp::AnyValue &thisVal, const std::vector<jspp::AnyValue> &)
17
17
  {
18
18
  // We calculate the diff against the captured startTime
19
19
  std::chrono::duration<double, std::milli> duration =
@@ -0,0 +1,121 @@
1
+ #pragma once
2
+
3
+ #include "types.hpp"
4
+ #include "values/promise.hpp"
5
+ #include "any_value.hpp"
6
+ #include "exception.hpp"
7
+
8
+ inline auto Promise = jspp::AnyValue::make_function([](const jspp::AnyValue& thisVal, const std::vector<jspp::AnyValue>& args) -> jspp::AnyValue {
9
+ if (args.empty() || !args[0].is_function()) {
10
+ throw jspp::Exception::make_exception("Promise resolver undefined is not a function", "TypeError");
11
+ }
12
+ auto executor = args[0].as_function();
13
+
14
+ jspp::JsPromise promise;
15
+ auto state = promise.state; // Share state
16
+
17
+ // resolve function
18
+ auto resolveFn = jspp::AnyValue::make_function([state](const jspp::AnyValue&, const std::vector<jspp::AnyValue>& args) -> jspp::AnyValue {
19
+ jspp::JsPromise p; p.state = state;
20
+ p.resolve(args.empty() ? jspp::AnyValue::make_undefined() : args[0]);
21
+ return jspp::AnyValue::make_undefined();
22
+ }, "resolve");
23
+
24
+ // reject function
25
+ auto rejectFn = jspp::AnyValue::make_function([state](const jspp::AnyValue&, const std::vector<jspp::AnyValue>& args) -> jspp::AnyValue {
26
+ jspp::JsPromise p; p.state = state;
27
+ p.reject(args.empty() ? jspp::AnyValue::make_undefined() : args[0]);
28
+ return jspp::AnyValue::make_undefined();
29
+ }, "reject");
30
+
31
+ try {
32
+ executor->call(jspp::AnyValue::make_undefined(), {resolveFn, rejectFn});
33
+ } catch (const jspp::Exception& e) {
34
+ promise.reject(*e.data);
35
+ } catch (...) {
36
+ promise.reject(jspp::AnyValue::make_string("Unknown error during Promise execution"));
37
+ }
38
+
39
+ return jspp::AnyValue::make_promise(promise);
40
+
41
+ }, "Promise");
42
+
43
+ struct PromiseInit {
44
+ PromiseInit() {
45
+ // Promise.resolve(value)
46
+ Promise.define_data_property("resolve", jspp::AnyValue::make_function([](const jspp::AnyValue&, const std::vector<jspp::AnyValue>& args) -> jspp::AnyValue {
47
+ jspp::JsPromise p;
48
+ p.resolve(args.empty() ? jspp::AnyValue::make_undefined() : args[0]);
49
+ return jspp::AnyValue::make_promise(p);
50
+ }, "resolve"));
51
+
52
+ // Promise.reject(reason)
53
+ Promise.define_data_property("reject", jspp::AnyValue::make_function([](const jspp::AnyValue&, const std::vector<jspp::AnyValue>& args) -> jspp::AnyValue {
54
+ jspp::JsPromise p;
55
+ p.reject(args.empty() ? jspp::AnyValue::make_undefined() : args[0]);
56
+ return jspp::AnyValue::make_promise(p);
57
+ }, "reject"));
58
+
59
+ // Promise.all(iterable)
60
+ Promise.define_data_property("all", jspp::AnyValue::make_function([](const jspp::AnyValue&, const std::vector<jspp::AnyValue>& args) -> jspp::AnyValue {
61
+ // Basic implementation for arrays
62
+ if (args.empty() || !args[0].is_array()) {
63
+ // If not array, reject? Or treat as non-iterable?
64
+ // Should throw TypeError if not iterable. For now assume array.
65
+ // If empty array, return resolved empty array.
66
+ // TODO: Strict iterable check
67
+ }
68
+
69
+ // Handle non-array iterable or empty args
70
+ if (args.empty() || !args[0].is_array()) {
71
+ jspp::JsPromise p; p.reject(jspp::AnyValue::make_string("Promise.all argument must be an array"));
72
+ return jspp::AnyValue::make_promise(p);
73
+ }
74
+
75
+ auto arr = args[0].as_array();
76
+ size_t len = arr->length;
77
+ if (len == 0) {
78
+ jspp::JsPromise p; p.resolve(jspp::AnyValue::make_array({}));
79
+ return jspp::AnyValue::make_promise(p);
80
+ }
81
+
82
+ jspp::JsPromise masterPromise;
83
+ auto masterState = masterPromise.state;
84
+
85
+ auto results = std::make_shared<std::vector<std::optional<jspp::AnyValue>>>(len);
86
+ auto count = std::make_shared<size_t>(len);
87
+
88
+ // Check if already rejected to avoid further processing
89
+ auto rejected = std::make_shared<bool>(false);
90
+
91
+ for (size_t i = 0; i < len; ++i) {
92
+ jspp::AnyValue item = arr->get_property(static_cast<uint32_t>(i));
93
+
94
+ auto handleResult = [masterState, results, count, i, rejected](jspp::AnyValue res) {
95
+ if (*rejected) return;
96
+ (*results)[i] = res;
97
+ (*count)--;
98
+ if (*count == 0) {
99
+ jspp::JsPromise p; p.state = masterState;
100
+ p.resolve(jspp::AnyValue::make_array(*results));
101
+ }
102
+ };
103
+
104
+ auto handleReject = [masterState, rejected](jspp::AnyValue reason) {
105
+ if (*rejected) return;
106
+ *rejected = true;
107
+ jspp::JsPromise p; p.state = masterState;
108
+ p.reject(reason);
109
+ };
110
+
111
+ if (item.is_promise()) {
112
+ item.as_promise()->then(handleResult, handleReject);
113
+ } else {
114
+ handleResult(item);
115
+ }
116
+ }
117
+
118
+ return jspp::AnyValue::make_promise(masterPromise);
119
+ }, "all"));
120
+ }
121
+ } promiseInit;
@@ -1,20 +1,19 @@
1
1
  #pragma once
2
2
 
3
3
  #include "types.hpp"
4
- #include "well_known_symbols.hpp"
4
+ #include "utils/well_known_symbols.hpp"
5
5
 
6
6
  #include "values/object.hpp"
7
7
  #include "any_value.hpp"
8
8
 
9
9
  inline auto Symbol = jspp::AnyValue::make_object({
10
10
  {"iterator", jspp::AnyValue::from_symbol(jspp::WellKnownSymbols::iterator)},
11
- {"toString", jspp::AnyValue::from_symbol(jspp::WellKnownSymbols::toString)},
12
11
  });
13
12
 
14
13
  // #pragma once
15
14
 
16
15
  // #include "types.hpp"
17
- // #include "well_known_symbols.hpp"
16
+ // #include "utils/well_known_symbols.hpp"
18
17
  // #include "values/object.hpp"
19
18
  // #include "any_value.hpp"
20
19
 
@@ -42,7 +41,7 @@ inline auto Symbol = jspp::AnyValue::make_object({
42
41
  // // Implementation of Symbol.keyFor(sym)
43
42
  // inline auto keyForFn = jspp::AnyValue::make_function([](const std::vector<jspp::AnyValue>& args) -> jspp::AnyValue {
44
43
  // if (args.empty() || !args[0].is_symbol()) {
45
- // throw jspp::RuntimeError::make_error("Symbol.keyFor requires a symbol argument", "TypeError");
44
+ // throw jspp::Exception::make_exception("Symbol.keyFor requires a symbol argument", "TypeError");
46
45
  // }
47
46
 
48
47
  // jspp::JsSymbol* sym = args[0].as_symbol();
@@ -0,0 +1,92 @@
1
+ #pragma once
2
+
3
+ #include "types.hpp"
4
+ #include "any_value.hpp"
5
+ #include "scheduler.hpp"
6
+ #include "values/function.hpp"
7
+ #include "exception.hpp"
8
+
9
+ // setTimeout(callback, delay, ...args)
10
+ inline auto setTimeout = jspp::AnyValue::make_function([](const jspp::AnyValue& thisVal, const std::vector<jspp::AnyValue>& args) -> jspp::AnyValue {
11
+ if (args.empty() || !args[0].is_function()) {
12
+ throw jspp::Exception::make_exception("Callback must be a function", "TypeError");
13
+ }
14
+
15
+ auto callback = args[0];
16
+ double delay = 0;
17
+ if (args.size() > 1 && args[1].is_number()) {
18
+ delay = args[1].as_double();
19
+ }
20
+
21
+ // Capture arguments
22
+ std::vector<jspp::AnyValue> callArgs;
23
+ for (size_t i = 2; i < args.size(); ++i) {
24
+ callArgs.push_back(args[i]);
25
+ }
26
+
27
+ auto task = [callback, callArgs]() {
28
+ try {
29
+ callback.as_function()->call(jspp::AnyValue::make_undefined(), callArgs);
30
+ } catch (const jspp::Exception& e) {
31
+ std::cerr << "Uncaught exception in setTimeout: " << e.what() << "\n";
32
+ } catch (const std::exception& e) {
33
+ std::cerr << "Uncaught exception in setTimeout: " << e.what() << "\n";
34
+ } catch (...) {
35
+ std::cerr << "Uncaught unknown exception in setTimeout\n";
36
+ }
37
+ };
38
+
39
+ size_t id = jspp::Scheduler::instance().set_timeout(task, static_cast<size_t>(delay));
40
+ return jspp::AnyValue::make_number(static_cast<double>(id));
41
+ }, "setTimeout");
42
+
43
+ // clearTimeout(id)
44
+ inline auto clearTimeout = jspp::AnyValue::make_function([](const jspp::AnyValue& thisVal, const std::vector<jspp::AnyValue>& args) -> jspp::AnyValue {
45
+ if (!args.empty() && args[0].is_number()) {
46
+ size_t id = static_cast<size_t>(args[0].as_double());
47
+ jspp::Scheduler::instance().clear_timer(id);
48
+ }
49
+ return jspp::AnyValue::make_undefined();
50
+ }, "clearTimeout");
51
+
52
+ // setInterval(callback, delay, ...args)
53
+ inline auto setInterval = jspp::AnyValue::make_function([](const jspp::AnyValue& thisVal, const std::vector<jspp::AnyValue>& args) -> jspp::AnyValue {
54
+ if (args.empty() || !args[0].is_function()) {
55
+ throw jspp::Exception::make_exception("Callback must be a function", "TypeError");
56
+ }
57
+
58
+ auto callback = args[0];
59
+ double delay = 0;
60
+ if (args.size() > 1 && args[1].is_number()) {
61
+ delay = args[1].as_double();
62
+ }
63
+
64
+ std::vector<jspp::AnyValue> callArgs;
65
+ for (size_t i = 2; i < args.size(); ++i) {
66
+ callArgs.push_back(args[i]);
67
+ }
68
+
69
+ auto task = [callback, callArgs]() {
70
+ try {
71
+ callback.as_function()->call(jspp::AnyValue::make_undefined(), callArgs);
72
+ } catch (const jspp::Exception& e) {
73
+ std::cerr << "Uncaught exception in setInterval: " << e.what() << "\n";
74
+ } catch (const std::exception& e) {
75
+ std::cerr << "Uncaught exception in setInterval: " << e.what() << "\n";
76
+ } catch (...) {
77
+ std::cerr << "Uncaught unknown exception in setInterval\n";
78
+ }
79
+ };
80
+
81
+ size_t id = jspp::Scheduler::instance().set_interval(task, static_cast<size_t>(delay));
82
+ return jspp::AnyValue::make_number(static_cast<double>(id));
83
+ }, "setInterval");
84
+
85
+ // clearInterval(id)
86
+ inline auto clearInterval = jspp::AnyValue::make_function([](const jspp::AnyValue& thisVal, const std::vector<jspp::AnyValue>& args) -> jspp::AnyValue {
87
+ if (!args.empty() && args[0].is_number()) {
88
+ size_t id = static_cast<size_t>(args[0].as_double());
89
+ jspp::Scheduler::instance().clear_timer(id);
90
+ }
91
+ return jspp::AnyValue::make_undefined();
92
+ }, "clearInterval");
@@ -0,0 +1,145 @@
1
+ #pragma once
2
+ #include <deque>
3
+ #include <functional>
4
+ #include <chrono>
5
+ #include <queue>
6
+ #include <unordered_set>
7
+ #include <vector>
8
+ #include <thread>
9
+ #include <iostream>
10
+
11
+ namespace jspp {
12
+ class Scheduler {
13
+ public:
14
+ using Task = std::function<void()>;
15
+ using TimePoint = std::chrono::steady_clock::time_point;
16
+
17
+ struct Timer {
18
+ size_t id;
19
+ TimePoint next_run;
20
+ std::chrono::milliseconds interval; // 0 if not repeating
21
+ Task task;
22
+
23
+ // Min-heap priority queue (smallest time at top)
24
+ bool operator>(const Timer& other) const {
25
+ return next_run > other.next_run;
26
+ }
27
+ };
28
+
29
+ static Scheduler& instance() {
30
+ static Scheduler s;
31
+ return s;
32
+ }
33
+
34
+ void enqueue(Task task) {
35
+ tasks.push_back(std::move(task));
36
+ }
37
+
38
+ size_t set_timeout(Task task, size_t delay_ms) {
39
+ return schedule_timer(std::move(task), delay_ms, false);
40
+ }
41
+
42
+ size_t set_interval(Task task, size_t delay_ms) {
43
+ return schedule_timer(std::move(task), delay_ms, true);
44
+ }
45
+
46
+ void clear_timer(size_t id) {
47
+ cancelled_timers.insert(id);
48
+ }
49
+
50
+ void run() {
51
+ while (true) {
52
+ bool has_work = false;
53
+
54
+ // 1. Process all immediate tasks (microtask/task queue)
55
+ while (!tasks.empty()) {
56
+ Task task = tasks.front();
57
+ tasks.pop_front();
58
+ task();
59
+ has_work = true;
60
+ }
61
+
62
+ // 2. Process timers
63
+ auto now = std::chrono::steady_clock::now();
64
+ while (!timers.empty()) {
65
+ // Peek top
66
+ const auto& top = timers.top();
67
+
68
+ // Cleanup cancelled timers lazily
69
+ if (cancelled_timers.count(top.id)) {
70
+ cancelled_timers.erase(top.id);
71
+ timers.pop();
72
+ continue;
73
+ }
74
+
75
+ if (top.next_run <= now) {
76
+ // Timer is ready
77
+ Timer t = top;
78
+ timers.pop();
79
+
80
+ // Execute task
81
+ t.task();
82
+ has_work = true;
83
+
84
+ // Reschedule if interval and not cancelled during execution
85
+ if (t.interval.count() > 0 && cancelled_timers.find(t.id) == cancelled_timers.end()) {
86
+ // Drift-safe(ish) scheduling: run next interval relative to now
87
+ // Note: JS allows drift.
88
+ t.next_run = std::chrono::steady_clock::now() + t.interval;
89
+ timers.push(t);
90
+ }
91
+ } else {
92
+ break; // Next timer is in the future
93
+ }
94
+ }
95
+
96
+ // 3. Exit or Wait
97
+ if (!has_work) {
98
+ if (tasks.empty() && timers.empty()) {
99
+ break; // No pending work, exit event loop
100
+ }
101
+
102
+ if (!timers.empty()) {
103
+ auto next_time = timers.top().next_run;
104
+ std::this_thread::sleep_until(next_time);
105
+ }
106
+ }
107
+ }
108
+ }
109
+
110
+ bool has_tasks() const {
111
+ return !tasks.empty() || !timers.empty();
112
+ }
113
+
114
+ private:
115
+ std::deque<Task> tasks;
116
+ std::priority_queue<Timer, std::vector<Timer>, std::greater<Timer>> timers;
117
+ std::unordered_set<size_t> cancelled_timers;
118
+ size_t next_timer_id = 1;
119
+ const size_t MAX_TIMER_ID = 2147483647; // 2^31 - 1
120
+
121
+ size_t schedule_timer(Task task, size_t delay_ms, bool repeat) {
122
+ size_t id = next_timer_id++;
123
+ if (next_timer_id > MAX_TIMER_ID) {
124
+ next_timer_id = 1;
125
+ }
126
+
127
+ // If we wrap around, ensure we don't accidentally treat this new ID as cancelled
128
+ // (in case an old timer with this ID is still lingering in the 'cancelled' set)
129
+ if (cancelled_timers.count(id)) {
130
+ cancelled_timers.erase(id);
131
+ }
132
+
133
+ auto now = std::chrono::steady_clock::now();
134
+
135
+ Timer t;
136
+ t.id = id;
137
+ t.next_run = now + std::chrono::milliseconds(delay_ms);
138
+ t.interval = repeat ? std::chrono::milliseconds(delay_ms) : std::chrono::milliseconds(0);
139
+ t.task = std::move(task);
140
+
141
+ timers.push(t);
142
+ return id;
143
+ }
144
+ };
145
+ }
@@ -26,6 +26,7 @@ namespace jspp
26
26
  struct JsObject; // can set property
27
27
  struct JsArray; // can set property
28
28
  struct JsFunction; // can set property
29
+ struct JsPromise; // can set property
29
30
  struct JsSymbol; // can set property (but usually doesn't have own props)
30
31
 
31
32
  template <typename T>
@@ -36,7 +37,7 @@ namespace jspp
36
37
  struct AccessorDescriptor;
37
38
 
38
39
  // Custom runtime exception
39
- struct RuntimeError;
40
+ struct Exception;
40
41
 
41
42
  // Dynamic AnyValue
42
43
  class AnyValue;
@@ -45,6 +46,10 @@ namespace jspp
45
46
  inline AnyValue pow(const AnyValue &lhs, const AnyValue &rhs);
46
47
 
47
48
  // AnyValue prototypes
49
+ namespace ObjectPrototypes
50
+ {
51
+ inline std::optional<AnyValue> get(const std::string &key, JsObject *self);
52
+ }
48
53
  namespace StringPrototypes
49
54
  {
50
55
  inline std::optional<AnyValue> get(const std::string &key, JsString *self);
@@ -57,6 +62,10 @@ namespace jspp
57
62
  {
58
63
  inline std::optional<AnyValue> get(const std::string &key, JsFunction *self);
59
64
  }
65
+ namespace PromisePrototypes
66
+ {
67
+ inline std::optional<AnyValue> get(const std::string &key, JsPromise *self);
68
+ }
60
69
  namespace IteratorPrototypes
61
70
  {
62
71
  inline std::optional<AnyValue> get(const std::string &key, JsIterator<AnyValue> *self);