@ugo-studio/jspp 0.1.4 → 0.1.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/analysis/scope.js +17 -0
- package/dist/analysis/typeAnalyzer.js +7 -1
- package/dist/ast/symbols.js +29 -0
- package/dist/ast/types.js +0 -6
- 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 -30
- package/dist/core/codegen/class-handlers.js +10 -6
- package/dist/core/codegen/control-flow-handlers.js +31 -21
- package/dist/core/codegen/declaration-handlers.js +10 -6
- package/dist/core/codegen/expression-handlers.js +202 -60
- package/dist/core/codegen/function-handlers.js +179 -70
- package/dist/core/codegen/helpers.js +107 -17
- package/dist/core/codegen/index.js +9 -8
- package/dist/core/codegen/literal-handlers.js +15 -6
- package/dist/core/codegen/statement-handlers.js +67 -53
- package/dist/core/codegen/visitor.js +3 -1
- package/package.json +1 -1
- package/src/prelude/any_value.hpp +195 -342
- package/src/prelude/any_value_access.hpp +78 -30
- package/src/prelude/any_value_defines.hpp +74 -35
- package/src/prelude/any_value_helpers.hpp +73 -180
- package/src/prelude/exception.hpp +1 -0
- package/src/prelude/exception_helpers.hpp +4 -4
- package/src/prelude/index.hpp +9 -2
- package/src/prelude/library/array.hpp +190 -0
- package/src/prelude/library/console.hpp +6 -5
- package/src/prelude/library/error.hpp +10 -8
- package/src/prelude/library/function.hpp +10 -0
- package/src/prelude/library/global.hpp +20 -0
- package/src/prelude/library/math.hpp +308 -0
- package/src/prelude/library/object.hpp +288 -0
- package/src/prelude/library/performance.hpp +1 -1
- package/src/prelude/library/process.hpp +39 -0
- package/src/prelude/library/promise.hpp +53 -43
- package/src/prelude/library/symbol.hpp +45 -57
- package/src/prelude/library/timer.hpp +6 -6
- package/src/prelude/types.hpp +48 -0
- package/src/prelude/utils/access.hpp +182 -11
- package/src/prelude/utils/assignment_operators.hpp +99 -0
- package/src/prelude/utils/log_any_value/array.hpp +8 -8
- package/src/prelude/utils/log_any_value/function.hpp +6 -4
- package/src/prelude/utils/log_any_value/object.hpp +41 -24
- package/src/prelude/utils/log_any_value/primitives.hpp +3 -1
- package/src/prelude/utils/operators.hpp +750 -274
- package/src/prelude/utils/well_known_symbols.hpp +12 -0
- package/src/prelude/values/array.hpp +8 -6
- package/src/prelude/values/descriptors.hpp +2 -2
- package/src/prelude/values/function.hpp +71 -62
- package/src/prelude/values/helpers/array.hpp +64 -28
- package/src/prelude/values/helpers/function.hpp +77 -92
- package/src/prelude/values/helpers/iterator.hpp +3 -3
- package/src/prelude/values/helpers/object.hpp +54 -9
- package/src/prelude/values/helpers/promise.hpp +3 -3
- package/src/prelude/values/iterator.hpp +1 -1
- package/src/prelude/values/object.hpp +10 -3
- package/src/prelude/values/promise.hpp +3 -3
- package/src/prelude/values/prototypes/array.hpp +851 -12
- package/src/prelude/values/prototypes/function.hpp +2 -2
- package/src/prelude/values/prototypes/iterator.hpp +5 -5
- package/src/prelude/values/prototypes/number.hpp +153 -0
- package/src/prelude/values/prototypes/object.hpp +2 -2
- package/src/prelude/values/prototypes/promise.hpp +40 -30
- package/src/prelude/values/prototypes/string.hpp +28 -28
- package/src/prelude/values/prototypes/symbol.hpp +20 -3
- package/src/prelude/values/shape.hpp +52 -0
|
@@ -0,0 +1,308 @@
|
|
|
1
|
+
#pragma once
|
|
2
|
+
|
|
3
|
+
#include <cmath>
|
|
4
|
+
#include <limits>
|
|
5
|
+
#include <random>
|
|
6
|
+
#include <algorithm>
|
|
7
|
+
#include <bit>
|
|
8
|
+
#include <numbers>
|
|
9
|
+
#include <stdfloat>
|
|
10
|
+
|
|
11
|
+
#include "types.hpp"
|
|
12
|
+
#include "any_value.hpp"
|
|
13
|
+
#include "utils/operators.hpp"
|
|
14
|
+
#include "utils/access.hpp"
|
|
15
|
+
|
|
16
|
+
namespace jspp {
|
|
17
|
+
namespace Library {
|
|
18
|
+
// --- Random Number Generator ---
|
|
19
|
+
static std::random_device rd;
|
|
20
|
+
static std::mt19937 gen(rd());
|
|
21
|
+
static std::uniform_real_distribution<> dis(0.0, 1.0);
|
|
22
|
+
|
|
23
|
+
// --- Helpers ---
|
|
24
|
+
inline double GetArgAsDouble(std::span<const jspp::AnyValue> args, size_t index) {
|
|
25
|
+
if (index >= args.size()) return std::numeric_limits<double>::quiet_NaN();
|
|
26
|
+
return Operators_Private::ToNumber(args[index]);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
inline jspp::AnyValue MathFunc1(std::span<const jspp::AnyValue> args, double (*func)(double)) {
|
|
30
|
+
return jspp::AnyValue::make_number(func(GetArgAsDouble(args, 0)));
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
inline jspp::AnyValue MathFunc2(std::span<const jspp::AnyValue> args, double (*func)(double, double)) {
|
|
34
|
+
return jspp::AnyValue::make_number(func(GetArgAsDouble(args, 0), GetArgAsDouble(args, 1)));
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
inline auto Math = jspp::AnyValue::make_object({});
|
|
40
|
+
|
|
41
|
+
struct MathInit {
|
|
42
|
+
MathInit() {
|
|
43
|
+
using namespace jspp;
|
|
44
|
+
using namespace jspp::Library;
|
|
45
|
+
|
|
46
|
+
// --- Constants ---
|
|
47
|
+
auto defConst = [](const std::string& key, double val) {
|
|
48
|
+
Math.define_data_property(key, AnyValue::make_number(val), false, false, false);
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
defConst("E", std::numbers::e);
|
|
52
|
+
defConst("LN10", std::numbers::ln10);
|
|
53
|
+
defConst("LN2", std::numbers::ln2);
|
|
54
|
+
defConst("LOG10E", std::numbers::log10e);
|
|
55
|
+
defConst("LOG2E", std::numbers::log2e);
|
|
56
|
+
defConst("PI", std::numbers::pi);
|
|
57
|
+
defConst("SQRT1_2", std::numbers::sqrt2 / 2.0);
|
|
58
|
+
defConst("SQRT2", std::numbers::sqrt2);
|
|
59
|
+
|
|
60
|
+
// --- Methods ---
|
|
61
|
+
|
|
62
|
+
// Math.abs(x)
|
|
63
|
+
Math.define_data_property("abs", AnyValue::make_function([](const AnyValue&, std::span<const AnyValue> args) -> AnyValue {
|
|
64
|
+
return AnyValue::make_number(std::abs(GetArgAsDouble(args, 0)));
|
|
65
|
+
}, "abs"));
|
|
66
|
+
|
|
67
|
+
// Math.acos(x)
|
|
68
|
+
Math.define_data_property("acos", AnyValue::make_function([](const AnyValue&, std::span<const AnyValue> args) -> AnyValue {
|
|
69
|
+
return MathFunc1(args, std::acos);
|
|
70
|
+
}, "acos"));
|
|
71
|
+
|
|
72
|
+
// Math.acosh(x)
|
|
73
|
+
Math.define_data_property("acosh", AnyValue::make_function([](const AnyValue&, std::span<const AnyValue> args) -> AnyValue {
|
|
74
|
+
return MathFunc1(args, std::acosh);
|
|
75
|
+
}, "acosh"));
|
|
76
|
+
|
|
77
|
+
// Math.asin(x)
|
|
78
|
+
Math.define_data_property("asin", AnyValue::make_function([](const AnyValue&, std::span<const AnyValue> args) -> AnyValue {
|
|
79
|
+
return MathFunc1(args, std::asin);
|
|
80
|
+
}, "asin"));
|
|
81
|
+
|
|
82
|
+
// Math.asinh(x)
|
|
83
|
+
Math.define_data_property("asinh", AnyValue::make_function([](const AnyValue&, std::span<const AnyValue> args) -> AnyValue {
|
|
84
|
+
return MathFunc1(args, std::asinh);
|
|
85
|
+
}, "asinh"));
|
|
86
|
+
|
|
87
|
+
// Math.atan(x)
|
|
88
|
+
Math.define_data_property("atan", AnyValue::make_function([](const AnyValue&, std::span<const AnyValue> args) -> AnyValue {
|
|
89
|
+
return MathFunc1(args, std::atan);
|
|
90
|
+
}, "atan"));
|
|
91
|
+
|
|
92
|
+
// Math.atan2(y, x)
|
|
93
|
+
Math.define_data_property("atan2", AnyValue::make_function([](const AnyValue&, std::span<const AnyValue> args) -> AnyValue {
|
|
94
|
+
return MathFunc2(args, std::atan2);
|
|
95
|
+
}, "atan2"));
|
|
96
|
+
|
|
97
|
+
// Math.atanh(x)
|
|
98
|
+
Math.define_data_property("atanh", AnyValue::make_function([](const AnyValue&, std::span<const AnyValue> args) -> AnyValue {
|
|
99
|
+
return MathFunc1(args, std::atanh);
|
|
100
|
+
}, "atanh"));
|
|
101
|
+
|
|
102
|
+
// Math.cbrt(x)
|
|
103
|
+
Math.define_data_property("cbrt", AnyValue::make_function([](const AnyValue&, std::span<const AnyValue> args) -> AnyValue {
|
|
104
|
+
return MathFunc1(args, std::cbrt);
|
|
105
|
+
}, "cbrt"));
|
|
106
|
+
|
|
107
|
+
// Math.ceil(x)
|
|
108
|
+
Math.define_data_property("ceil", AnyValue::make_function([](const AnyValue&, std::span<const AnyValue> args) -> AnyValue {
|
|
109
|
+
return MathFunc1(args, std::ceil);
|
|
110
|
+
}, "ceil"));
|
|
111
|
+
|
|
112
|
+
// Math.clz32(x)
|
|
113
|
+
Math.define_data_property("clz32", AnyValue::make_function([](const AnyValue&, std::span<const AnyValue> args) -> AnyValue {
|
|
114
|
+
uint32_t val = Operators_Private::ToInt32(args.empty() ? AnyValue::make_undefined() : args[0]);
|
|
115
|
+
if (val == 0) return AnyValue::make_number(32);
|
|
116
|
+
return AnyValue::make_number(std::countl_zero(val));
|
|
117
|
+
}, "clz32"));
|
|
118
|
+
|
|
119
|
+
// Math.cos(x)
|
|
120
|
+
Math.define_data_property("cos", AnyValue::make_function([](const AnyValue&, std::span<const AnyValue> args) -> AnyValue {
|
|
121
|
+
return MathFunc1(args, std::cos);
|
|
122
|
+
}, "cos"));
|
|
123
|
+
|
|
124
|
+
// Math.cosh(x)
|
|
125
|
+
Math.define_data_property("cosh", AnyValue::make_function([](const AnyValue&, std::span<const AnyValue> args) -> AnyValue {
|
|
126
|
+
return MathFunc1(args, std::cosh);
|
|
127
|
+
}, "cosh"));
|
|
128
|
+
|
|
129
|
+
// Math.exp(x)
|
|
130
|
+
Math.define_data_property("exp", AnyValue::make_function([](const AnyValue&, std::span<const AnyValue> args) -> AnyValue {
|
|
131
|
+
return MathFunc1(args, std::exp);
|
|
132
|
+
}, "exp"));
|
|
133
|
+
|
|
134
|
+
// Math.expm1(x)
|
|
135
|
+
Math.define_data_property("expm1", AnyValue::make_function([](const AnyValue&, std::span<const AnyValue> args) -> AnyValue {
|
|
136
|
+
return MathFunc1(args, std::expm1);
|
|
137
|
+
}, "expm1"));
|
|
138
|
+
|
|
139
|
+
// Math.f16round(x)
|
|
140
|
+
Math.define_data_property("f16round", AnyValue::make_function([](const AnyValue&, std::span<const AnyValue> args) -> AnyValue {
|
|
141
|
+
double d = GetArgAsDouble(args, 0);
|
|
142
|
+
#if defined(__STDCPP_FLOAT16_T__)
|
|
143
|
+
return AnyValue::make_number(static_cast<double>(static_cast<std::float16_t>(d)));
|
|
144
|
+
#else
|
|
145
|
+
// Manual fallback if float16_t is not available
|
|
146
|
+
// This is a very rough approximation, actual f16round is more complex
|
|
147
|
+
float f = static_cast<float>(d);
|
|
148
|
+
return AnyValue::make_number(static_cast<double>(f));
|
|
149
|
+
#endif
|
|
150
|
+
}, "f16round"));
|
|
151
|
+
|
|
152
|
+
// Math.floor(x)
|
|
153
|
+
Math.define_data_property("floor", AnyValue::make_function([](const AnyValue&, std::span<const AnyValue> args) -> AnyValue {
|
|
154
|
+
return MathFunc1(args, std::floor);
|
|
155
|
+
}, "floor"));
|
|
156
|
+
|
|
157
|
+
// Math.fround(x)
|
|
158
|
+
Math.define_data_property("fround", AnyValue::make_function([](const AnyValue&, std::span<const AnyValue> args) -> AnyValue {
|
|
159
|
+
double d = GetArgAsDouble(args, 0);
|
|
160
|
+
return AnyValue::make_number(static_cast<double>(static_cast<float>(d)));
|
|
161
|
+
}, "fround"));
|
|
162
|
+
|
|
163
|
+
// Math.hypot(...args)
|
|
164
|
+
Math.define_data_property("hypot", AnyValue::make_function([](const AnyValue&, std::span<const AnyValue> args) -> AnyValue {
|
|
165
|
+
double result = 0;
|
|
166
|
+
for (const auto& arg : args) {
|
|
167
|
+
double val = Operators_Private::ToNumber(arg);
|
|
168
|
+
if (std::isinf(val)) return AnyValue::make_number(std::numeric_limits<double>::infinity());
|
|
169
|
+
result = std::hypot(result, val);
|
|
170
|
+
}
|
|
171
|
+
return AnyValue::make_number(result);
|
|
172
|
+
}, "hypot"));
|
|
173
|
+
|
|
174
|
+
// Math.imul(x, y)
|
|
175
|
+
Math.define_data_property("imul", AnyValue::make_function([](const AnyValue&, std::span<const AnyValue> args) -> AnyValue {
|
|
176
|
+
int32_t a = Operators_Private::ToInt32(args.empty() ? AnyValue::make_undefined() : args[0]);
|
|
177
|
+
int32_t b = Operators_Private::ToInt32(args.size() < 2 ? AnyValue::make_undefined() : args[1]);
|
|
178
|
+
return AnyValue::make_number(a * b);
|
|
179
|
+
}, "imul"));
|
|
180
|
+
|
|
181
|
+
// Math.log(x)
|
|
182
|
+
Math.define_data_property("log", AnyValue::make_function([](const AnyValue&, std::span<const AnyValue> args) -> AnyValue {
|
|
183
|
+
return MathFunc1(args, std::log);
|
|
184
|
+
}, "log"));
|
|
185
|
+
|
|
186
|
+
// Math.log10(x)
|
|
187
|
+
Math.define_data_property("log10", AnyValue::make_function([](const AnyValue&, std::span<const AnyValue> args) -> AnyValue {
|
|
188
|
+
return MathFunc1(args, std::log10);
|
|
189
|
+
}, "log10"));
|
|
190
|
+
|
|
191
|
+
// Math.log1p(x)
|
|
192
|
+
Math.define_data_property("log1p", AnyValue::make_function([](const AnyValue&, std::span<const AnyValue> args) -> AnyValue {
|
|
193
|
+
return MathFunc1(args, std::log1p);
|
|
194
|
+
}, "log1p"));
|
|
195
|
+
|
|
196
|
+
// Math.log2(x)
|
|
197
|
+
Math.define_data_property("log2", AnyValue::make_function([](const AnyValue&, std::span<const AnyValue> args) -> AnyValue {
|
|
198
|
+
return MathFunc1(args, std::log2);
|
|
199
|
+
}, "log2"));
|
|
200
|
+
|
|
201
|
+
// Math.max(...args)
|
|
202
|
+
Math.define_data_property("max", AnyValue::make_function([](const AnyValue&, std::span<const AnyValue> args) -> AnyValue {
|
|
203
|
+
double maxVal = -std::numeric_limits<double>::infinity();
|
|
204
|
+
for (const auto& arg : args) {
|
|
205
|
+
double val = Operators_Private::ToNumber(arg);
|
|
206
|
+
if (std::isnan(val)) return AnyValue::make_nan();
|
|
207
|
+
if (val > maxVal) maxVal = val;
|
|
208
|
+
}
|
|
209
|
+
return AnyValue::make_number(maxVal);
|
|
210
|
+
}, "max"));
|
|
211
|
+
|
|
212
|
+
// Math.min(...args)
|
|
213
|
+
Math.define_data_property("min", AnyValue::make_function([](const AnyValue&, std::span<const AnyValue> args) -> AnyValue {
|
|
214
|
+
double minVal = std::numeric_limits<double>::infinity();
|
|
215
|
+
for (const auto& arg : args) {
|
|
216
|
+
double val = Operators_Private::ToNumber(arg);
|
|
217
|
+
if (std::isnan(val)) return AnyValue::make_nan();
|
|
218
|
+
if (val < minVal) minVal = val;
|
|
219
|
+
}
|
|
220
|
+
return AnyValue::make_number(minVal);
|
|
221
|
+
}, "min"));
|
|
222
|
+
|
|
223
|
+
// Math.pow(base, exponent)
|
|
224
|
+
Math.define_data_property("pow", AnyValue::make_function([](const AnyValue&, std::span<const AnyValue> args) -> AnyValue {
|
|
225
|
+
return MathFunc2(args, std::pow);
|
|
226
|
+
}, "pow"));
|
|
227
|
+
|
|
228
|
+
// Math.random()
|
|
229
|
+
Math.define_data_property("random", AnyValue::make_function([](const AnyValue&, std::span<const AnyValue> args) -> AnyValue {
|
|
230
|
+
return AnyValue::make_number(dis(gen));
|
|
231
|
+
}, "random"));
|
|
232
|
+
|
|
233
|
+
// Math.round(x)
|
|
234
|
+
Math.define_data_property("round", AnyValue::make_function([](const AnyValue&, std::span<const AnyValue> args) -> AnyValue {
|
|
235
|
+
double d = GetArgAsDouble(args, 0);
|
|
236
|
+
if (std::isnan(d)) return AnyValue::make_nan();
|
|
237
|
+
return AnyValue::make_number(std::floor(d + 0.5));
|
|
238
|
+
}, "round"));
|
|
239
|
+
|
|
240
|
+
// Math.sign(x)
|
|
241
|
+
Math.define_data_property("sign", AnyValue::make_function([](const AnyValue&, std::span<const AnyValue> args) -> AnyValue {
|
|
242
|
+
double d = GetArgAsDouble(args, 0);
|
|
243
|
+
if (std::isnan(d)) return AnyValue::make_nan();
|
|
244
|
+
if (d == 0) return AnyValue::make_number(d);
|
|
245
|
+
return AnyValue::make_number(d > 0 ? 1.0 : -1.0);
|
|
246
|
+
}, "sign"));
|
|
247
|
+
|
|
248
|
+
// Math.sin(x)
|
|
249
|
+
Math.define_data_property("sin", AnyValue::make_function([](const AnyValue&, std::span<const AnyValue> args) -> AnyValue {
|
|
250
|
+
return MathFunc1(args, std::sin);
|
|
251
|
+
}, "sin"));
|
|
252
|
+
|
|
253
|
+
// Math.sinh(x)
|
|
254
|
+
Math.define_data_property("sinh", AnyValue::make_function([](const AnyValue&, std::span<const AnyValue> args) -> AnyValue {
|
|
255
|
+
return MathFunc1(args, std::sinh);
|
|
256
|
+
}, "sinh"));
|
|
257
|
+
|
|
258
|
+
// Math.sqrt(x)
|
|
259
|
+
Math.define_data_property("sqrt", AnyValue::make_function([](const AnyValue&, std::span<const AnyValue> args) -> AnyValue {
|
|
260
|
+
return MathFunc1(args, std::sqrt);
|
|
261
|
+
}, "sqrt"));
|
|
262
|
+
|
|
263
|
+
// Math.sumPrecise(iterable)
|
|
264
|
+
Math.define_data_property("sumPrecise", AnyValue::make_function([](const AnyValue&, std::span<const AnyValue> args) -> AnyValue {
|
|
265
|
+
if (args.empty()) throw Exception::make_exception("Math.sumPrecise requires an iterable", "TypeError");
|
|
266
|
+
|
|
267
|
+
auto iterObj = jspp::Access::get_object_value_iterator(args[0], "iterable");
|
|
268
|
+
auto nextFunc = iterObj.get_own_property("next");
|
|
269
|
+
|
|
270
|
+
double sum = 0;
|
|
271
|
+
// Kahan summation algorithm for better precision
|
|
272
|
+
double c = 0;
|
|
273
|
+
|
|
274
|
+
while (true) {
|
|
275
|
+
auto nextRes = nextFunc.call(iterObj, std::span<const jspp::AnyValue>{}, "next");
|
|
276
|
+
if (is_truthy(nextRes.get_own_property("done"))) break;
|
|
277
|
+
|
|
278
|
+
double val = Operators_Private::ToNumber(nextRes.get_own_property("value"));
|
|
279
|
+
if (std::isnan(val)) {
|
|
280
|
+
sum = std::numeric_limits<double>::quiet_NaN();
|
|
281
|
+
break;
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
double y = val - c;
|
|
285
|
+
double t = sum + y;
|
|
286
|
+
c = (t - sum) - y;
|
|
287
|
+
sum = t;
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
return AnyValue::make_number(sum);
|
|
291
|
+
}, "sumPrecise"));
|
|
292
|
+
|
|
293
|
+
// Math.tan(x)
|
|
294
|
+
Math.define_data_property("tan", AnyValue::make_function([](const AnyValue&, std::span<const AnyValue> args) -> AnyValue {
|
|
295
|
+
return MathFunc1(args, std::tan);
|
|
296
|
+
}, "tan"));
|
|
297
|
+
|
|
298
|
+
// Math.tanh(x)
|
|
299
|
+
Math.define_data_property("tanh", AnyValue::make_function([](const AnyValue&, std::span<const AnyValue> args) -> AnyValue {
|
|
300
|
+
return MathFunc1(args, std::tanh);
|
|
301
|
+
}, "tanh"));
|
|
302
|
+
|
|
303
|
+
// Math.trunc(x)
|
|
304
|
+
Math.define_data_property("trunc", AnyValue::make_function([](const AnyValue&, std::span<const AnyValue> args) -> AnyValue {
|
|
305
|
+
return MathFunc1(args, std::trunc);
|
|
306
|
+
}, "trunc"));
|
|
307
|
+
}
|
|
308
|
+
} mathInit;
|
|
@@ -0,0 +1,288 @@
|
|
|
1
|
+
#pragma once
|
|
2
|
+
|
|
3
|
+
#include "types.hpp"
|
|
4
|
+
#include "any_value.hpp"
|
|
5
|
+
#include "utils/access.hpp"
|
|
6
|
+
#include "exception.hpp"
|
|
7
|
+
|
|
8
|
+
// Define Object constructor
|
|
9
|
+
inline auto Object = jspp::AnyValue::make_class([](const jspp::AnyValue &thisVal, std::span<const jspp::AnyValue> args) -> jspp::AnyValue
|
|
10
|
+
{
|
|
11
|
+
if (args.empty() || args[0].is_undefined() || args[0].is_null()) {
|
|
12
|
+
return jspp::AnyValue::make_object({});
|
|
13
|
+
}
|
|
14
|
+
// Return argument if it is an object
|
|
15
|
+
if (args[0].is_object() || args[0].is_array() || args[0].is_function() || args[0].is_promise() || args[0].is_iterator()) {
|
|
16
|
+
return args[0];
|
|
17
|
+
}
|
|
18
|
+
// TODO: Wrapper objects for primitives
|
|
19
|
+
return jspp::AnyValue::make_object({}); }, "Object");
|
|
20
|
+
|
|
21
|
+
struct ObjectInit
|
|
22
|
+
{
|
|
23
|
+
ObjectInit()
|
|
24
|
+
{
|
|
25
|
+
// Object.keys(obj)
|
|
26
|
+
Object.define_data_property("keys", jspp::AnyValue::make_function([](const jspp::AnyValue &, std::span<const jspp::AnyValue> args) -> jspp::AnyValue
|
|
27
|
+
{
|
|
28
|
+
if (args.empty()) throw jspp::Exception::make_exception("Object.keys called on non-object", "TypeError");
|
|
29
|
+
auto obj = args[0];
|
|
30
|
+
if (obj.is_null() || obj.is_undefined()) throw jspp::Exception::make_exception("Object.keys called on null or undefined", "TypeError");
|
|
31
|
+
|
|
32
|
+
auto keys = jspp::Access::get_object_keys(obj);
|
|
33
|
+
std::vector<jspp::AnyValue> keyValues;
|
|
34
|
+
for(const auto& k : keys) {
|
|
35
|
+
keyValues.push_back(jspp::AnyValue::make_string(k));
|
|
36
|
+
}
|
|
37
|
+
return jspp::AnyValue::make_array(std::move(keyValues)); }, "keys"));
|
|
38
|
+
|
|
39
|
+
// Object.values(obj)
|
|
40
|
+
Object.define_data_property("values", jspp::AnyValue::make_function([](const jspp::AnyValue &, std::span<const jspp::AnyValue> args) -> jspp::AnyValue
|
|
41
|
+
{
|
|
42
|
+
if (args.empty()) throw jspp::Exception::make_exception("Object.values called on non-object", "TypeError");
|
|
43
|
+
auto obj = args[0];
|
|
44
|
+
if (obj.is_null() || obj.is_undefined()) throw jspp::Exception::make_exception("Object.values called on null or undefined", "TypeError");
|
|
45
|
+
|
|
46
|
+
auto keys = jspp::Access::get_object_keys(obj);
|
|
47
|
+
std::vector<jspp::AnyValue> values;
|
|
48
|
+
for(const auto& k : keys) {
|
|
49
|
+
values.push_back(obj.get_property_with_receiver(k, obj));
|
|
50
|
+
}
|
|
51
|
+
return jspp::AnyValue::make_array(std::move(values)); }, "values"));
|
|
52
|
+
|
|
53
|
+
// Object.entries(obj)
|
|
54
|
+
Object.define_data_property("entries", jspp::AnyValue::make_function([](const jspp::AnyValue &, std::span<const jspp::AnyValue> args) -> jspp::AnyValue
|
|
55
|
+
{
|
|
56
|
+
if (args.empty()) throw jspp::Exception::make_exception("Object.entries called on non-object", "TypeError");
|
|
57
|
+
auto obj = args[0];
|
|
58
|
+
if (obj.is_null() || obj.is_undefined()) throw jspp::Exception::make_exception("Object.entries called on null or undefined", "TypeError");
|
|
59
|
+
|
|
60
|
+
auto keys = jspp::Access::get_object_keys(obj);
|
|
61
|
+
std::vector<jspp::AnyValue> entries;
|
|
62
|
+
for(const auto& k : keys) {
|
|
63
|
+
std::vector<jspp::AnyValue> entry;
|
|
64
|
+
entry.push_back(jspp::AnyValue::make_string(k));
|
|
65
|
+
entry.push_back(obj.get_property_with_receiver(k, obj));
|
|
66
|
+
entries.push_back(jspp::AnyValue::make_array(std::move(entry)));
|
|
67
|
+
}
|
|
68
|
+
return jspp::AnyValue::make_array(std::move(entries)); }, "entries"));
|
|
69
|
+
|
|
70
|
+
// Object.assign(target, ...sources)
|
|
71
|
+
Object.define_data_property("assign", jspp::AnyValue::make_function([](const jspp::AnyValue &, std::span<const jspp::AnyValue> args) -> jspp::AnyValue
|
|
72
|
+
{
|
|
73
|
+
if (args.empty()) throw jspp::Exception::make_exception("Cannot convert undefined or null to object", "TypeError");
|
|
74
|
+
auto target = args[0];
|
|
75
|
+
if (target.is_null() || target.is_undefined()) throw jspp::Exception::make_exception("Cannot convert undefined or null to object", "TypeError");
|
|
76
|
+
|
|
77
|
+
// In JS, Object.assign modifies target in place if it's an object.
|
|
78
|
+
// If it's a primitive, it wraps it (but our primitives are tricky, let's assume objects for now).
|
|
79
|
+
|
|
80
|
+
for (size_t i = 1; i < args.size(); ++i) {
|
|
81
|
+
auto source = args[i];
|
|
82
|
+
if (source.is_null() || source.is_undefined()) continue;
|
|
83
|
+
|
|
84
|
+
auto keys = jspp::Access::get_object_keys(source);
|
|
85
|
+
for(const auto& k : keys) {
|
|
86
|
+
auto val = source.get_property_with_receiver(k, source);
|
|
87
|
+
target.set_own_property(k, val);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
return target; }, "assign"));
|
|
91
|
+
|
|
92
|
+
// Object.is(value1, value2)
|
|
93
|
+
Object.define_data_property("is", jspp::AnyValue::make_function([](const jspp::AnyValue &, std::span<const jspp::AnyValue> args) -> jspp::AnyValue
|
|
94
|
+
{
|
|
95
|
+
jspp::AnyValue v1 = args.size() > 0 ? args[0] : jspp::AnyValue::make_undefined();
|
|
96
|
+
jspp::AnyValue v2 = args.size() > 1 ? args[1] : jspp::AnyValue::make_undefined();
|
|
97
|
+
|
|
98
|
+
if (v1.is_number() && v2.is_number()) {
|
|
99
|
+
double d1 = v1.as_double();
|
|
100
|
+
double d2 = v2.as_double();
|
|
101
|
+
if (std::isnan(d1) && std::isnan(d2)) return jspp::Constants::TRUE;
|
|
102
|
+
if (d1 == 0 && d2 == 0) {
|
|
103
|
+
// check signs
|
|
104
|
+
return jspp::AnyValue::make_boolean(std::signbit(d1) == std::signbit(d2));
|
|
105
|
+
}
|
|
106
|
+
return jspp::AnyValue::make_boolean(d1 == d2);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
return jspp::is_strictly_equal_to(v1, v2); }, "is"));
|
|
110
|
+
|
|
111
|
+
// Object.getPrototypeOf(obj)
|
|
112
|
+
Object.define_data_property("getPrototypeOf", jspp::AnyValue::make_function([](const jspp::AnyValue &, std::span<const jspp::AnyValue> args) -> jspp::AnyValue
|
|
113
|
+
{
|
|
114
|
+
if (args.empty()) throw jspp::Exception::make_exception("Object.getPrototypeOf called on non-object", "TypeError");
|
|
115
|
+
auto obj = args[0];
|
|
116
|
+
// In ES6+, primitives are coerced to objects.
|
|
117
|
+
// We'll focus on Objects/Arrays/Functions for now.
|
|
118
|
+
|
|
119
|
+
if (obj.is_object()) {
|
|
120
|
+
auto p = obj.as_object()->proto;
|
|
121
|
+
return p ? *p : jspp::AnyValue::make_null();
|
|
122
|
+
}
|
|
123
|
+
if (obj.is_array()) {
|
|
124
|
+
auto p = obj.as_array()->proto;
|
|
125
|
+
return p ? *p : jspp::AnyValue::make_null();
|
|
126
|
+
}
|
|
127
|
+
if (obj.is_function()) {
|
|
128
|
+
auto p = obj.as_function()->proto;
|
|
129
|
+
return p ? *p : jspp::AnyValue::make_null();
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
// For primitives, they use their prototype from the global constructors usually
|
|
133
|
+
// e.g. Number.prototype. For now return null or implement if needed.
|
|
134
|
+
return jspp::AnyValue::make_null(); }, "getPrototypeOf"));
|
|
135
|
+
|
|
136
|
+
// Object.setPrototypeOf(obj, proto)
|
|
137
|
+
Object.define_data_property("setPrototypeOf", jspp::AnyValue::make_function([](const jspp::AnyValue &, std::span<const jspp::AnyValue> args) -> jspp::AnyValue
|
|
138
|
+
{
|
|
139
|
+
if (args.size() < 2) throw jspp::Exception::make_exception("Object.setPrototypeOf requires at least 2 arguments", "TypeError");
|
|
140
|
+
auto obj = args[0];
|
|
141
|
+
auto proto = args[1];
|
|
142
|
+
|
|
143
|
+
if (!proto.is_object() && !proto.is_null()) {
|
|
144
|
+
throw jspp::Exception::make_exception("Object prototype may only be an Object or null", "TypeError");
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
if (obj.is_object()) {
|
|
148
|
+
obj.as_object()->proto = std::make_shared<jspp::AnyValue>(proto);
|
|
149
|
+
} else if (obj.is_array()) {
|
|
150
|
+
obj.as_array()->proto = std::make_shared<jspp::AnyValue>(proto);
|
|
151
|
+
} else if (obj.is_function()) {
|
|
152
|
+
obj.as_function()->proto = std::make_shared<jspp::AnyValue>(proto);
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
return obj; }, "setPrototypeOf"));
|
|
156
|
+
|
|
157
|
+
// Object.create(proto, [propertiesObject])
|
|
158
|
+
Object.define_data_property("create", jspp::AnyValue::make_function([](const jspp::AnyValue &, std::span<const jspp::AnyValue> args) -> jspp::AnyValue
|
|
159
|
+
{
|
|
160
|
+
if (args.empty()) throw jspp::Exception::make_exception("Object prototype may only be an Object or null", "TypeError");
|
|
161
|
+
auto proto = args[0];
|
|
162
|
+
if (!proto.is_object() && !proto.is_null()) {
|
|
163
|
+
throw jspp::Exception::make_exception("Object prototype may only be an Object or null", "TypeError");
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
auto newObj = jspp::AnyValue::make_object_with_proto({}, proto);
|
|
167
|
+
|
|
168
|
+
if (args.size() > 1 && !args[1].is_undefined()) {
|
|
169
|
+
// Object.defineProperties(newObj, propertiesObject)
|
|
170
|
+
// TODO: implement defineProperties logic if needed.
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
return newObj; }, "create"));
|
|
174
|
+
|
|
175
|
+
// Object.defineProperty(obj, prop, descriptor)
|
|
176
|
+
Object.define_data_property("defineProperty", jspp::AnyValue::make_function([](const jspp::AnyValue &, std::span<const jspp::AnyValue> args) -> jspp::AnyValue
|
|
177
|
+
{
|
|
178
|
+
if (args.size() < 3) throw jspp::Exception::make_exception("Object.defineProperty requires 3 arguments", "TypeError");
|
|
179
|
+
auto obj = args[0];
|
|
180
|
+
if (!obj.is_object() && !obj.is_array() && !obj.is_function()) throw jspp::Exception::make_exception("Object.defineProperty called on non-object", "TypeError");
|
|
181
|
+
|
|
182
|
+
std::string prop = args[1].to_std_string();
|
|
183
|
+
auto descObj = args[2];
|
|
184
|
+
|
|
185
|
+
bool enumerable = false;
|
|
186
|
+
bool configurable = false;
|
|
187
|
+
bool writable = false;
|
|
188
|
+
|
|
189
|
+
if (descObj.has_property("enumerable")) enumerable = jspp::is_truthy(descObj.get_own_property("enumerable"));
|
|
190
|
+
if (descObj.has_property("configurable")) configurable = jspp::is_truthy(descObj.get_own_property("configurable"));
|
|
191
|
+
if (descObj.has_property("writable")) writable = jspp::is_truthy(descObj.get_own_property("writable"));
|
|
192
|
+
|
|
193
|
+
bool hasValue = descObj.has_property("value");
|
|
194
|
+
bool hasGet = descObj.has_property("get");
|
|
195
|
+
bool hasSet = descObj.has_property("set");
|
|
196
|
+
|
|
197
|
+
if (hasValue && (hasGet || hasSet)) {
|
|
198
|
+
throw jspp::Exception::make_exception("Invalid property descriptor. Cannot both specify accessors and a value or writable attribute", "TypeError");
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
if (hasValue) {
|
|
202
|
+
auto value = descObj.get_own_property("value");
|
|
203
|
+
obj.define_data_property(prop, value, writable, enumerable, configurable);
|
|
204
|
+
} else {
|
|
205
|
+
jspp::AnyValue getter = jspp::AnyValue::make_undefined();
|
|
206
|
+
jspp::AnyValue setter = jspp::AnyValue::make_undefined();
|
|
207
|
+
|
|
208
|
+
if (hasGet) getter = descObj.get_own_property("get");
|
|
209
|
+
if (hasSet) setter = descObj.get_own_property("set");
|
|
210
|
+
|
|
211
|
+
if (!getter.is_undefined() && !getter.is_function()) throw jspp::Exception::make_exception("Getter must be a function: " + getter.to_std_string(), "TypeError");
|
|
212
|
+
if (!setter.is_undefined() && !setter.is_function()) throw jspp::Exception::make_exception("Setter must be a function", "TypeError");
|
|
213
|
+
|
|
214
|
+
if (obj.is_object()) {
|
|
215
|
+
auto o_ptr = obj.as_object();
|
|
216
|
+
std::optional<std::function<jspp::AnyValue(const jspp::AnyValue &, std::span<const jspp::AnyValue>)>> getFunc;
|
|
217
|
+
std::optional<std::function<jspp::AnyValue(const jspp::AnyValue &, std::span<const jspp::AnyValue>)>> setFunc;
|
|
218
|
+
|
|
219
|
+
if (getter.is_function()) {
|
|
220
|
+
getFunc = [getter](const jspp::AnyValue &thisVal, std::span<const jspp::AnyValue> args) -> jspp::AnyValue {
|
|
221
|
+
return getter.call(thisVal, args);
|
|
222
|
+
};
|
|
223
|
+
}
|
|
224
|
+
if (setter.is_function()) {
|
|
225
|
+
setFunc = [setter](const jspp::AnyValue &thisVal, std::span<const jspp::AnyValue> args) -> jspp::AnyValue {
|
|
226
|
+
return setter.call(thisVal, args);
|
|
227
|
+
};
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
auto desc = jspp::AnyValue::make_accessor_descriptor(getFunc, setFunc, enumerable, configurable);
|
|
231
|
+
auto offset = o_ptr->shape->get_offset(prop);
|
|
232
|
+
if (offset.has_value()) {
|
|
233
|
+
o_ptr->storage[offset.value()] = desc;
|
|
234
|
+
} else {
|
|
235
|
+
o_ptr->shape = o_ptr->shape->transition(prop);
|
|
236
|
+
o_ptr->storage.push_back(desc);
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
// TODO: Handle Array/Function/others
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
return obj; }, "defineProperty"));
|
|
243
|
+
|
|
244
|
+
// Object.hasOwn(obj, prop)
|
|
245
|
+
Object.define_data_property("hasOwn", jspp::AnyValue::make_function([](const jspp::AnyValue &, std::span<const jspp::AnyValue> args) -> jspp::AnyValue
|
|
246
|
+
{
|
|
247
|
+
if (args.empty()) throw jspp::Exception::make_exception("Object.hasOwn called on non-object", "TypeError");
|
|
248
|
+
auto obj = args[0];
|
|
249
|
+
if (obj.is_null() || obj.is_undefined()) throw jspp::Exception::make_exception("Object.hasOwn called on null or undefined", "TypeError");
|
|
250
|
+
std::string prop = args.size() > 1 ? args[1].to_std_string() : "undefined";
|
|
251
|
+
|
|
252
|
+
if (obj.is_object()) return jspp::AnyValue::make_boolean(obj.as_object()->shape->get_offset(prop).has_value());
|
|
253
|
+
if (obj.is_function()) return jspp::AnyValue::make_boolean(obj.as_function()->props.count(prop));
|
|
254
|
+
if (obj.is_array()) {
|
|
255
|
+
if (prop == "length") return jspp::Constants::TRUE;
|
|
256
|
+
if (jspp::JsArray::is_array_index(prop)) {
|
|
257
|
+
uint32_t idx = static_cast<uint32_t>(std::stoull(prop));
|
|
258
|
+
auto arr = obj.as_array();
|
|
259
|
+
if (idx < arr->dense.size() && !(arr->dense[idx].is_uninitialized())) return jspp::Constants::TRUE;
|
|
260
|
+
if (arr->sparse.count(idx)) return jspp::Constants::TRUE;
|
|
261
|
+
return jspp::Constants::FALSE;
|
|
262
|
+
}
|
|
263
|
+
return jspp::AnyValue::make_boolean(obj.as_array()->props.count(prop));
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
return jspp::Constants::FALSE; }, "hasOwn"));
|
|
267
|
+
|
|
268
|
+
// Object.prototype.hasOwnProperty
|
|
269
|
+
auto proto = Object.get_own_property("prototype");
|
|
270
|
+
proto.define_data_property("hasOwnProperty", jspp::AnyValue::make_function([](const jspp::AnyValue &thisVal, std::span<const jspp::AnyValue> args) -> jspp::AnyValue
|
|
271
|
+
{
|
|
272
|
+
std::string prop = args.size() > 0 ? args[0].to_std_string() : "undefined";
|
|
273
|
+
if (thisVal.is_object()) return jspp::AnyValue::make_boolean(thisVal.as_object()->shape->get_offset(prop).has_value());
|
|
274
|
+
if (thisVal.is_function()) return jspp::AnyValue::make_boolean(thisVal.as_function()->props.count(prop));
|
|
275
|
+
if (thisVal.is_array()) {
|
|
276
|
+
if (prop == "length") return jspp::Constants::TRUE;
|
|
277
|
+
if (jspp::JsArray::is_array_index(prop)) {
|
|
278
|
+
uint32_t idx = static_cast<uint32_t>(std::stoull(prop));
|
|
279
|
+
auto arr = thisVal.as_array();
|
|
280
|
+
if (idx < arr->dense.size() && !(arr->dense[idx].is_uninitialized())) return jspp::Constants::TRUE;
|
|
281
|
+
if (arr->sparse.count(idx)) return jspp::Constants::TRUE;
|
|
282
|
+
return jspp::Constants::FALSE;
|
|
283
|
+
}
|
|
284
|
+
return jspp::AnyValue::make_boolean(thisVal.as_array()->props.count(prop));
|
|
285
|
+
}
|
|
286
|
+
return jspp::Constants::FALSE; }, "hasOwnProperty"));
|
|
287
|
+
}
|
|
288
|
+
} objectInit;
|
|
@@ -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 jspp::AnyValue &thisVal,
|
|
16
|
+
[startTime = std::chrono::steady_clock::now()](const jspp::AnyValue &thisVal, std::span<const 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,39 @@
|
|
|
1
|
+
#pragma once
|
|
2
|
+
|
|
3
|
+
#include "types.hpp"
|
|
4
|
+
#include "any_value.hpp"
|
|
5
|
+
|
|
6
|
+
#ifdef _WIN32
|
|
7
|
+
#define JSPP_PLATFORM "win32"
|
|
8
|
+
#else
|
|
9
|
+
#define JSPP_PLATFORM "linux"
|
|
10
|
+
#endif
|
|
11
|
+
|
|
12
|
+
inline auto process = jspp::AnyValue::make_object({
|
|
13
|
+
{"argv", jspp::AnyValue::make_array(std::vector<jspp::AnyValue>{})},
|
|
14
|
+
{"env", jspp::AnyValue::make_object({})},
|
|
15
|
+
{"platform", jspp::AnyValue::make_string(JSPP_PLATFORM)},
|
|
16
|
+
{"exit", jspp::AnyValue::make_function([](const jspp::AnyValue&, std::span<const jspp::AnyValue> args) -> jspp::AnyValue {
|
|
17
|
+
int code = 0;
|
|
18
|
+
if (!args.empty() && args[0].is_number()) {
|
|
19
|
+
code = static_cast<int>(args[0].as_double());
|
|
20
|
+
}
|
|
21
|
+
std::exit(code);
|
|
22
|
+
return jspp::Constants::UNDEFINED;
|
|
23
|
+
}, "exit")}
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
namespace jspp {
|
|
27
|
+
inline void setup_process_argv(int argc, char** argv) {
|
|
28
|
+
std::vector<jspp::AnyValue> args;
|
|
29
|
+
if (argc > 0) {
|
|
30
|
+
args.push_back(jspp::AnyValue::make_string(argv[0]));
|
|
31
|
+
args.push_back(jspp::AnyValue::make_string("index.js"));
|
|
32
|
+
for (int i = 1; i < argc; ++i) {
|
|
33
|
+
args.push_back(jspp::AnyValue::make_string(argv[i]));
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
process.set_own_property("argv", jspp::AnyValue::make_array(std::move(args)));
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|