@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
|
@@ -7,54 +7,102 @@ jspp::AnyValue jspp::AnyValue::get_own_property(const std::string &key) const
|
|
|
7
7
|
{
|
|
8
8
|
return get_property_with_receiver(key, *this);
|
|
9
9
|
}
|
|
10
|
-
|
|
10
|
+
bool jspp::AnyValue::has_property(const std::string &key) const
|
|
11
11
|
{
|
|
12
|
-
switch (
|
|
12
|
+
switch (get_type())
|
|
13
13
|
{
|
|
14
|
+
case JsType::Object:
|
|
15
|
+
return std::get<std::shared_ptr<JsObject>>(storage)->has_property(key);
|
|
14
16
|
case JsType::Array:
|
|
15
|
-
return storage
|
|
17
|
+
return std::get<std::shared_ptr<JsArray>>(storage)->has_property(key);
|
|
18
|
+
case JsType::Function:
|
|
19
|
+
return std::get<std::shared_ptr<JsFunction>>(storage)->has_property(key);
|
|
20
|
+
case JsType::Promise:
|
|
21
|
+
// Promises don't have their own props usually, but could.
|
|
22
|
+
return false;
|
|
23
|
+
case JsType::Iterator:
|
|
24
|
+
return false;
|
|
25
|
+
case JsType::Symbol:
|
|
26
|
+
return false;
|
|
16
27
|
case JsType::String:
|
|
17
|
-
|
|
28
|
+
if (key == "length")
|
|
29
|
+
return true;
|
|
30
|
+
if (JsArray::is_array_index(key))
|
|
31
|
+
{
|
|
32
|
+
uint32_t idx = static_cast<uint32_t>(std::stoull(key));
|
|
33
|
+
return idx < std::get<std::shared_ptr<JsString>>(storage)->value.length();
|
|
34
|
+
}
|
|
35
|
+
return false;
|
|
36
|
+
case JsType::Number:
|
|
37
|
+
return false;
|
|
38
|
+
case JsType::Uninitialized:
|
|
39
|
+
Exception::throw_uninitialized_reference("#<Object>");
|
|
40
|
+
return false;
|
|
41
|
+
default:
|
|
42
|
+
return false;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
jspp::AnyValue jspp::AnyValue::get_own_property(uint32_t idx) const
|
|
46
|
+
{
|
|
47
|
+
switch (storage.index())
|
|
48
|
+
{
|
|
49
|
+
case 7: // Array
|
|
50
|
+
return std::get<std::shared_ptr<JsArray>>(storage)->get_property(idx);
|
|
51
|
+
case 5: // String
|
|
52
|
+
return std::get<std::shared_ptr<JsString>>(storage)->get_property(idx);
|
|
53
|
+
case 4: // Number
|
|
54
|
+
return get_own_property(std::to_string(idx));
|
|
18
55
|
default:
|
|
19
56
|
return get_own_property(std::to_string(idx));
|
|
20
57
|
}
|
|
21
58
|
}
|
|
22
|
-
jspp::AnyValue jspp::AnyValue::get_own_property(const AnyValue &key) const
|
|
59
|
+
jspp::AnyValue jspp::AnyValue::get_own_property(const AnyValue &key) const
|
|
23
60
|
{
|
|
24
|
-
if (key.
|
|
25
|
-
return storage
|
|
26
|
-
if (key.
|
|
27
|
-
return storage
|
|
61
|
+
if (key.is_number() && is_array())
|
|
62
|
+
return std::get<std::shared_ptr<JsArray>>(storage)->get_property(key.as_double());
|
|
63
|
+
if (key.is_number() && is_string())
|
|
64
|
+
return std::get<std::shared_ptr<JsString>>(storage)->get_property(key.as_double());
|
|
28
65
|
|
|
29
66
|
// If the key is a Symbol, use its internal key string
|
|
30
|
-
if (key.
|
|
31
|
-
return get_own_property(key.
|
|
67
|
+
if (key.is_symbol())
|
|
68
|
+
return get_own_property(key.as_symbol()->key);
|
|
32
69
|
|
|
33
70
|
return get_own_property(key.to_std_string());
|
|
34
71
|
}
|
|
35
72
|
|
|
36
73
|
jspp::AnyValue jspp::AnyValue::get_property_with_receiver(const std::string &key, const AnyValue &receiver) const
|
|
37
74
|
{
|
|
38
|
-
switch (
|
|
75
|
+
switch (get_type())
|
|
39
76
|
{
|
|
40
77
|
case JsType::Object:
|
|
41
|
-
return storage
|
|
78
|
+
return std::get<std::shared_ptr<JsObject>>(storage)->get_property(key, receiver);
|
|
42
79
|
case JsType::Array:
|
|
43
|
-
return storage
|
|
80
|
+
return std::get<std::shared_ptr<JsArray>>(storage)->get_property(key, receiver);
|
|
44
81
|
case JsType::Function:
|
|
45
|
-
return storage
|
|
82
|
+
return std::get<std::shared_ptr<JsFunction>>(storage)->get_property(key, receiver);
|
|
46
83
|
case JsType::Promise:
|
|
47
|
-
return storage
|
|
84
|
+
return std::get<std::shared_ptr<JsPromise>>(storage)->get_property(key, receiver);
|
|
48
85
|
case JsType::Iterator:
|
|
49
|
-
return storage
|
|
86
|
+
return std::get<std::shared_ptr<JsIterator<AnyValue>>>(storage)->get_property(key, receiver);
|
|
50
87
|
case JsType::Symbol:
|
|
51
|
-
return storage
|
|
88
|
+
return std::get<std::shared_ptr<JsSymbol>>(storage)->get_property(key, receiver);
|
|
52
89
|
case JsType::String:
|
|
53
|
-
return storage
|
|
90
|
+
return std::get<std::shared_ptr<JsString>>(storage)->get_property(key, receiver);
|
|
91
|
+
case JsType::Number:
|
|
92
|
+
{
|
|
93
|
+
auto proto_it = NumberPrototypes::get(key, std::get<double>(storage));
|
|
94
|
+
if (proto_it.has_value())
|
|
95
|
+
{
|
|
96
|
+
return AnyValue::resolve_property_for_read(proto_it.value(), receiver, key);
|
|
97
|
+
}
|
|
98
|
+
return AnyValue::make_undefined();
|
|
99
|
+
}
|
|
54
100
|
case JsType::Undefined:
|
|
55
101
|
throw Exception::make_exception("Cannot read properties of undefined (reading '" + key + "')", "TypeError");
|
|
56
102
|
case JsType::Null:
|
|
57
103
|
throw Exception::make_exception("Cannot read properties of null (reading '" + key + "')", "TypeError");
|
|
104
|
+
case JsType::Uninitialized:
|
|
105
|
+
Exception::throw_uninitialized_reference("#<Object>");
|
|
58
106
|
default:
|
|
59
107
|
return AnyValue::make_undefined();
|
|
60
108
|
}
|
|
@@ -62,16 +110,16 @@ jspp::AnyValue jspp::AnyValue::get_property_with_receiver(const std::string &key
|
|
|
62
110
|
|
|
63
111
|
jspp::AnyValue jspp::AnyValue::set_own_property(const std::string &key, const AnyValue &value) const
|
|
64
112
|
{
|
|
65
|
-
switch (
|
|
113
|
+
switch (get_type())
|
|
66
114
|
{
|
|
67
115
|
case JsType::Object:
|
|
68
|
-
return storage
|
|
116
|
+
return std::get<std::shared_ptr<JsObject>>(storage)->set_property(key, value, *this);
|
|
69
117
|
case JsType::Array:
|
|
70
|
-
return storage
|
|
118
|
+
return std::get<std::shared_ptr<JsArray>>(storage)->set_property(key, value, *this);
|
|
71
119
|
case JsType::Function:
|
|
72
|
-
return storage
|
|
120
|
+
return std::get<std::shared_ptr<JsFunction>>(storage)->set_property(key, value, *this);
|
|
73
121
|
case JsType::Promise:
|
|
74
|
-
return storage
|
|
122
|
+
return std::get<std::shared_ptr<JsPromise>>(storage)->set_property(key, value, *this);
|
|
75
123
|
case JsType::Undefined:
|
|
76
124
|
throw Exception::make_exception("Cannot set properties of undefined (setting '" + key + "')", "TypeError");
|
|
77
125
|
case JsType::Null:
|
|
@@ -82,22 +130,22 @@ jspp::AnyValue jspp::AnyValue::set_own_property(const std::string &key, const An
|
|
|
82
130
|
}
|
|
83
131
|
jspp::AnyValue jspp::AnyValue::set_own_property(uint32_t idx, const AnyValue &value) const
|
|
84
132
|
{
|
|
85
|
-
if (
|
|
133
|
+
if (is_array())
|
|
86
134
|
{
|
|
87
|
-
return storage
|
|
135
|
+
return std::get<std::shared_ptr<JsArray>>(storage)->set_property(idx, value);
|
|
88
136
|
}
|
|
89
137
|
return set_own_property(std::to_string(idx), value);
|
|
90
138
|
}
|
|
91
139
|
jspp::AnyValue jspp::AnyValue::set_own_property(const AnyValue &key, const AnyValue &value) const
|
|
92
140
|
{
|
|
93
|
-
if (key.
|
|
141
|
+
if (key.is_number() && is_array())
|
|
94
142
|
{
|
|
95
|
-
return storage
|
|
143
|
+
return std::get<std::shared_ptr<JsArray>>(storage)->set_property(key.as_double(), value);
|
|
96
144
|
}
|
|
97
145
|
|
|
98
146
|
// If the key is a Symbol, use its internal key string
|
|
99
|
-
if (key.
|
|
100
|
-
return set_own_property(key.
|
|
147
|
+
if (key.is_symbol())
|
|
148
|
+
return set_own_property(key.as_symbol()->key, value);
|
|
101
149
|
|
|
102
150
|
return set_own_property(key.to_std_string(), value);
|
|
103
151
|
}
|
|
@@ -7,16 +7,25 @@
|
|
|
7
7
|
|
|
8
8
|
namespace jspp
|
|
9
9
|
{
|
|
10
|
-
|
|
11
10
|
inline void AnyValue::define_data_property(const std::string &key, const AnyValue &value)
|
|
12
11
|
{
|
|
13
12
|
if (is_object())
|
|
14
13
|
{
|
|
15
|
-
|
|
14
|
+
auto obj = std::get<std::shared_ptr<JsObject>>(storage);
|
|
15
|
+
auto offset = obj->shape->get_offset(key);
|
|
16
|
+
if (offset.has_value())
|
|
17
|
+
{
|
|
18
|
+
obj->storage[offset.value()] = value;
|
|
19
|
+
}
|
|
20
|
+
else
|
|
21
|
+
{
|
|
22
|
+
obj->shape = obj->shape->transition(key);
|
|
23
|
+
obj->storage.push_back(value);
|
|
24
|
+
}
|
|
16
25
|
}
|
|
17
26
|
else if (is_function())
|
|
18
27
|
{
|
|
19
|
-
storage
|
|
28
|
+
std::get<std::shared_ptr<JsFunction>>(storage)->props[key] = value;
|
|
20
29
|
}
|
|
21
30
|
}
|
|
22
31
|
|
|
@@ -37,42 +46,56 @@ namespace jspp
|
|
|
37
46
|
{
|
|
38
47
|
if (is_object())
|
|
39
48
|
{
|
|
40
|
-
auto
|
|
41
|
-
auto
|
|
42
|
-
|
|
49
|
+
auto obj = std::get<std::shared_ptr<JsObject>>(storage);
|
|
50
|
+
auto offset = obj->shape->get_offset(key);
|
|
51
|
+
|
|
52
|
+
if (offset.has_value())
|
|
43
53
|
{
|
|
44
|
-
auto
|
|
45
|
-
|
|
54
|
+
auto &val = obj->storage[offset.value()];
|
|
55
|
+
if (val.is_accessor_descriptor())
|
|
46
56
|
{
|
|
47
|
-
|
|
48
|
-
|
|
57
|
+
auto desc = val.as_accessor_descriptor();
|
|
58
|
+
desc->get = [getter](const AnyValue &thisVal, std::span<const AnyValue> args) -> AnyValue
|
|
59
|
+
{
|
|
60
|
+
return getter.call(thisVal, args);
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
else
|
|
64
|
+
{
|
|
65
|
+
auto getFunc = [getter](const AnyValue &thisVal, std::span<const AnyValue> args) -> AnyValue
|
|
66
|
+
{
|
|
67
|
+
return getter.call(thisVal, args);
|
|
68
|
+
};
|
|
69
|
+
obj->storage[offset.value()] = AnyValue::make_accessor_descriptor(getFunc, std::nullopt, true, true);
|
|
70
|
+
}
|
|
49
71
|
}
|
|
50
72
|
else
|
|
51
73
|
{
|
|
52
|
-
auto getFunc = [getter](const AnyValue &thisVal,
|
|
74
|
+
auto getFunc = [getter](const AnyValue &thisVal, std::span<const AnyValue> args) -> AnyValue
|
|
53
75
|
{
|
|
54
|
-
return getter.
|
|
76
|
+
return getter.call(thisVal, args);
|
|
55
77
|
};
|
|
56
|
-
|
|
78
|
+
obj->shape = obj->shape->transition(key);
|
|
79
|
+
obj->storage.push_back(AnyValue::make_accessor_descriptor(getFunc, std::nullopt, true, true));
|
|
57
80
|
}
|
|
58
81
|
}
|
|
59
82
|
else if (is_function())
|
|
60
83
|
{
|
|
61
|
-
auto &props = storage
|
|
84
|
+
auto &props = std::get<std::shared_ptr<JsFunction>>(storage)->props;
|
|
62
85
|
auto it = props.find(key);
|
|
63
86
|
if (it != props.end() && it->second.is_accessor_descriptor())
|
|
64
87
|
{
|
|
65
88
|
auto desc = it->second.as_accessor_descriptor();
|
|
66
|
-
desc->get = [getter](const AnyValue &thisVal,
|
|
89
|
+
desc->get = [getter](const AnyValue &thisVal, std::span<const AnyValue> args) -> AnyValue
|
|
67
90
|
{
|
|
68
|
-
return getter.
|
|
91
|
+
return getter.call(thisVal, args);
|
|
69
92
|
};
|
|
70
93
|
}
|
|
71
94
|
else
|
|
72
95
|
{
|
|
73
|
-
auto getFunc = [getter](const AnyValue &thisVal,
|
|
96
|
+
auto getFunc = [getter](const AnyValue &thisVal, std::span<const AnyValue> args) -> AnyValue
|
|
74
97
|
{
|
|
75
|
-
return getter.
|
|
98
|
+
return getter.call(thisVal, args);
|
|
76
99
|
};
|
|
77
100
|
props[key] = AnyValue::make_accessor_descriptor(getFunc, std::nullopt, true, true);
|
|
78
101
|
}
|
|
@@ -91,50 +114,66 @@ namespace jspp
|
|
|
91
114
|
{
|
|
92
115
|
if (is_object())
|
|
93
116
|
{
|
|
94
|
-
auto
|
|
95
|
-
auto
|
|
96
|
-
|
|
117
|
+
auto obj = std::get<std::shared_ptr<JsObject>>(storage);
|
|
118
|
+
auto offset = obj->shape->get_offset(key);
|
|
119
|
+
|
|
120
|
+
if (offset.has_value())
|
|
97
121
|
{
|
|
98
|
-
auto
|
|
99
|
-
|
|
122
|
+
auto &val = obj->storage[offset.value()];
|
|
123
|
+
if (val.is_accessor_descriptor())
|
|
100
124
|
{
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
125
|
+
auto desc = val.as_accessor_descriptor();
|
|
126
|
+
desc->set = [setter](const AnyValue &thisVal, std::span<const AnyValue> args) -> AnyValue
|
|
127
|
+
{
|
|
128
|
+
if (args.empty())
|
|
129
|
+
return AnyValue::make_undefined();
|
|
130
|
+
return setter.call(thisVal, args);
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
else
|
|
134
|
+
{
|
|
135
|
+
auto setFunc = [setter](const AnyValue &thisVal, std::span<const AnyValue> args) -> AnyValue
|
|
136
|
+
{
|
|
137
|
+
if (args.empty())
|
|
138
|
+
return AnyValue::make_undefined();
|
|
139
|
+
return setter.call(thisVal, args);
|
|
140
|
+
};
|
|
141
|
+
obj->storage[offset.value()] = AnyValue::make_accessor_descriptor(std::nullopt, setFunc, true, true);
|
|
142
|
+
}
|
|
105
143
|
}
|
|
106
144
|
else
|
|
107
145
|
{
|
|
108
|
-
auto setFunc = [setter](const AnyValue &thisVal,
|
|
146
|
+
auto setFunc = [setter](const AnyValue &thisVal, std::span<const AnyValue> args) -> AnyValue
|
|
109
147
|
{
|
|
110
148
|
if (args.empty())
|
|
111
149
|
return AnyValue::make_undefined();
|
|
112
|
-
return setter.
|
|
150
|
+
return setter.call(thisVal, args);
|
|
113
151
|
};
|
|
114
|
-
|
|
152
|
+
obj->shape = obj->shape->transition(key);
|
|
153
|
+
obj->storage.push_back(AnyValue::make_accessor_descriptor(std::nullopt, setFunc, true, true));
|
|
115
154
|
}
|
|
116
155
|
}
|
|
117
156
|
else if (is_function())
|
|
118
157
|
{
|
|
119
|
-
auto &props = storage
|
|
158
|
+
auto &props = std::get<std::shared_ptr<JsFunction>>(storage)->props;
|
|
120
159
|
auto it = props.find(key);
|
|
121
160
|
if (it != props.end() && it->second.is_accessor_descriptor())
|
|
122
161
|
{
|
|
123
162
|
auto desc = it->second.as_accessor_descriptor();
|
|
124
|
-
desc->set = [setter](const AnyValue &thisVal,
|
|
163
|
+
desc->set = [setter](const AnyValue &thisVal, std::span<const AnyValue> args) -> AnyValue
|
|
125
164
|
{
|
|
126
165
|
if (args.empty())
|
|
127
166
|
return AnyValue::make_undefined();
|
|
128
|
-
return setter.
|
|
167
|
+
return setter.call(thisVal, args);
|
|
129
168
|
};
|
|
130
169
|
}
|
|
131
170
|
else
|
|
132
171
|
{
|
|
133
|
-
auto setFunc = [setter](const AnyValue &thisVal,
|
|
172
|
+
auto setFunc = [setter](const AnyValue &thisVal, std::span<const AnyValue> args) -> AnyValue
|
|
134
173
|
{
|
|
135
174
|
if (args.empty())
|
|
136
175
|
return AnyValue::make_undefined();
|
|
137
|
-
return setter.
|
|
176
|
+
return setter.call(thisVal, args);
|
|
138
177
|
};
|
|
139
178
|
props[key] = AnyValue::make_accessor_descriptor(std::nullopt, setFunc, true, true);
|
|
140
179
|
}
|
|
@@ -3,217 +3,60 @@
|
|
|
3
3
|
#include "types.hpp"
|
|
4
4
|
#include "any_value.hpp"
|
|
5
5
|
#include "values/string.hpp"
|
|
6
|
+
#include "exception.hpp"
|
|
6
7
|
|
|
7
8
|
namespace jspp
|
|
8
9
|
{
|
|
9
|
-
|
|
10
|
+
std::string AnyValue::to_std_string() const
|
|
10
11
|
{
|
|
11
|
-
switch (
|
|
12
|
-
{
|
|
13
|
-
case JsType::Boolean:
|
|
14
|
-
return storage.boolean;
|
|
15
|
-
case JsType::Number:
|
|
16
|
-
return storage.number != 0.0;
|
|
17
|
-
case JsType::String:
|
|
18
|
-
return !storage.str->value.empty();
|
|
19
|
-
case JsType::Undefined:
|
|
20
|
-
return false;
|
|
21
|
-
case JsType::Null:
|
|
22
|
-
return false;
|
|
23
|
-
case JsType::Uninitialized:
|
|
24
|
-
return false;
|
|
25
|
-
default:
|
|
26
|
-
return true;
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
const bool AnyValue::is_strictly_equal_to_primitive(const AnyValue &other) const noexcept
|
|
31
|
-
{
|
|
32
|
-
if (storage.type == other.storage.type)
|
|
33
|
-
{
|
|
34
|
-
switch (storage.type)
|
|
35
|
-
{
|
|
36
|
-
case JsType::Boolean:
|
|
37
|
-
return storage.boolean == other.storage.boolean;
|
|
38
|
-
case JsType::Number:
|
|
39
|
-
return storage.number == other.storage.number;
|
|
40
|
-
case JsType::String:
|
|
41
|
-
return (storage.str->value == other.storage.str->value);
|
|
42
|
-
case JsType::Array:
|
|
43
|
-
return (storage.array == other.storage.array);
|
|
44
|
-
case JsType::Object:
|
|
45
|
-
return (storage.object == other.storage.object);
|
|
46
|
-
case JsType::Function:
|
|
47
|
-
return (storage.function == other.storage.function);
|
|
48
|
-
case JsType::Iterator:
|
|
49
|
-
return (storage.iterator == other.storage.iterator);
|
|
50
|
-
case JsType::Promise:
|
|
51
|
-
return (storage.promise == other.storage.promise);
|
|
52
|
-
case JsType::Symbol:
|
|
53
|
-
// Symbols are unique by reference/pointer identity
|
|
54
|
-
return (storage.symbol == other.storage.symbol);
|
|
55
|
-
case JsType::DataDescriptor:
|
|
56
|
-
return storage.data_desc == other.storage.data_desc;
|
|
57
|
-
case JsType::AccessorDescriptor:
|
|
58
|
-
return storage.accessor_desc == other.storage.accessor_desc;
|
|
59
|
-
default:
|
|
60
|
-
return true;
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
return false;
|
|
64
|
-
}
|
|
65
|
-
const bool AnyValue::is_equal_to_primitive(const AnyValue &other) const noexcept
|
|
66
|
-
{
|
|
67
|
-
// Implements JavaScript's Abstract Equality Comparison Algorithm (==)
|
|
68
|
-
// Step 1: If types are the same, use strict equality (===)
|
|
69
|
-
if (storage.type == other.storage.type)
|
|
70
|
-
{
|
|
71
|
-
return is_strictly_equal_to_primitive(other);
|
|
72
|
-
}
|
|
73
|
-
// Steps 2 & 3: null == undefined
|
|
74
|
-
if ((is_null() && other.is_undefined()) || (is_undefined() && other.is_null()))
|
|
75
|
-
{
|
|
76
|
-
return true;
|
|
77
|
-
}
|
|
78
|
-
// Step 4 & 5: number == string
|
|
79
|
-
if (is_number() && other.is_string())
|
|
80
|
-
{
|
|
81
|
-
double num_this = this->as_double();
|
|
82
|
-
double num_other;
|
|
83
|
-
try
|
|
84
|
-
{
|
|
85
|
-
const std::string &s = other.as_string()->value;
|
|
86
|
-
// JS considers empty string or whitespace-only string to be 0
|
|
87
|
-
if (s.empty() || std::all_of(s.begin(), s.end(), [](unsigned char c)
|
|
88
|
-
{ return std::isspace(c); }))
|
|
89
|
-
{
|
|
90
|
-
num_other = 0.0;
|
|
91
|
-
}
|
|
92
|
-
else
|
|
93
|
-
{
|
|
94
|
-
size_t pos;
|
|
95
|
-
num_other = std::stod(s, &pos);
|
|
96
|
-
// Check if the entire string was consumed, allowing for trailing whitespace
|
|
97
|
-
while (pos < s.length() && std::isspace(static_cast<unsigned char>(s[pos])))
|
|
98
|
-
{
|
|
99
|
-
pos++;
|
|
100
|
-
}
|
|
101
|
-
if (pos != s.length())
|
|
102
|
-
{
|
|
103
|
-
num_other = std::numeric_limits<double>::quiet_NaN();
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
catch (...)
|
|
108
|
-
{
|
|
109
|
-
num_other = std::numeric_limits<double>::quiet_NaN();
|
|
110
|
-
}
|
|
111
|
-
return num_this == num_other;
|
|
112
|
-
}
|
|
113
|
-
if (is_string() && other.is_number())
|
|
114
|
-
{
|
|
115
|
-
// Delegate to the other operand to avoid code duplication
|
|
116
|
-
return other.is_equal_to_primitive(*this);
|
|
117
|
-
}
|
|
118
|
-
// Step 6 & 7: boolean == any
|
|
119
|
-
if (is_boolean())
|
|
120
|
-
{
|
|
121
|
-
// Convert boolean to number and re-compare
|
|
122
|
-
return AnyValue::make_number(as_boolean() ? 1.0 : 0.0).is_equal_to_primitive(other);
|
|
123
|
-
}
|
|
124
|
-
if (other.is_boolean())
|
|
125
|
-
{
|
|
126
|
-
// Convert boolean to number and re-compare
|
|
127
|
-
return is_equal_to_primitive(AnyValue::make_number(other.as_boolean() ? 1.0 : 0.0));
|
|
128
|
-
}
|
|
129
|
-
// Step 8 & 9: object == (string or number or symbol)
|
|
130
|
-
// Simplified: Objects convert to primitives.
|
|
131
|
-
if ((is_object() || is_array() || is_function() || is_promise() || is_iterator()) && (other.is_string() || other.is_number() || other.is_symbol()))
|
|
132
|
-
{
|
|
133
|
-
// Convert object to primitive (string) and re-compare.
|
|
134
|
-
// This is a simplification of JS's ToPrimitive.
|
|
135
|
-
return AnyValue::make_string(to_std_string()).is_equal_to_primitive(other);
|
|
136
|
-
}
|
|
137
|
-
if ((other.is_object() || other.is_array() || other.is_function() || other.is_promise() || other.is_iterator()) && (is_string() || is_number() || is_symbol()))
|
|
138
|
-
{
|
|
139
|
-
return other.is_equal_to_primitive(*this);
|
|
140
|
-
}
|
|
141
|
-
// Step 10: Datacriptor or accessor descriptor
|
|
142
|
-
if (is_data_descriptor() || is_accessor_descriptor())
|
|
143
|
-
{
|
|
144
|
-
return (*this).is_strictly_equal_to_primitive(other);
|
|
145
|
-
}
|
|
146
|
-
// Step 11: All other cases (e.g., object == null) are false.
|
|
147
|
-
return false;
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
const AnyValue AnyValue::is_strictly_equal_to(const AnyValue &other) const noexcept
|
|
151
|
-
{
|
|
152
|
-
return AnyValue::make_boolean(is_strictly_equal_to_primitive(other));
|
|
153
|
-
}
|
|
154
|
-
const AnyValue AnyValue::is_equal_to(const AnyValue &other) const noexcept
|
|
155
|
-
{
|
|
156
|
-
return AnyValue::make_boolean(is_equal_to_primitive(other));
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
const AnyValue AnyValue::not_strictly_equal_to(const AnyValue &other) const noexcept
|
|
160
|
-
{
|
|
161
|
-
return AnyValue::make_boolean(!is_strictly_equal_to_primitive(other));
|
|
162
|
-
}
|
|
163
|
-
const AnyValue AnyValue::not_equal_to(const AnyValue &other) const noexcept
|
|
164
|
-
{
|
|
165
|
-
return AnyValue::make_boolean(!is_equal_to_primitive(other));
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
const std::string AnyValue::to_std_string() const noexcept
|
|
169
|
-
{
|
|
170
|
-
switch (storage.type)
|
|
12
|
+
switch (get_type())
|
|
171
13
|
{
|
|
172
14
|
case JsType::Undefined:
|
|
173
15
|
return "undefined";
|
|
174
16
|
case JsType::Null:
|
|
175
17
|
return "null";
|
|
176
18
|
case JsType::Boolean:
|
|
177
|
-
return storage
|
|
19
|
+
return std::get<bool>(storage) ? "true" : "false";
|
|
178
20
|
case JsType::String:
|
|
179
|
-
return storage
|
|
21
|
+
return std::get<std::shared_ptr<JsString>>(storage)->to_std_string();
|
|
180
22
|
case JsType::Object:
|
|
181
|
-
return storage
|
|
23
|
+
return std::get<std::shared_ptr<JsObject>>(storage)->to_std_string();
|
|
182
24
|
case JsType::Array:
|
|
183
|
-
return storage
|
|
25
|
+
return std::get<std::shared_ptr<JsArray>>(storage)->to_std_string();
|
|
184
26
|
case JsType::Function:
|
|
185
|
-
return storage
|
|
27
|
+
return std::get<std::shared_ptr<JsFunction>>(storage)->to_std_string();
|
|
186
28
|
case JsType::Iterator:
|
|
187
|
-
return storage
|
|
29
|
+
return std::get<std::shared_ptr<JsIterator<AnyValue>>>(storage)->to_std_string();
|
|
188
30
|
case JsType::Promise:
|
|
189
|
-
return storage
|
|
31
|
+
return std::get<std::shared_ptr<JsPromise>>(storage)->to_std_string();
|
|
190
32
|
case JsType::Symbol:
|
|
191
|
-
return storage
|
|
33
|
+
return std::get<std::shared_ptr<JsSymbol>>(storage)->to_std_string();
|
|
192
34
|
case JsType::DataDescriptor:
|
|
193
|
-
return storage
|
|
35
|
+
return std::get<std::shared_ptr<DataDescriptor>>(storage)->value->to_std_string();
|
|
194
36
|
case JsType::AccessorDescriptor:
|
|
195
37
|
{
|
|
196
|
-
if (storage
|
|
197
|
-
return storage
|
|
38
|
+
if (std::get<std::shared_ptr<AccessorDescriptor>>(storage)->get.has_value())
|
|
39
|
+
return std::get<std::shared_ptr<AccessorDescriptor>>(storage)->get.value()(*this, {}).to_std_string();
|
|
198
40
|
else
|
|
199
41
|
return "undefined";
|
|
200
42
|
}
|
|
201
43
|
case JsType::Number:
|
|
202
44
|
{
|
|
203
|
-
|
|
45
|
+
double num = std::get<double>(storage);
|
|
46
|
+
if (std::isnan(num))
|
|
204
47
|
{
|
|
205
48
|
return "NaN";
|
|
206
49
|
}
|
|
207
|
-
if (std::abs(
|
|
50
|
+
if (std::abs(num) >= 1e21 || (std::abs(num) > 0 && std::abs(num) < 1e-6))
|
|
208
51
|
{
|
|
209
52
|
std::ostringstream oss;
|
|
210
|
-
oss << std::scientific << std::setprecision(4) <<
|
|
53
|
+
oss << std::scientific << std::setprecision(4) << num;
|
|
211
54
|
return oss.str();
|
|
212
55
|
}
|
|
213
56
|
else
|
|
214
57
|
{
|
|
215
58
|
std::ostringstream oss;
|
|
216
|
-
oss << std::setprecision(6) << std::fixed <<
|
|
59
|
+
oss << std::setprecision(6) << std::fixed << num;
|
|
217
60
|
std::string s = oss.str();
|
|
218
61
|
s.erase(s.find_last_not_of('0') + 1, std::string::npos);
|
|
219
62
|
if (!s.empty() && s.back() == '.')
|
|
@@ -223,9 +66,8 @@ namespace jspp
|
|
|
223
66
|
return s;
|
|
224
67
|
}
|
|
225
68
|
}
|
|
226
|
-
// Uninitialized and default should not be reached under normal circumstances
|
|
227
69
|
case JsType::Uninitialized:
|
|
228
|
-
|
|
70
|
+
Exception::throw_uninitialized_reference("#<Object>");
|
|
229
71
|
default:
|
|
230
72
|
return "";
|
|
231
73
|
}
|
|
@@ -235,12 +77,63 @@ namespace jspp
|
|
|
235
77
|
{
|
|
236
78
|
if (is_object())
|
|
237
79
|
{
|
|
238
|
-
storage
|
|
80
|
+
std::get<std::shared_ptr<JsObject>>(storage)->proto = std::make_shared<AnyValue>(proto);
|
|
81
|
+
}
|
|
82
|
+
else if (is_array())
|
|
83
|
+
{
|
|
84
|
+
std::get<std::shared_ptr<JsArray>>(storage)->proto = std::make_shared<AnyValue>(proto);
|
|
239
85
|
}
|
|
240
86
|
else if (is_function())
|
|
241
87
|
{
|
|
242
|
-
storage
|
|
88
|
+
std::get<std::shared_ptr<JsFunction>>(storage)->proto = std::make_shared<AnyValue>(proto);
|
|
89
|
+
}
|
|
90
|
+
else if (is_uninitialized())
|
|
91
|
+
{
|
|
92
|
+
Exception::throw_uninitialized_reference("#<Object>");
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// AnyValue::call implementation
|
|
97
|
+
const AnyValue AnyValue::call(const AnyValue &thisVal, std::span<const AnyValue> args, const std::optional<std::string> &expr = std::nullopt) const
|
|
98
|
+
{
|
|
99
|
+
if (!is_function())
|
|
100
|
+
{
|
|
101
|
+
throw Exception::make_exception(expr.value_or(to_std_string()) + " is not a function", "TypeError");
|
|
102
|
+
}
|
|
103
|
+
return as_function()->call(thisVal, args); // Convert to function before calling, to avoid an infinite loop
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// AnyValue::construct implementation
|
|
107
|
+
const AnyValue AnyValue::construct(std::span<const AnyValue> args, const std::optional<std::string> &name) const
|
|
108
|
+
{
|
|
109
|
+
if (!is_function() || !as_function()->is_constructor)
|
|
110
|
+
{
|
|
111
|
+
// std::cerr << "Construct fail: " << name.value_or(to_std_string()) << " is_function=" << is_function() << " is_constructor=" << (is_function() ? as_function()->is_constructor : false) << std::endl;
|
|
112
|
+
throw Exception::make_exception(name.value_or(to_std_string()) + " is not a constructor", "TypeError");
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// 1. Get prototype
|
|
116
|
+
AnyValue proto = get_own_property("prototype");
|
|
117
|
+
// If prototype is not an object, default to a plain object (which ideally inherits from Object.prototype)
|
|
118
|
+
// Here we just make a plain object.
|
|
119
|
+
if (!proto.is_object())
|
|
120
|
+
{
|
|
121
|
+
proto = AnyValue::make_object({});
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// 2. Create instance
|
|
125
|
+
AnyValue instance = AnyValue::make_object_with_proto({}, proto);
|
|
126
|
+
|
|
127
|
+
// 3. Call function
|
|
128
|
+
// We pass 'instance' as 'this'
|
|
129
|
+
AnyValue result = call(instance, args);
|
|
130
|
+
|
|
131
|
+
// 4. Return result if object, else instance
|
|
132
|
+
if (result.is_object() || result.is_function() || result.is_array() || result.is_promise())
|
|
133
|
+
{
|
|
134
|
+
return result;
|
|
243
135
|
}
|
|
136
|
+
return instance;
|
|
244
137
|
}
|
|
245
138
|
|
|
246
139
|
}
|