@ugo-studio/jspp 0.1.3 → 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.
- package/README.md +2 -2
- package/dist/analysis/scope.js +33 -4
- package/dist/analysis/typeAnalyzer.js +260 -21
- package/dist/ast/symbols.js +29 -0
- package/dist/cli-utils/args.js +57 -0
- package/dist/cli-utils/colors.js +9 -0
- package/dist/cli-utils/file-utils.js +20 -0
- package/dist/cli-utils/spinner.js +55 -0
- package/dist/cli.js +105 -31
- package/dist/core/codegen/class-handlers.js +131 -0
- package/dist/core/codegen/control-flow-handlers.js +474 -0
- package/dist/core/codegen/declaration-handlers.js +36 -15
- package/dist/core/codegen/expression-handlers.js +579 -125
- package/dist/core/codegen/function-handlers.js +222 -37
- package/dist/core/codegen/helpers.js +158 -4
- package/dist/core/codegen/index.js +20 -8
- package/dist/core/codegen/literal-handlers.js +18 -6
- package/dist/core/codegen/statement-handlers.js +171 -228
- package/dist/core/codegen/visitor.js +31 -3
- package/package.json +3 -3
- package/src/prelude/any_value.hpp +510 -633
- package/src/prelude/any_value_access.hpp +151 -0
- package/src/prelude/any_value_defines.hpp +190 -0
- package/src/prelude/any_value_helpers.hpp +139 -225
- package/src/prelude/exception.hpp +32 -0
- package/src/prelude/exception_helpers.hpp +49 -0
- package/src/prelude/index.hpp +25 -9
- package/src/prelude/library/array.hpp +190 -0
- package/src/prelude/library/console.hpp +14 -13
- package/src/prelude/library/error.hpp +113 -0
- package/src/prelude/library/function.hpp +10 -0
- package/src/prelude/library/global.hpp +35 -4
- package/src/prelude/library/math.hpp +308 -0
- package/src/prelude/library/object.hpp +288 -0
- package/src/prelude/library/performance.hpp +2 -2
- package/src/prelude/library/process.hpp +39 -0
- package/src/prelude/library/promise.hpp +131 -0
- package/src/prelude/library/symbol.hpp +46 -59
- package/src/prelude/library/timer.hpp +92 -0
- package/src/prelude/scheduler.hpp +145 -0
- package/src/prelude/types.hpp +58 -1
- package/src/prelude/utils/access.hpp +345 -0
- package/src/prelude/utils/assignment_operators.hpp +99 -0
- package/src/prelude/utils/log_any_value/array.hpp +245 -0
- package/src/prelude/utils/log_any_value/config.hpp +32 -0
- package/src/prelude/utils/log_any_value/function.hpp +39 -0
- package/src/prelude/utils/log_any_value/fwd.hpp +15 -0
- package/src/prelude/utils/log_any_value/helpers.hpp +62 -0
- package/src/prelude/utils/log_any_value/log_any_value.hpp +94 -0
- package/src/prelude/utils/log_any_value/object.hpp +136 -0
- package/src/prelude/utils/log_any_value/primitives.hpp +43 -0
- package/src/prelude/utils/operators.hpp +751 -0
- package/src/prelude/utils/well_known_symbols.hpp +25 -0
- package/src/prelude/values/array.hpp +10 -7
- package/src/prelude/{descriptors.hpp → values/descriptors.hpp} +2 -2
- package/src/prelude/values/function.hpp +85 -51
- package/src/prelude/values/helpers/array.hpp +80 -35
- package/src/prelude/values/helpers/function.hpp +110 -77
- package/src/prelude/values/helpers/iterator.hpp +16 -10
- package/src/prelude/values/helpers/object.hpp +85 -10
- package/src/prelude/values/helpers/promise.hpp +181 -0
- package/src/prelude/values/helpers/string.hpp +3 -3
- package/src/prelude/values/helpers/symbol.hpp +2 -2
- package/src/prelude/values/iterator.hpp +14 -6
- package/src/prelude/values/object.hpp +14 -3
- package/src/prelude/values/promise.hpp +73 -0
- package/src/prelude/values/prototypes/array.hpp +855 -16
- package/src/prelude/values/prototypes/function.hpp +4 -4
- package/src/prelude/values/prototypes/iterator.hpp +11 -10
- package/src/prelude/values/prototypes/number.hpp +153 -0
- package/src/prelude/values/prototypes/object.hpp +26 -0
- package/src/prelude/values/prototypes/promise.hpp +134 -0
- package/src/prelude/values/prototypes/string.hpp +29 -29
- package/src/prelude/values/prototypes/symbol.hpp +22 -3
- package/src/prelude/values/shape.hpp +52 -0
- package/src/prelude/values/string.hpp +1 -1
- package/src/prelude/values/symbol.hpp +1 -1
- package/src/prelude/access.hpp +0 -91
- package/src/prelude/error.hpp +0 -31
- package/src/prelude/error_helpers.hpp +0 -59
- package/src/prelude/log_string.hpp +0 -407
- package/src/prelude/operators.hpp +0 -256
- package/src/prelude/well_known_symbols.hpp +0 -14
|
@@ -0,0 +1,131 @@
|
|
|
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, std::span<const jspp::AnyValue> args) -> jspp::AnyValue
|
|
9
|
+
{
|
|
10
|
+
if (args.empty() || !args[0].is_function())
|
|
11
|
+
{
|
|
12
|
+
throw jspp::Exception::make_exception("Promise resolver undefined is not a function", "TypeError");
|
|
13
|
+
}
|
|
14
|
+
auto executor = args[0].as_function();
|
|
15
|
+
|
|
16
|
+
jspp::JsPromise promise;
|
|
17
|
+
auto state = promise.state; // Share state
|
|
18
|
+
|
|
19
|
+
// resolve function
|
|
20
|
+
auto resolveFn = jspp::AnyValue::make_function([state](const jspp::AnyValue &, std::span<const jspp::AnyValue> args) -> jspp::AnyValue
|
|
21
|
+
{
|
|
22
|
+
jspp::JsPromise p; p.state = state;
|
|
23
|
+
p.resolve(args.empty() ? jspp::AnyValue::make_undefined() : args[0]);
|
|
24
|
+
return jspp::AnyValue::make_undefined(); }, "resolve");
|
|
25
|
+
|
|
26
|
+
// reject function
|
|
27
|
+
auto rejectFn = jspp::AnyValue::make_function([state](const jspp::AnyValue &, std::span<const jspp::AnyValue> args) -> jspp::AnyValue
|
|
28
|
+
{
|
|
29
|
+
jspp::JsPromise p; p.state = state;
|
|
30
|
+
p.reject(args.empty() ? jspp::AnyValue::make_undefined() : args[0]);
|
|
31
|
+
return jspp::AnyValue::make_undefined(); }, "reject");
|
|
32
|
+
|
|
33
|
+
try
|
|
34
|
+
{
|
|
35
|
+
const jspp::AnyValue executorArgs[] = {resolveFn, rejectFn};
|
|
36
|
+
executor->call(jspp::Constants::UNDEFINED, std::span<const jspp::AnyValue>(executorArgs, 2));
|
|
37
|
+
}
|
|
38
|
+
catch (const jspp::Exception &e)
|
|
39
|
+
{
|
|
40
|
+
promise.reject(*e.data);
|
|
41
|
+
}
|
|
42
|
+
catch (...)
|
|
43
|
+
{
|
|
44
|
+
promise.reject(jspp::AnyValue::make_string("Unknown error during Promise execution"));
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
return jspp::AnyValue::make_promise(promise); },
|
|
48
|
+
"Promise");
|
|
49
|
+
|
|
50
|
+
struct PromiseInit
|
|
51
|
+
{
|
|
52
|
+
PromiseInit()
|
|
53
|
+
{
|
|
54
|
+
// Promise.resolve(value)
|
|
55
|
+
Promise.define_data_property("resolve", jspp::AnyValue::make_function([](const jspp::AnyValue &, std::span<const jspp::AnyValue> args) -> jspp::AnyValue
|
|
56
|
+
{
|
|
57
|
+
jspp::JsPromise p;
|
|
58
|
+
p.resolve(args.empty() ? jspp::AnyValue::make_undefined() : args[0]);
|
|
59
|
+
return jspp::AnyValue::make_promise(p); }, "resolve"));
|
|
60
|
+
|
|
61
|
+
// Promise.reject(reason)
|
|
62
|
+
Promise.define_data_property("reject", jspp::AnyValue::make_function([](const jspp::AnyValue &, std::span<const jspp::AnyValue> args) -> jspp::AnyValue
|
|
63
|
+
{
|
|
64
|
+
jspp::JsPromise p;
|
|
65
|
+
p.reject(args.empty() ? jspp::AnyValue::make_undefined() : args[0]);
|
|
66
|
+
return jspp::AnyValue::make_promise(p); }, "reject"));
|
|
67
|
+
|
|
68
|
+
// Promise.all(iterable)
|
|
69
|
+
Promise.define_data_property("all", jspp::AnyValue::make_function([](const jspp::AnyValue &, std::span<const jspp::AnyValue> args) -> jspp::AnyValue
|
|
70
|
+
{
|
|
71
|
+
// Basic implementation for arrays
|
|
72
|
+
if (args.empty() || !args[0].is_array()) {
|
|
73
|
+
// If not array, reject? Or treat as non-iterable?
|
|
74
|
+
// Should throw TypeError if not iterable. For now assume array.
|
|
75
|
+
// If empty array, return resolved empty array.
|
|
76
|
+
// TODO: Strict iterable check
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// Handle non-array iterable or empty args
|
|
80
|
+
if (args.empty() || !args[0].is_array()) {
|
|
81
|
+
jspp::JsPromise p; p.reject(jspp::AnyValue::make_string("Promise.all argument must be an array"));
|
|
82
|
+
return jspp::AnyValue::make_promise(p);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
auto arr = args[0].as_array();
|
|
86
|
+
size_t len = static_cast<size_t>(arr->length);
|
|
87
|
+
if (len == 0) {
|
|
88
|
+
jspp::JsPromise p; p.resolve(jspp::AnyValue::make_array(std::vector<jspp::AnyValue>()));
|
|
89
|
+
return jspp::AnyValue::make_promise(p);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
jspp::JsPromise masterPromise;
|
|
93
|
+
auto masterState = masterPromise.state;
|
|
94
|
+
|
|
95
|
+
auto results = std::make_shared<std::vector<jspp::AnyValue>>(len, jspp::Constants::UNDEFINED);
|
|
96
|
+
auto count = std::make_shared<size_t>(len);
|
|
97
|
+
|
|
98
|
+
// Check if already rejected to avoid further processing
|
|
99
|
+
auto rejected = std::make_shared<bool>(false);
|
|
100
|
+
|
|
101
|
+
for (size_t i = 0; i < len; ++i) {
|
|
102
|
+
jspp::AnyValue item = arr->get_property(static_cast<uint32_t>(i));
|
|
103
|
+
|
|
104
|
+
auto handleResult = [masterState, results, count, i, rejected](const jspp::AnyValue& res) {
|
|
105
|
+
if (*rejected) return;
|
|
106
|
+
(*results)[i] = res;
|
|
107
|
+
(*count)--;
|
|
108
|
+
if (*count == 0) {
|
|
109
|
+
jspp::JsPromise p; p.state = masterState;
|
|
110
|
+
p.resolve(jspp::AnyValue::make_array(std::move(*results)));
|
|
111
|
+
}
|
|
112
|
+
};
|
|
113
|
+
|
|
114
|
+
auto handleReject = [masterState, rejected](const jspp::AnyValue& reason) {
|
|
115
|
+
if (*rejected) return;
|
|
116
|
+
*rejected = true;
|
|
117
|
+
jspp::JsPromise p; p.state = masterState;
|
|
118
|
+
masterState->status =jspp::PromiseStatus::Rejected; // ensure master state updated
|
|
119
|
+
p.reject(reason);
|
|
120
|
+
};
|
|
121
|
+
|
|
122
|
+
if (item.is_promise()) {
|
|
123
|
+
item.as_promise()->then(handleResult, handleReject);
|
|
124
|
+
} else {
|
|
125
|
+
handleResult(item);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
return jspp::AnyValue::make_promise(masterPromise); }, "all"));
|
|
130
|
+
}
|
|
131
|
+
} promiseInit;
|
|
@@ -1,65 +1,52 @@
|
|
|
1
1
|
#pragma once
|
|
2
2
|
|
|
3
3
|
#include "types.hpp"
|
|
4
|
-
#include "well_known_symbols.hpp"
|
|
5
|
-
|
|
4
|
+
#include "utils/well_known_symbols.hpp"
|
|
6
5
|
#include "values/object.hpp"
|
|
7
6
|
#include "any_value.hpp"
|
|
8
7
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
//
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
//
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
//
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
// return jspp::AnyValue::make_undefined();
|
|
55
|
-
// }
|
|
56
|
-
// }, "keyFor");
|
|
57
|
-
|
|
58
|
-
// // Attach static properties/methods to the Symbol object
|
|
59
|
-
// struct SymbolInit {
|
|
60
|
-
// SymbolInit() {
|
|
61
|
-
// Symbol.set_own_property("iterator", jspp::AnyValue::from_symbol(jspp::WellKnownSymbols::iterator));
|
|
62
|
-
// Symbol.set_own_property("for", forFn);
|
|
63
|
-
// Symbol.set_own_property("keyFor", keyForFn);
|
|
64
|
-
// }
|
|
65
|
-
// } symbolInit;
|
|
8
|
+
// Define Symbol as a function
|
|
9
|
+
inline auto Symbol = jspp::AnyValue::make_function([](const jspp::AnyValue &thisVal, std::span<const jspp::AnyValue> args) -> jspp::AnyValue
|
|
10
|
+
{
|
|
11
|
+
std::string description = "";
|
|
12
|
+
if (!args.empty() && !args[0].is_undefined()) {
|
|
13
|
+
description = args[0].to_std_string();
|
|
14
|
+
}
|
|
15
|
+
return jspp::AnyValue::make_symbol(description); }, "Symbol", false);
|
|
16
|
+
|
|
17
|
+
// Initialize Symbol properties
|
|
18
|
+
struct SymbolInit
|
|
19
|
+
{
|
|
20
|
+
SymbolInit()
|
|
21
|
+
{
|
|
22
|
+
// Static methods
|
|
23
|
+
Symbol.define_data_property("for", jspp::AnyValue::make_function([](const jspp::AnyValue &, std::span<const jspp::AnyValue> args) -> jspp::AnyValue
|
|
24
|
+
{
|
|
25
|
+
std::string key = "";
|
|
26
|
+
if (!args.empty()) key = args[0].to_std_string();
|
|
27
|
+
return jspp::AnyValue::from_symbol(jspp::JsSymbol::for_global(key)); }, "for"));
|
|
28
|
+
|
|
29
|
+
Symbol.define_data_property("keyFor", jspp::AnyValue::make_function([](const jspp::AnyValue &, std::span<const jspp::AnyValue> args) -> jspp::AnyValue
|
|
30
|
+
{
|
|
31
|
+
if (args.empty() || !args[0].is_symbol()) throw jspp::Exception::make_exception("Symbol.keyFor requires a symbol", "TypeError");
|
|
32
|
+
auto sym = args[0].as_symbol();
|
|
33
|
+
auto key = jspp::JsSymbol::key_for(sym);
|
|
34
|
+
if (key.has_value()) return jspp::AnyValue::make_string(key.value());
|
|
35
|
+
return jspp::AnyValue::make_undefined(); }, "keyFor"));
|
|
36
|
+
|
|
37
|
+
// Well-known symbols
|
|
38
|
+
Symbol.define_data_property("iterator", jspp::AnyValue::from_symbol(jspp::WellKnownSymbols::iterator), false, false, false);
|
|
39
|
+
Symbol.define_data_property("asyncIterator", jspp::AnyValue::from_symbol(jspp::WellKnownSymbols::asyncIterator), false, false, false);
|
|
40
|
+
Symbol.define_data_property("hasInstance", jspp::AnyValue::from_symbol(jspp::WellKnownSymbols::hasInstance), false, false, false);
|
|
41
|
+
Symbol.define_data_property("isConcatSpreadable", jspp::AnyValue::from_symbol(jspp::WellKnownSymbols::isConcatSpreadable), false, false, false);
|
|
42
|
+
Symbol.define_data_property("match", jspp::AnyValue::from_symbol(jspp::WellKnownSymbols::match), false, false, false);
|
|
43
|
+
Symbol.define_data_property("matchAll", jspp::AnyValue::from_symbol(jspp::WellKnownSymbols::matchAll), false, false, false);
|
|
44
|
+
Symbol.define_data_property("replace", jspp::AnyValue::from_symbol(jspp::WellKnownSymbols::replace), false, false, false);
|
|
45
|
+
Symbol.define_data_property("search", jspp::AnyValue::from_symbol(jspp::WellKnownSymbols::search), false, false, false);
|
|
46
|
+
Symbol.define_data_property("species", jspp::AnyValue::from_symbol(jspp::WellKnownSymbols::species), false, false, false);
|
|
47
|
+
Symbol.define_data_property("split", jspp::AnyValue::from_symbol(jspp::WellKnownSymbols::split), false, false, false);
|
|
48
|
+
Symbol.define_data_property("toPrimitive", jspp::AnyValue::from_symbol(jspp::WellKnownSymbols::toPrimitive), false, false, false);
|
|
49
|
+
Symbol.define_data_property("toStringTag", jspp::AnyValue::from_symbol(jspp::WellKnownSymbols::toStringTag), false, false, false);
|
|
50
|
+
Symbol.define_data_property("unscopables", jspp::AnyValue::from_symbol(jspp::WellKnownSymbols::unscopables), false, false, false);
|
|
51
|
+
}
|
|
52
|
+
} symbolInit;
|
|
@@ -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, std::span<const 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.call(jspp::Constants::UNDEFINED, std::span<const jspp::AnyValue>(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, std::span<const 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, std::span<const 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.call(jspp::Constants::UNDEFINED, std::span<const jspp::AnyValue>(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, std::span<const 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
|
+
}
|
package/src/prelude/types.hpp
CHANGED
|
@@ -14,6 +14,7 @@
|
|
|
14
14
|
#include <set>
|
|
15
15
|
#include <cmath>
|
|
16
16
|
#include <optional>
|
|
17
|
+
#include <span>
|
|
17
18
|
|
|
18
19
|
// JSPP standard library
|
|
19
20
|
namespace jspp
|
|
@@ -26,6 +27,7 @@ namespace jspp
|
|
|
26
27
|
struct JsObject; // can set property
|
|
27
28
|
struct JsArray; // can set property
|
|
28
29
|
struct JsFunction; // can set property
|
|
30
|
+
struct JsPromise; // can set property
|
|
29
31
|
struct JsSymbol; // can set property (but usually doesn't have own props)
|
|
30
32
|
|
|
31
33
|
template <typename T>
|
|
@@ -36,19 +38,70 @@ namespace jspp
|
|
|
36
38
|
struct AccessorDescriptor;
|
|
37
39
|
|
|
38
40
|
// Custom runtime exception
|
|
39
|
-
struct
|
|
41
|
+
struct Exception;
|
|
40
42
|
|
|
41
43
|
// Dynamic AnyValue
|
|
42
44
|
class AnyValue;
|
|
43
45
|
|
|
46
|
+
// Truthiness checker
|
|
47
|
+
const bool is_truthy(const double &val) noexcept;
|
|
48
|
+
const bool is_truthy(const std::string &val) noexcept;
|
|
49
|
+
const bool is_truthy(const AnyValue &val) noexcept;
|
|
50
|
+
|
|
51
|
+
// Basic equality operators
|
|
52
|
+
inline const bool is_strictly_equal_to_primitive(const AnyValue &lhs, const double &rhs) noexcept;
|
|
53
|
+
inline const bool is_strictly_equal_to_primitive(const double &lhs, const AnyValue &rhs) noexcept;
|
|
54
|
+
inline const bool is_strictly_equal_to_primitive(const double &lhs, const double &rhs) noexcept;
|
|
55
|
+
inline const bool is_strictly_equal_to_primitive(const AnyValue &lhs, const AnyValue &rhs) noexcept;
|
|
56
|
+
|
|
57
|
+
inline const bool is_equal_to_primitive(const AnyValue &lhs, const double &rhs) noexcept;
|
|
58
|
+
inline const bool is_equal_to_primitive(const double &lhs, const AnyValue &rhs) noexcept;
|
|
59
|
+
inline const bool is_equal_to_primitive(const double &lhs, const double &rhs) noexcept;
|
|
60
|
+
inline const bool is_equal_to_primitive(const AnyValue &lhs, const AnyValue &rhs) noexcept;
|
|
61
|
+
|
|
62
|
+
inline const AnyValue is_strictly_equal_to(const AnyValue &lhs, const double &rhs) noexcept;
|
|
63
|
+
inline const AnyValue is_strictly_equal_to(const double &lhs, const AnyValue &rhs) noexcept;
|
|
64
|
+
inline const AnyValue is_strictly_equal_to(const double &lhs, const double &rhs) noexcept;
|
|
65
|
+
inline const AnyValue is_strictly_equal_to(const AnyValue &lhs, const AnyValue &rhs) noexcept;
|
|
66
|
+
|
|
67
|
+
inline const AnyValue is_equal_to(const AnyValue &lhs, const double &rhs) noexcept;
|
|
68
|
+
inline const AnyValue is_equal_to(const double &lhs, const AnyValue &rhs) noexcept;
|
|
69
|
+
inline const AnyValue is_equal_to(const double &lhs, const double &rhs) noexcept;
|
|
70
|
+
inline const AnyValue is_equal_to(const AnyValue &lhs, const AnyValue &rhs) noexcept;
|
|
71
|
+
|
|
72
|
+
inline const AnyValue not_strictly_equal_to(const AnyValue &lhs, const double &rhs) noexcept;
|
|
73
|
+
inline const AnyValue not_strictly_equal_to(const double &lhs, const AnyValue &rhs) noexcept;
|
|
74
|
+
inline const AnyValue not_strictly_equal_to(const double &lhs, const double &rhs) noexcept;
|
|
75
|
+
inline const AnyValue not_strictly_equal_to(const AnyValue &lhs, const AnyValue &rhs) noexcept;
|
|
76
|
+
|
|
77
|
+
inline const AnyValue not_equal_to(const AnyValue &lhs, const double &rhs) noexcept;
|
|
78
|
+
inline const AnyValue not_equal_to(const double &lhs, const AnyValue &rhs) noexcept;
|
|
79
|
+
inline const AnyValue not_equal_to(const AnyValue &lhs, const AnyValue &rhs) noexcept;
|
|
80
|
+
|
|
81
|
+
// Bitwise operators
|
|
82
|
+
inline AnyValue unsigned_right_shift(const AnyValue &lhs, const AnyValue &rhs);
|
|
83
|
+
inline AnyValue unsigned_right_shift(const AnyValue &lhs, const double &rhs);
|
|
84
|
+
inline AnyValue unsigned_right_shift(const double &lhs, const AnyValue &rhs);
|
|
85
|
+
|
|
44
86
|
// Arithemetic operators
|
|
87
|
+
|
|
88
|
+
inline AnyValue pow(const double &lhs, const double &rhs);
|
|
89
|
+
inline AnyValue pow(const AnyValue &lhs, const double &rhs);
|
|
45
90
|
inline AnyValue pow(const AnyValue &lhs, const AnyValue &rhs);
|
|
46
91
|
|
|
47
92
|
// AnyValue prototypes
|
|
93
|
+
namespace ObjectPrototypes
|
|
94
|
+
{
|
|
95
|
+
inline std::optional<AnyValue> get(const std::string &key, JsObject *self);
|
|
96
|
+
}
|
|
48
97
|
namespace StringPrototypes
|
|
49
98
|
{
|
|
50
99
|
inline std::optional<AnyValue> get(const std::string &key, JsString *self);
|
|
51
100
|
}
|
|
101
|
+
namespace NumberPrototypes
|
|
102
|
+
{
|
|
103
|
+
inline std::optional<AnyValue> get(const std::string &key, double self);
|
|
104
|
+
}
|
|
52
105
|
namespace ArrayPrototypes
|
|
53
106
|
{
|
|
54
107
|
inline std::optional<AnyValue> get(const std::string &key, JsArray *self);
|
|
@@ -57,6 +110,10 @@ namespace jspp
|
|
|
57
110
|
{
|
|
58
111
|
inline std::optional<AnyValue> get(const std::string &key, JsFunction *self);
|
|
59
112
|
}
|
|
113
|
+
namespace PromisePrototypes
|
|
114
|
+
{
|
|
115
|
+
inline std::optional<AnyValue> get(const std::string &key, JsPromise *self);
|
|
116
|
+
}
|
|
60
117
|
namespace IteratorPrototypes
|
|
61
118
|
{
|
|
62
119
|
inline std::optional<AnyValue> get(const std::string &key, JsIterator<AnyValue> *self);
|