Coverage Report

Created: 2025-12-11 06:40

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/hermes/lib/VM/JSProxy.cpp
Line
Count
Source
1
/*
2
 * Copyright (c) Meta Platforms, Inc. and affiliates.
3
 *
4
 * This source code is licensed under the MIT license found in the
5
 * LICENSE file in the root directory of this source tree.
6
 */
7
8
#include "hermes/VM/JSProxy.h"
9
10
#include "hermes/VM/ArrayLike.h"
11
#include "hermes/VM/Callable.h"
12
#include "hermes/VM/JSArray.h"
13
#include "hermes/VM/JSCallableProxy.h"
14
#include "hermes/VM/OrderedHashMap.h"
15
#include "hermes/VM/PropertyAccessor.h"
16
17
#include "llvh/ADT/SmallSet.h"
18
19
#pragma GCC diagnostic push
20
21
#ifdef HERMES_COMPILER_SUPPORTS_WSHORTEN_64_TO_32
22
#pragma GCC diagnostic ignored "-Wshorten-64-to-32"
23
#endif
24
namespace hermes {
25
namespace vm {
26
27
namespace detail {
28
29
0
ProxySlots &slots(JSObject *self) {
30
0
  if (auto *proxy = dyn_vmcast<JSProxy>(self)) {
31
0
    return proxy->slots_;
32
0
  } else {
33
0
    auto *cproxy = dyn_vmcast<JSCallableProxy>(self);
34
0
    assert(
35
0
        cproxy && "JSProxy methods must be passed JSProxy or JSCallableProxy");
36
0
    return cproxy->slots_;
37
0
  }
38
0
}
39
40
CallResult<Handle<Callable>>
41
0
findTrap(Handle<JSObject> selfHandle, Runtime &runtime, Predefined::Str name) {
42
  // 2. Let handler be O.[[ProxyHandler]].
43
  // 3. If handler is null, throw a TypeError exception.
44
0
  JSObject *handlerPtr = detail::slots(*selfHandle).handler.get(runtime);
45
0
  if (!handlerPtr) {
46
0
    return runtime.raiseTypeError("Proxy handler is null");
47
0
  }
48
  // 4. Assert: Type(handler) is Object.
49
  // 5. Let target be O.[[ProxyTarget]].
50
  // 6. Let trap be ? GetMethod(handler, « name »).
51
0
  CallResult<PseudoHandle<>> trapVal = [&]() {
52
0
    GCScope gcScope(runtime);
53
0
    Handle<JSObject> handler = runtime.makeHandle(handlerPtr);
54
0
    return JSObject::getNamed_RJS(
55
0
        handler, runtime, Predefined::getSymbolID(name));
56
0
  }();
57
58
0
  if (trapVal == ExecutionStatus::EXCEPTION) {
59
0
    return ExecutionStatus::EXCEPTION;
60
0
  }
61
0
  if ((*trapVal)->isUndefined() || (*trapVal)->isNull()) {
62
0
    return Runtime::makeNullHandle<Callable>();
63
0
  }
64
65
0
  if (!vmisa<Callable>(trapVal->get())) {
66
0
    return runtime.raiseTypeErrorForValue(
67
0
        runtime.makeHandle(std::move(*trapVal)),
68
0
        " is not a Proxy trap function");
69
0
  }
70
0
  return runtime.makeHandle<Callable>(trapVal->get());
71
0
}
72
73
} // namespace detail
74
75
//===----------------------------------------------------------------------===//
76
// class JSProxy
77
78
const ObjectVTable JSProxy::vt{
79
    VTable(CellKind::JSProxyKind, cellSize<JSProxy>()),
80
    JSProxy::_getOwnIndexedRangeImpl,
81
    JSProxy::_haveOwnIndexedImpl,
82
    JSProxy::_getOwnIndexedPropertyFlagsImpl,
83
    JSProxy::_getOwnIndexedImpl,
84
    JSProxy::_setOwnIndexedImpl,
85
    JSProxy::_deleteOwnIndexedImpl,
86
    JSProxy::_checkAllOwnIndexedImpl,
87
};
88
89
1
void JSProxyBuildMeta(const GCCell *cell, Metadata::Builder &mb) {
90
1
  mb.addJSObjectOverlapSlots(JSObject::numOverlapSlots<JSProxy>());
91
1
  JSObjectBuildMeta(cell, mb);
92
1
  const auto *self = static_cast<const JSProxy *>(cell);
93
1
  mb.setVTable(&JSProxy::vt);
94
1
  mb.addField("@target", &self->slots_.target);
95
1
  mb.addField("@handler", &self->slots_.handler);
96
1
}
97
98
0
PseudoHandle<JSProxy> JSProxy::create(Runtime &runtime) {
99
0
  JSProxy *proxy = runtime.makeAFixed<JSProxy>(
100
0
      runtime,
101
      // Proxy should not have an observable prototype, so we just set it to
102
      // null.
103
0
      Runtime::makeNullHandle<JSObject>(),
104
0
      runtime.getHiddenClassForPrototype(
105
0
          nullptr, JSObject::numOverlapSlots<JSProxy>()));
106
107
0
  proxy->flags_.proxyObject = true;
108
109
0
  return JSObjectInit::initToPseudoHandle(runtime, proxy);
110
0
}
111
112
void JSProxy::setTargetAndHandler(
113
    Handle<JSObject> selfHandle,
114
    Runtime &runtime,
115
    Handle<JSObject> target,
116
0
    Handle<JSObject> handler) {
117
0
  auto &slots = detail::slots(*selfHandle);
118
0
  slots.target.set(runtime, target.get(), runtime.getHeap());
119
0
  slots.handler.set(runtime, handler.get(), runtime.getHeap());
120
0
}
121
122
namespace {
123
124
0
void completePropertyDescriptor(DefinePropertyFlags &desc) {
125
0
  if ((desc.setValue || desc.setWritable) ||
126
0
      (!desc.setGetter && !desc.setSetter)) {
127
0
    if (!desc.setWritable) {
128
0
      desc.writable = false;
129
0
    }
130
0
  }
131
0
  if (!desc.setEnumerable) {
132
0
    desc.enumerable = false;
133
0
  }
134
0
  if (!desc.setConfigurable) {
135
0
    desc.configurable = false;
136
0
  }
137
0
}
138
139
// ES9 9.1.6.2 IsCompatiblePropertyDescriptor
140
// prereq: step 2 is already done externally.
141
// The abstract definition returns a boolean; this returns EXCEPTION
142
// (and sets an exception) or RETURNED instead of false or true, so
143
// the exception messages can be more specific.
144
ExecutionStatus isCompatiblePropertyDescriptor(
145
    Runtime &runtime,
146
    const DefinePropertyFlags &desc,
147
    Handle<> descValueOrAccessor,
148
    const ComputedPropertyDescriptor &current,
149
0
    Handle<> currentValueOrAccessor) {
150
  // 4. If current.[[Configurable]] is false, then
151
0
  if (!current.flags.configurable) {
152
    // a. If Desc.[[Configurable]] is present and its value is true, return
153
    // false.
154
0
    if (desc.setConfigurable && desc.configurable) {
155
0
      return runtime.raiseTypeError(
156
0
          "trap result is configurable but target property is non-configurable");
157
0
    }
158
    // b. If Desc.[[Enumerable]] is present and the [[Enumerable]]
159
    // fields of current and Desc are the Boolean negation of each
160
    // other, return false.
161
0
    if (desc.setEnumerable && desc.enumerable != current.flags.enumerable) {
162
0
      return runtime.raiseTypeError(
163
0
          TwineChar16("trap result is ") + (desc.enumerable ? "" : "not ") +
164
0
          "enumerable but target property is " +
165
0
          (current.flags.enumerable ? "" : "not ") + "enumerable");
166
0
    }
167
0
  }
168
169
  // 5. If IsGenericDescriptor(Desc) is true, no further validation is required.
170
0
  bool descIsAccessor = desc.setSetter || desc.setGetter;
171
0
  bool descIsData = desc.setValue || desc.setWritable;
172
0
  assert(
173
0
      (!descIsData || !descIsAccessor) &&
174
0
      "descriptor cannot be both Data and Accessor");
175
0
  if (!descIsData && !descIsAccessor) {
176
0
    return ExecutionStatus::RETURNED;
177
0
  }
178
  // 6. Else if IsDataDescriptor(current) and IsDataDescriptor(Desc)
179
  // have different results, then
180
0
  bool currentIsAccessor = current.flags.accessor;
181
0
  bool currentIsData = !currentIsAccessor;
182
0
  if (currentIsData != descIsData) {
183
    // a. If current.[[Configurable]] is false, return false.
184
0
    if (!current.flags.configurable) {
185
0
      return runtime.raiseTypeError(
186
0
          TwineChar16("trap result is ") +
187
0
          (currentIsData ? "data " : "accessor ") + "but target property is " +
188
0
          (descIsData ? "data " : "accessor ") + "and non-configurable");
189
0
    }
190
0
  }
191
  // 7. Else if IsDataDescriptor(current) and IsDataDescriptor(Desc) are both
192
  // true, then
193
  //   a. If current.[[Configurable]] is false and current.[[Writable]] is
194
  //   false, then
195
0
  if (currentIsData && descIsData && !current.flags.configurable &&
196
0
      !current.flags.writable) {
197
    // i. If Desc.[[Writable]] is present and Desc.[[Writable]] is true,
198
    // return false.
199
0
    if (desc.setWritable && desc.writable) {
200
0
      return runtime.raiseTypeError(
201
0
          "trap result is writable but "
202
0
          "target property is non-configurable and non-writable");
203
0
    }
204
    // ii. If Desc.[[Value]] is present and SameValue(Desc.[[Value]],
205
    // current.[[Value]]) is false, return false.
206
0
    if (desc.setValue &&
207
0
        !isSameValue(
208
0
            descValueOrAccessor.getHermesValue(),
209
0
            currentValueOrAccessor.getHermesValue())) {
210
0
      return runtime.raiseTypeError(
211
0
          "trap result has different value than target property but "
212
0
          "target property is non-configurable and non-writable");
213
0
    }
214
    // iii. Return true.
215
0
    return ExecutionStatus::RETURNED;
216
0
  }
217
  // 8. Else IsAccessorDescriptor(current) and IsAccessorDescriptor(Desc) are
218
  // both true,
219
  //   a. If current.[[Configurable]] is false, then
220
0
  if (currentIsAccessor && descIsAccessor && !current.flags.configurable) {
221
0
    PropertyAccessor *descAccessor =
222
0
        vmcast<PropertyAccessor>(descValueOrAccessor.get());
223
0
    PropertyAccessor *currentAccessor =
224
0
        vmcast<PropertyAccessor>(currentValueOrAccessor.get());
225
    // i. If Desc.[[Set]] is present and SameValue(Desc.[[Set]],
226
    // current.[[Set]]) is false, return false.
227
0
    if (descAccessor->setter &&
228
0
        descAccessor->setter != currentAccessor->setter) {
229
0
      return runtime.raiseTypeError(
230
0
          "trap result has different setter than target property but "
231
0
          "target property is non-configurable");
232
0
    }
233
    // ii. If Desc.[[Get]] is present and SameValue(Desc.[[Get]],
234
    // current.[[Get]]) is false, return false.
235
0
    if (descAccessor->getter &&
236
0
        descAccessor->getter != currentAccessor->getter) {
237
0
      return runtime.raiseTypeError(
238
0
          "trap result has different getter than target property but "
239
0
          "target property is non-configurable");
240
0
    }
241
    // iii. Return true.
242
0
  }
243
0
  return ExecutionStatus::RETURNED;
244
0
}
245
246
} // namespace
247
248
CallResult<PseudoHandle<JSObject>> JSProxy::getPrototypeOf(
249
    Handle<JSObject> selfHandle,
250
0
    Runtime &runtime) {
251
  // Proxies are complex, and various parts of the logic (finding
252
  // traps, undefined traps handling, calling traps, etc) are all
253
  // potentially recursive.  Therefore, every entry point creates a
254
  // scope and a ScopedNativeDepthTracker, as it's possible to use up
255
  // arbitrary native stack depth with nested proxies.
256
0
  GCScope gcScope(runtime);
257
0
  ScopedNativeDepthTracker depthTracker(runtime);
258
0
  if (LLVM_UNLIKELY(depthTracker.overflowed())) {
259
0
    return runtime.raiseStackOverflow(Runtime::StackOverflowKind::NativeStack);
260
0
  }
261
  // Make sure to retrieve the target and handler before any JS can execute.
262
0
  auto &slots = detail::slots(*selfHandle);
263
0
  Handle<JSObject> target = runtime.makeHandle(slots.target);
264
0
  Handle<JSObject> handler = runtime.makeHandle(slots.handler);
265
0
  CallResult<Handle<Callable>> trapRes =
266
0
      detail::findTrap(selfHandle, runtime, Predefined::getPrototypeOf);
267
0
  if (LLVM_UNLIKELY(trapRes == ExecutionStatus::EXCEPTION)) {
268
0
    return ExecutionStatus::EXCEPTION;
269
0
  }
270
  // 6. If trap is undefined, then
271
0
  if (!*trapRes) {
272
    //   a. Return ? target.[[GetPrototypeOf]](P).
273
0
    return JSObject::getPrototypeOf(target, runtime);
274
0
  }
275
  // 7. Let handlerProto be ? Call(trap, handler, « target »).
276
0
  CallResult<PseudoHandle<>> handlerProtoRes = Callable::executeCall1(
277
0
      *trapRes, runtime, handler, target.getHermesValue());
278
0
  if (handlerProtoRes == ExecutionStatus::EXCEPTION) {
279
0
    return ExecutionStatus::EXCEPTION;
280
0
  }
281
  // 8. If Type(handlerProto) is neither Object nor Null, throw a TypeError
282
  // exception.
283
0
  if (!(*handlerProtoRes)->isObject() && !(*handlerProtoRes)->isNull()) {
284
0
    return runtime.raiseTypeError(
285
0
        "getPrototypeOf trap result is neither Object nor Null");
286
0
  }
287
0
  Handle<JSObject> handlerProto =
288
0
      runtime.makeHandle(dyn_vmcast<JSObject>(handlerProtoRes->get()));
289
290
  // 9. Let extensibleTarget be ? IsExtensible(target).
291
0
  CallResult<bool> extensibleRes = JSObject::isExtensible(target, runtime);
292
0
  if (extensibleRes == ExecutionStatus::EXCEPTION) {
293
0
    return ExecutionStatus::EXCEPTION;
294
0
  }
295
  // 10. If extensibleTarget is true, return handlerProto.
296
0
  if (*extensibleRes) {
297
0
    return createPseudoHandle(*handlerProto);
298
0
  }
299
  // 11. Let targetProto be ? target.[[GetPrototypeOf]]().
300
0
  CallResult<PseudoHandle<JSObject>> targetProtoRes =
301
0
      JSObject::getPrototypeOf(target, runtime);
302
0
  if (targetProtoRes == ExecutionStatus::EXCEPTION) {
303
0
    return ExecutionStatus::EXCEPTION;
304
0
  }
305
  // 12. If SameValue(handlerProto, targetProto) is false, throw a TypeError
306
  // exception.
307
0
  if (handlerProto.get() != targetProtoRes->get()) {
308
0
    return runtime.raiseTypeError(
309
0
        "getPrototypeOf trap result is not the same as non-extensible target getPrototypeOf");
310
0
  }
311
  // 13. Return handlerProto.
312
0
  return std::move(*targetProtoRes);
313
0
}
314
315
CallResult<bool> JSProxy::setPrototypeOf(
316
    Handle<JSObject> selfHandle,
317
    Runtime &runtime,
318
0
    Handle<JSObject> parent) {
319
0
  GCScope gcScope{runtime};
320
0
  ScopedNativeDepthTracker depthTracker(runtime);
321
0
  if (LLVM_UNLIKELY(depthTracker.overflowed())) {
322
0
    return runtime.raiseStackOverflow(Runtime::StackOverflowKind::NativeStack);
323
0
  }
324
  // Make sure to retrieve the target and handler before any JS can execute.
325
0
  auto &slots = detail::slots(*selfHandle);
326
0
  Handle<JSObject> target = runtime.makeHandle(slots.target);
327
0
  Handle<JSObject> handler = runtime.makeHandle(slots.handler);
328
0
  CallResult<Handle<Callable>> trapRes =
329
0
      detail::findTrap(selfHandle, runtime, Predefined::setPrototypeOf);
330
0
  if (trapRes == ExecutionStatus::EXCEPTION) {
331
0
    return ExecutionStatus::EXCEPTION;
332
0
  }
333
  // 7. If trap is undefined, then
334
0
  if (!*trapRes) {
335
    //   a. Return ? target.[[SetPrototypeOf]](V).
336
0
    return JSObject::setParent(*target, runtime, *parent);
337
0
  }
338
  // 8. Let booleanTrapResult be ToBoolean(? Call(trap, handler, « target, V
339
  // »)).
340
0
  CallResult<PseudoHandle<>> booleanTrapRes = Callable::executeCall2(
341
0
      *trapRes,
342
0
      runtime,
343
0
      handler,
344
0
      target.getHermesValue(),
345
0
      *parent ? parent.getHermesValue() : HermesValue::encodeNullValue());
346
0
  if (booleanTrapRes == ExecutionStatus::EXCEPTION) {
347
0
    return ExecutionStatus::EXCEPTION;
348
0
  }
349
  // 9. If booleanTrapResult is false, return false.
350
0
  if (!toBoolean(booleanTrapRes->get())) {
351
0
    return false;
352
0
  }
353
  // 10. Let extensibleTarget be ? IsExtensible(target).
354
0
  CallResult<bool> extensibleRes = JSObject::isExtensible(target, runtime);
355
0
  if (extensibleRes == ExecutionStatus::EXCEPTION) {
356
0
    return ExecutionStatus::EXCEPTION;
357
0
  }
358
  // 11. If extensibleTarget is true, return true.
359
0
  if (*extensibleRes) {
360
0
    return true;
361
0
  }
362
  // 12. Let targetProto be ? target.[[GetPrototypeOf]]().
363
0
  CallResult<PseudoHandle<JSObject>> targetProtoRes =
364
0
      JSObject::getPrototypeOf(target, runtime);
365
0
  if (targetProtoRes == ExecutionStatus::EXCEPTION) {
366
0
    return ExecutionStatus::EXCEPTION;
367
0
  }
368
  // 13. If SameValue(V, targetProto) is false, throw a TypeError exception.
369
0
  if (parent.get() != targetProtoRes->get()) {
370
0
    return runtime.raiseTypeError(
371
0
        "setPrototypeOf trap changed prototype on non-extensible target");
372
0
  }
373
  // 14. Return true.
374
0
  return true;
375
0
}
376
377
CallResult<bool> JSProxy::isExtensible(
378
    Handle<JSObject> selfHandle,
379
0
    Runtime &runtime) {
380
0
  GCScope gcScope{runtime};
381
0
  ScopedNativeDepthTracker depthTracker(runtime);
382
0
  if (LLVM_UNLIKELY(depthTracker.overflowed())) {
383
0
    return runtime.raiseStackOverflow(Runtime::StackOverflowKind::NativeStack);
384
0
  }
385
  // Make sure to retrieve the target and handler before any JS can execute.
386
0
  auto &slots = detail::slots(*selfHandle);
387
0
  Handle<JSObject> target = runtime.makeHandle(slots.target);
388
0
  Handle<JSObject> handler = runtime.makeHandle(slots.handler);
389
0
  CallResult<Handle<Callable>> trapRes =
390
0
      detail::findTrap(selfHandle, runtime, Predefined::isExtensible);
391
0
  if (trapRes == ExecutionStatus::EXCEPTION) {
392
0
    return ExecutionStatus::EXCEPTION;
393
0
  }
394
  // 6. If trap is undefined, then
395
0
  if (!*trapRes) {
396
    //   a. Return ? target.[[IsExtensible]]().
397
0
    return JSObject::isExtensible(target, runtime);
398
0
  }
399
  // 7. Let booleanTrapResult be ToBoolean(? Call(trap, handler, « target »)).
400
0
  CallResult<PseudoHandle<>> res = Callable::executeCall1(
401
0
      *trapRes, runtime, handler, target.getHermesValue());
402
0
  if (res == ExecutionStatus::EXCEPTION) {
403
0
    return ExecutionStatus::EXCEPTION;
404
0
  }
405
0
  bool booleanTrapResult = toBoolean(res->get());
406
0
  res->invalidate();
407
  // 8. Let targetResult be ? target.[[IsExtensible]]().
408
0
  CallResult<bool> targetRes = JSObject::isExtensible(target, runtime);
409
0
  if (targetRes == ExecutionStatus::EXCEPTION) {
410
0
    return ExecutionStatus::EXCEPTION;
411
0
  }
412
  // 9. If SameValue(booleanTrapResult, targetResult) is false, throw
413
  //    a TypeError exception.
414
0
  if (booleanTrapResult != *targetRes) {
415
0
    return runtime.raiseTypeError(
416
0
        "isExtensible trap returned different value than target");
417
0
  }
418
  // 10. Return booleanTrapResult.
419
0
  return booleanTrapResult;
420
0
}
421
422
CallResult<bool> JSProxy::preventExtensions(
423
    Handle<JSObject> selfHandle,
424
    Runtime &runtime,
425
0
    PropOpFlags opFlags) {
426
0
  GCScope gcScope{runtime};
427
0
  ScopedNativeDepthTracker depthTracker(runtime);
428
0
  if (LLVM_UNLIKELY(depthTracker.overflowed())) {
429
0
    return runtime.raiseStackOverflow(Runtime::StackOverflowKind::NativeStack);
430
0
  }
431
  // Make sure to retrieve the target and handler before any JS can execute.
432
0
  auto &slots = detail::slots(*selfHandle);
433
0
  Handle<JSObject> target = runtime.makeHandle(slots.target);
434
0
  Handle<JSObject> handler = runtime.makeHandle(slots.handler);
435
0
  CallResult<Handle<Callable>> trapRes =
436
0
      detail::findTrap(selfHandle, runtime, Predefined::preventExtensions);
437
0
  if (trapRes == ExecutionStatus::EXCEPTION) {
438
0
    return ExecutionStatus::EXCEPTION;
439
0
  }
440
  // 6. If trap is undefined, then
441
0
  if (!*trapRes) {
442
    //   a. Return ? target.[[PreventExtensions]]().
443
    // We pass in opFlags here.  If getThrowOnError, then this will cause
444
    // the underlying exception to bubble up.  If !getThrowOnError, then
445
    // we don't get a chance to raise a particular exception anyway.  So in
446
    // either case, just return the CallResult.
447
0
    return JSObject::preventExtensions(target, runtime, opFlags);
448
0
  }
449
  // 7. Let booleanTrapResult be ToBoolean(? Call(trap, handler, « target »)).
450
0
  CallResult<PseudoHandle<>> res = Callable::executeCall1(
451
0
      *trapRes, runtime, handler, target.getHermesValue());
452
0
  if (res == ExecutionStatus::EXCEPTION) {
453
0
    return ExecutionStatus::EXCEPTION;
454
0
  }
455
0
  bool booleanTrapResult = toBoolean(res->get());
456
0
  if (booleanTrapResult) {
457
    // a. Let targetIsExtensible be ? target.[[IsExtensible]]().
458
0
    CallResult<bool> targetRes = JSObject::isExtensible(target, runtime);
459
0
    if (targetRes == ExecutionStatus::EXCEPTION) {
460
0
      return ExecutionStatus::EXCEPTION;
461
0
    }
462
    // b. If targetIsExtensible is true, throw a TypeError exception.
463
0
    if (*targetRes) {
464
0
      return runtime.raiseTypeError(
465
0
          "preventExtensions trap returned true for extensible target");
466
0
    }
467
0
  }
468
  // 10. Return booleanTrapResult.
469
0
  if (!booleanTrapResult && opFlags.getThrowOnError()) {
470
0
    return runtime.raiseTypeError("preventExtensions trap returned false");
471
0
  }
472
0
  return booleanTrapResult;
473
0
}
474
475
CallResult<bool> JSProxy::getOwnProperty(
476
    Handle<JSObject> selfHandle,
477
    Runtime &runtime,
478
    Handle<> nameValHandle,
479
    ComputedPropertyDescriptor &desc,
480
0
    MutableHandle<> *valueOrAccessor) {
481
0
  GCScope gcScope{runtime};
482
0
  ScopedNativeDepthTracker depthTracker(runtime);
483
0
  if (LLVM_UNLIKELY(depthTracker.overflowed())) {
484
0
    return runtime.raiseStackOverflow(Runtime::StackOverflowKind::NativeStack);
485
0
  }
486
  // Make sure to retrieve the target and handler before any JS can execute.
487
0
  auto &slots = detail::slots(*selfHandle);
488
0
  Handle<JSObject> target = runtime.makeHandle(slots.target);
489
0
  Handle<JSObject> handler = runtime.makeHandle(slots.handler);
490
0
  CallResult<Handle<Callable>> trapRes = detail::findTrap(
491
0
      selfHandle, runtime, Predefined::getOwnPropertyDescriptor);
492
0
  if (trapRes == ExecutionStatus::EXCEPTION) {
493
0
    return ExecutionStatus::EXCEPTION;
494
0
  }
495
0
  MutableHandle<SymbolID> tmpPropNameStorage{runtime};
496
  // 7. If trap is undefined, then
497
0
  if (!*trapRes) {
498
    //   a. Return ? target.[[GetOwnProperty]](P).
499
0
    return valueOrAccessor
500
0
        ? JSObject::getOwnComputedDescriptor(
501
0
              target,
502
0
              runtime,
503
0
              nameValHandle,
504
0
              tmpPropNameStorage,
505
0
              desc,
506
0
              *valueOrAccessor)
507
0
        : JSObject::getOwnComputedDescriptor(
508
0
              target, runtime, nameValHandle, tmpPropNameStorage, desc);
509
0
  }
510
  // 8. Let trapResultObj be ? Call(trap, handler, « target, P »).
511
  // 9. If Type(trapResultObj) is neither Object nor Undefined, throw a
512
  // TypeError exception.
513
0
  CallResult<PseudoHandle<>> trapResultRes = Callable::executeCall2(
514
0
      *trapRes,
515
0
      runtime,
516
0
      handler,
517
0
      target.getHermesValue(),
518
0
      nameValHandle.getHermesValue());
519
0
  if (trapResultRes == ExecutionStatus::EXCEPTION) {
520
0
    return ExecutionStatus::EXCEPTION;
521
0
  }
522
0
  Handle<> trapResultObj = runtime.makeHandle(std::move(*trapResultRes));
523
  // 10. Let targetDesc be ? target.[[GetOwnProperty]](P).
524
0
  ComputedPropertyDescriptor targetDesc;
525
0
  MutableHandle<> targetValueOrAccessor{runtime};
526
0
  CallResult<bool> targetDescRes = JSObject::getOwnComputedDescriptor(
527
0
      target,
528
0
      runtime,
529
0
      nameValHandle,
530
0
      tmpPropNameStorage,
531
0
      targetDesc,
532
0
      targetValueOrAccessor);
533
0
  if (targetDescRes == ExecutionStatus::EXCEPTION) {
534
0
    return ExecutionStatus::EXCEPTION;
535
0
  }
536
  // 11. If trapResultObj is undefined, then
537
0
  if (trapResultObj->isUndefined()) {
538
    //   a. If targetDesc is undefined, return undefined.
539
0
    if (!*targetDescRes) {
540
0
      return false;
541
0
    }
542
    //   b. If targetDesc.[[Configurable]] is false, throw a TypeError
543
    //   exception.
544
0
    if (!targetDesc.flags.configurable) {
545
0
      return runtime.raiseTypeError(
546
0
          "getOwnPropertyDescriptor trap result is not configurable");
547
0
    }
548
    //   c. Let extensibleTarget be ? IsExtensible(target).
549
0
    CallResult<bool> extensibleRes = JSObject::isExtensible(target, runtime);
550
0
    if (extensibleRes == ExecutionStatus::EXCEPTION) {
551
0
      return ExecutionStatus::EXCEPTION;
552
0
    }
553
    //   d. Assert: Type(extensibleTarget) is Boolean.
554
    //   e. If extensibleTarget is false, throw a TypeError exception.
555
0
    if (!*extensibleRes) {
556
0
      return runtime.raiseTypeErrorForValue(
557
0
          target, " is not extensible (getOwnPropertyDescriptor target)");
558
0
    }
559
    //   f. Return undefined.
560
0
    return false;
561
0
  } else if (!trapResultObj->isObject()) {
562
    // 9. If Type(trapResultObj) is neither Object nor Undefined, throw a
563
    // TypeError exception.
564
0
    return runtime.raiseTypeErrorForValue(
565
0
        trapResultObj,
566
0
        " is not undefined or Object (Proxy getOwnPropertyDescriptor)");
567
0
  }
568
  // 12. Let extensibleTarget be ? IsExtensible(target).
569
0
  CallResult<bool> extensibleRes = JSObject::isExtensible(target, runtime);
570
0
  if (extensibleRes == ExecutionStatus::EXCEPTION) {
571
0
    return ExecutionStatus::EXCEPTION;
572
0
  }
573
  // 13. Let resultDesc be ? ToPropertyDescriptor(trapResultObj).
574
  // 14. Call CompletePropertyDescriptor(resultDesc).
575
0
  DefinePropertyFlags resultDesc;
576
0
  MutableHandle<> resultValueOrAccessor{runtime};
577
0
  Handle<JSObject> trapResult = runtime.makeHandle<JSObject>(*trapResultObj);
578
0
  if (LLVM_UNLIKELY(
579
0
          toPropertyDescriptor(
580
0
              trapResult, runtime, resultDesc, resultValueOrAccessor) ==
581
0
          ExecutionStatus::EXCEPTION)) {
582
0
    return ExecutionStatus::EXCEPTION;
583
0
  }
584
0
  completePropertyDescriptor(resultDesc);
585
  // 15. Let valid be IsCompatiblePropertyDescriptor(extensibleTarget,
586
  // resultDesc, targetDesc).
587
  // 16. If valid is false, throw a TypeError exception.
588
589
  // ES9 9.1.6.3 ValidateAndApplyPropertyDescriptor step 2 [O is undefined]
590
0
  if (!*targetDescRes) {
591
    // a. If extensible is false, return false.
592
0
    if (!*extensibleRes) {
593
0
      return runtime.raiseTypeErrorForValue(
594
0
          "getOwnPropertyDescriptor target is not extensible and has no property ",
595
0
          nameValHandle,
596
0
          "");
597
0
    }
598
    // e. return true
599
    // this concludes steps 15 and 16.
600
0
  } else {
601
0
    if (LLVM_UNLIKELY(
602
0
            isCompatiblePropertyDescriptor(
603
0
                runtime,
604
0
                resultDesc,
605
0
                resultValueOrAccessor,
606
0
                targetDesc,
607
0
                targetValueOrAccessor) == ExecutionStatus::EXCEPTION)) {
608
0
      return ExecutionStatus::EXCEPTION;
609
0
    }
610
0
  }
611
  // 17. If resultDesc.[[Configurable]] is false, then
612
  //   a. If targetDesc is undefined or targetDesc.[[Configurable]] is true,
613
  //   then
614
  //     i. Throw a TypeError exception.
615
0
  if (!resultDesc.configurable &&
616
0
      (!*targetDescRes || targetDesc.flags.configurable)) {
617
0
    return runtime.raiseTypeErrorForValue(
618
0
        "getOwnPropertyDescriptor trap result is not configurable but "
619
0
        "target property ",
620
0
        nameValHandle,
621
0
        " is configurable or non-existent");
622
0
  }
623
  // 18. Return resultDesc.
624
0
  desc.flags.enumerable = resultDesc.enumerable;
625
0
  desc.flags.configurable = resultDesc.configurable;
626
0
  desc.flags.writable = resultDesc.writable;
627
0
  if (resultDesc.setGetter || resultDesc.setSetter) {
628
0
    desc.flags.accessor = true;
629
0
  }
630
0
  if (valueOrAccessor) {
631
0
    *valueOrAccessor = std::move(resultValueOrAccessor);
632
0
  }
633
0
  return true;
634
0
}
635
636
CallResult<bool> JSProxy::defineOwnProperty(
637
    Handle<JSObject> selfHandle,
638
    Runtime &runtime,
639
    Handle<> nameValHandle,
640
    DefinePropertyFlags dpFlags,
641
    Handle<> valueOrAccessor,
642
0
    PropOpFlags opFlags) {
643
0
  GCScope gcScope{runtime};
644
0
  ScopedNativeDepthTracker depthTracker(runtime);
645
0
  if (LLVM_UNLIKELY(depthTracker.overflowed())) {
646
0
    return runtime.raiseStackOverflow(Runtime::StackOverflowKind::NativeStack);
647
0
  }
648
  // Make sure to retrieve the target and handler before any JS can execute.
649
0
  auto &slots = detail::slots(*selfHandle);
650
0
  Handle<JSObject> target = runtime.makeHandle(slots.target);
651
0
  Handle<JSObject> handler = runtime.makeHandle(slots.handler);
652
0
  CallResult<Handle<Callable>> trapRes =
653
0
      detail::findTrap(selfHandle, runtime, Predefined::defineProperty);
654
0
  if (trapRes == ExecutionStatus::EXCEPTION) {
655
0
    return ExecutionStatus::EXCEPTION;
656
0
  }
657
  // 7. If trap is undefined, then
658
0
  if (!*trapRes) {
659
    //   a. Return ? target.[[GetOwnProperty]](P).
660
0
    return JSObject::defineOwnComputedPrimitive(
661
0
        target, runtime, nameValHandle, dpFlags, valueOrAccessor, opFlags);
662
0
  }
663
  // 8. Let descObj be FromPropertyDescriptor(Desc).
664
0
  CallResult<HermesValue> descObjRes =
665
0
      objectFromPropertyDescriptor(runtime, dpFlags, valueOrAccessor);
666
0
  if (descObjRes == ExecutionStatus::EXCEPTION) {
667
0
    return ExecutionStatus::EXCEPTION;
668
0
  }
669
  // 9. Let booleanTrapResult be ToBoolean(? Call(trap, handler, « target, P,
670
  // descObj »)).
671
0
  CallResult<PseudoHandle<>> trapResultRes = Callable::executeCall3(
672
0
      *trapRes,
673
0
      runtime,
674
0
      handler,
675
0
      target.getHermesValue(),
676
0
      nameValHandle.getHermesValue(),
677
0
      *descObjRes);
678
0
  if (trapResultRes == ExecutionStatus::EXCEPTION) {
679
0
    return ExecutionStatus::EXCEPTION;
680
0
  }
681
0
  bool trapResult = toBoolean(trapResultRes->get());
682
  // 10. If booleanTrapResult is false, return false.
683
0
  if (!trapResult) {
684
0
    if (opFlags.getThrowOnError()) {
685
0
      return runtime.raiseTypeError("defineProperty proxy trap returned false");
686
0
    } else {
687
0
      return false;
688
0
    }
689
0
  }
690
  // 11. Let targetDesc be ? target.[[GetOwnProperty]](P).
691
0
  ComputedPropertyDescriptor targetDesc;
692
0
  MutableHandle<> targetDescValueOrAccessor{runtime};
693
0
  MutableHandle<SymbolID> tmpPropNameStorage{runtime};
694
0
  CallResult<bool> targetDescRes = JSObject::getOwnComputedDescriptor(
695
0
      target,
696
0
      runtime,
697
0
      nameValHandle,
698
0
      tmpPropNameStorage,
699
0
      targetDesc,
700
0
      targetDescValueOrAccessor);
701
0
  if (targetDescRes == ExecutionStatus::EXCEPTION) {
702
0
    return ExecutionStatus::EXCEPTION;
703
0
  }
704
  // 12. Let extensibleTarget be ? IsExtensible(target).
705
0
  CallResult<bool> extensibleRes = JSObject::isExtensible(target, runtime);
706
0
  if (extensibleRes == ExecutionStatus::EXCEPTION) {
707
0
    return ExecutionStatus::EXCEPTION;
708
0
  }
709
  // 13. If Desc has a [[Configurable]] field and if Desc.[[Configurable]] is
710
  // false, then
711
  //   a. Let settingConfigFalse be true.
712
  // 14. Else, let settingConfigFalse be false.
713
0
  bool settingConfigFalse = dpFlags.setConfigurable && !dpFlags.configurable;
714
  // 15. If targetDesc is undefined, then
715
0
  if (!*targetDescRes) {
716
    //   a. If extensibleTarget is false, throw a TypeError exception.
717
0
    if (!*extensibleRes) {
718
0
      return runtime.raiseTypeError(
719
0
          "defineProperty trap called for non-existent property on non-extensible target");
720
0
    }
721
    //   b. If settingConfigFalse is true, throw a TypeError exception.
722
0
    if (settingConfigFalse) {
723
0
      return runtime.raiseTypeError(
724
0
          "defineProperty trap attempted to define non-configurable property for non-existent property in the target");
725
0
    }
726
0
  } else {
727
    // 16. Else targetDesc is not undefined,
728
    //   a. If IsCompatiblePropertyDescriptor(extensibleTarget, Desc,
729
    //   targetDesc) is false, throw a TypeError exception.
730
0
    if (LLVM_UNLIKELY(
731
0
            isCompatiblePropertyDescriptor(
732
0
                runtime,
733
0
                dpFlags,
734
0
                valueOrAccessor,
735
0
                targetDesc,
736
0
                targetDescValueOrAccessor) == ExecutionStatus::EXCEPTION)) {
737
0
      return ExecutionStatus::EXCEPTION;
738
0
    }
739
    //   b. If settingConfigFalse is true and targetDesc.[[Configurable]] is
740
    //   true, throw a TypeError exception.
741
0
    if (settingConfigFalse && targetDesc.flags.configurable) {
742
0
      return runtime.raiseTypeError(
743
0
          "defineProperty trap attempted to define non-configurable property for configurable property in the target");
744
0
    }
745
0
  }
746
  // 17. Return true.
747
0
  return true;
748
0
}
749
750
namespace {
751
752
/// Common parts of hasNamed/hasComputed
753
CallResult<bool> hasWithTrap(
754
    Runtime &runtime,
755
    Handle<> nameValHandle,
756
    Handle<Callable> trap,
757
    Handle<JSObject> handler,
758
0
    Handle<JSObject> target) {
759
  // 1. Assert: IsPropertyKey(P) is true.
760
0
  assert(isPropertyKey(nameValHandle) && "key is not a String or Symbol");
761
  // 8. Let booleanTrapResult be ToBoolean(? Call(trap, handler, « target, P
762
  // »)).
763
0
  CallResult<PseudoHandle<>> trapResultRes = Callable::executeCall2(
764
0
      trap,
765
0
      runtime,
766
0
      handler,
767
0
      target.getHermesValue(),
768
0
      nameValHandle.getHermesValue());
769
0
  if (trapResultRes == ExecutionStatus::EXCEPTION) {
770
0
    return ExecutionStatus::EXCEPTION;
771
0
  }
772
0
  bool trapResult = toBoolean(trapResultRes->get());
773
  // 9. If booleanTrapResult is false, then
774
0
  if (!trapResult) {
775
    //   a. Let targetDesc be ? target.[[GetOwnProperty]](P).
776
0
    ComputedPropertyDescriptor targetDesc;
777
0
    MutableHandle<SymbolID> tmpPropNameStorage{runtime};
778
0
    CallResult<bool> targetDescRes = JSObject::getOwnComputedDescriptor(
779
0
        target, runtime, nameValHandle, tmpPropNameStorage, targetDesc);
780
0
    if (targetDescRes == ExecutionStatus::EXCEPTION) {
781
0
      return ExecutionStatus::EXCEPTION;
782
0
    }
783
    //   b. If targetDesc is not undefined, then
784
0
    if (*targetDescRes) {
785
      //     i. If targetDesc.[[Configurable]] is false, throw a TypeError
786
      //     exception.
787
0
      if (!targetDesc.flags.configurable) {
788
0
        return runtime.raiseTypeError(
789
0
            "HasProperty trap result is not configurable");
790
0
      }
791
      //     ii. Let extensibleTarget be ? IsExtensible(target).
792
0
      CallResult<bool> extensibleRes = JSObject::isExtensible(target, runtime);
793
0
      if (extensibleRes == ExecutionStatus::EXCEPTION) {
794
0
        return ExecutionStatus::EXCEPTION;
795
0
      }
796
      //     iii. If extensibleTarget is false, throw a TypeError exception.
797
0
      if (!*extensibleRes) {
798
0
        return runtime.raiseTypeError(
799
0
            "HasProperty proxy target is not extensible");
800
0
      }
801
0
    }
802
0
  }
803
  // 11. Return trapResult.
804
0
  return trapResult;
805
0
}
806
807
} // namespace
808
809
CallResult<bool> JSProxy::hasNamed(
810
    Handle<JSObject> selfHandle,
811
    Runtime &runtime,
812
0
    SymbolID name) {
813
0
  GCScope gcScope{runtime};
814
0
  ScopedNativeDepthTracker depthTracker(runtime);
815
0
  if (LLVM_UNLIKELY(depthTracker.overflowed())) {
816
0
    return runtime.raiseStackOverflow(Runtime::StackOverflowKind::NativeStack);
817
0
  }
818
  // Make sure to retrieve the target and handler before any JS can execute.
819
0
  auto &slots = detail::slots(*selfHandle);
820
0
  Handle<JSObject> target = runtime.makeHandle(slots.target);
821
0
  Handle<JSObject> handler = runtime.makeHandle(slots.handler);
822
0
  CallResult<Handle<Callable>> trapRes =
823
0
      detail::findTrap(selfHandle, runtime, Predefined::has);
824
0
  if (trapRes == ExecutionStatus::EXCEPTION) {
825
0
    return ExecutionStatus::EXCEPTION;
826
0
  }
827
  // 7. If trap is undefined, then
828
0
  if (!*trapRes) {
829
    //   a. Return ? target.[[HasProperty]](P, Receiver).
830
0
    return JSObject::hasNamed(target, runtime, name);
831
0
  }
832
0
  return hasWithTrap(
833
0
      runtime,
834
0
      runtime.makeHandle(
835
0
          HermesValue::encodeStringValue(
836
0
              runtime.getStringPrimFromSymbolID(name))),
837
0
      *trapRes,
838
0
      handler,
839
0
      target);
840
0
}
841
842
CallResult<bool> JSProxy::hasComputed(
843
    Handle<JSObject> selfHandle,
844
    Runtime &runtime,
845
0
    Handle<> nameValHandle) {
846
0
  GCScope gcScope{runtime};
847
0
  ScopedNativeDepthTracker depthTracker(runtime);
848
0
  if (LLVM_UNLIKELY(depthTracker.overflowed())) {
849
0
    return runtime.raiseStackOverflow(Runtime::StackOverflowKind::NativeStack);
850
0
  }
851
  // Make sure to retrieve the target and handler before any JS can execute.
852
0
  auto &slots = detail::slots(*selfHandle);
853
0
  Handle<JSObject> target = runtime.makeHandle(slots.target);
854
0
  Handle<JSObject> handler = runtime.makeHandle(slots.handler);
855
0
  CallResult<Handle<Callable>> trapRes =
856
0
      detail::findTrap(selfHandle, runtime, Predefined::has);
857
0
  if (trapRes == ExecutionStatus::EXCEPTION) {
858
0
    return ExecutionStatus::EXCEPTION;
859
0
  }
860
0
  if (!*trapRes) {
861
    // 7. If trap is undefined, then
862
    //   a. Return ? target.[[HasProperty]](P, Receiver).
863
0
    return JSObject::hasComputed(target, runtime, nameValHandle);
864
0
  }
865
0
  return hasWithTrap(runtime, nameValHandle, *trapRes, handler, target);
866
0
}
867
868
namespace {
869
870
/// Common parts of getNamed/getComputed
871
CallResult<PseudoHandle<>> getWithTrap(
872
    Runtime &runtime,
873
    Handle<> nameValHandle,
874
    Handle<Callable> trap,
875
    Handle<JSObject> handler,
876
    Handle<JSObject> target,
877
0
    Handle<> receiver) {
878
  // 1. Assert: IsPropertyKey(P) is true.
879
0
  assert(isPropertyKey(nameValHandle) && "key is not a String or Symbol");
880
  // 8. Let trapResult be ? Call(trap, handler, « target, P, Receiver »).
881
0
  CallResult<PseudoHandle<>> trapResultRes = Callable::executeCall3(
882
0
      trap,
883
0
      runtime,
884
0
      handler,
885
0
      target.getHermesValue(),
886
0
      nameValHandle.getHermesValue(),
887
0
      receiver.getHermesValue());
888
0
  if (trapResultRes == ExecutionStatus::EXCEPTION) {
889
0
    return ExecutionStatus::EXCEPTION;
890
0
  }
891
0
  Handle<> trapResult = runtime.makeHandle(std::move(*trapResultRes));
892
  // 9. Let targetDesc be ? target.[[GetOwnProperty]](P).
893
0
  ComputedPropertyDescriptor targetDesc;
894
0
  MutableHandle<> targetValueOrAccessor{runtime};
895
0
  MutableHandle<SymbolID> tmpPropNameStorage{runtime};
896
0
  CallResult<bool> targetDescRes = JSObject::getOwnComputedDescriptor(
897
0
      target,
898
0
      runtime,
899
0
      nameValHandle,
900
0
      tmpPropNameStorage,
901
0
      targetDesc,
902
0
      targetValueOrAccessor);
903
0
  if (targetDescRes == ExecutionStatus::EXCEPTION) {
904
0
    return ExecutionStatus::EXCEPTION;
905
0
  }
906
  // 10. If targetDesc is not undefined and targetDesc.[[Configurable]] is
907
  // false, then
908
0
  if (*targetDescRes && !targetDesc.flags.configurable) {
909
    //   a. If IsDataDescriptor(targetDesc) is true and targetDesc.[[Writable]]
910
    //   is false, then
911
0
    if (!targetDesc.flags.accessor && !targetDesc.flags.writable) {
912
      //     i. If SameValue(trapResult, targetDesc.[[Value]]) is false, throw a
913
      //     TypeError exception.
914
0
      if (!isSameValue(*trapResult, targetValueOrAccessor.getHermesValue())) {
915
0
        return runtime.raiseTypeError(
916
0
            "target property is non-configurable and non-writable, and get trap result differs from target property value");
917
0
      }
918
0
    }
919
    //   b. If IsAccessorDescriptor(targetDesc) is true and targetDesc.[[Get]]
920
    //   is undefined, then
921
    //     i. If trapResult is not undefined, throw a TypeError exception.
922
0
    if (targetDesc.flags.accessor &&
923
0
        !vmcast<PropertyAccessor>(*targetValueOrAccessor)->getter &&
924
0
        !trapResult->isUndefined()) {
925
0
      return runtime.raiseTypeError(
926
0
          "target property is non-configurable accessor with no getter, but get trap returned not undefined");
927
0
    }
928
0
  }
929
930
  // 11. Return trapResult.
931
0
  return {trapResult};
932
0
}
933
934
} // namespace
935
936
CallResult<PseudoHandle<>> JSProxy::getNamed(
937
    Handle<JSObject> selfHandle,
938
    Runtime &runtime,
939
    SymbolID name,
940
0
    Handle<> receiver) {
941
0
  GCScope gcScope{runtime};
942
0
  ScopedNativeDepthTracker depthTracker(runtime);
943
0
  if (LLVM_UNLIKELY(depthTracker.overflowed())) {
944
0
    return runtime.raiseStackOverflow(Runtime::StackOverflowKind::NativeStack);
945
0
  }
946
  // Make sure to retrieve the target and handler before calling findTrap, as
947
  // that may result in these fields being erased if the proxy is revoked in the
948
  // handler.
949
0
  auto &slots = detail::slots(*selfHandle);
950
0
  Handle<JSObject> target = runtime.makeHandle(slots.target);
951
0
  Handle<JSObject> handler = runtime.makeHandle(slots.handler);
952
0
  CallResult<Handle<Callable>> trapRes =
953
0
      detail::findTrap(selfHandle, runtime, Predefined::get);
954
0
  if (trapRes == ExecutionStatus::EXCEPTION) {
955
0
    return ExecutionStatus::EXCEPTION;
956
0
  }
957
  // 7. If trap is undefined, then
958
0
  if (!*trapRes) {
959
    //   a. Return ? target.[[Get]](P, Receiver).
960
0
    return JSObject::getNamedWithReceiver_RJS(target, runtime, name, receiver);
961
0
  }
962
0
  return getWithTrap(
963
0
      runtime,
964
0
      name.isUniqued() ? runtime.makeHandle(
965
0
                             HermesValue::encodeStringValue(
966
0
                                 runtime.getStringPrimFromSymbolID(name)))
967
0
                       : runtime.makeHandle(name),
968
0
      *trapRes,
969
0
      handler,
970
0
      target,
971
0
      receiver);
972
0
}
973
974
CallResult<PseudoHandle<>> JSProxy::getComputed(
975
    Handle<JSObject> selfHandle,
976
    Runtime &runtime,
977
    Handle<> nameValHandle,
978
0
    Handle<> receiver) {
979
0
  GCScope gcScope{runtime};
980
0
  ScopedNativeDepthTracker depthTracker(runtime);
981
0
  if (LLVM_UNLIKELY(depthTracker.overflowed())) {
982
0
    return runtime.raiseStackOverflow(Runtime::StackOverflowKind::NativeStack);
983
0
  }
984
  // Make sure to retrieve the target and handler before any JS can execute.
985
0
  auto &slots = detail::slots(*selfHandle);
986
0
  Handle<JSObject> target = runtime.makeHandle(slots.target);
987
0
  Handle<JSObject> handler = runtime.makeHandle(slots.handler);
988
0
  CallResult<Handle<Callable>> trapRes =
989
0
      detail::findTrap(selfHandle, runtime, Predefined::get);
990
0
  if (trapRes == ExecutionStatus::EXCEPTION) {
991
0
    return ExecutionStatus::EXCEPTION;
992
0
  }
993
  // 7. If trap is undefined, then
994
0
  if (!*trapRes) {
995
    //   a. Return ? target.[[Get]](P, Receiver).
996
0
    return JSObject::getComputedWithReceiver_RJS(
997
0
        target, runtime, nameValHandle, receiver);
998
0
  }
999
0
  return getWithTrap(
1000
0
      runtime, nameValHandle, *trapRes, handler, target, receiver);
1001
0
}
1002
1003
namespace {
1004
1005
/// Common parts of setNamed/setComputed
1006
CallResult<bool> setWithTrap(
1007
    Runtime &runtime,
1008
    Handle<> nameValHandle,
1009
    Handle<> valueHandle,
1010
    Handle<Callable> trap,
1011
    Handle<JSObject> handler,
1012
    Handle<JSObject> target,
1013
0
    Handle<> receiver) {
1014
  // 1. Assert: IsPropertyKey(P) is true.
1015
0
  assert(isPropertyKey(nameValHandle) && "key is not a String or Symbol");
1016
  // 8. Let booleanTrapResult be ToBoolean(? Call(trap, handler, « target, P, V,
1017
  // Receiver »)).
1018
0
  CallResult<PseudoHandle<>> trapResultRes = Callable::executeCall4(
1019
0
      trap,
1020
0
      runtime,
1021
0
      handler,
1022
0
      target.getHermesValue(),
1023
0
      nameValHandle.getHermesValue(),
1024
0
      valueHandle.getHermesValue(),
1025
0
      receiver.getHermesValue());
1026
0
  if (trapResultRes == ExecutionStatus::EXCEPTION) {
1027
0
    return ExecutionStatus::EXCEPTION;
1028
0
  }
1029
  // 9. If booleanTrapResult is false, return false.
1030
0
  if (!toBoolean(trapResultRes->get())) {
1031
0
    return false;
1032
0
  }
1033
  // 10. Let targetDesc be ? target.[[GetOwnProperty]](P).
1034
0
  ComputedPropertyDescriptor targetDesc;
1035
0
  MutableHandle<> targetValueOrAccessor{runtime};
1036
0
  MutableHandle<SymbolID> tmpPropNameStorage{runtime};
1037
0
  CallResult<bool> targetDescRes = JSObject::getOwnComputedDescriptor(
1038
0
      target,
1039
0
      runtime,
1040
0
      nameValHandle,
1041
0
      tmpPropNameStorage,
1042
0
      targetDesc,
1043
0
      targetValueOrAccessor);
1044
0
  if (targetDescRes == ExecutionStatus::EXCEPTION) {
1045
0
    return ExecutionStatus::EXCEPTION;
1046
0
  }
1047
  // 11. If targetDesc is not undefined and targetDesc.[[Configurable]] is
1048
  // false, then
1049
0
  if (*targetDescRes && !targetDesc.flags.configurable) {
1050
    //   a. If IsDataDescriptor(targetDesc) is true and targetDesc.[[Writable]]
1051
    //   is false, then
1052
0
    if (!targetDesc.flags.accessor && !targetDesc.flags.writable) {
1053
      //     i. If SameValue(V, targetDesc.[[Value]]) is false, throw a
1054
      //     TypeError exception.
1055
0
      if (!isSameValue(
1056
0
              valueHandle.getHermesValue(),
1057
0
              targetValueOrAccessor.getHermesValue())) {
1058
0
        return runtime.raiseTypeError(
1059
0
            "target property is non-configurable and non-writable, and set trap value differs from target property value");
1060
0
      }
1061
0
    }
1062
    //   b. If IsAccessorDescriptor(targetDesc) is true, then
1063
    //     i. If targetDesc.[[Set]] is undefined, throw a TypeError exception.
1064
0
    if (targetDesc.flags.accessor &&
1065
0
        !vmcast<PropertyAccessor>(*targetValueOrAccessor)->setter) {
1066
0
      return runtime.raiseTypeError(
1067
0
          "set trap called, but target property is non-configurable accessor with no setter");
1068
0
    }
1069
0
  }
1070
1071
  // 12. Return true.
1072
0
  return true;
1073
0
}
1074
1075
} // namespace
1076
1077
CallResult<bool> JSProxy::setNamed(
1078
    Handle<JSObject> selfHandle,
1079
    Runtime &runtime,
1080
    SymbolID name,
1081
    Handle<> valueHandle,
1082
    // TODO could be HermesValue
1083
0
    Handle<> receiver) {
1084
0
  GCScope gcScope{runtime};
1085
0
  ScopedNativeDepthTracker depthTracker(runtime);
1086
0
  if (LLVM_UNLIKELY(depthTracker.overflowed())) {
1087
0
    return runtime.raiseStackOverflow(Runtime::StackOverflowKind::NativeStack);
1088
0
  }
1089
  // Make sure to retrieve the target and handler before any JS can execute.
1090
0
  auto &slots = detail::slots(*selfHandle);
1091
0
  Handle<JSObject> target = runtime.makeHandle(slots.target);
1092
0
  Handle<JSObject> handler = runtime.makeHandle(slots.handler);
1093
0
  CallResult<Handle<Callable>> trapRes =
1094
0
      detail::findTrap(selfHandle, runtime, Predefined::set);
1095
0
  if (trapRes == ExecutionStatus::EXCEPTION) {
1096
0
    return ExecutionStatus::EXCEPTION;
1097
0
  }
1098
  // 7. If trap is undefined, then
1099
0
  if (!*trapRes) {
1100
    //   a. Return ? target.[[Set]](P, V, Receiver).
1101
0
    return JSObject::putNamedWithReceiver_RJS(
1102
0
        target, runtime, name, valueHandle, receiver);
1103
0
  }
1104
0
  return setWithTrap(
1105
0
      runtime,
1106
0
      name.isUniqued() ? runtime.makeHandle(
1107
0
                             HermesValue::encodeStringValue(
1108
0
                                 runtime.getStringPrimFromSymbolID(name)))
1109
0
                       : runtime.makeHandle(name),
1110
0
      valueHandle,
1111
0
      *trapRes,
1112
0
      handler,
1113
0
      target,
1114
0
      receiver);
1115
0
}
1116
1117
CallResult<bool> JSProxy::setComputed(
1118
    Handle<JSObject> selfHandle,
1119
    Runtime &runtime,
1120
    Handle<> nameValHandle,
1121
    Handle<> valueHandle,
1122
    // TODO could be HermesValue
1123
0
    Handle<> receiver) {
1124
0
  GCScope gcScope{runtime};
1125
0
  ScopedNativeDepthTracker depthTracker(runtime);
1126
0
  if (LLVM_UNLIKELY(depthTracker.overflowed())) {
1127
0
    return runtime.raiseStackOverflow(Runtime::StackOverflowKind::NativeStack);
1128
0
  }
1129
  // Make sure to retrieve the target and handler before any JS can execute.
1130
0
  auto &slots = detail::slots(*selfHandle);
1131
0
  Handle<JSObject> target = runtime.makeHandle(slots.target);
1132
0
  Handle<JSObject> handler = runtime.makeHandle(slots.handler);
1133
0
  CallResult<Handle<Callable>> trapRes =
1134
0
      detail::findTrap(selfHandle, runtime, Predefined::set);
1135
0
  if (trapRes == ExecutionStatus::EXCEPTION) {
1136
0
    return ExecutionStatus::EXCEPTION;
1137
0
  }
1138
  // 7. If trap is undefined, then
1139
0
  if (!*trapRes) {
1140
    //   a. Return ? target.[[Set]](P, V, Receiver).
1141
0
    return JSObject::putComputedWithReceiver_RJS(
1142
0
        target, runtime, nameValHandle, valueHandle, receiver);
1143
0
  }
1144
0
  return setWithTrap(
1145
0
      runtime, nameValHandle, valueHandle, *trapRes, handler, target, receiver);
1146
0
}
1147
1148
namespace {
1149
1150
/// Common parts of deleteNamed/deleteComputed
1151
CallResult<bool> deleteWithTrap(
1152
    Runtime &runtime,
1153
    Handle<> nameValHandle,
1154
    Handle<Callable> trap,
1155
    Handle<JSObject> handler,
1156
0
    Handle<JSObject> target) {
1157
  // 1. Assert: IsPropertyKey(P) is true.
1158
0
  assert(isPropertyKey(nameValHandle) && "key is not a String or Symbol");
1159
  // 8. Let booleanTrapResult be ToBoolean(? Call(trap, handler, « target, P
1160
  // »)).
1161
0
  CallResult<PseudoHandle<>> trapResultRes = Callable::executeCall2(
1162
0
      trap,
1163
0
      runtime,
1164
0
      handler,
1165
0
      target.getHermesValue(),
1166
0
      nameValHandle.getHermesValue());
1167
0
  if (trapResultRes == ExecutionStatus::EXCEPTION) {
1168
0
    return ExecutionStatus::EXCEPTION;
1169
0
  }
1170
0
  bool trapResult = toBoolean(trapResultRes->getHermesValue());
1171
  // 9. If booleanTrapResult is false, return false.
1172
0
  if (!trapResult) {
1173
0
    return false;
1174
0
  }
1175
  // 10. Let targetDesc be ? target.[[GetOwnProperty]](P).
1176
0
  ComputedPropertyDescriptor targetDesc;
1177
0
  MutableHandle<> targetValueOrAccessor{runtime};
1178
0
  MutableHandle<SymbolID> tmpPropNameStorage{runtime};
1179
0
  CallResult<bool> targetDescRes = JSObject::getOwnComputedDescriptor(
1180
0
      target,
1181
0
      runtime,
1182
0
      nameValHandle,
1183
0
      tmpPropNameStorage,
1184
0
      targetDesc,
1185
0
      targetValueOrAccessor);
1186
0
  if (targetDescRes == ExecutionStatus::EXCEPTION) {
1187
0
    return ExecutionStatus::EXCEPTION;
1188
0
  }
1189
  // 11. If targetDesc is undefined, return true.
1190
0
  if (!*targetDescRes) {
1191
0
    return true;
1192
0
  }
1193
  // 12. If targetDesc.[[Configurable]] is false, throw a TypeError exception.
1194
0
  if (!targetDesc.flags.configurable) {
1195
0
    return runtime.raiseTypeError(
1196
0
        "Delete trap target called, but target property is non-configurable");
1197
0
  }
1198
  // 13. Return true.
1199
0
  return true;
1200
0
}
1201
1202
} // namespace
1203
1204
CallResult<bool> JSProxy::deleteNamed(
1205
    Handle<JSObject> selfHandle,
1206
    Runtime &runtime,
1207
0
    SymbolID name) {
1208
0
  GCScope gcScope{runtime};
1209
0
  ScopedNativeDepthTracker depthTracker(runtime);
1210
0
  if (LLVM_UNLIKELY(depthTracker.overflowed())) {
1211
0
    return runtime.raiseStackOverflow(Runtime::StackOverflowKind::NativeStack);
1212
0
  }
1213
  // Make sure to retrieve the target and handler before any JS can execute.
1214
0
  auto &slots = detail::slots(*selfHandle);
1215
0
  Handle<JSObject> target = runtime.makeHandle(slots.target);
1216
0
  Handle<JSObject> handler = runtime.makeHandle(slots.handler);
1217
0
  CallResult<Handle<Callable>> trapRes =
1218
0
      detail::findTrap(selfHandle, runtime, Predefined::deleteProperty);
1219
0
  if (trapRes == ExecutionStatus::EXCEPTION) {
1220
0
    return ExecutionStatus::EXCEPTION;
1221
0
  }
1222
  // 7. If trap is undefined, then
1223
0
  if (!*trapRes) {
1224
    //   a. Return ? target.[[Delete]](P, Receiver).
1225
0
    return JSObject::deleteNamed(target, runtime, name);
1226
0
  }
1227
0
  return deleteWithTrap(
1228
0
      runtime,
1229
0
      runtime.makeHandle(
1230
0
          HermesValue::encodeStringValue(
1231
0
              runtime.getStringPrimFromSymbolID(name))),
1232
0
      *trapRes,
1233
0
      handler,
1234
0
      target);
1235
0
}
1236
1237
CallResult<bool> JSProxy::deleteComputed(
1238
    Handle<JSObject> selfHandle,
1239
    Runtime &runtime,
1240
0
    Handle<> nameValHandle) {
1241
0
  GCScope gcScope{runtime};
1242
0
  ScopedNativeDepthTracker depthTracker(runtime);
1243
0
  if (LLVM_UNLIKELY(depthTracker.overflowed())) {
1244
0
    return runtime.raiseStackOverflow(Runtime::StackOverflowKind::NativeStack);
1245
0
  }
1246
  // Make sure to retrieve the target and handler before any JS can execute.
1247
0
  auto &slots = detail::slots(*selfHandle);
1248
0
  Handle<JSObject> target = runtime.makeHandle(slots.target);
1249
0
  Handle<JSObject> handler = runtime.makeHandle(slots.handler);
1250
0
  CallResult<Handle<Callable>> trapRes =
1251
0
      detail::findTrap(selfHandle, runtime, Predefined::deleteProperty);
1252
0
  if (trapRes == ExecutionStatus::EXCEPTION) {
1253
0
    return ExecutionStatus::EXCEPTION;
1254
0
  }
1255
  // 7. If trap is undefined, then
1256
0
  if (!*trapRes) {
1257
    //   a. Return ? target.[[Delete]](P, Receiver).
1258
0
    return JSObject::deleteComputed(target, runtime, nameValHandle);
1259
0
  }
1260
0
  return deleteWithTrap(runtime, nameValHandle, *trapRes, handler, target);
1261
0
}
1262
1263
namespace {
1264
1265
CallResult<PseudoHandle<JSArray>> filterKeys(
1266
    Handle<JSObject> selfHandle,
1267
    Handle<JSArray> keys,
1268
    Runtime &runtime,
1269
0
    OwnKeysFlags okFlags) {
1270
0
  assert(
1271
0
      (okFlags.getIncludeNonSymbols() || okFlags.getIncludeSymbols()) &&
1272
0
      "Can't exclude symbols and strings");
1273
  // If nothing is excluded, just return the array as-is.
1274
0
  if (okFlags.getIncludeNonSymbols() && okFlags.getIncludeSymbols() &&
1275
0
      okFlags.getIncludeNonEnumerable()) {
1276
0
    return createPseudoHandle(*keys);
1277
0
  }
1278
  // Count number of matching elements by type.
1279
0
  assert(
1280
0
      ((okFlags.getIncludeSymbols() ? 0 : 1) +
1281
0
       (okFlags.getIncludeNonSymbols() ? 0 : 1)) == 1 &&
1282
0
      "Exactly one of Symbols or non-Symbols is included here");
1283
0
  bool onlySymbols = okFlags.getIncludeSymbols();
1284
0
  uint32_t len = JSArray::getLength(*keys, runtime);
1285
0
  uint32_t count = 0;
1286
  // Verify this loop is alloc-free
1287
0
  {
1288
0
    NoAllocScope noAlloc(runtime);
1289
0
    for (uint32_t i = 0; i < len; ++i) {
1290
0
      if (keys->at(runtime, i).isSymbol() == onlySymbols) {
1291
0
        ++count;
1292
0
      }
1293
0
    }
1294
0
  }
1295
  // If everything in the array matches the filter by type, return
1296
  // the list as-is.
1297
0
  if (len == count && okFlags.getIncludeNonEnumerable()) {
1298
0
    return createPseudoHandle(*keys);
1299
0
  }
1300
  // Filter the desired elements we want into the result
1301
0
  auto resultRes = JSArray::create(runtime, count, count);
1302
0
  if (LLVM_UNLIKELY(resultRes == ExecutionStatus::EXCEPTION)) {
1303
0
    return ExecutionStatus::EXCEPTION;
1304
0
  }
1305
0
  Handle<JSArray> resultHandle = *resultRes;
1306
0
  MutableHandle<> elemHandle{runtime};
1307
0
  uint32_t resultIndex = 0;
1308
0
  GCScopeMarkerRAII marker{runtime};
1309
0
  for (uint32_t i = 0; i < len; ++i) {
1310
0
    marker.flush();
1311
0
    SmallHermesValue elem = keys->at(runtime, i);
1312
0
    if (elem.isSymbol() ? !okFlags.getIncludeSymbols()
1313
0
                        : !okFlags.getIncludeNonSymbols()) {
1314
0
      continue;
1315
0
    }
1316
0
    elemHandle = elem.unboxToHV(runtime);
1317
0
    if (!okFlags.getIncludeNonEnumerable()) {
1318
0
      ComputedPropertyDescriptor desc;
1319
0
      CallResult<bool> propRes = JSProxy::getOwnProperty(
1320
0
          selfHandle, runtime, elemHandle, desc, nullptr);
1321
0
      if (LLVM_UNLIKELY(propRes == ExecutionStatus::EXCEPTION)) {
1322
0
        return ExecutionStatus::EXCEPTION;
1323
0
      }
1324
0
      if (!*propRes || !desc.flags.enumerable) {
1325
0
        continue;
1326
0
      }
1327
0
    }
1328
0
    JSArray::setElementAt(resultHandle, runtime, resultIndex++, elemHandle);
1329
0
  }
1330
0
  assert(
1331
0
      (!okFlags.getIncludeNonEnumerable() || resultIndex == count) &&
1332
0
      "Expected count was not correct");
1333
0
  CallResult<bool> setLenRes =
1334
0
      JSArray::setLengthProperty(resultHandle, runtime, resultIndex);
1335
0
  if (LLVM_UNLIKELY(setLenRes == ExecutionStatus::EXCEPTION)) {
1336
0
    return ExecutionStatus::EXCEPTION;
1337
0
  }
1338
0
  return createPseudoHandle(*resultHandle);
1339
0
}
1340
1341
} // namespace
1342
1343
CallResult<PseudoHandle<JSArray>> JSProxy::ownPropertyKeys(
1344
    Handle<JSObject> selfHandle,
1345
    Runtime &runtime,
1346
0
    OwnKeysFlags okFlags) {
1347
0
  GCScope gcScope{runtime};
1348
0
  ScopedNativeDepthTracker depthTracker(runtime);
1349
0
  if (LLVM_UNLIKELY(depthTracker.overflowed())) {
1350
0
    return runtime.raiseStackOverflow(Runtime::StackOverflowKind::NativeStack);
1351
0
  }
1352
  // Make sure to retrieve the target and handler before any JS can execute.
1353
0
  auto &slots = detail::slots(*selfHandle);
1354
0
  Handle<JSObject> target = runtime.makeHandle(slots.target);
1355
0
  Handle<JSObject> handler = runtime.makeHandle(slots.handler);
1356
0
  CallResult<Handle<Callable>> trapRes =
1357
0
      detail::findTrap(selfHandle, runtime, Predefined::ownKeys);
1358
0
  if (trapRes == ExecutionStatus::EXCEPTION) {
1359
0
    return ExecutionStatus::EXCEPTION;
1360
0
  }
1361
  // 6. If trap is undefined, then
1362
0
  if (!*trapRes) {
1363
    //   a. Return ? target.[[OwnPropertyKeys]]().
1364
0
    CallResult<Handle<JSArray>> targetRes =
1365
        // Include everything here, so that filterKeys has a chance to
1366
        // make observable trap calls.
1367
0
        JSObject::getOwnPropertyKeys(
1368
0
            target,
1369
0
            runtime,
1370
0
            OwnKeysFlags()
1371
0
                .plusIncludeSymbols()
1372
0
                .plusIncludeNonSymbols()
1373
0
                .plusIncludeNonEnumerable());
1374
0
    if (targetRes == ExecutionStatus::EXCEPTION) {
1375
0
      return ExecutionStatus::EXCEPTION;
1376
0
    }
1377
0
    return filterKeys(selfHandle, *targetRes, runtime, okFlags);
1378
0
  }
1379
  // 7. Let trapResultArray be ? Call(trap, handler, « target »).
1380
0
  CallResult<PseudoHandle<>> trapResultArrayRes = Callable::executeCall1(
1381
0
      *trapRes, runtime, handler, target.getHermesValue());
1382
0
  if (trapResultArrayRes == ExecutionStatus::EXCEPTION) {
1383
0
    return ExecutionStatus::EXCEPTION;
1384
0
  }
1385
0
  if (!vmisa<JSObject>(trapResultArrayRes->get())) {
1386
0
    return runtime.raiseTypeErrorForValue(
1387
0
        runtime.makeHandle(std::move(*trapResultArrayRes)),
1388
0
        " ownKeys trap result is not an Object");
1389
0
  }
1390
0
  auto trapResultArray =
1391
0
      runtime.makeHandle<JSObject>(trapResultArrayRes->get());
1392
  // 8. Let trapResult be ? CreateListFromArrayLike(trapResultArray, « String,
1393
  // Symbol »)
1394
  // 9. If trapResult contains any duplicate entries, throw a TypeError
1395
  // exception.
1396
0
  CallResult<uint64_t> countRes =
1397
0
      getArrayLikeLength_RJS(trapResultArray, runtime);
1398
0
  if (LLVM_UNLIKELY(countRes == ExecutionStatus::EXCEPTION)) {
1399
0
    return ExecutionStatus::EXCEPTION;
1400
0
  }
1401
0
  if (*countRes > UINT32_MAX) {
1402
0
    return runtime.raiseRangeError(
1403
0
        "Too many elements returned from ownKeys trap");
1404
0
  }
1405
0
  uint32_t count = static_cast<uint32_t>(*countRes);
1406
0
  auto trapResultRes = JSArray::create(runtime, count, count);
1407
0
  if (LLVM_UNLIKELY(trapResultRes == ExecutionStatus::EXCEPTION)) {
1408
0
    return ExecutionStatus::EXCEPTION;
1409
0
  }
1410
0
  Handle<JSArray> trapResult = *trapResultRes;
1411
0
  CallResult<PseudoHandle<OrderedHashMap>> dupcheckRes =
1412
0
      OrderedHashMap::create(runtime);
1413
0
  if (LLVM_UNLIKELY(dupcheckRes == ExecutionStatus::EXCEPTION)) {
1414
0
    return ExecutionStatus::EXCEPTION;
1415
0
  }
1416
0
  Handle<OrderedHashMap> dupcheck = runtime.makeHandle(std::move(*dupcheckRes));
1417
0
  if (LLVM_UNLIKELY(
1418
0
          createListFromArrayLike_RJS(
1419
0
              trapResultArray,
1420
0
              runtime,
1421
0
              count,
1422
0
              [&dupcheck, &trapResult](
1423
0
                  Runtime &runtime, uint64_t index, PseudoHandle<> value) {
1424
0
                Handle<> valHandle = runtime.makeHandle(std::move(value));
1425
0
                if (!valHandle->isString() && !valHandle->isSymbol()) {
1426
0
                  return runtime.raiseTypeErrorForValue(
1427
0
                      valHandle,
1428
0
                      " ownKeys trap result element is not String or Symbol");
1429
0
                }
1430
0
                if (OrderedHashMap::has(dupcheck, runtime, valHandle)) {
1431
0
                  return runtime.raiseTypeErrorForValue(
1432
0
                      "ownKeys trap result has duplicate ", valHandle, "");
1433
0
                }
1434
0
                if (LLVM_UNLIKELY(
1435
0
                        OrderedHashMap::insert(
1436
0
                            dupcheck, runtime, valHandle, valHandle) ==
1437
0
                        ExecutionStatus::EXCEPTION))
1438
0
                  return ExecutionStatus::RETURNED;
1439
0
                JSArray::setElementAt(trapResult, runtime, index, valHandle);
1440
0
                return ExecutionStatus::RETURNED;
1441
0
              }) == ExecutionStatus::EXCEPTION)) {
1442
0
    return ExecutionStatus::EXCEPTION;
1443
0
  }
1444
1445
  // 10. Let extensibleTarget be ? IsExtensible(target).
1446
0
  CallResult<bool> extensibleRes = JSObject::isExtensible(target, runtime);
1447
0
  if (extensibleRes == ExecutionStatus::EXCEPTION) {
1448
0
    return ExecutionStatus::EXCEPTION;
1449
0
  }
1450
  // 11. Let targetKeys be ? target.[[OwnPropertyKeys]]().
1451
0
  CallResult<Handle<JSArray>> targetKeysRes = JSObject::getOwnPropertyKeys(
1452
0
      target,
1453
0
      runtime,
1454
0
      OwnKeysFlags()
1455
0
          .plusIncludeSymbols()
1456
0
          .plusIncludeNonSymbols()
1457
0
          .plusIncludeNonEnumerable());
1458
0
  if (targetKeysRes == ExecutionStatus::EXCEPTION) {
1459
0
    return ExecutionStatus::EXCEPTION;
1460
0
  }
1461
0
  Handle<JSArray> targetKeys = *targetKeysRes;
1462
  // 12. Assert: targetKeys is a List containing only String and Symbol values.
1463
  // 13. Assert: targetKeys contains no duplicate entries.
1464
  // 14. Let targetConfigurableKeys be a new empty List.
1465
  // 15. Let targetNonconfigurableKeys be a new empty List.
1466
0
  llvh::SmallSet<uint32_t, 8> nonConfigurable;
1467
0
  MutableHandle<SymbolID> tmpPropNameStorage{runtime};
1468
  // 16. For each element key of targetKeys, do
1469
0
  GCScopeMarkerRAII marker{runtime};
1470
0
  for (uint32_t i = 0, len = JSArray::getLength(*targetKeys, runtime); i < len;
1471
0
       ++i) {
1472
0
    marker.flush();
1473
    //   a. Let desc be ? target.[[GetOwnProperty]](key).
1474
0
    ComputedPropertyDescriptor desc;
1475
0
    CallResult<bool> descRes = JSObject::getOwnComputedDescriptor(
1476
0
        target,
1477
0
        runtime,
1478
0
        runtime.makeHandle(targetKeys->at(runtime, i).unboxToHV(runtime)),
1479
0
        tmpPropNameStorage,
1480
0
        desc);
1481
0
    if (descRes == ExecutionStatus::EXCEPTION) {
1482
0
      return ExecutionStatus::EXCEPTION;
1483
0
    }
1484
    //   b. If desc is not undefined and desc.[[Configurable]] is false, then
1485
    //     i. Append key as an element of targetNonconfigurableKeys.
1486
    //   c. Else,
1487
    //     i. Append key as an element of targetConfigurableKeys.
1488
0
    if (*descRes && !desc.flags.configurable) {
1489
0
      nonConfigurable.insert(i);
1490
0
    }
1491
0
  }
1492
  // 17. If extensibleTarget is true and targetNonconfigurableKeys is empty,
1493
  // then
1494
0
  if (*extensibleRes && nonConfigurable.empty()) {
1495
    //   a. Return trapResult.
1496
0
    return filterKeys(selfHandle, trapResult, runtime, okFlags);
1497
0
  }
1498
  // 18. Let uncheckedResultKeys be a new List which is a copy of trapResult.
1499
  // 19. For each key that is an element of targetNonconfigurableKeys, do
1500
  //   a. If key is not an element of uncheckedResultKeys, throw a TypeError
1501
  //   exception. b. Remove key from uncheckedResultKeys.
1502
0
  auto inTrapResult = [&runtime, &trapResult](HermesValue value) {
1503
0
    for (uint32_t j = 0, len = JSArray::getLength(*trapResult, runtime);
1504
0
         j < len;
1505
0
         ++j) {
1506
0
      if (isSameValue(value, trapResult->at(runtime, j).unboxToHV(runtime))) {
1507
0
        return true;
1508
0
      }
1509
0
    }
1510
0
    return false;
1511
0
  };
1512
0
  for (auto i : nonConfigurable) {
1513
0
    if (!inTrapResult(targetKeys->at(runtime, i).unboxToHV(runtime))) {
1514
0
      return runtime.raiseTypeError(
1515
0
          "ownKeys target key is non-configurable but not present in trap result");
1516
0
    }
1517
0
  }
1518
  // 20. If extensibleTarget is true, return trapResult.
1519
0
  if (*extensibleRes) {
1520
0
    return filterKeys(selfHandle, trapResult, runtime, okFlags);
1521
0
  }
1522
  // 21. For each key that is an element of targetConfigurableKeys, do
1523
  //   a. If key is not an element of uncheckedResultKeys, throw a TypeError
1524
  //   exception. b. Remove key from uncheckedResultKeys.
1525
0
  for (uint32_t i = 0, len = JSArray::getLength(*targetKeys, runtime); i < len;
1526
0
       ++i) {
1527
0
    if (nonConfigurable.count(i) > 0) {
1528
0
      continue;
1529
0
    }
1530
0
    if (!inTrapResult(targetKeys->at(runtime, i).unboxToHV(runtime))) {
1531
0
      return runtime.raiseTypeError(
1532
0
          "ownKeys target is non-extensible but key is missing from trap result");
1533
0
    }
1534
0
  }
1535
  // 22. If uncheckedResultKeys is not empty, throw a TypeError exception.
1536
0
  if (JSArray::getLength(*targetKeys, runtime) !=
1537
0
      JSArray::getLength(*trapResult, runtime)) {
1538
0
    return runtime.raiseTypeError(
1539
0
        "ownKeys target is non-extensible but trap result keys differ from target keys");
1540
0
  }
1541
  // 23. Return trapResult.
1542
0
  return filterKeys(selfHandle, trapResult, runtime, okFlags);
1543
0
}
1544
1545
} // namespace vm
1546
} // namespace hermes