@ugo-studio/jspp 0.1.0
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/LICENSE +21 -0
- package/README.md +162 -0
- package/dist/analysis/scope.js +77 -0
- package/dist/analysis/typeAnalyzer.js +224 -0
- package/dist/ast/types.js +1 -0
- package/dist/cli.js +63 -0
- package/dist/core/codegen/declaration-handlers.js +49 -0
- package/dist/core/codegen/expression-handlers.js +333 -0
- package/dist/core/codegen/function-handlers.js +94 -0
- package/dist/core/codegen/helpers.js +83 -0
- package/dist/core/codegen/index.js +54 -0
- package/dist/core/codegen/literal-handlers.js +32 -0
- package/dist/core/codegen/statement-handlers.js +485 -0
- package/dist/core/codegen/visitor.js +86 -0
- package/dist/core/parser.js +6 -0
- package/dist/core/traverser.js +19 -0
- package/dist/index.js +16 -0
- package/package.json +41 -0
- package/src/prelude/access.hpp +86 -0
- package/src/prelude/any_value.hpp +734 -0
- package/src/prelude/descriptors.hpp +25 -0
- package/src/prelude/error.hpp +31 -0
- package/src/prelude/error_helpers.hpp +59 -0
- package/src/prelude/index.hpp +29 -0
- package/src/prelude/library/console.hpp +111 -0
- package/src/prelude/library/global.hpp +10 -0
- package/src/prelude/library/symbol.hpp +8 -0
- package/src/prelude/log_string.hpp +403 -0
- package/src/prelude/operators.hpp +256 -0
- package/src/prelude/types.hpp +50 -0
- package/src/prelude/values/array.hpp +50 -0
- package/src/prelude/values/function.hpp +19 -0
- package/src/prelude/values/non_values.hpp +20 -0
- package/src/prelude/values/object.hpp +17 -0
- package/src/prelude/values/operators/array.hpp +165 -0
- package/src/prelude/values/operators/function.hpp +34 -0
- package/src/prelude/values/operators/object.hpp +34 -0
- package/src/prelude/values/prototypes/array.hpp +228 -0
- package/src/prelude/values/prototypes/function.hpp +0 -0
- package/src/prelude/values/prototypes/object.hpp +0 -0
- package/src/prelude/values/prototypes/string.hpp +357 -0
- package/src/prelude/well_known_symbols.hpp +10 -0
|
@@ -0,0 +1,256 @@
|
|
|
1
|
+
#pragma once
|
|
2
|
+
|
|
3
|
+
#include "types.hpp"
|
|
4
|
+
#include "any_value.hpp"
|
|
5
|
+
#include <cstdint> // For int32_t
|
|
6
|
+
#include <cmath> // For fmod, isnan, isinf, floor, abs, pow
|
|
7
|
+
|
|
8
|
+
namespace jspp
|
|
9
|
+
{
|
|
10
|
+
// Private namespace for helper functions that implement JS type conversions.
|
|
11
|
+
namespace Operators_Private
|
|
12
|
+
{
|
|
13
|
+
// Implements the ToNumber abstract operation from ECMA-262.
|
|
14
|
+
inline double ToNumber(const AnyValue &val)
|
|
15
|
+
{
|
|
16
|
+
if (val.is_number())
|
|
17
|
+
return val.as_double();
|
|
18
|
+
if (val.is_null())
|
|
19
|
+
return 0.0;
|
|
20
|
+
if (val.is_undefined() || val.is_uninitialized())
|
|
21
|
+
return std::numeric_limits<double>::quiet_NaN();
|
|
22
|
+
if (val.is_boolean())
|
|
23
|
+
return val.as_boolean() ? 1.0 : 0.0;
|
|
24
|
+
if (val.is_string())
|
|
25
|
+
{
|
|
26
|
+
const std::string &s = *val.as_string();
|
|
27
|
+
// JS considers empty or whitespace-only strings as 0.
|
|
28
|
+
if (s.empty() || std::all_of(s.begin(), s.end(), isspace))
|
|
29
|
+
return 0.0;
|
|
30
|
+
try
|
|
31
|
+
{
|
|
32
|
+
size_t pos;
|
|
33
|
+
double num = std::stod(s, &pos);
|
|
34
|
+
// Ensure the entire string was parsed, allowing for trailing whitespace.
|
|
35
|
+
while (pos < s.length() && std::isspace(s[pos]))
|
|
36
|
+
pos++;
|
|
37
|
+
if (pos != s.length())
|
|
38
|
+
return std::numeric_limits<double>::quiet_NaN();
|
|
39
|
+
return num;
|
|
40
|
+
}
|
|
41
|
+
catch (...)
|
|
42
|
+
{
|
|
43
|
+
return std::numeric_limits<double>::quiet_NaN();
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
// In a full engine, objects would be converted via valueOf/toString.
|
|
47
|
+
// Here we simplify and return NaN.
|
|
48
|
+
return std::numeric_limits<double>::quiet_NaN();
|
|
49
|
+
}
|
|
50
|
+
// Implements the ToInt32 abstract operation from ECMA-262.
|
|
51
|
+
inline int32_t ToInt32(const AnyValue &val)
|
|
52
|
+
{
|
|
53
|
+
double num = ToNumber(val);
|
|
54
|
+
|
|
55
|
+
if (std::isnan(num) || std::isinf(num) || num == 0)
|
|
56
|
+
return 0;
|
|
57
|
+
|
|
58
|
+
double posInt = std::signbit(num) ? -std::floor(std::abs(num)) : std::floor(std::abs(num));
|
|
59
|
+
double int32bit = fmod(posInt, 4294967296.0); // 2^32
|
|
60
|
+
|
|
61
|
+
if (int32bit >= 2147483648.0) // 2^31
|
|
62
|
+
return static_cast<int32_t>(int32bit - 4294967296.0);
|
|
63
|
+
else
|
|
64
|
+
return static_cast<int32_t>(int32bit);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// --- BASIC ARITHEMETIC
|
|
69
|
+
inline AnyValue operator+(const AnyValue &lhs, const AnyValue &rhs)
|
|
70
|
+
{
|
|
71
|
+
// Special case for addition: string concatenation has priority
|
|
72
|
+
if (lhs.is_string() || rhs.is_string())
|
|
73
|
+
return AnyValue::make_string(lhs.to_std_string() + rhs.to_std_string());
|
|
74
|
+
if (lhs.is_number() && rhs.is_number())
|
|
75
|
+
return AnyValue::make_number(lhs.as_double() + rhs.as_double());
|
|
76
|
+
// Fallback to numeric conversion
|
|
77
|
+
return AnyValue::make_number(Operators_Private::ToNumber(lhs) + Operators_Private::ToNumber(rhs));
|
|
78
|
+
}
|
|
79
|
+
inline AnyValue operator-(const AnyValue &lhs, const AnyValue &rhs)
|
|
80
|
+
{
|
|
81
|
+
return AnyValue::make_number(Operators_Private::ToNumber(lhs) - Operators_Private::ToNumber(rhs));
|
|
82
|
+
}
|
|
83
|
+
inline AnyValue operator*(const AnyValue &lhs, const AnyValue &rhs)
|
|
84
|
+
{
|
|
85
|
+
return AnyValue::make_number(Operators_Private::ToNumber(lhs) * Operators_Private::ToNumber(rhs));
|
|
86
|
+
}
|
|
87
|
+
inline AnyValue operator/(const AnyValue &lhs, const AnyValue &rhs)
|
|
88
|
+
{
|
|
89
|
+
return AnyValue::make_number(Operators_Private::ToNumber(lhs) / Operators_Private::ToNumber(rhs));
|
|
90
|
+
}
|
|
91
|
+
inline AnyValue operator%(const AnyValue &lhs, const AnyValue &rhs)
|
|
92
|
+
{
|
|
93
|
+
return AnyValue::make_number(std::fmod(Operators_Private::ToNumber(lhs), Operators_Private::ToNumber(rhs)));
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// --- UNARY OPERATORS
|
|
97
|
+
inline AnyValue operator-(const AnyValue &val)
|
|
98
|
+
{
|
|
99
|
+
return AnyValue::make_number(-Operators_Private::ToNumber(val));
|
|
100
|
+
}
|
|
101
|
+
inline AnyValue operator~(const AnyValue &val)
|
|
102
|
+
{
|
|
103
|
+
return AnyValue::make_number(~Operators_Private::ToInt32(val));
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// --- EXPONENTIATION
|
|
107
|
+
inline AnyValue pow(const AnyValue &lhs, const AnyValue &rhs)
|
|
108
|
+
{
|
|
109
|
+
double base = Operators_Private::ToNumber(lhs);
|
|
110
|
+
double exp = Operators_Private::ToNumber(rhs);
|
|
111
|
+
return AnyValue::make_number(std::pow(base, exp));
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// --- COMPARISON OPERATORS
|
|
115
|
+
inline AnyValue operator<(const AnyValue &lhs, const AnyValue &rhs)
|
|
116
|
+
{
|
|
117
|
+
// Simplified Abstract Relational Comparison
|
|
118
|
+
if (lhs.is_string() && rhs.is_string())
|
|
119
|
+
return AnyValue::make_boolean(*lhs.as_string() < *rhs.as_string());
|
|
120
|
+
|
|
121
|
+
double l = Operators_Private::ToNumber(lhs);
|
|
122
|
+
double r = Operators_Private::ToNumber(rhs);
|
|
123
|
+
|
|
124
|
+
if (std::isnan(l) || std::isnan(r))
|
|
125
|
+
return AnyValue::make_boolean(false); // Comparison with NaN is false
|
|
126
|
+
|
|
127
|
+
return AnyValue::make_boolean(l < r);
|
|
128
|
+
}
|
|
129
|
+
inline AnyValue operator>(const AnyValue &lhs, const AnyValue &rhs)
|
|
130
|
+
{
|
|
131
|
+
return rhs < lhs;
|
|
132
|
+
}
|
|
133
|
+
inline AnyValue operator<=(const AnyValue &lhs, const AnyValue &rhs)
|
|
134
|
+
{
|
|
135
|
+
// a <= b is equivalent to !(b < a)
|
|
136
|
+
AnyValue result = rhs < lhs;
|
|
137
|
+
return AnyValue::make_boolean(!result.as_boolean());
|
|
138
|
+
}
|
|
139
|
+
inline AnyValue operator>=(const AnyValue &lhs, const AnyValue &rhs)
|
|
140
|
+
{
|
|
141
|
+
// a >= b is equivalent to !(a < b)
|
|
142
|
+
AnyValue result = lhs < rhs;
|
|
143
|
+
return AnyValue::make_boolean(!result.as_boolean());
|
|
144
|
+
}
|
|
145
|
+
inline AnyValue operator==(const AnyValue &lhs, const AnyValue &rhs)
|
|
146
|
+
{
|
|
147
|
+
return AnyValue::make_boolean(lhs.is_equal_to(rhs));
|
|
148
|
+
}
|
|
149
|
+
inline AnyValue operator!=(const AnyValue &lhs, const AnyValue &rhs)
|
|
150
|
+
{
|
|
151
|
+
return AnyValue::make_boolean(!lhs.is_equal_to(rhs));
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
// --- BITWISE OPERATORS
|
|
155
|
+
inline AnyValue operator^(const AnyValue &lhs, const AnyValue &rhs)
|
|
156
|
+
{
|
|
157
|
+
return AnyValue::make_number(Operators_Private::ToInt32(lhs) ^ Operators_Private::ToInt32(rhs));
|
|
158
|
+
}
|
|
159
|
+
inline AnyValue operator&(const AnyValue &lhs, const AnyValue &rhs)
|
|
160
|
+
{
|
|
161
|
+
return AnyValue::make_number(Operators_Private::ToInt32(lhs) & Operators_Private::ToInt32(rhs));
|
|
162
|
+
}
|
|
163
|
+
inline AnyValue operator|(const AnyValue &lhs, const AnyValue &rhs)
|
|
164
|
+
{
|
|
165
|
+
return AnyValue::make_number(Operators_Private::ToInt32(lhs) | Operators_Private::ToInt32(rhs));
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
// --- SHIFT OPERATORS
|
|
169
|
+
inline AnyValue operator<<(const AnyValue &lhs, const AnyValue &rhs)
|
|
170
|
+
{
|
|
171
|
+
// The right operand is treated as an unsigned 32-bit integer, and only the lower 5 bits are used.
|
|
172
|
+
return AnyValue::make_number(Operators_Private::ToInt32(lhs) << (Operators_Private::ToInt32(rhs) & 0x1F));
|
|
173
|
+
}
|
|
174
|
+
inline AnyValue operator>>(const AnyValue &lhs, const AnyValue &rhs)
|
|
175
|
+
{
|
|
176
|
+
return AnyValue::make_number(Operators_Private::ToInt32(lhs) >> (Operators_Private::ToInt32(rhs) & 0x1F));
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
// --- INCREMENT / DECREMENT
|
|
180
|
+
inline AnyValue &operator++(AnyValue &val) // pre-increment
|
|
181
|
+
{
|
|
182
|
+
double num = Operators_Private::ToNumber(val);
|
|
183
|
+
val = AnyValue::make_number(num + 1.0);
|
|
184
|
+
return val;
|
|
185
|
+
}
|
|
186
|
+
inline AnyValue operator++(AnyValue &val, int) // post-increment
|
|
187
|
+
{
|
|
188
|
+
AnyValue old = AnyValue::make_number(Operators_Private::ToNumber(val));
|
|
189
|
+
++val;
|
|
190
|
+
return old;
|
|
191
|
+
}
|
|
192
|
+
inline AnyValue &operator--(AnyValue &val) // pre-decrement
|
|
193
|
+
{
|
|
194
|
+
double num = Operators_Private::ToNumber(val);
|
|
195
|
+
val = AnyValue::make_number(num - 1.0);
|
|
196
|
+
return val;
|
|
197
|
+
}
|
|
198
|
+
inline AnyValue operator--(AnyValue &val, int) // post-decrement
|
|
199
|
+
{
|
|
200
|
+
AnyValue old = AnyValue::make_number(Operators_Private::ToNumber(val));
|
|
201
|
+
--val;
|
|
202
|
+
return old;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
// --- COMPOUND ASSIGNMENT
|
|
206
|
+
inline AnyValue &operator+=(AnyValue &lhs, const AnyValue &rhs)
|
|
207
|
+
{
|
|
208
|
+
lhs = lhs + rhs;
|
|
209
|
+
return lhs;
|
|
210
|
+
}
|
|
211
|
+
inline AnyValue &operator-=(AnyValue &lhs, const AnyValue &rhs)
|
|
212
|
+
{
|
|
213
|
+
lhs = lhs - rhs;
|
|
214
|
+
return lhs;
|
|
215
|
+
}
|
|
216
|
+
inline AnyValue &operator*=(AnyValue &lhs, const AnyValue &rhs)
|
|
217
|
+
{
|
|
218
|
+
lhs = lhs * rhs;
|
|
219
|
+
return lhs;
|
|
220
|
+
}
|
|
221
|
+
inline AnyValue &operator/=(AnyValue &lhs, const AnyValue &rhs)
|
|
222
|
+
{
|
|
223
|
+
lhs = lhs / rhs;
|
|
224
|
+
return lhs;
|
|
225
|
+
}
|
|
226
|
+
inline AnyValue &operator%=(AnyValue &lhs, const AnyValue &rhs)
|
|
227
|
+
{
|
|
228
|
+
lhs = lhs % rhs;
|
|
229
|
+
return lhs;
|
|
230
|
+
}
|
|
231
|
+
inline AnyValue &operator^=(AnyValue &lhs, const AnyValue &rhs)
|
|
232
|
+
{
|
|
233
|
+
lhs = lhs ^ rhs;
|
|
234
|
+
return lhs;
|
|
235
|
+
}
|
|
236
|
+
inline AnyValue &operator&=(AnyValue &lhs, const AnyValue &rhs)
|
|
237
|
+
{
|
|
238
|
+
lhs = lhs & rhs;
|
|
239
|
+
return lhs;
|
|
240
|
+
}
|
|
241
|
+
inline AnyValue &operator|=(AnyValue &lhs, const AnyValue &rhs)
|
|
242
|
+
{
|
|
243
|
+
lhs = lhs | rhs;
|
|
244
|
+
return lhs;
|
|
245
|
+
}
|
|
246
|
+
inline AnyValue &operator<<=(AnyValue &lhs, const AnyValue &rhs)
|
|
247
|
+
{
|
|
248
|
+
lhs = lhs << rhs;
|
|
249
|
+
return lhs;
|
|
250
|
+
}
|
|
251
|
+
inline AnyValue &operator>>=(AnyValue &lhs, const AnyValue &rhs)
|
|
252
|
+
{
|
|
253
|
+
lhs = lhs >> rhs;
|
|
254
|
+
return lhs;
|
|
255
|
+
}
|
|
256
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
#pragma once
|
|
2
|
+
|
|
3
|
+
#include <iostream>
|
|
4
|
+
#include <string>
|
|
5
|
+
#include <vector>
|
|
6
|
+
#include <variant>
|
|
7
|
+
#include <functional>
|
|
8
|
+
#include <memory>
|
|
9
|
+
#include <unordered_map>
|
|
10
|
+
#include <algorithm>
|
|
11
|
+
#include <cctype>
|
|
12
|
+
#include <cstring>
|
|
13
|
+
#include <set>
|
|
14
|
+
#include <cmath>
|
|
15
|
+
#include <optional>
|
|
16
|
+
|
|
17
|
+
// JSPP standard library
|
|
18
|
+
namespace jspp
|
|
19
|
+
{
|
|
20
|
+
// Forward declarations
|
|
21
|
+
struct JsUndefined; // cannot set property
|
|
22
|
+
struct JsNull; // cannot set property
|
|
23
|
+
struct JsUninitialized; // cannot set property
|
|
24
|
+
struct JsObject; // can set property
|
|
25
|
+
struct JsArray; // can set property
|
|
26
|
+
struct JsFunction; // can set property
|
|
27
|
+
|
|
28
|
+
// Object property configuration forward declarations
|
|
29
|
+
struct DataDescriptor;
|
|
30
|
+
struct AccessorDescriptor;
|
|
31
|
+
|
|
32
|
+
// Dynamic AnyValue
|
|
33
|
+
class AnyValue;
|
|
34
|
+
|
|
35
|
+
// Custom runtime exception
|
|
36
|
+
struct RuntimeError;
|
|
37
|
+
|
|
38
|
+
// Arithemetic operators
|
|
39
|
+
inline AnyValue pow(const AnyValue &lhs, const AnyValue &rhs);
|
|
40
|
+
|
|
41
|
+
// AnyValue prototypes
|
|
42
|
+
namespace StringPrototypes
|
|
43
|
+
{
|
|
44
|
+
inline std::optional<AnyValue> get(const std::string &key, const std::unique_ptr<std::string> &self);
|
|
45
|
+
}
|
|
46
|
+
namespace ArrayPrototypes
|
|
47
|
+
{
|
|
48
|
+
inline std::optional<AnyValue> get(const std::string &key, JsArray *self);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
#pragma once
|
|
2
|
+
|
|
3
|
+
#include "types.hpp"
|
|
4
|
+
#include <optional>
|
|
5
|
+
|
|
6
|
+
namespace jspp
|
|
7
|
+
{
|
|
8
|
+
class AnyValue;
|
|
9
|
+
|
|
10
|
+
struct JsArray
|
|
11
|
+
{
|
|
12
|
+
std::vector<std::optional<AnyValue>> dense; // dense storage for small/contiguous indices
|
|
13
|
+
std::unordered_map<uint32_t, std::optional<AnyValue>> sparse; // sparse indices (very large indices)
|
|
14
|
+
std::unordered_map<std::string, AnyValue> props; // non-index string properties
|
|
15
|
+
uint64_t length = 0;
|
|
16
|
+
|
|
17
|
+
JsArray() = default;
|
|
18
|
+
explicit JsArray(const std::vector<std::optional<AnyValue>> &items) : dense(items), length(items.size()) {}
|
|
19
|
+
|
|
20
|
+
std::string to_std_string() const;
|
|
21
|
+
|
|
22
|
+
AnyValue get_property(const std::string &key);
|
|
23
|
+
AnyValue get_property(uint32_t idx);
|
|
24
|
+
AnyValue set_property(const std::string &key, const AnyValue &value);
|
|
25
|
+
AnyValue set_property(uint32_t idx, const AnyValue &value);
|
|
26
|
+
|
|
27
|
+
static bool is_array_index(const std::string &s)
|
|
28
|
+
{
|
|
29
|
+
if (s.empty())
|
|
30
|
+
return false;
|
|
31
|
+
// must be all digits
|
|
32
|
+
for (char c : s)
|
|
33
|
+
if (!std::isdigit(static_cast<unsigned char>(c)))
|
|
34
|
+
return false;
|
|
35
|
+
// reject the special 2^32-1 string
|
|
36
|
+
if (s == "4294967295")
|
|
37
|
+
return false;
|
|
38
|
+
// parse without overflow up to 2^32-1
|
|
39
|
+
uint64_t v = 0;
|
|
40
|
+
for (char c : s)
|
|
41
|
+
{
|
|
42
|
+
v = v * 10 + (c - '0');
|
|
43
|
+
if (v >= (1ULL << 32))
|
|
44
|
+
return false;
|
|
45
|
+
}
|
|
46
|
+
// ToString(ToUint32(v)) === s?
|
|
47
|
+
return std::to_string(static_cast<uint32_t>(v)) == s;
|
|
48
|
+
}
|
|
49
|
+
};
|
|
50
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
#pragma once
|
|
2
|
+
|
|
3
|
+
#include "types.hpp"
|
|
4
|
+
|
|
5
|
+
namespace jspp
|
|
6
|
+
{
|
|
7
|
+
class AnyValue;
|
|
8
|
+
|
|
9
|
+
struct JsFunction
|
|
10
|
+
{
|
|
11
|
+
std::function<AnyValue(const std::vector<AnyValue> &)> call;
|
|
12
|
+
std::string name;
|
|
13
|
+
std::unordered_map<std::string, AnyValue> props;
|
|
14
|
+
|
|
15
|
+
std::string to_std_string() const;
|
|
16
|
+
AnyValue get_property(const std::string &key);
|
|
17
|
+
AnyValue set_property(const std::string &key, const AnyValue &value);
|
|
18
|
+
};
|
|
19
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
#pragma once
|
|
2
|
+
|
|
3
|
+
#include "types.hpp"
|
|
4
|
+
|
|
5
|
+
namespace jspp
|
|
6
|
+
{
|
|
7
|
+
class AnyValue;
|
|
8
|
+
|
|
9
|
+
struct JsObject
|
|
10
|
+
{
|
|
11
|
+
std::unordered_map<std::string, AnyValue> props;
|
|
12
|
+
|
|
13
|
+
std::string to_std_string() const;
|
|
14
|
+
AnyValue get_property(const std::string &key);
|
|
15
|
+
AnyValue set_property(const std::string &key, const AnyValue &value);
|
|
16
|
+
};
|
|
17
|
+
}
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
#pragma once
|
|
2
|
+
|
|
3
|
+
#include "types.hpp"
|
|
4
|
+
#include "values/array.hpp"
|
|
5
|
+
#include "error.hpp"
|
|
6
|
+
#include "any_value.hpp"
|
|
7
|
+
#include "values/prototypes/array.hpp"
|
|
8
|
+
|
|
9
|
+
std::string jspp::JsArray::to_std_string() const
|
|
10
|
+
{
|
|
11
|
+
if (length == 0)
|
|
12
|
+
{
|
|
13
|
+
return "";
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
std::string result = "";
|
|
17
|
+
for (uint64_t i = 0; i < length; ++i)
|
|
18
|
+
{
|
|
19
|
+
std::optional<AnyValue> itemVal;
|
|
20
|
+
if (i < dense.size())
|
|
21
|
+
{
|
|
22
|
+
itemVal = dense[i];
|
|
23
|
+
}
|
|
24
|
+
else
|
|
25
|
+
{
|
|
26
|
+
auto it = sparse.find(static_cast<uint32_t>(i));
|
|
27
|
+
if (it != sparse.end())
|
|
28
|
+
{
|
|
29
|
+
itemVal = it->second;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
if (itemVal.has_value())
|
|
34
|
+
{
|
|
35
|
+
const auto &item = itemVal.value();
|
|
36
|
+
if (!item.is_undefined() && !item.is_null())
|
|
37
|
+
{
|
|
38
|
+
result += item.to_std_string();
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
if (i < length - 1)
|
|
43
|
+
{
|
|
44
|
+
result += ",";
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
return result;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
jspp::AnyValue jspp::JsArray::get_property(const std::string &key)
|
|
51
|
+
{
|
|
52
|
+
if (
|
|
53
|
+
!key.empty() && std::isdigit(static_cast<unsigned char>(key[0])) // Quick check: if the first character is not a digit, it can't be a standard index.
|
|
54
|
+
&& is_array_index(key))
|
|
55
|
+
{
|
|
56
|
+
uint32_t idx = static_cast<uint32_t>(std::stoull(key));
|
|
57
|
+
return get_property(idx);
|
|
58
|
+
}
|
|
59
|
+
else
|
|
60
|
+
{
|
|
61
|
+
auto it = props.find(key);
|
|
62
|
+
if (it == props.end())
|
|
63
|
+
{
|
|
64
|
+
// check prototype
|
|
65
|
+
auto proto_it = ArrayPrototypes::get(key, this);
|
|
66
|
+
if (proto_it.has_value())
|
|
67
|
+
{
|
|
68
|
+
return AnyValue::resolve_property_for_read(proto_it.value());
|
|
69
|
+
}
|
|
70
|
+
// not found
|
|
71
|
+
return AnyValue::make_undefined();
|
|
72
|
+
}
|
|
73
|
+
return AnyValue::resolve_property_for_read(it->second);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
jspp::AnyValue jspp::JsArray::get_property(uint32_t idx)
|
|
78
|
+
{
|
|
79
|
+
if (idx < dense.size())
|
|
80
|
+
{
|
|
81
|
+
return AnyValue::resolve_property_for_read(dense[idx].value_or(AnyValue::make_undefined()));
|
|
82
|
+
}
|
|
83
|
+
const auto &it = sparse.find(idx);
|
|
84
|
+
if (it != sparse.end())
|
|
85
|
+
{
|
|
86
|
+
return AnyValue::resolve_property_for_read(it->second.value_or(AnyValue::make_undefined()));
|
|
87
|
+
}
|
|
88
|
+
return AnyValue::make_undefined();
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
jspp::AnyValue jspp::JsArray::set_property(const std::string &key, const AnyValue &value)
|
|
92
|
+
{
|
|
93
|
+
if (
|
|
94
|
+
!key.empty() && std::isdigit(static_cast<unsigned char>(key[0])) // Quick check: if the first character is not a digit, it can't be a standard index.
|
|
95
|
+
&& is_array_index(key))
|
|
96
|
+
{
|
|
97
|
+
uint32_t idx = static_cast<uint32_t>(std::stoull(key));
|
|
98
|
+
return set_property(idx, value);
|
|
99
|
+
}
|
|
100
|
+
else
|
|
101
|
+
{
|
|
102
|
+
// set prototype property if accessor descriptor
|
|
103
|
+
auto proto_it = ArrayPrototypes::get(key, this);
|
|
104
|
+
if (proto_it.has_value())
|
|
105
|
+
{
|
|
106
|
+
auto proto_value = proto_it.value();
|
|
107
|
+
if (proto_value.is_accessor_descriptor())
|
|
108
|
+
{
|
|
109
|
+
return AnyValue::resolve_property_for_write(proto_it.value(), value);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// set own property
|
|
114
|
+
auto it = props.find(key);
|
|
115
|
+
if (it != props.end())
|
|
116
|
+
{
|
|
117
|
+
return AnyValue::resolve_property_for_write(it->second, value);
|
|
118
|
+
}
|
|
119
|
+
else
|
|
120
|
+
{
|
|
121
|
+
props[key] = value;
|
|
122
|
+
return value;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
jspp::AnyValue jspp::JsArray::set_property(uint32_t idx, const AnyValue &value)
|
|
128
|
+
{
|
|
129
|
+
uint64_t newLen = static_cast<uint64_t>(idx) + 1;
|
|
130
|
+
if (newLen > length)
|
|
131
|
+
length = newLen;
|
|
132
|
+
|
|
133
|
+
const uint32_t DENSE_GROW_THRESHOLD = 1024;
|
|
134
|
+
if (idx < dense.size())
|
|
135
|
+
{
|
|
136
|
+
if (!dense[idx].has_value())
|
|
137
|
+
{
|
|
138
|
+
dense[idx] = AnyValue::make_undefined();
|
|
139
|
+
}
|
|
140
|
+
return AnyValue::resolve_property_for_write(dense[idx].value(), value);
|
|
141
|
+
}
|
|
142
|
+
else if (idx <= dense.size() + DENSE_GROW_THRESHOLD)
|
|
143
|
+
{
|
|
144
|
+
dense.resize(idx + 1);
|
|
145
|
+
dense[idx] = value;
|
|
146
|
+
return value;
|
|
147
|
+
}
|
|
148
|
+
else
|
|
149
|
+
{
|
|
150
|
+
auto it = sparse.find(idx);
|
|
151
|
+
if (it != sparse.end())
|
|
152
|
+
{
|
|
153
|
+
if (!it->second.has_value())
|
|
154
|
+
{
|
|
155
|
+
it->second = AnyValue::make_undefined();
|
|
156
|
+
}
|
|
157
|
+
return AnyValue::resolve_property_for_write(it->second.value(), value);
|
|
158
|
+
}
|
|
159
|
+
else
|
|
160
|
+
{
|
|
161
|
+
sparse[idx] = value;
|
|
162
|
+
return value;
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
#pragma once
|
|
2
|
+
|
|
3
|
+
#include "types.hpp"
|
|
4
|
+
#include "values/function.hpp"
|
|
5
|
+
#include "any_value.hpp"
|
|
6
|
+
|
|
7
|
+
std::string jspp::JsFunction::to_std_string() const
|
|
8
|
+
{
|
|
9
|
+
return "function " + name + "() { [native code] }";
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
jspp::AnyValue jspp::JsFunction::get_property(const std::string &key)
|
|
13
|
+
{
|
|
14
|
+
auto it = props.find(key);
|
|
15
|
+
if (it != props.end())
|
|
16
|
+
{
|
|
17
|
+
return jspp::AnyValue::resolve_property_for_read(it->second);
|
|
18
|
+
}
|
|
19
|
+
return jspp::AnyValue::make_undefined();
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
jspp::AnyValue jspp::JsFunction::set_property(const std::string &key, const AnyValue &value)
|
|
23
|
+
{
|
|
24
|
+
auto it = props.find(key);
|
|
25
|
+
if (it != props.end())
|
|
26
|
+
{
|
|
27
|
+
return jspp::AnyValue::resolve_property_for_write(it->second, value);
|
|
28
|
+
}
|
|
29
|
+
else
|
|
30
|
+
{
|
|
31
|
+
props[key] = value;
|
|
32
|
+
return value;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
#pragma once
|
|
2
|
+
|
|
3
|
+
#include "types.hpp"
|
|
4
|
+
#include "values/object.hpp"
|
|
5
|
+
#include "any_value.hpp"
|
|
6
|
+
|
|
7
|
+
std::string jspp::JsObject::to_std_string() const
|
|
8
|
+
{
|
|
9
|
+
return "[Object Object]";
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
jspp::AnyValue jspp::JsObject::get_property(const std::string &key)
|
|
13
|
+
{
|
|
14
|
+
auto it = props.find(key);
|
|
15
|
+
if (it != props.end())
|
|
16
|
+
{
|
|
17
|
+
return jspp::AnyValue::resolve_property_for_read(it->second);
|
|
18
|
+
}
|
|
19
|
+
return jspp::AnyValue::make_undefined();
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
jspp::AnyValue jspp::JsObject::set_property(const std::string &key, const AnyValue &value)
|
|
23
|
+
{
|
|
24
|
+
auto it = props.find(key);
|
|
25
|
+
if (it != props.end())
|
|
26
|
+
{
|
|
27
|
+
return jspp::AnyValue::resolve_property_for_write(it->second, value);
|
|
28
|
+
}
|
|
29
|
+
else
|
|
30
|
+
{
|
|
31
|
+
props[key] = value;
|
|
32
|
+
return value;
|
|
33
|
+
}
|
|
34
|
+
}
|