@gjsify/napi 0.22.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/README.md +203 -0
- package/lib/esm/_virtual/_rolldown/runtime.js +1 -0
- package/lib/esm/index.js +1 -0
- package/lib/types/index.d.ts +25 -0
- package/meson.build +126 -0
- package/package.json +100 -0
- package/prebuilds/linux-x86_64/GjsifyNapi-1.0.gir +19 -0
- package/prebuilds/linux-x86_64/GjsifyNapi-1.0.typelib +0 -0
- package/prebuilds/linux-x86_64/libgjsifynapi.so +0 -0
- package/src/cc/arraybuffer.cc +772 -0
- package/src/cc/async_work.cc +439 -0
- package/src/cc/common.h +519 -0
- package/src/cc/env.cc +262 -0
- package/src/cc/error.cc +300 -0
- package/src/cc/function.cc +358 -0
- package/src/cc/module.cc +625 -0
- package/src/cc/napi.def +22 -0
- package/src/cc/napi.export.map +16 -0
- package/src/cc/napi.exported-symbols.txt +15 -0
- package/src/cc/object.cc +728 -0
- package/src/cc/ref.cc +510 -0
- package/src/cc/stubs.cc +100 -0
- package/src/cc/tsfn.cc +602 -0
- package/src/cc/value.cc +656 -0
- package/src/cc/wrap.cc +321 -0
- package/src/napi-headers/js_native_api.h +639 -0
- package/src/napi-headers/js_native_api_types.h +249 -0
- package/src/napi-headers/node_api.h +270 -0
- package/src/napi-headers/node_api_types.h +63 -0
- package/src/vala/gjs-1.0.vapi +6 -0
- package/src/vala/mozjs-140.vapi +6 -0
- package/src/vala/napi.vala +49 -0
package/src/cc/object.cc
ADDED
|
@@ -0,0 +1,728 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
// @gjsify/napi — property/element ops, descriptors, arrays, misc object API.
|
|
3
|
+
//
|
|
4
|
+
// Reference: refs/node/src/js_native_api_v8.cc (Node.js contributors, MIT) —
|
|
5
|
+
// napi_define_properties (cc:1748-1824), property/element ops (cc:1826-2131),
|
|
6
|
+
// napi_get_all_property_names (cc:1602-1746), napi_object_freeze/seal
|
|
7
|
+
// (cc:1826-1860), napi_get_prototype, napi_instanceof (cc:3032-3062),
|
|
8
|
+
// napi_run_script (cc:3704-3730), napi_strict_equals. Reimplemented for
|
|
9
|
+
// SpiderMonkey: keys via js::GetPropertyKeys (jsfriendapi.h:441 — the
|
|
10
|
+
// Reflect.ownKeys analog), descriptors via JS_GetOwnPropertyDescriptorById,
|
|
11
|
+
// evaluation via JS::Evaluate + JS::SourceText.
|
|
12
|
+
|
|
13
|
+
#include "common.h"
|
|
14
|
+
|
|
15
|
+
#include <cstring>
|
|
16
|
+
|
|
17
|
+
namespace gjsify_napi {
|
|
18
|
+
|
|
19
|
+
// Shared JSAPI-false → napi_status mapping honoring the §6 contract.
|
|
20
|
+
static napi_status status_from_false(napi_env env) {
|
|
21
|
+
return set_last_error(env, JS_IsExceptionPending(env->cx)
|
|
22
|
+
? napi_pending_exception
|
|
23
|
+
: napi_generic_failure);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// ToObject coercion matching Node's CHECK_TO_OBJECT: primitives box,
|
|
27
|
+
// null/undefined throw TypeError → napi_object_expected with the exception
|
|
28
|
+
// left pending (V8-Node's TryCatch re-sets it at exit; SM keeps it).
|
|
29
|
+
static napi_status coerce_object_arg(napi_env env, napi_value value,
|
|
30
|
+
JS::MutableHandleObject out) {
|
|
31
|
+
JS::RootedValue v(env->cx, napi_value_to_js(value));
|
|
32
|
+
JSObject* obj = JS::ToObject(env->cx, v);
|
|
33
|
+
if (obj == nullptr) {
|
|
34
|
+
return set_last_error(env, napi_object_expected);
|
|
35
|
+
}
|
|
36
|
+
out.set(obj);
|
|
37
|
+
return napi_ok;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
napi_status property_key_to_id(napi_env env, const char* utf8name,
|
|
41
|
+
napi_value name, JS::MutableHandleId id) {
|
|
42
|
+
if (utf8name != nullptr) {
|
|
43
|
+
JSString* str =
|
|
44
|
+
new_string_utf8_lossy(env->cx, utf8name, strlen(utf8name));
|
|
45
|
+
if (str == nullptr) {
|
|
46
|
+
return set_last_error(env, napi_generic_failure);
|
|
47
|
+
}
|
|
48
|
+
JS::RootedString rooted(env->cx, str);
|
|
49
|
+
if (!JS_StringToId(env->cx, rooted, id)) {
|
|
50
|
+
return status_from_false(env);
|
|
51
|
+
}
|
|
52
|
+
return napi_ok;
|
|
53
|
+
}
|
|
54
|
+
if (name == nullptr) {
|
|
55
|
+
return set_last_error(env, napi_name_expected);
|
|
56
|
+
}
|
|
57
|
+
JS::RootedValue name_v(env->cx, napi_value_to_js(name));
|
|
58
|
+
if (!name_v.isString() && !name_v.isSymbol()) {
|
|
59
|
+
return set_last_error(env, napi_name_expected);
|
|
60
|
+
}
|
|
61
|
+
if (!JS_ValueToId(env->cx, name_v, id)) {
|
|
62
|
+
return status_from_false(env);
|
|
63
|
+
}
|
|
64
|
+
return napi_ok;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
napi_status apply_property_descriptor(napi_env env, JS::HandleObject target,
|
|
68
|
+
const napi_property_descriptor& p) {
|
|
69
|
+
JS::RootedId id(env->cx);
|
|
70
|
+
napi_status status = property_key_to_id(env, p.utf8name, p.name, &id);
|
|
71
|
+
if (status != napi_ok) {
|
|
72
|
+
return status;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// napi attrs → JSPROP inverses (PropertyDescriptor.h:36-47):
|
|
76
|
+
// enumerable → JSPROP_ENUMERATE; !configurable → JSPROP_PERMANENT;
|
|
77
|
+
// !writable → JSPROP_READONLY (data properties only).
|
|
78
|
+
unsigned attrs = 0;
|
|
79
|
+
if (p.attributes & napi_enumerable) {
|
|
80
|
+
attrs |= JSPROP_ENUMERATE;
|
|
81
|
+
}
|
|
82
|
+
if (!(p.attributes & napi_configurable)) {
|
|
83
|
+
attrs |= JSPROP_PERMANENT;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
if (p.getter != nullptr || p.setter != nullptr) {
|
|
87
|
+
// Accessors via the function-object overload (JSPROP_GETTER/SETTER
|
|
88
|
+
// no longer exist in 140 — passing the functions expresses it).
|
|
89
|
+
JS::RootedObject getter(env->cx);
|
|
90
|
+
JS::RootedObject setter(env->cx);
|
|
91
|
+
if (p.getter != nullptr) {
|
|
92
|
+
getter = new_bundle_function(env, p.utf8name, NAPI_AUTO_LENGTH,
|
|
93
|
+
p.getter, p.data, 0, false);
|
|
94
|
+
if (!getter) {
|
|
95
|
+
return set_last_error(env, napi_generic_failure);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
if (p.setter != nullptr) {
|
|
99
|
+
setter = new_bundle_function(env, p.utf8name, NAPI_AUTO_LENGTH,
|
|
100
|
+
p.setter, p.data, 0, false);
|
|
101
|
+
if (!setter) {
|
|
102
|
+
return set_last_error(env, napi_generic_failure);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
if (!JS_DefinePropertyById(env->cx, target, id, getter, setter,
|
|
106
|
+
attrs)) {
|
|
107
|
+
return status_from_false(env);
|
|
108
|
+
}
|
|
109
|
+
return napi_ok;
|
|
110
|
+
}
|
|
111
|
+
if (p.method != nullptr) {
|
|
112
|
+
// A method created via napi_define_properties is a plain napi function
|
|
113
|
+
// and therefore constructible, exactly like napi_create_function (V8
|
|
114
|
+
// FunctionTemplate default): test_new_target uses such method
|
|
115
|
+
// properties as `extends` targets and with `new`. JSFUN_CONSTRUCTOR is
|
|
116
|
+
// what makes `new fn()` legal in SM; getters/setters stay flags=0.
|
|
117
|
+
JS::RootedObject method(
|
|
118
|
+
env->cx, new_bundle_function(env, p.utf8name, NAPI_AUTO_LENGTH,
|
|
119
|
+
p.method, p.data, JSFUN_CONSTRUCTOR,
|
|
120
|
+
false));
|
|
121
|
+
if (!method) {
|
|
122
|
+
return set_last_error(env, napi_generic_failure);
|
|
123
|
+
}
|
|
124
|
+
if (!(p.attributes & napi_writable)) {
|
|
125
|
+
attrs |= JSPROP_READONLY;
|
|
126
|
+
}
|
|
127
|
+
JS::RootedValue method_v(env->cx, JS::ObjectValue(*method));
|
|
128
|
+
if (!JS_DefinePropertyById(env->cx, target, id, method_v, attrs)) {
|
|
129
|
+
return status_from_false(env);
|
|
130
|
+
}
|
|
131
|
+
return napi_ok;
|
|
132
|
+
}
|
|
133
|
+
// Data property.
|
|
134
|
+
if (p.value == nullptr) {
|
|
135
|
+
return set_last_error(env, napi_invalid_arg);
|
|
136
|
+
}
|
|
137
|
+
if (!(p.attributes & napi_writable)) {
|
|
138
|
+
attrs |= JSPROP_READONLY;
|
|
139
|
+
}
|
|
140
|
+
JS::RootedValue value(env->cx, napi_value_to_js(p.value));
|
|
141
|
+
if (!JS_DefinePropertyById(env->cx, target, id, value, attrs)) {
|
|
142
|
+
return status_from_false(env);
|
|
143
|
+
}
|
|
144
|
+
return napi_ok;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
} // namespace gjsify_napi
|
|
148
|
+
|
|
149
|
+
napi_status NAPI_CDECL napi_define_properties(
|
|
150
|
+
napi_env env, napi_value object, size_t property_count,
|
|
151
|
+
const napi_property_descriptor* properties) {
|
|
152
|
+
GJSIFY_NAPI_PREAMBLE(env);
|
|
153
|
+
GJSIFY_NAPI_CHECK_ARG(env, object);
|
|
154
|
+
if (property_count > 0) {
|
|
155
|
+
GJSIFY_NAPI_CHECK_ARG(env, properties);
|
|
156
|
+
}
|
|
157
|
+
JS::RootedObject obj(env->cx);
|
|
158
|
+
napi_status status = gjsify_napi::coerce_object_arg(env, object, &obj);
|
|
159
|
+
if (status != napi_ok) {
|
|
160
|
+
return status;
|
|
161
|
+
}
|
|
162
|
+
for (size_t i = 0; i < property_count; i++) {
|
|
163
|
+
status = gjsify_napi::apply_property_descriptor(env, obj,
|
|
164
|
+
properties[i]);
|
|
165
|
+
if (status != napi_ok) {
|
|
166
|
+
return status;
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
return napi_ok;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
// ---- generic-key property ops ----
|
|
173
|
+
|
|
174
|
+
napi_status NAPI_CDECL napi_set_property(napi_env env, napi_value object,
|
|
175
|
+
napi_value key, napi_value value) {
|
|
176
|
+
GJSIFY_NAPI_PREAMBLE(env);
|
|
177
|
+
GJSIFY_NAPI_CHECK_ARG(env, key);
|
|
178
|
+
GJSIFY_NAPI_CHECK_ARG(env, value);
|
|
179
|
+
GJSIFY_NAPI_CHECK_ARG(env, object);
|
|
180
|
+
JS::RootedObject obj(env->cx);
|
|
181
|
+
napi_status status = gjsify_napi::coerce_object_arg(env, object, &obj);
|
|
182
|
+
if (status != napi_ok) {
|
|
183
|
+
return status;
|
|
184
|
+
}
|
|
185
|
+
JS::RootedId id(env->cx);
|
|
186
|
+
JS::RootedValue key_v(env->cx, gjsify_napi::napi_value_to_js(key));
|
|
187
|
+
if (!JS_ValueToId(env->cx, key_v, &id)) {
|
|
188
|
+
return gjsify_napi::status_from_false(env);
|
|
189
|
+
}
|
|
190
|
+
JS::RootedValue v(env->cx, gjsify_napi::napi_value_to_js(value));
|
|
191
|
+
if (!JS_SetPropertyById(env->cx, obj, id, v)) {
|
|
192
|
+
return gjsify_napi::status_from_false(env);
|
|
193
|
+
}
|
|
194
|
+
return napi_ok;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
napi_status NAPI_CDECL napi_get_property(napi_env env, napi_value object,
|
|
198
|
+
napi_value key, napi_value* result) {
|
|
199
|
+
GJSIFY_NAPI_PREAMBLE(env);
|
|
200
|
+
GJSIFY_NAPI_CHECK_ARG(env, key);
|
|
201
|
+
GJSIFY_NAPI_CHECK_ARG(env, object);
|
|
202
|
+
GJSIFY_NAPI_CHECK_ARG(env, result);
|
|
203
|
+
JS::RootedObject obj(env->cx);
|
|
204
|
+
napi_status status = gjsify_napi::coerce_object_arg(env, object, &obj);
|
|
205
|
+
if (status != napi_ok) {
|
|
206
|
+
return status;
|
|
207
|
+
}
|
|
208
|
+
JS::RootedId id(env->cx);
|
|
209
|
+
JS::RootedValue key_v(env->cx, gjsify_napi::napi_value_to_js(key));
|
|
210
|
+
if (!JS_ValueToId(env->cx, key_v, &id)) {
|
|
211
|
+
return gjsify_napi::status_from_false(env);
|
|
212
|
+
}
|
|
213
|
+
JS::RootedValue v(env->cx);
|
|
214
|
+
if (!JS_GetPropertyById(env->cx, obj, id, &v)) {
|
|
215
|
+
return gjsify_napi::status_from_false(env);
|
|
216
|
+
}
|
|
217
|
+
*result = gjsify_napi::arena_push(env, v);
|
|
218
|
+
return napi_ok;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
napi_status NAPI_CDECL napi_has_property(napi_env env, napi_value object,
|
|
222
|
+
napi_value key, bool* result) {
|
|
223
|
+
GJSIFY_NAPI_PREAMBLE(env);
|
|
224
|
+
GJSIFY_NAPI_CHECK_ARG(env, key);
|
|
225
|
+
GJSIFY_NAPI_CHECK_ARG(env, object);
|
|
226
|
+
GJSIFY_NAPI_CHECK_ARG(env, result);
|
|
227
|
+
JS::RootedObject obj(env->cx);
|
|
228
|
+
napi_status status = gjsify_napi::coerce_object_arg(env, object, &obj);
|
|
229
|
+
if (status != napi_ok) {
|
|
230
|
+
return status;
|
|
231
|
+
}
|
|
232
|
+
JS::RootedId id(env->cx);
|
|
233
|
+
JS::RootedValue key_v(env->cx, gjsify_napi::napi_value_to_js(key));
|
|
234
|
+
if (!JS_ValueToId(env->cx, key_v, &id)) {
|
|
235
|
+
return gjsify_napi::status_from_false(env);
|
|
236
|
+
}
|
|
237
|
+
if (!JS_HasPropertyById(env->cx, obj, id, result)) {
|
|
238
|
+
return gjsify_napi::status_from_false(env);
|
|
239
|
+
}
|
|
240
|
+
return napi_ok;
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
napi_status NAPI_CDECL napi_delete_property(napi_env env, napi_value object,
|
|
244
|
+
napi_value key, bool* result) {
|
|
245
|
+
GJSIFY_NAPI_PREAMBLE(env);
|
|
246
|
+
GJSIFY_NAPI_CHECK_ARG(env, key);
|
|
247
|
+
GJSIFY_NAPI_CHECK_ARG(env, object);
|
|
248
|
+
JS::RootedObject obj(env->cx);
|
|
249
|
+
napi_status status = gjsify_napi::coerce_object_arg(env, object, &obj);
|
|
250
|
+
if (status != napi_ok) {
|
|
251
|
+
return status;
|
|
252
|
+
}
|
|
253
|
+
JS::RootedId id(env->cx);
|
|
254
|
+
JS::RootedValue key_v(env->cx, gjsify_napi::napi_value_to_js(key));
|
|
255
|
+
if (!JS_ValueToId(env->cx, key_v, &id)) {
|
|
256
|
+
return gjsify_napi::status_from_false(env);
|
|
257
|
+
}
|
|
258
|
+
JS::ObjectOpResult op_result;
|
|
259
|
+
if (!JS_DeletePropertyById(env->cx, obj, id, op_result)) {
|
|
260
|
+
return gjsify_napi::status_from_false(env);
|
|
261
|
+
}
|
|
262
|
+
if (result != nullptr) {
|
|
263
|
+
*result = op_result.ok(); // non-strict delete result
|
|
264
|
+
}
|
|
265
|
+
return napi_ok;
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
napi_status NAPI_CDECL napi_has_own_property(napi_env env, napi_value object,
|
|
269
|
+
napi_value key, bool* result) {
|
|
270
|
+
GJSIFY_NAPI_PREAMBLE(env);
|
|
271
|
+
GJSIFY_NAPI_CHECK_ARG(env, key);
|
|
272
|
+
GJSIFY_NAPI_CHECK_ARG(env, object);
|
|
273
|
+
GJSIFY_NAPI_CHECK_ARG(env, result);
|
|
274
|
+
JS::RootedObject obj(env->cx);
|
|
275
|
+
napi_status status = gjsify_napi::coerce_object_arg(env, object, &obj);
|
|
276
|
+
if (status != napi_ok) {
|
|
277
|
+
return status;
|
|
278
|
+
}
|
|
279
|
+
// Key must be a string or symbol (cc: napi_name_expected).
|
|
280
|
+
JS::RootedValue key_v(env->cx, gjsify_napi::napi_value_to_js(key));
|
|
281
|
+
if (!key_v.isString() && !key_v.isSymbol()) {
|
|
282
|
+
return gjsify_napi::set_last_error(env, napi_name_expected);
|
|
283
|
+
}
|
|
284
|
+
JS::RootedId id(env->cx);
|
|
285
|
+
if (!JS_ValueToId(env->cx, key_v, &id)) {
|
|
286
|
+
return gjsify_napi::status_from_false(env);
|
|
287
|
+
}
|
|
288
|
+
if (!JS_HasOwnPropertyById(env->cx, obj, id, result)) {
|
|
289
|
+
return gjsify_napi::status_from_false(env);
|
|
290
|
+
}
|
|
291
|
+
return napi_ok;
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
// ---- named-property convenience (utf8 keys) ----
|
|
295
|
+
// napi_set/get_named_property live in value.cc since P0.0; napi_has_named_
|
|
296
|
+
// property completes the trio here.
|
|
297
|
+
|
|
298
|
+
napi_status NAPI_CDECL napi_has_named_property(napi_env env, napi_value object,
|
|
299
|
+
const char* utf8name,
|
|
300
|
+
bool* result) {
|
|
301
|
+
GJSIFY_NAPI_PREAMBLE(env);
|
|
302
|
+
GJSIFY_NAPI_CHECK_ARG(env, object);
|
|
303
|
+
GJSIFY_NAPI_CHECK_ARG(env, utf8name);
|
|
304
|
+
GJSIFY_NAPI_CHECK_ARG(env, result);
|
|
305
|
+
JS::RootedObject obj(env->cx);
|
|
306
|
+
napi_status status = gjsify_napi::coerce_object_arg(env, object, &obj);
|
|
307
|
+
if (status != napi_ok) {
|
|
308
|
+
return status;
|
|
309
|
+
}
|
|
310
|
+
JS::RootedId id(env->cx);
|
|
311
|
+
status = gjsify_napi::property_key_to_id(env, utf8name, nullptr, &id);
|
|
312
|
+
if (status != napi_ok) {
|
|
313
|
+
return status;
|
|
314
|
+
}
|
|
315
|
+
if (!JS_HasPropertyById(env->cx, obj, id, result)) {
|
|
316
|
+
return gjsify_napi::status_from_false(env);
|
|
317
|
+
}
|
|
318
|
+
return napi_ok;
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
// ---- element ops ----
|
|
322
|
+
|
|
323
|
+
napi_status NAPI_CDECL napi_set_element(napi_env env, napi_value object,
|
|
324
|
+
uint32_t index, napi_value value) {
|
|
325
|
+
GJSIFY_NAPI_PREAMBLE(env);
|
|
326
|
+
GJSIFY_NAPI_CHECK_ARG(env, object);
|
|
327
|
+
GJSIFY_NAPI_CHECK_ARG(env, value);
|
|
328
|
+
JS::RootedObject obj(env->cx);
|
|
329
|
+
napi_status status = gjsify_napi::coerce_object_arg(env, object, &obj);
|
|
330
|
+
if (status != napi_ok) {
|
|
331
|
+
return status;
|
|
332
|
+
}
|
|
333
|
+
JS::RootedValue v(env->cx, gjsify_napi::napi_value_to_js(value));
|
|
334
|
+
if (!JS_SetElement(env->cx, obj, index, v)) {
|
|
335
|
+
return gjsify_napi::status_from_false(env);
|
|
336
|
+
}
|
|
337
|
+
return napi_ok;
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
napi_status NAPI_CDECL napi_get_element(napi_env env, napi_value object,
|
|
341
|
+
uint32_t index, napi_value* result) {
|
|
342
|
+
GJSIFY_NAPI_PREAMBLE(env);
|
|
343
|
+
GJSIFY_NAPI_CHECK_ARG(env, object);
|
|
344
|
+
GJSIFY_NAPI_CHECK_ARG(env, result);
|
|
345
|
+
JS::RootedObject obj(env->cx);
|
|
346
|
+
napi_status status = gjsify_napi::coerce_object_arg(env, object, &obj);
|
|
347
|
+
if (status != napi_ok) {
|
|
348
|
+
return status;
|
|
349
|
+
}
|
|
350
|
+
JS::RootedValue v(env->cx);
|
|
351
|
+
if (!JS_GetElement(env->cx, obj, index, &v)) {
|
|
352
|
+
return gjsify_napi::status_from_false(env);
|
|
353
|
+
}
|
|
354
|
+
*result = gjsify_napi::arena_push(env, v);
|
|
355
|
+
return napi_ok;
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
napi_status NAPI_CDECL napi_has_element(napi_env env, napi_value object,
|
|
359
|
+
uint32_t index, bool* result) {
|
|
360
|
+
GJSIFY_NAPI_PREAMBLE(env);
|
|
361
|
+
GJSIFY_NAPI_CHECK_ARG(env, object);
|
|
362
|
+
GJSIFY_NAPI_CHECK_ARG(env, result);
|
|
363
|
+
JS::RootedObject obj(env->cx);
|
|
364
|
+
napi_status status = gjsify_napi::coerce_object_arg(env, object, &obj);
|
|
365
|
+
if (status != napi_ok) {
|
|
366
|
+
return status;
|
|
367
|
+
}
|
|
368
|
+
if (!JS_HasElement(env->cx, obj, index, result)) {
|
|
369
|
+
return gjsify_napi::status_from_false(env);
|
|
370
|
+
}
|
|
371
|
+
return napi_ok;
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
napi_status NAPI_CDECL napi_delete_element(napi_env env, napi_value object,
|
|
375
|
+
uint32_t index, bool* result) {
|
|
376
|
+
GJSIFY_NAPI_PREAMBLE(env);
|
|
377
|
+
GJSIFY_NAPI_CHECK_ARG(env, object);
|
|
378
|
+
JS::RootedObject obj(env->cx);
|
|
379
|
+
napi_status status = gjsify_napi::coerce_object_arg(env, object, &obj);
|
|
380
|
+
if (status != napi_ok) {
|
|
381
|
+
return status;
|
|
382
|
+
}
|
|
383
|
+
JS::ObjectOpResult op_result;
|
|
384
|
+
if (!JS_DeleteElement(env->cx, obj, index, op_result)) {
|
|
385
|
+
return gjsify_napi::status_from_false(env);
|
|
386
|
+
}
|
|
387
|
+
if (result != nullptr) {
|
|
388
|
+
*result = op_result.ok();
|
|
389
|
+
}
|
|
390
|
+
return napi_ok;
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
// ---- arrays ----
|
|
394
|
+
|
|
395
|
+
napi_status NAPI_CDECL napi_create_array(napi_env env, napi_value* result) {
|
|
396
|
+
GJSIFY_NAPI_CHECK_ENV(env);
|
|
397
|
+
GJSIFY_NAPI_CHECK_ARG(env, result);
|
|
398
|
+
JSObject* arr = JS::NewArrayObject(env->cx, 0);
|
|
399
|
+
if (arr == nullptr) {
|
|
400
|
+
return gjsify_napi::set_last_error(env, napi_generic_failure);
|
|
401
|
+
}
|
|
402
|
+
*result = gjsify_napi::arena_push(env, JS::ObjectValue(*arr));
|
|
403
|
+
return gjsify_napi::clear_last_error(env);
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
napi_status NAPI_CDECL napi_create_array_with_length(napi_env env,
|
|
407
|
+
size_t length,
|
|
408
|
+
napi_value* result) {
|
|
409
|
+
GJSIFY_NAPI_CHECK_ENV(env);
|
|
410
|
+
GJSIFY_NAPI_CHECK_ARG(env, result);
|
|
411
|
+
// Match V8's Array::New(length): set the length WITHOUT pre-allocating the
|
|
412
|
+
// backing store. JS::NewArrayObject(cx, length) reserves `length` dense
|
|
413
|
+
// slots, so a documented large length (up to 2^32-1) OOMs; create an empty
|
|
414
|
+
// array and set its length property instead → a sparse array like Node.
|
|
415
|
+
JS::RootedObject arr(env->cx, JS::NewArrayObject(env->cx, 0));
|
|
416
|
+
if (arr == nullptr) {
|
|
417
|
+
return gjsify_napi::set_last_error(env, napi_generic_failure);
|
|
418
|
+
}
|
|
419
|
+
if (length > 0 &&
|
|
420
|
+
!JS::SetArrayLength(env->cx, arr, static_cast<uint32_t>(length))) {
|
|
421
|
+
return gjsify_napi::set_last_error(env, napi_generic_failure);
|
|
422
|
+
}
|
|
423
|
+
*result = gjsify_napi::arena_push(env, JS::ObjectValue(*arr));
|
|
424
|
+
return gjsify_napi::clear_last_error(env);
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
napi_status NAPI_CDECL napi_is_array(napi_env env, napi_value value,
|
|
428
|
+
bool* result) {
|
|
429
|
+
GJSIFY_NAPI_CHECK_ENV(env);
|
|
430
|
+
GJSIFY_NAPI_CHECK_ARG(env, value);
|
|
431
|
+
GJSIFY_NAPI_CHECK_ARG(env, result);
|
|
432
|
+
JS::RootedValue v(env->cx, gjsify_napi::napi_value_to_js(value));
|
|
433
|
+
if (!JS::IsArrayObject(env->cx, v, result)) {
|
|
434
|
+
return gjsify_napi::status_from_false(env);
|
|
435
|
+
}
|
|
436
|
+
return gjsify_napi::clear_last_error(env);
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
napi_status NAPI_CDECL napi_get_array_length(napi_env env, napi_value value,
|
|
440
|
+
uint32_t* result) {
|
|
441
|
+
GJSIFY_NAPI_PREAMBLE(env);
|
|
442
|
+
GJSIFY_NAPI_CHECK_ARG(env, value);
|
|
443
|
+
GJSIFY_NAPI_CHECK_ARG(env, result);
|
|
444
|
+
JS::Value v = gjsify_napi::napi_value_to_js(value);
|
|
445
|
+
GJSIFY_NAPI_RETURN_STATUS_IF_FALSE(env, v.isObject(),
|
|
446
|
+
napi_array_expected);
|
|
447
|
+
JS::RootedObject obj(env->cx, &v.toObject());
|
|
448
|
+
bool is_array = false;
|
|
449
|
+
if (!JS::IsArrayObject(env->cx, obj, &is_array)) {
|
|
450
|
+
return gjsify_napi::status_from_false(env);
|
|
451
|
+
}
|
|
452
|
+
GJSIFY_NAPI_RETURN_STATUS_IF_FALSE(env, is_array, napi_array_expected);
|
|
453
|
+
if (!JS::GetArrayLength(env->cx, obj, result)) {
|
|
454
|
+
return gjsify_napi::status_from_false(env);
|
|
455
|
+
}
|
|
456
|
+
return napi_ok;
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
// ---- prototype / freeze / seal ----
|
|
460
|
+
|
|
461
|
+
napi_status NAPI_CDECL napi_get_prototype(napi_env env, napi_value object,
|
|
462
|
+
napi_value* result) {
|
|
463
|
+
GJSIFY_NAPI_PREAMBLE(env);
|
|
464
|
+
GJSIFY_NAPI_CHECK_ARG(env, object);
|
|
465
|
+
GJSIFY_NAPI_CHECK_ARG(env, result);
|
|
466
|
+
JS::RootedObject obj(env->cx);
|
|
467
|
+
napi_status status = gjsify_napi::coerce_object_arg(env, object, &obj);
|
|
468
|
+
if (status != napi_ok) {
|
|
469
|
+
return status;
|
|
470
|
+
}
|
|
471
|
+
JS::RootedObject proto(env->cx);
|
|
472
|
+
if (!JS_GetPrototype(env->cx, obj, &proto)) {
|
|
473
|
+
return gjsify_napi::status_from_false(env);
|
|
474
|
+
}
|
|
475
|
+
*result = gjsify_napi::arena_push(
|
|
476
|
+
env, proto ? JS::ObjectValue(*proto) : JS::NullValue());
|
|
477
|
+
return napi_ok;
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
napi_status NAPI_CDECL napi_object_freeze(napi_env env, napi_value object) {
|
|
481
|
+
GJSIFY_NAPI_PREAMBLE(env);
|
|
482
|
+
GJSIFY_NAPI_CHECK_ARG(env, object);
|
|
483
|
+
JS::Value v = gjsify_napi::napi_value_to_js(object);
|
|
484
|
+
GJSIFY_NAPI_RETURN_STATUS_IF_FALSE(env, v.isObject(),
|
|
485
|
+
napi_object_expected);
|
|
486
|
+
JS::RootedObject obj(env->cx, &v.toObject());
|
|
487
|
+
if (!JS_FreezeObject(env->cx, obj)) { // SetIntegrityLevel(kFrozen)
|
|
488
|
+
return gjsify_napi::status_from_false(env);
|
|
489
|
+
}
|
|
490
|
+
return napi_ok;
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
napi_status NAPI_CDECL napi_object_seal(napi_env env, napi_value object) {
|
|
494
|
+
GJSIFY_NAPI_PREAMBLE(env);
|
|
495
|
+
GJSIFY_NAPI_CHECK_ARG(env, object);
|
|
496
|
+
JS::Value v = gjsify_napi::napi_value_to_js(object);
|
|
497
|
+
GJSIFY_NAPI_RETURN_STATUS_IF_FALSE(env, v.isObject(),
|
|
498
|
+
napi_object_expected);
|
|
499
|
+
// No public JSAPI SetIntegrityLevel(kSealed) in 140 — delegate to the
|
|
500
|
+
// builtin Object.seal (spec SetIntegrityLevel; we run under preamble
|
|
501
|
+
// like Node's napi_object_seal).
|
|
502
|
+
JS::RootedObject global(env->cx, env->global);
|
|
503
|
+
JS::RootedValue object_ctor(env->cx);
|
|
504
|
+
JS::RootedValue seal_fn(env->cx);
|
|
505
|
+
if (!JS_GetProperty(env->cx, global, "Object", &object_ctor) ||
|
|
506
|
+
!object_ctor.isObject()) {
|
|
507
|
+
return gjsify_napi::status_from_false(env);
|
|
508
|
+
}
|
|
509
|
+
JS::RootedObject object_ctor_obj(env->cx, &object_ctor.toObject());
|
|
510
|
+
if (!JS_GetProperty(env->cx, object_ctor_obj, "seal", &seal_fn)) {
|
|
511
|
+
return gjsify_napi::status_from_false(env);
|
|
512
|
+
}
|
|
513
|
+
JS::RootedValue target(env->cx, v);
|
|
514
|
+
JS::RootedValue rval(env->cx);
|
|
515
|
+
if (!JS::Call(env->cx, object_ctor, seal_fn,
|
|
516
|
+
JS::HandleValueArray(target), &rval)) {
|
|
517
|
+
return gjsify_napi::status_from_false(env);
|
|
518
|
+
}
|
|
519
|
+
return napi_ok;
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
// ---- property names ----
|
|
523
|
+
|
|
524
|
+
static bool id_matches_filter(napi_env env, JS::HandleObject owner,
|
|
525
|
+
JS::HandleId id, napi_key_filter filter,
|
|
526
|
+
bool* matches) {
|
|
527
|
+
*matches = true;
|
|
528
|
+
if (id.isSymbol()) {
|
|
529
|
+
if (filter & napi_key_skip_symbols) {
|
|
530
|
+
*matches = false;
|
|
531
|
+
return true;
|
|
532
|
+
}
|
|
533
|
+
} else if (filter & napi_key_skip_strings) {
|
|
534
|
+
// Node's kSkipStrings also skips integer indices converted from
|
|
535
|
+
// numbers only when key conversion keeps them as strings — mirror
|
|
536
|
+
// V8 PropertyFilter: ONLY_/SKIP_ classify by name kind.
|
|
537
|
+
*matches = false;
|
|
538
|
+
return true;
|
|
539
|
+
}
|
|
540
|
+
if (!(filter &
|
|
541
|
+
(napi_key_writable | napi_key_enumerable | napi_key_configurable))) {
|
|
542
|
+
return true;
|
|
543
|
+
}
|
|
544
|
+
JS::Rooted<mozilla::Maybe<JS::PropertyDescriptor>> desc(env->cx);
|
|
545
|
+
if (!JS_GetOwnPropertyDescriptorById(env->cx, owner, id, &desc)) {
|
|
546
|
+
return false;
|
|
547
|
+
}
|
|
548
|
+
if (desc.isNothing()) {
|
|
549
|
+
*matches = false;
|
|
550
|
+
return true;
|
|
551
|
+
}
|
|
552
|
+
const JS::PropertyDescriptor& d = *desc.get();
|
|
553
|
+
if ((filter & napi_key_writable) &&
|
|
554
|
+
!(d.isDataDescriptor() ? d.writable() : d.hasSetter())) {
|
|
555
|
+
*matches = false;
|
|
556
|
+
}
|
|
557
|
+
if ((filter & napi_key_enumerable) && !d.enumerable()) {
|
|
558
|
+
*matches = false;
|
|
559
|
+
}
|
|
560
|
+
if ((filter & napi_key_configurable) && !d.configurable()) {
|
|
561
|
+
*matches = false;
|
|
562
|
+
}
|
|
563
|
+
return true;
|
|
564
|
+
}
|
|
565
|
+
|
|
566
|
+
napi_status NAPI_CDECL napi_get_all_property_names(
|
|
567
|
+
napi_env env, napi_value object, napi_key_collection_mode key_mode,
|
|
568
|
+
napi_key_filter key_filter, napi_key_conversion key_conversion,
|
|
569
|
+
napi_value* result) {
|
|
570
|
+
GJSIFY_NAPI_PREAMBLE(env);
|
|
571
|
+
GJSIFY_NAPI_CHECK_ARG(env, object);
|
|
572
|
+
GJSIFY_NAPI_CHECK_ARG(env, result);
|
|
573
|
+
GJSIFY_NAPI_RETURN_STATUS_IF_FALSE(
|
|
574
|
+
env,
|
|
575
|
+
key_mode == napi_key_include_prototypes || key_mode == napi_key_own_only,
|
|
576
|
+
napi_invalid_arg);
|
|
577
|
+
GJSIFY_NAPI_RETURN_STATUS_IF_FALSE(
|
|
578
|
+
env,
|
|
579
|
+
key_conversion == napi_key_keep_numbers ||
|
|
580
|
+
key_conversion == napi_key_numbers_to_strings,
|
|
581
|
+
napi_invalid_arg);
|
|
582
|
+
JS::RootedObject obj(env->cx);
|
|
583
|
+
napi_status status = gjsify_napi::coerce_object_arg(env, object, &obj);
|
|
584
|
+
if (status != napi_ok) {
|
|
585
|
+
return status;
|
|
586
|
+
}
|
|
587
|
+
|
|
588
|
+
// Collect own keys per prototype-chain level (Reflect.ownKeys flags:
|
|
589
|
+
// OWNONLY|HIDDEN|SYMBOLS — filtered below), deduped across levels via
|
|
590
|
+
// js::AppendUnique. Own-only mode stops after the first level.
|
|
591
|
+
JS::RootedIdVector keys(env->cx);
|
|
592
|
+
JS::RootedObject current(env->cx, obj);
|
|
593
|
+
while (current) {
|
|
594
|
+
JS::RootedIdVector level_keys(env->cx);
|
|
595
|
+
if (!js::GetPropertyKeys(env->cx, current,
|
|
596
|
+
JSITER_OWNONLY | JSITER_HIDDEN |
|
|
597
|
+
JSITER_SYMBOLS,
|
|
598
|
+
&level_keys)) {
|
|
599
|
+
return gjsify_napi::status_from_false(env);
|
|
600
|
+
}
|
|
601
|
+
JS::RootedIdVector filtered(env->cx);
|
|
602
|
+
for (size_t i = 0; i < level_keys.length(); i++) {
|
|
603
|
+
JS::RootedId id(env->cx, level_keys[i]);
|
|
604
|
+
bool matches = false;
|
|
605
|
+
if (!id_matches_filter(env, current, id, key_filter,
|
|
606
|
+
&matches)) {
|
|
607
|
+
return gjsify_napi::status_from_false(env);
|
|
608
|
+
}
|
|
609
|
+
if (matches && !filtered.append(id)) {
|
|
610
|
+
return gjsify_napi::set_last_error(env, napi_generic_failure);
|
|
611
|
+
}
|
|
612
|
+
}
|
|
613
|
+
if (!js::AppendUnique(env->cx, &keys, filtered)) {
|
|
614
|
+
return gjsify_napi::set_last_error(env, napi_generic_failure);
|
|
615
|
+
}
|
|
616
|
+
if (key_mode == napi_key_own_only) {
|
|
617
|
+
break;
|
|
618
|
+
}
|
|
619
|
+
JS::RootedObject proto(env->cx);
|
|
620
|
+
if (!JS_GetPrototype(env->cx, current, &proto)) {
|
|
621
|
+
return gjsify_napi::status_from_false(env);
|
|
622
|
+
}
|
|
623
|
+
current = proto;
|
|
624
|
+
}
|
|
625
|
+
|
|
626
|
+
JS::RootedObject arr(env->cx, JS::NewArrayObject(env->cx, keys.length()));
|
|
627
|
+
if (!arr) {
|
|
628
|
+
return gjsify_napi::set_last_error(env, napi_generic_failure);
|
|
629
|
+
}
|
|
630
|
+
for (size_t i = 0; i < keys.length(); i++) {
|
|
631
|
+
JS::RootedId id(env->cx, keys[i]);
|
|
632
|
+
JS::RootedValue key_v(env->cx);
|
|
633
|
+
if (!JS_IdToValue(env->cx, id, &key_v)) {
|
|
634
|
+
return gjsify_napi::status_from_false(env);
|
|
635
|
+
}
|
|
636
|
+
if (key_v.isNumber() &&
|
|
637
|
+
key_conversion == napi_key_numbers_to_strings) {
|
|
638
|
+
JSString* str = JS::ToString(env->cx, key_v);
|
|
639
|
+
if (str == nullptr) {
|
|
640
|
+
return gjsify_napi::status_from_false(env);
|
|
641
|
+
}
|
|
642
|
+
key_v.setString(str);
|
|
643
|
+
}
|
|
644
|
+
if (!JS_SetElement(env->cx, arr, static_cast<uint32_t>(i), key_v)) {
|
|
645
|
+
return gjsify_napi::status_from_false(env);
|
|
646
|
+
}
|
|
647
|
+
}
|
|
648
|
+
*result = gjsify_napi::arena_push(env, JS::ObjectValue(*arr));
|
|
649
|
+
return napi_ok;
|
|
650
|
+
}
|
|
651
|
+
|
|
652
|
+
napi_status NAPI_CDECL napi_get_property_names(napi_env env, napi_value object,
|
|
653
|
+
napi_value* result) {
|
|
654
|
+
// Enumerable string-keyed, walking the prototype chain, numbers as
|
|
655
|
+
// strings — exactly Node's delegation (cc:1826-1835).
|
|
656
|
+
return napi_get_all_property_names(
|
|
657
|
+
env, object, napi_key_include_prototypes,
|
|
658
|
+
static_cast<napi_key_filter>(napi_key_enumerable |
|
|
659
|
+
napi_key_skip_symbols),
|
|
660
|
+
napi_key_numbers_to_strings, result);
|
|
661
|
+
}
|
|
662
|
+
|
|
663
|
+
// ---- instanceof / run_script / strict_equals ----
|
|
664
|
+
|
|
665
|
+
napi_status NAPI_CDECL napi_instanceof(napi_env env, napi_value object,
|
|
666
|
+
napi_value constructor, bool* result) {
|
|
667
|
+
GJSIFY_NAPI_PREAMBLE(env);
|
|
668
|
+
GJSIFY_NAPI_CHECK_ARG(env, object);
|
|
669
|
+
GJSIFY_NAPI_CHECK_ARG(env, constructor);
|
|
670
|
+
GJSIFY_NAPI_CHECK_ARG(env, result);
|
|
671
|
+
*result = false;
|
|
672
|
+
JS::Value ctor_v = gjsify_napi::napi_value_to_js(constructor);
|
|
673
|
+
GJSIFY_NAPI_RETURN_STATUS_IF_FALSE(env, ctor_v.isObject(),
|
|
674
|
+
napi_object_expected);
|
|
675
|
+
GJSIFY_NAPI_RETURN_STATUS_IF_FALSE(env,
|
|
676
|
+
JS::IsCallable(&ctor_v.toObject()),
|
|
677
|
+
napi_function_expected);
|
|
678
|
+
JS::RootedObject ctor(env->cx, &ctor_v.toObject());
|
|
679
|
+
JS::RootedValue v(env->cx, gjsify_napi::napi_value_to_js(object));
|
|
680
|
+
if (!JS_HasInstance(env->cx, ctor, v, result)) {
|
|
681
|
+
return gjsify_napi::status_from_false(env);
|
|
682
|
+
}
|
|
683
|
+
return napi_ok;
|
|
684
|
+
}
|
|
685
|
+
|
|
686
|
+
napi_status NAPI_CDECL napi_run_script(napi_env env, napi_value script,
|
|
687
|
+
napi_value* result) {
|
|
688
|
+
GJSIFY_NAPI_PREAMBLE(env);
|
|
689
|
+
GJSIFY_NAPI_CHECK_ARG(env, script);
|
|
690
|
+
GJSIFY_NAPI_CHECK_ARG(env, result);
|
|
691
|
+
JS::Value script_v = gjsify_napi::napi_value_to_js(script);
|
|
692
|
+
GJSIFY_NAPI_RETURN_STATUS_IF_FALSE(env, script_v.isString(),
|
|
693
|
+
napi_string_expected);
|
|
694
|
+
// Copy out the source (JS::SourceText borrows; the JSString could move).
|
|
695
|
+
JS::RootedString str(env->cx, script_v.toString());
|
|
696
|
+
JS::UniqueTwoByteChars chars(JS_CopyStringCharsZ(env->cx, str));
|
|
697
|
+
if (!chars) {
|
|
698
|
+
return gjsify_napi::set_last_error(env, napi_generic_failure);
|
|
699
|
+
}
|
|
700
|
+
const size_t source_len = JS_GetStringLength(str);
|
|
701
|
+
JS::SourceText<char16_t> source;
|
|
702
|
+
if (!source.init(env->cx, chars.get(), source_len,
|
|
703
|
+
JS::SourceOwnership::Borrowed)) {
|
|
704
|
+
return gjsify_napi::status_from_false(env);
|
|
705
|
+
}
|
|
706
|
+
JS::CompileOptions options(env->cx);
|
|
707
|
+
options.setFileAndLine("napi_run_script", 1);
|
|
708
|
+
JS::RootedValue rval(env->cx);
|
|
709
|
+
if (!JS::Evaluate(env->cx, options, source, &rval)) {
|
|
710
|
+
return gjsify_napi::status_from_false(env);
|
|
711
|
+
}
|
|
712
|
+
*result = gjsify_napi::arena_push(env, rval);
|
|
713
|
+
return napi_ok;
|
|
714
|
+
}
|
|
715
|
+
|
|
716
|
+
napi_status NAPI_CDECL napi_strict_equals(napi_env env, napi_value lhs,
|
|
717
|
+
napi_value rhs, bool* result) {
|
|
718
|
+
GJSIFY_NAPI_CHECK_ENV(env);
|
|
719
|
+
GJSIFY_NAPI_CHECK_ARG(env, lhs);
|
|
720
|
+
GJSIFY_NAPI_CHECK_ARG(env, rhs);
|
|
721
|
+
GJSIFY_NAPI_CHECK_ARG(env, result);
|
|
722
|
+
JS::RootedValue a(env->cx, gjsify_napi::napi_value_to_js(lhs));
|
|
723
|
+
JS::RootedValue b(env->cx, gjsify_napi::napi_value_to_js(rhs));
|
|
724
|
+
if (!JS::StrictlyEqual(env->cx, a, b, result)) {
|
|
725
|
+
return gjsify_napi::status_from_false(env);
|
|
726
|
+
}
|
|
727
|
+
return gjsify_napi::clear_last_error(env);
|
|
728
|
+
}
|