@auto_js/napi 0.0.11 → 0.0.12

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/transfer/visit.cc CHANGED
@@ -1,4 +1,5 @@
1
1
  export module napi_js:visit;
2
+ import :lock;
2
3
  import :utility;
3
4
  import :value;
4
5
  import std;
@@ -24,7 +25,7 @@ struct visit_property_name {
24
25
  auto operator()(napi_value subject, const Accept& accept) -> accept_target_t<Accept> {
25
26
  return visit_.get().lookup_or_visit(subject, [ & ] -> accept_target_t<Accept> {
26
27
  auto accept_as = [ & ]<class Tag>(Tag tag) -> auto {
27
- auto value = value_of{napi_env{visit_.get()}, local_of<Tag>::from(subject)};
28
+ auto value = value_of{visit_.get().lock(), local_of<Tag>::from(subject)};
28
29
  return accept(tag, *this, value);
29
30
  };
30
31
  switch (napi::invoke(napi_typeof, napi_env{visit_.get()}, subject)) {
@@ -36,7 +37,7 @@ struct visit_property_name {
36
37
  });
37
38
  }
38
39
 
39
- [[nodiscard]] auto environment() const -> auto& { return visit_.get().environment(); }
40
+ [[nodiscard]] auto lock() const -> const auto& { return visit_.get().lock(); }
40
41
 
41
42
  private:
42
43
  std::reference_wrapper<Visit> visit_;
@@ -50,34 +51,34 @@ struct visit_napi_key_literal {
50
51
 
51
52
  auto operator()(const auto& /*could_be_literally_anything*/, const auto& accept_or_visit) -> napi_value {
52
53
  const auto make = util::overloaded{
53
- [](auto& env, std::string_view subject) -> napi::local_of<string_tag_of<char>> {
54
- auto* value = napi::invoke(node_api_create_property_key_latin1, napi_env{env}, subject.data(), subject.length());
54
+ [](environment_lock_witness lock, std::string_view subject) -> napi::local_of<string_tag_of<char>> {
55
+ auto* value = napi::invoke(node_api_create_property_key_latin1, napi_env{lock}, subject.data(), subject.length());
55
56
  return napi::local_of<string_tag_of<char>>::from(value);
56
57
  },
57
- [](auto& env, std::u16string_view subject) -> napi::local_of<string_tag_of<char16_t>> {
58
- auto* value = napi::invoke(node_api_create_property_key_utf16, napi_env{env}, subject.data(), subject.length());
58
+ [](environment_lock_witness lock, std::u16string_view subject) -> napi::local_of<string_tag_of<char16_t>> {
59
+ auto* value = napi::invoke(node_api_create_property_key_utf16, napi_env{lock}, subject.data(), subject.length());
59
60
  return napi::local_of<string_tag_of<char16_t>>::from(value);
60
61
  },
61
- [](auto& env, std::u8string_view subject) -> napi::local_of<string_tag_of<char8_t>> {
62
- auto* value = napi::invoke(node_api_create_property_key_utf8, napi_env{env}, reinterpret_cast<const char*>(subject.data()), subject.length());
62
+ [](environment_lock_witness lock, std::u8string_view subject) -> napi::local_of<string_tag_of<char8_t>> {
63
+ auto* value = napi::invoke(node_api_create_property_key_utf8, napi_env{lock}, reinterpret_cast<const char*>(subject.data()), subject.length());
63
64
  return napi::local_of<string_tag_of<char8_t>>::from(value);
64
65
  },
65
66
  };
66
67
  if (local_key_ == napi_value{}) {
67
68
  constexpr auto key = util::make_consteval_string_view(Key);
68
- auto& environment = accept_or_visit.environment();
69
- auto storage = environment.string_table_storage(Key);
69
+ const auto& lock = accept_or_visit.lock();
70
+ auto storage = lock->string_table_storage(Key);
70
71
  if (storage) {
71
72
  auto& reference = *storage;
72
73
  if (reference) {
73
- local_key_ = reference.get(environment);
74
+ local_key_ = reference.get(lock);
74
75
  } else {
75
- auto value = make(environment, key);
76
- reference.reset(environment, value);
76
+ auto value = make(lock, key);
77
+ reference.reset(lock, value);
77
78
  local_key_ = napi_value{value};
78
79
  }
79
80
  } else {
80
- return make(environment, key);
81
+ return make(lock, key);
81
82
  }
82
83
  }
83
84
  return local_key_;
@@ -90,7 +91,7 @@ struct visit_napi_key_literal {
90
91
  // Base napi visitor implementing all functionality. Napi doesn't give us granular information like
91
92
  // "is this a latin1 string" and all checks must be made at once. So it's structured it great deal
92
93
  // differently than the v8 visitor.
93
- template <auto_environment Environment, class Ref>
94
+ template <class Lock, class Ref>
94
95
  struct visit_value;
95
96
 
96
97
  template <class Meta>
@@ -98,85 +99,85 @@ using visit_value_with = visit_value<
98
99
  typename Meta::visit_context_type,
99
100
  typename Meta::accept_reference_type>;
100
101
 
101
- template <auto_environment Environment, class Reference>
102
+ template <class Lock, class Reference>
102
103
  struct visit_value : reference_map_t<Reference, reference_map_type> {
103
104
  public:
104
105
  using reference_map_t<Reference, reference_map_type>::lookup_or_visit;
105
106
 
106
- visit_value(auto* /*transfer*/, Environment& env) :
107
- env_{env} {}
107
+ visit_value(auto* /*transfer*/, const Lock& lock) :
108
+ lock_{lock} {}
108
109
 
109
- // If the private `immediate` operation is defined: this public operation will first
110
- // perform a reference map lookup, then delegate to the private operation if not found.
110
+ // If the `immediate` operation is defined: this operation will first perform a
111
+ // reference map lookup, then delegate to the `immediate` operation if not found.
111
112
  template <class Tag, class Accept>
112
- auto operator()(local_of<Tag> subject, const Accept& accept) -> accept_target_t<Accept>
113
- requires requires { immediate(subject, accept); } {
114
- return lookup_or_visit(subject, [ & ] -> accept_target_t<Accept> {
115
- return immediate(subject, accept);
113
+ auto operator()(this auto& self, local_of<Tag> subject, const Accept& accept) -> accept_target_t<Accept>
114
+ requires requires { self.immediate(subject, accept); } {
115
+ return self.lookup_or_visit(subject, [ & ] -> accept_target_t<Accept> {
116
+ return self.immediate(subject, accept);
116
117
  });
117
118
  }
118
119
 
119
120
  // Visit operations for non-refable types.
120
121
  template <class Accept>
121
- auto operator()(local_of<null_tag> subject, const Accept& accept) -> accept_target_t<Accept> {
122
- return immediate(subject, accept);
122
+ auto operator()(this auto& self, local_of<null_tag> subject, const Accept& accept) -> accept_target_t<Accept> {
123
+ return self.immediate(subject, accept);
123
124
  }
124
125
 
125
126
  template <class Accept>
126
- auto operator()(local_of<undefined_tag> subject, const Accept& accept) -> accept_target_t<Accept> {
127
- return immediate(subject, accept);
127
+ auto operator()(this auto& self, local_of<undefined_tag> subject, const Accept& accept) -> accept_target_t<Accept> {
128
+ return self.immediate(subject, accept);
128
129
  }
129
130
 
130
131
  template <class Accept>
131
- auto operator()(local_of<boolean_tag> subject, const Accept& accept) -> accept_target_t<Accept> {
132
- return immediate(subject, accept);
132
+ auto operator()(this auto& self, local_of<boolean_tag> subject, const Accept& accept) -> accept_target_t<Accept> {
133
+ return self.immediate(subject, accept);
133
134
  }
134
135
 
135
136
  template <class Accept>
136
- auto operator()(local_of<number_tag> subject, const Accept& accept) -> accept_target_t<Accept> {
137
+ auto operator()(this auto& self, local_of<number_tag> subject, const Accept& accept) -> accept_target_t<Accept> {
137
138
  if (maybe_is_number_int32(subject).value_or(false)) {
138
- return immediate(local_of<number_tag_of<std::int32_t>>::from(subject), accept);
139
+ return self.immediate(local_of<number_tag_of<std::int32_t>>::from(subject), accept);
139
140
  } else {
140
- return immediate(local_of<number_tag_of<double>>::from(subject), accept);
141
+ return self.immediate(local_of<number_tag_of<double>>::from(subject), accept);
141
142
  }
142
143
  }
143
144
 
144
145
  template <class Accept, class Type>
145
- auto operator()(local_of<number_tag_of<Type>> subject, const Accept& accept) -> accept_target_t<Accept> {
146
- return immediate(subject, accept);
146
+ auto operator()(this auto& self, local_of<number_tag_of<Type>> subject, const Accept& accept) -> accept_target_t<Accept> {
147
+ return self.immediate(subject, accept);
147
148
  }
148
149
 
149
150
  // General purpose visit operation which actually performs the type check
150
151
  template <class Accept>
151
- auto operator()(napi_value subject, const Accept& accept) -> accept_target_t<Accept> {
152
+ auto operator()(this auto& self, napi_value subject, const Accept& accept) -> accept_target_t<Accept> {
152
153
 
153
154
  // Check the reference map, and lookup type via napi
154
- return lookup_or_visit(subject, [ & ] -> accept_target_t<Accept> {
155
+ return self.lookup_or_visit(subject, [ & ] -> accept_target_t<Accept> {
155
156
  // This is the pure-napi implementation
156
157
  auto slow_path = util::overloaded{
157
158
  [ & ] -> accept_target_t<Accept> {
158
- auto type_of = napi::invoke(napi_typeof, napi_env{*this}, subject);
159
+ auto type_of = napi::invoke(napi_typeof, napi_env{self}, subject);
159
160
  switch (type_of) {
160
161
  case napi_undefined:
161
- return (*this)(local_of<undefined_tag>::from(subject), accept);
162
+ return self(local_of<undefined_tag>::from(subject), accept);
162
163
  case napi_null:
163
- return (*this)(local_of<null_tag>::from(subject), accept);
164
+ return self(local_of<null_tag>::from(subject), accept);
164
165
  case napi_boolean:
165
- return (*this)(local_of<boolean_tag>::from(subject), accept);
166
+ return self(local_of<boolean_tag>::from(subject), accept);
166
167
  case napi_number:
167
- return (*this)(local_of<number_tag>::from(subject), accept);
168
+ return self(local_of<number_tag>::from(subject), accept);
168
169
  case napi_string:
169
- return immediate(local_of<string_tag>::from(subject), accept);
170
+ return self.immediate(local_of<string_tag>::from(subject), accept);
170
171
  case napi_symbol:
171
- return immediate(local_of<symbol_tag>::from(subject), accept);
172
+ return self.immediate(local_of<symbol_tag>::from(subject), accept);
172
173
  case napi_object:
173
- return immediate(local_of<object_tag>::from(subject), accept);
174
+ return self.immediate(local_of<object_tag>::from(subject), accept);
174
175
  case napi_function:
175
- return immediate(local_of<function_tag>::from(subject), accept);
176
+ return self.immediate(local_of<function_tag>::from(subject), accept);
176
177
  case napi_external:
177
- return immediate(local_of<external_tag>::from(subject), accept);
178
+ return self.immediate(local_of<external_tag>::from(subject), accept);
178
179
  case napi_bigint:
179
- return immediate(local_of<bigint_tag>::from(subject), accept);
180
+ return self.immediate(local_of<bigint_tag>::from(subject), accept);
180
181
  }
181
182
  std::unreachable();
182
183
  },
@@ -187,16 +188,16 @@ struct visit_value : reference_map_t<Reference, reference_map_type> {
187
188
  // Universal fast checks
188
189
  auto fast_path = util::overloaded{
189
190
  [ & ](undefined_tag /*tag*/, auto next) -> accept_target_t<Accept> {
190
- return fast_is_undefined(napi_env{*this}, subject) ? (*this)(local_of<undefined_tag>::from(subject), accept) : next();
191
+ return fast_is_undefined(napi_env{self}, subject) ? self(local_of<undefined_tag>::from(subject), accept) : next();
191
192
  },
192
193
  [ & ](null_tag /*tag*/, auto next) -> accept_target_t<Accept> {
193
- return fast_is_null(napi_env{*this}, subject) ? (*this)(local_of<null_tag>::from(subject), accept) : next();
194
+ return fast_is_null(napi_env{self}, subject) ? self(local_of<null_tag>::from(subject), accept) : next();
194
195
  },
195
196
  [ & ](boolean_tag /*tag*/, auto next) -> accept_target_t<Accept> {
196
- if (fast_is_false(napi_env{*this}, subject)) {
197
- return (*this)(local_of<false_tag>::from(subject), accept);
198
- } else if (fast_is_true(napi_env{*this}, subject)) {
199
- return (*this)(local_of<true_tag>::from(subject), accept);
197
+ if (fast_is_false(napi_env{self}, subject)) {
198
+ return self(local_of<false_tag>::from(subject), accept);
199
+ } else if (fast_is_true(napi_env{self}, subject)) {
200
+ return self(local_of<true_tag>::from(subject), accept);
200
201
  } else {
201
202
  return next();
202
203
  }
@@ -206,27 +207,27 @@ struct visit_value : reference_map_t<Reference, reference_map_type> {
206
207
  // Extended fast checks
207
208
  auto extended_fast_path = util::overloaded{
208
209
  [ & ](number_tag /*tag*/, auto next) -> accept_target_t<Accept> {
209
- return fast_is_number(subject) ? (*this)(local_of<number_tag>::from(subject), accept) : next();
210
+ return fast_is_number(subject) ? self(local_of<number_tag>::from(subject), accept) : next();
210
211
  },
211
212
  [ & ](string_tag /*tag*/, auto next) -> accept_target_t<Accept> {
212
- return fast_is_string(subject) ? immediate(local_of<string_tag>::from(subject), accept) : next();
213
+ return fast_is_string(subject) ? self.immediate(local_of<string_tag>::from(subject), accept) : next();
213
214
  },
214
215
  [ & ](bigint_tag /*tag*/, auto next) -> accept_target_t<Accept> {
215
- return fast_is_bigint(subject) ? immediate(local_of<bigint_tag>::from(subject), accept) : next();
216
+ return fast_is_bigint(subject) ? self.immediate(local_of<bigint_tag>::from(subject), accept) : next();
216
217
  },
217
218
  [ & ](date_tag /*tag*/, auto next) -> accept_target_t<Accept> {
218
- return napi::invoke(napi_is_date, napi_env{*this}, subject) ? immediate(local_of<date_tag>::from(subject), accept) : next();
219
+ return napi::invoke(napi_is_date, napi_env{self}, subject) ? self.immediate(local_of<date_tag>::from(subject), accept) : next();
219
220
  },
220
221
  [ & ](list_tag /*tag*/, auto next) -> accept_target_t<Accept> {
221
- return fast_is_array(subject) ? immediate(local_of<list_tag>::from(subject), accept) : next();
222
+ return fast_is_array(subject) ? self.immediate(local_of<list_tag>::from(subject), accept) : next();
222
223
  },
223
224
  [ & ](object_tag /*tag*/, auto next) -> accept_target_t<Accept> {
224
225
  // nb: You can't really skip the subsequent is promise, is date, is arraybuffer, etc
225
226
  // checks. We don't really want to accept those types here, I don't think..
226
- return fast_is_object(subject) ? immediate(local_of<object_tag>::from(subject), accept) : next();
227
+ return fast_is_object(subject) ? self.immediate(local_of<object_tag>::from(subject), accept) : next();
227
228
  },
228
229
  [ & ](function_tag /*tag*/, auto next) -> accept_target_t<Accept> {
229
- return fast_is_function(subject) ? immediate(local_of<function_tag>::from(subject), accept) : next();
230
+ return fast_is_function(subject) ? self.immediate(local_of<function_tag>::from(subject), accept) : next();
230
231
  },
231
232
 
232
233
  // Skip these, otherwise `number_tag` and `string_tag` are invoked twice, which we don't
@@ -246,19 +247,19 @@ struct visit_value : reference_map_t<Reference, reference_map_type> {
246
247
  }
247
248
 
248
249
  template <class Accept>
249
- auto operator()(local_of<data_block_tag> subject, const Accept& accept) -> accept_target_t<Accept> {
250
- return lookup_or_visit(subject, [ & ] -> accept_target_t<Accept> {
250
+ auto operator()(this auto& self, local_of<data_block_tag> subject, const Accept& accept) -> accept_target_t<Accept> {
251
+ return self.lookup_or_visit(subject, [ & ] -> accept_target_t<Accept> {
251
252
  return util::template_traverse(
252
253
  accept_tags_of_v<Accept>,
253
254
  util::overloaded{
254
255
  // Fast paths
255
- [ & ](data_block_tag /*tag*/, auto /*next*/) -> accept_target_t<Accept> { return accept_tagged(subject, accept); },
256
+ [ & ](data_block_tag /*tag*/, auto /*next*/) -> accept_target_t<Accept> { return self.accept_tagged(subject, accept); },
256
257
  [ & ](array_buffer_tag /*tag*/, auto next) -> accept_target_t<Accept> { return next(); },
257
258
  [ & ](shared_array_buffer_tag /*tag*/, auto next) -> accept_target_t<Accept> { return next(); },
258
259
 
259
260
  // Slow path
260
261
  [ & ] -> accept_target_t<Accept> {
261
- return immediate(subject, accept);
262
+ return self.immediate(subject, accept);
262
263
  }
263
264
  }
264
265
  );
@@ -266,88 +267,88 @@ struct visit_value : reference_map_t<Reference, reference_map_type> {
266
267
  }
267
268
 
268
269
  // extras
269
- [[nodiscard]] auto environment() const -> Environment& { return env_; }
270
- explicit operator napi_env() const { return napi_env{env_.get()}; }
270
+ [[nodiscard]] auto lock() const -> const Lock& { return lock_; }
271
+ explicit operator napi_env() const { return napi_env{lock_.get()}; }
271
272
  consteval static auto types(auto /*recursive*/) { return util::type_pack{}; }
272
273
 
273
- private:
274
+ protected:
274
275
  // I think this only applies to `symbol_tag`
275
276
  template <class Accept, class Tag>
276
277
  requires std::is_convertible_v<Tag, primitive_tag>
277
- auto immediate(local_of<Tag> subject, const Accept& accept) -> accept_target_t<Accept> {
278
- return accept_tagged(subject, accept);
278
+ auto immediate(this auto& self, local_of<Tag> subject, const Accept& accept) -> accept_target_t<Accept> {
279
+ return self.accept_tagged(subject, accept);
279
280
  }
280
281
 
281
282
  // strings
282
283
  template <class Accept>
283
- auto immediate(local_of<string_tag> subject, const Accept& accept) -> accept_target_t<Accept> {
284
+ auto immediate(this auto& self, local_of<string_tag> subject, const Accept& accept) -> accept_target_t<Accept> {
284
285
  if (maybe_is_string_latin1(subject).value_or(false)) {
285
- return accept_tagged(local_of<string_tag_of<char>>::from(subject), accept);
286
+ return self.accept_tagged(local_of<string_tag_of<char>>::from(subject), accept);
286
287
  } else {
287
- return accept_tagged(local_of<string_tag_of<char16_t>>::from(subject), accept);
288
+ return self.accept_tagged(local_of<string_tag_of<char16_t>>::from(subject), accept);
288
289
  }
289
290
  }
290
291
 
291
292
  // bigint
292
293
  template <class Accept>
293
- auto immediate(local_of<bigint_tag> subject, const Accept& accept) -> accept_target_t<Accept> {
294
- return accept_tagged(local_of<bigint_tag_of<bigint>>::from(subject), accept);
294
+ auto immediate(this auto& self, local_of<bigint_tag> subject, const Accept& accept) -> accept_target_t<Accept> {
295
+ return self.accept_tagged(local_of<bigint_tag_of<bigint>>::from(subject), accept);
295
296
  }
296
297
 
297
- // date
298
+ // objects
298
299
  template <class Accept>
299
- auto immediate(local_of<object_tag> subject, const Accept& accept) -> accept_target_t<Accept> {
300
- if (napi::invoke(napi_is_array, napi_env{*this}, subject)) {
301
- return immediate(local_of<list_tag>::from(subject), accept);
302
- } else if (napi::invoke(napi_is_date, napi_env{*this}, subject)) {
303
- return immediate(local_of<date_tag>::from(subject), accept);
304
- } else if (napi::invoke(napi_is_typedarray, napi_env{*this}, subject)) {
305
- return immediate(local_of<typed_array_tag>::from(subject), accept);
306
- } else if (napi::invoke(napi_is_dataview, napi_env{*this}, subject)) {
307
- return immediate(local_of<data_view_tag>::from(subject), accept);
308
- } else if (maybe_is_shared_array_buffer(env_.get(), subject).value_or(false)) {
309
- return immediate(local_of<shared_array_buffer_tag>::from(subject), accept);
310
- } else if (is_object_array_buffer(env_.get(), subject)) {
311
- return immediate(local_of<array_buffer_tag>::from(subject), accept);
312
- } else if (napi::invoke(napi_is_promise, napi_env{*this}, subject)) {
313
- return immediate(local_of<promise_tag>::from(subject), accept);
300
+ auto immediate(this auto& self, local_of<object_tag> subject, const Accept& accept) -> accept_target_t<Accept> {
301
+ if (napi::invoke(napi_is_array, napi_env{self}, subject)) {
302
+ return self.immediate(local_of<list_tag>::from(subject), accept);
303
+ } else if (napi::invoke(napi_is_date, napi_env{self}, subject)) {
304
+ return self.immediate(local_of<date_tag>::from(subject), accept);
305
+ } else if (napi::invoke(napi_is_typedarray, napi_env{self}, subject)) {
306
+ return self.immediate(local_of<typed_array_tag>::from(subject), accept);
307
+ } else if (napi::invoke(napi_is_dataview, napi_env{self}, subject)) {
308
+ return self.immediate(local_of<data_view_tag>::from(subject), accept);
309
+ } else if (maybe_is_shared_array_buffer(napi_env{self}, subject).value_or(false)) {
310
+ return self.immediate(local_of<shared_array_buffer_tag>::from(subject), accept);
311
+ } else if (is_object_array_buffer(napi_env{self}, subject)) {
312
+ return self.immediate(local_of<array_buffer_tag>::from(subject), accept);
313
+ } else if (napi::invoke(napi_is_promise, napi_env{self}, subject)) {
314
+ return self.immediate(local_of<promise_tag>::from(subject), accept);
314
315
  } else {
315
- return immediate(local_of<dictionary_tag>::from(subject), accept);
316
+ return self.immediate(local_of<dictionary_tag>::from(subject), accept);
316
317
  }
317
318
  }
318
319
 
319
320
  // date
320
321
  template <class Accept>
321
- auto immediate(local_of<date_tag> subject, const Accept& accept) -> accept_target_t<Accept> {
322
- return accept_tagged(subject, accept);
322
+ auto immediate(this auto& self, local_of<date_tag> subject, const Accept& accept) -> accept_target_t<Accept> {
323
+ return self.accept_tagged(subject, accept);
323
324
  }
324
325
 
325
326
  // data blocks
326
327
  template <class Accept>
327
- auto immediate(local_of<data_block_tag> subject, const Accept& accept) -> accept_target_t<Accept> {
328
- if (is_data_block_array_buffer(env_.get(), subject)) {
329
- return immediate(local_of<array_buffer_tag>::from(subject), accept);
328
+ auto immediate(this auto& self, local_of<data_block_tag> subject, const Accept& accept) -> accept_target_t<Accept> {
329
+ if (is_data_block_array_buffer(napi_env{self}, subject)) {
330
+ return self.immediate(local_of<array_buffer_tag>::from(subject), accept);
330
331
  } else {
331
- return immediate(local_of<shared_array_buffer_tag>::from(subject), accept);
332
+ return self.immediate(local_of<shared_array_buffer_tag>::from(subject), accept);
332
333
  }
333
334
  }
334
335
 
335
336
  template <class Accept, class Tag>
336
337
  requires std::is_convertible_v<Tag, data_block_tag>
337
- auto immediate(local_of<Tag> subject, const Accept& accept) -> accept_target_t<Accept> {
338
- return accept_tagged(subject, accept);
338
+ auto immediate(this auto& self, local_of<Tag> subject, const Accept& accept) -> accept_target_t<Accept> {
339
+ return self.accept_tagged(subject, accept);
339
340
  }
340
341
 
341
342
  // array buffer views
342
343
  template <class Accept>
343
- auto immediate(local_of<typed_array_tag> subject, const Accept& accept) -> accept_target_t<Accept> {
344
- auto bound_subject_variant = value_for_typed_array::make_bound(environment(), subject);
344
+ auto immediate(this auto& self, local_of<typed_array_tag> subject, const Accept& accept) -> accept_target_t<Accept> {
345
+ auto bound_subject_variant = value_for_typed_array::make_bound(self.lock(), subject);
345
346
  if (bound_subject_variant.index() == std::variant_npos) {
346
347
  std::unreachable();
347
348
  } else {
348
349
  return std::visit(
349
350
  [ & ]<class Tag>(value_of<Tag> value) -> accept_target_t<Accept> {
350
- return accept(Tag{}, *this, value);
351
+ return accept(Tag{}, self, value);
351
352
  },
352
353
  bound_subject_variant
353
354
  );
@@ -355,51 +356,95 @@ struct visit_value : reference_map_t<Reference, reference_map_type> {
355
356
  }
356
357
 
357
358
  template <class Accept>
358
- auto immediate(local_of<data_view_tag> subject, const Accept& accept) -> accept_target_t<Accept> {
359
- return accept_tagged(subject, accept);
359
+ auto immediate(this auto& self, local_of<data_view_tag> subject, const Accept& accept) -> accept_target_t<Accept> {
360
+ return self.accept_tagged(subject, accept);
360
361
  }
361
362
 
362
363
  // externals
363
364
  template <class Accept>
364
- auto immediate(local_of<external_tag> subject, const Accept& accept) -> accept_target_t<Accept> {
365
- return accept_tagged(subject, accept);
365
+ auto immediate(this auto& self, local_of<external_tag> subject, const Accept& accept) -> accept_target_t<Accept> {
366
+ return self.accept_tagged(subject, accept);
366
367
  }
367
368
 
368
369
  // promise
369
370
  template <class Accept>
370
- auto immediate(local_of<promise_tag> subject, const Accept& accept) -> accept_target_t<Accept> {
371
- return accept_tagged(subject, accept);
371
+ auto immediate(this auto& self, local_of<promise_tag> subject, const Accept& accept) -> accept_target_t<Accept> {
372
+ return self.accept_tagged(subject, accept);
372
373
  }
373
374
 
374
375
  // function
375
376
  template <class Accept>
376
- auto immediate(local_of<function_tag> subject, const Accept& accept) -> accept_target_t<Accept> {
377
- return accept_tagged(subject, accept);
377
+ auto immediate(this auto& self, local_of<function_tag> subject, const Accept& accept) -> accept_target_t<Accept> {
378
+ return self.accept_tagged(subject, accept);
378
379
  }
379
380
 
380
381
  // array
381
- template <class Accept>
382
- auto immediate(local_of<list_tag> subject, const Accept& accept) -> accept_target_t<Accept> {
383
- auto target = napi::value_of{napi_env{*this}, local_of<list_tag>::from(subject)};
384
- auto visit_entry = visit_entry_pair<visit_property_name<visit_value>, visit_value&>{*this};
382
+ template <class Visit, class Accept>
383
+ auto immediate(this Visit& self, local_of<list_tag> subject, const Accept& accept) -> accept_target_t<Accept> {
384
+ auto target = napi::value_of{self.lock(), local_of<list_tag>::from(subject)};
385
+ auto visit_entry = visit_entry_pair<visit_property_name<Visit>, Visit&>{self};
385
386
  return accept(list_tag{}, visit_entry, target);
386
387
  }
387
388
 
388
389
  // object / record
389
- template <class Accept>
390
- auto immediate(local_of<dictionary_tag> subject, const Accept& accept) -> accept_target_t<Accept> {
391
- auto target = napi::value_of{napi_env{*this}, local_of<dictionary_tag>::from(subject)};
392
- auto visit_entry = visit_entry_pair<visit_property_name<visit_value>, visit_value&>{*this};
390
+ template <class Visit, class Accept>
391
+ auto immediate(this Visit& self, local_of<dictionary_tag> subject, const Accept& accept) -> accept_target_t<Accept> {
392
+ auto target = napi::value_of{self.lock(), local_of<dictionary_tag>::from(subject)};
393
+ auto visit_entry = visit_entry_pair<visit_property_name<Visit>, Visit&>{self};
393
394
  return accept(dictionary_tag{}, visit_entry, target);
394
395
  }
395
396
 
396
397
  // Convenience function which wraps in `napi::value_of` and invokes `accept`.
397
398
  template <class Tag, class Accept>
398
- [[nodiscard]] auto accept_tagged(local_of<Tag> subject, const Accept& accept) -> accept_target_t<Accept> {
399
- return accept(Tag{}, *this, napi::value_of{environment(), subject});
399
+ [[nodiscard]] auto accept_tagged(this auto& self, local_of<Tag> subject, const Accept& accept) -> accept_target_t<Accept> {
400
+ return accept(Tag{}, self, napi::value_of{self.lock(), subject});
401
+ }
402
+
403
+ private:
404
+ std::reference_wrapper<const Lock> lock_;
405
+ };
406
+
407
+ // Visitor with transfer delegate
408
+ template <class Lock, class Reference, class Delegate>
409
+ struct visit_value_delegate;
410
+
411
+ template <class Meta, class Delegate>
412
+ using visit_value_delegate_with =
413
+ visit_value_delegate<typename Meta::visit_context_type, typename Meta::accept_reference_type, Delegate>;
414
+
415
+ template <class Lock, class Reference, class Delegate>
416
+ struct visit_value_delegate : visit_value<Lock, Reference> {
417
+ private:
418
+ using visit_type = visit_value<Lock, Reference>;
419
+
420
+ public:
421
+ visit_value_delegate(auto* transfer, const Lock& lock, Delegate& delegate) :
422
+ visit_type{transfer, lock},
423
+ delegate_{delegate} {}
424
+
425
+ using visit_type::operator();
426
+ using visit_type::immediate;
427
+
428
+ template <class Accept>
429
+ auto immediate(this auto& self, local_of<object_tag> subject, const Accept& accept) -> accept_target_t<Accept> {
430
+ return self.claim_or_immediate(subject, accept);
431
+ }
432
+
433
+ template <class Accept>
434
+ auto immediate(this auto& self, local_of<data_block_tag> subject, const Accept& accept) -> accept_target_t<Accept> {
435
+ return self.claim_or_immediate(subject, accept);
436
+ }
437
+
438
+ private:
439
+ template <class Accept>
440
+ auto claim_or_immediate(this auto& self, auto subject, const Accept& accept) -> accept_target_t<Accept> {
441
+ if (auto claimed = self.delegate_.get()(subject, self, accept)) {
442
+ return *std::move(claimed);
443
+ }
444
+ return self.visit_value<Lock, Reference>::immediate(subject, accept);
400
445
  }
401
446
 
402
- std::reference_wrapper<Environment> env_;
447
+ std::reference_wrapper<Delegate> delegate_;
403
448
  };
404
449
 
405
450
  // Forward `value_of<T>` back to acceptor
@@ -409,7 +454,7 @@ struct visit_napi_value_of {
409
454
  using visit_type = visit_value_with<Meta>;
410
455
 
411
456
  public:
412
- constexpr explicit visit_napi_value_of(auto* transfer, auto& environment) : visit_{transfer, environment} {}
457
+ constexpr explicit visit_napi_value_of(auto* transfer, const auto& lock) : visit_{transfer, lock} {}
413
458
 
414
459
  template <class Accept>
415
460
  constexpr auto operator()(value_of<Tag> subject, const Accept& accept) -> accept_target_t<Accept> {
@@ -451,6 +496,21 @@ struct visit<Meta, napi::local_of<Tag>> : napi::visit_value_with<Meta> {
451
496
  using napi::visit_value_with<Meta>::visit_value_with;
452
497
  };
453
498
 
499
+ // `transferee_visit_subject` subject visitor
500
+ template <class Meta, class Tag, class Delegate>
501
+ struct visit<Meta, transferee_visit_subject<napi::local_of<Tag>, Delegate>> : napi::visit_value_delegate_with<Meta, Delegate> {
502
+ private:
503
+ using visit_type = napi::visit_value_delegate_with<Meta, Delegate>;
504
+
505
+ public:
506
+ using visit_type::visit_type;
507
+
508
+ template <class Accept>
509
+ auto operator()(const transferee_visit_subject<napi::local_of<Tag>, Delegate>& subject, const Accept& accept) -> accept_target_t<Accept> {
510
+ return util::invoke_as<visit_type>(*this, *subject, accept);
511
+ }
512
+ };
513
+
454
514
  // Pass through `value_of<Tag>`
455
515
  template <class Meta, class Tag>
456
516
  struct visit<Meta, napi::value_of<Tag>> : napi::visit_napi_value_of<Meta, Tag> {
package/value/array.cc CHANGED
@@ -22,7 +22,12 @@ value_for_vector::iterator::iterator(value_for_vector subject, size_type index)
22
22
  index_{index} {}
23
23
 
24
24
  auto value_for_vector::iterator::operator*() const -> value_type {
25
- return value_type::from(napi::invoke(napi_get_element, subject_.env(), napi_value{subject_}, index_));
25
+ auto lock = subject_.lock();
26
+ auto* subject = napi_value{subject_};
27
+ if (subject_.maybe_sparse_ && !fast_has_element(lock, subject, index_)) {
28
+ throw js::type_error{u"Sparse arrays are not supported"};
29
+ }
30
+ return value_type::from(fast_get_element(lock, subject, index_));
26
31
  }
27
32
 
28
33
  } // namespace js::napi
package/value/array.h.cc CHANGED
@@ -8,15 +8,20 @@ namespace js::napi {
8
8
  class value_for_vector : public value_next<vector_tag> {
9
9
  public:
10
10
  class iterator;
11
- using value_next<vector_tag>::value_next;
12
- using value_type = local_of<value_tag>;
11
+ using value_type = local_of<>;
12
+
13
+ value_for_vector() = default;
14
+ value_for_vector(environment_lock_witness lock, local_of<vector_tag> value, bool maybe_sparse = true) :
15
+ value_next{lock, value},
16
+ maybe_sparse_{maybe_sparse} {}
13
17
 
14
18
  [[nodiscard]] auto begin() const -> iterator;
15
19
  [[nodiscard]] auto end() const -> iterator;
16
20
  [[nodiscard]] auto size() const -> std::uint32_t;
17
21
 
18
22
  private:
19
- mutable std::uint32_t size_{};
23
+ bool maybe_sparse_ : 1 = true;
24
+ mutable std::uint32_t size_ : 31 {};
20
25
  };
21
26
 
22
27
  class value_for_vector::iterator : public util::random_access_iterator_facade<std::int32_t, std::int64_t> {