Source code

Revision control

Other Tools

1
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2
* vim: set ts=8 sts=2 et sw=2 tw=80:
3
* This Source Code Form is subject to the terms of the Mozilla Public
4
* License, v. 2.0. If a copy of the MPL was not distributed with this
5
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6
7
#include "proxy/ScriptedProxyHandler.h"
8
9
#include "jsapi.h"
10
11
#include "js/CharacterEncoding.h"
12
#include "js/PropertyDescriptor.h" // JS::FromPropertyDescriptor
13
#include "vm/EqualityOperations.h" // js::SameValue
14
#include "vm/JSFunction.h"
15
#include "vm/JSObject.h"
16
17
#include "vm/JSObject-inl.h"
18
#include "vm/NativeObject-inl.h"
19
20
using namespace js;
21
22
using JS::IsArrayAnswer;
23
24
// ES8 rev 0c1bd3004329336774cbc90de727cd0cf5f11e93
25
// 9.1.6.2 IsCompatiblePropertyDescriptor. BUT that method just calls
26
// 9.1.6.3 ValidateAndApplyPropertyDescriptor with two additional constant
27
// arguments. Therefore step numbering is from the latter method, and
28
// resulting dead code has been removed.
29
30
// If an exception should be thrown, we will set errorDetails.
31
static bool IsCompatiblePropertyDescriptor(JSContext* cx, bool extensible,
32
Handle<PropertyDescriptor> desc,
33
Handle<PropertyDescriptor> current,
34
const char** errorDetails) {
35
// precondition: we won't set details if checks pass, so it must be null
36
// here.
37
MOZ_ASSERT(*errorDetails == nullptr);
38
39
// Step 2.
40
if (!current.object()) {
41
// Step 2a-b,e. As |O| is always undefined, steps 2c-d fall away.
42
if (!extensible) {
43
static const char DETAILS_NOT_EXTENSIBLE[] =
44
"proxy can't report an extensible object as non-extensible";
45
*errorDetails = DETAILS_NOT_EXTENSIBLE;
46
}
47
return true;
48
}
49
50
// Step 3.
51
if (!desc.hasValue() && !desc.hasWritable() && !desc.hasGetterObject() &&
52
!desc.hasSetterObject() && !desc.hasEnumerable() &&
53
!desc.hasConfigurable()) {
54
return true;
55
}
56
57
// Step 4.
58
if ((!desc.hasWritable() ||
59
(current.hasWritable() && desc.writable() == current.writable())) &&
60
(!desc.hasGetterObject() || desc.getter() == current.getter()) &&
61
(!desc.hasSetterObject() || desc.setter() == current.setter()) &&
62
(!desc.hasEnumerable() || desc.enumerable() == current.enumerable()) &&
63
(!desc.hasConfigurable() ||
64
desc.configurable() == current.configurable())) {
65
if (!desc.hasValue()) {
66
return true;
67
}
68
69
bool same = false;
70
if (!SameValue(cx, desc.value(), current.value(), &same)) {
71
return false;
72
}
73
74
if (same) {
75
return true;
76
}
77
}
78
79
// Step 5.
80
if (!current.configurable()) {
81
// Step 5a.
82
if (desc.hasConfigurable() && desc.configurable()) {
83
static const char DETAILS_CANT_REPORT_NC_AS_C[] =
84
"proxy can't report an existing non-configurable property as "
85
"configurable";
86
*errorDetails = DETAILS_CANT_REPORT_NC_AS_C;
87
return true;
88
}
89
90
// Step 5b.
91
if (desc.hasEnumerable() && desc.enumerable() != current.enumerable()) {
92
static const char DETAILS_ENUM_DIFFERENT[] =
93
"proxy can't report a different 'enumerable' from target when target "
94
"is not configurable";
95
*errorDetails = DETAILS_ENUM_DIFFERENT;
96
return true;
97
}
98
}
99
100
// Step 6.
101
if (desc.isGenericDescriptor()) {
102
return true;
103
}
104
105
// Step 7.
106
if (current.isDataDescriptor() != desc.isDataDescriptor()) {
107
// Steps 7a, 11. As |O| is always undefined, steps 2b-c fall away.
108
if (!current.configurable()) {
109
static const char DETAILS_CURRENT_NC_DIFF_TYPE[] =
110
"proxy can't report a different descriptor type when target is not "
111
"configurable";
112
*errorDetails = DETAILS_CURRENT_NC_DIFF_TYPE;
113
}
114
return true;
115
}
116
117
// Step 8.
118
if (current.isDataDescriptor()) {
119
MOZ_ASSERT(desc.isDataDescriptor()); // by step 7
120
if (!current.configurable() && !current.writable()) {
121
if (desc.hasWritable() && desc.writable()) {
122
static const char DETAILS_CANT_REPORT_NW_AS_W[] =
123
"proxy can't report a non-configurable, non-writable property as "
124
"writable";
125
*errorDetails = DETAILS_CANT_REPORT_NW_AS_W;
126
return true;
127
}
128
129
if (desc.hasValue()) {
130
bool same;
131
if (!SameValue(cx, desc.value(), current.value(), &same)) {
132
return false;
133
}
134
if (!same) {
135
static const char DETAILS_DIFFERENT_VALUE[] =
136
"proxy must report the same value for the non-writable, "
137
"non-configurable property";
138
*errorDetails = DETAILS_DIFFERENT_VALUE;
139
return true;
140
}
141
}
142
}
143
144
return true;
145
}
146
147
// Step 9.
148
MOZ_ASSERT(current.isAccessorDescriptor()); // by step 8
149
MOZ_ASSERT(desc.isAccessorDescriptor()); // by step 7
150
151
if (current.configurable()) {
152
return true;
153
}
154
if (desc.hasSetterObject() && (desc.setter() != current.setter())) {
155
static const char DETAILS_SETTERS_DIFFERENT[] =
156
"proxy can't report different setters for a currently non-configurable "
157
"property";
158
*errorDetails = DETAILS_SETTERS_DIFFERENT;
159
} else if (desc.hasGetterObject() && (desc.getter() != current.getter())) {
160
static const char DETAILS_GETTERS_DIFFERENT[] =
161
"proxy can't report different getters for a currently non-configurable "
162
"property";
163
*errorDetails = DETAILS_GETTERS_DIFFERENT;
164
}
165
return true;
166
}
167
168
// Get the [[ProxyHandler]] of a scripted proxy.
169
/* static */
170
JSObject* ScriptedProxyHandler::handlerObject(const JSObject* proxy) {
171
MOZ_ASSERT(proxy->as<ProxyObject>().handler() ==
172
&ScriptedProxyHandler::singleton);
173
return proxy->as<ProxyObject>()
174
.reservedSlot(ScriptedProxyHandler::HANDLER_EXTRA)
175
.toObjectOrNull();
176
}
177
178
// ES8 rev 0c1bd3004329336774cbc90de727cd0cf5f11e93
179
// 7.3.9 GetMethod, reimplemented for proxy handler trap-getting to produce
180
// better error messages.
181
static bool GetProxyTrap(JSContext* cx, HandleObject handler,
182
HandlePropertyName name, MutableHandleValue func) {
183
// Steps 2, 5.
184
if (!GetProperty(cx, handler, handler, name, func)) {
185
return false;
186
}
187
188
// Step 3.
189
if (func.isUndefined()) {
190
return true;
191
}
192
193
if (func.isNull()) {
194
func.setUndefined();
195
return true;
196
}
197
198
// Step 4.
199
if (!IsCallable(func)) {
200
UniqueChars bytes = EncodeAscii(cx, name);
201
if (!bytes) {
202
return false;
203
}
204
205
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_BAD_TRAP,
206
bytes.get());
207
return false;
208
}
209
210
return true;
211
}
212
213
// ES8 rev 0c1bd3004329336774cbc90de727cd0cf5f11e93
214
// 9.5.1 Proxy.[[GetPrototypeOf]].
215
bool ScriptedProxyHandler::getPrototype(JSContext* cx, HandleObject proxy,
216
MutableHandleObject protop) const {
217
// Steps 1-3.
218
RootedObject handler(cx, ScriptedProxyHandler::handlerObject(proxy));
219
if (!handler) {
220
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
221
JSMSG_PROXY_REVOKED);
222
return false;
223
}
224
225
// Step 4.
226
RootedObject target(cx, proxy->as<ProxyObject>().target());
227
MOZ_ASSERT(target);
228
229
// Step 5.
230
RootedValue trap(cx);
231
if (!GetProxyTrap(cx, handler, cx->names().getPrototypeOf, &trap)) {
232
return false;
233
}
234
235
// Step 6.
236
if (trap.isUndefined()) {
237
return GetPrototype(cx, target, protop);
238
}
239
240
// Step 7.
241
RootedValue handlerProto(cx);
242
{
243
FixedInvokeArgs<1> args(cx);
244
245
args[0].setObject(*target);
246
247
handlerProto.setObject(*handler);
248
249
if (!js::Call(cx, trap, handlerProto, args, &handlerProto)) {
250
return false;
251
}
252
}
253
254
// Step 8.
255
if (!handlerProto.isObjectOrNull()) {
256
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
257
JSMSG_BAD_GETPROTOTYPEOF_TRAP_RETURN);
258
return false;
259
}
260
261
// Step 9.
262
bool extensibleTarget;
263
if (!IsExtensible(cx, target, &extensibleTarget)) {
264
return false;
265
}
266
267
// Step 10.
268
if (extensibleTarget) {
269
protop.set(handlerProto.toObjectOrNull());
270
return true;
271
}
272
273
// Step 11.
274
RootedObject targetProto(cx);
275
if (!GetPrototype(cx, target, &targetProto)) {
276
return false;
277
}
278
279
// Step 12.
280
if (handlerProto.toObjectOrNull() != targetProto) {
281
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
282
JSMSG_INCONSISTENT_GETPROTOTYPEOF_TRAP);
283
return false;
284
}
285
286
// Step 13.
287
protop.set(handlerProto.toObjectOrNull());
288
return true;
289
}
290
291
// ES8 rev 0c1bd3004329336774cbc90de727cd0cf5f11e93
292
// 9.5.2 Proxy.[[SetPrototypeOf]].
293
bool ScriptedProxyHandler::setPrototype(JSContext* cx, HandleObject proxy,
294
HandleObject proto,
295
ObjectOpResult& result) const {
296
// Steps 1-4.
297
RootedObject handler(cx, ScriptedProxyHandler::handlerObject(proxy));
298
if (!handler) {
299
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
300
JSMSG_PROXY_REVOKED);
301
return false;
302
}
303
304
// Step 5.
305
RootedObject target(cx, proxy->as<ProxyObject>().target());
306
MOZ_ASSERT(target);
307
308
// Step 6.
309
RootedValue trap(cx);
310
if (!GetProxyTrap(cx, handler, cx->names().setPrototypeOf, &trap)) {
311
return false;
312
}
313
314
// Step 7.
315
if (trap.isUndefined()) {
316
return SetPrototype(cx, target, proto, result);
317
}
318
319
// Step 8.
320
bool booleanTrapResult;
321
{
322
FixedInvokeArgs<2> args(cx);
323
324
args[0].setObject(*target);
325
args[1].setObjectOrNull(proto);
326
327
RootedValue hval(cx, ObjectValue(*handler));
328
if (!js::Call(cx, trap, hval, args, &hval)) {
329
return false;
330
}
331
332
booleanTrapResult = ToBoolean(hval);
333
}
334
335
// Step 9.
336
if (!booleanTrapResult) {
337
return result.fail(JSMSG_PROXY_SETPROTOTYPEOF_RETURNED_FALSE);
338
}
339
340
// Step 10.
341
bool extensibleTarget;
342
if (!IsExtensible(cx, target, &extensibleTarget)) {
343
return false;
344
}
345
346
// Step 11.
347
if (extensibleTarget) {
348
return result.succeed();
349
}
350
351
// Step 12.
352
RootedObject targetProto(cx);
353
if (!GetPrototype(cx, target, &targetProto)) {
354
return false;
355
}
356
357
// Step 13.
358
if (proto != targetProto) {
359
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
360
JSMSG_INCONSISTENT_SETPROTOTYPEOF_TRAP);
361
return false;
362
}
363
364
// Step 14.
365
return result.succeed();
366
}
367
368
bool ScriptedProxyHandler::getPrototypeIfOrdinary(
369
JSContext* cx, HandleObject proxy, bool* isOrdinary,
370
MutableHandleObject protop) const {
371
*isOrdinary = false;
372
return true;
373
}
374
375
// Not yet part of ES6, but hopefully to be standards-tracked -- and needed to
376
// handle revoked proxies in any event.
377
bool ScriptedProxyHandler::setImmutablePrototype(JSContext* cx,
378
HandleObject proxy,
379
bool* succeeded) const {
380
RootedObject target(cx, proxy->as<ProxyObject>().target());
381
if (!target) {
382
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
383
JSMSG_PROXY_REVOKED);
384
return false;
385
}
386
387
return SetImmutablePrototype(cx, target, succeeded);
388
}
389
390
// ES8 rev 0c1bd3004329336774cbc90de727cd0cf5f11e93
391
// 9.5.4 Proxy.[[PreventExtensions]]()
392
bool ScriptedProxyHandler::preventExtensions(JSContext* cx, HandleObject proxy,
393
ObjectOpResult& result) const {
394
// Steps 1-3.
395
RootedObject handler(cx, ScriptedProxyHandler::handlerObject(proxy));
396
if (!handler) {
397
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
398
JSMSG_PROXY_REVOKED);
399
return false;
400
}
401
402
// Step 4.
403
RootedObject target(cx, proxy->as<ProxyObject>().target());
404
MOZ_ASSERT(target);
405
406
// Step 5.
407
RootedValue trap(cx);
408
if (!GetProxyTrap(cx, handler, cx->names().preventExtensions, &trap)) {
409
return false;
410
}
411
412
// Step 6.
413
if (trap.isUndefined()) {
414
return PreventExtensions(cx, target, result);
415
}
416
417
// Step 7.
418
bool booleanTrapResult;
419
{
420
RootedValue arg(cx, ObjectValue(*target));
421
RootedValue trapResult(cx);
422
if (!Call(cx, trap, handler, arg, &trapResult)) {
423
return false;
424
}
425
426
booleanTrapResult = ToBoolean(trapResult);
427
}
428
429
// Step 8.
430
if (booleanTrapResult) {
431
// Step 8a.
432
bool targetIsExtensible;
433
if (!IsExtensible(cx, target, &targetIsExtensible)) {
434
return false;
435
}
436
437
if (targetIsExtensible) {
438
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
439
JSMSG_CANT_REPORT_AS_NON_EXTENSIBLE);
440
return false;
441
}
442
443
// Step 9.
444
return result.succeed();
445
}
446
447
// Also step 9.
448
return result.fail(JSMSG_PROXY_PREVENTEXTENSIONS_RETURNED_FALSE);
449
}
450
451
// ES8 rev 0c1bd3004329336774cbc90de727cd0cf5f11e93
452
// 9.5.3 Proxy.[[IsExtensible]]()
453
bool ScriptedProxyHandler::isExtensible(JSContext* cx, HandleObject proxy,
454
bool* extensible) const {
455
// Steps 1-3.
456
RootedObject handler(cx, ScriptedProxyHandler::handlerObject(proxy));
457
if (!handler) {
458
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
459
JSMSG_PROXY_REVOKED);
460
return false;
461
}
462
463
// Step 4.
464
RootedObject target(cx, proxy->as<ProxyObject>().target());
465
MOZ_ASSERT(target);
466
467
// Step 5.
468
RootedValue trap(cx);
469
if (!GetProxyTrap(cx, handler, cx->names().isExtensible, &trap)) {
470
return false;
471
}
472
473
// Step 6.
474
if (trap.isUndefined()) {
475
return IsExtensible(cx, target, extensible);
476
}
477
478
// Step 7.
479
bool booleanTrapResult;
480
{
481
RootedValue arg(cx, ObjectValue(*target));
482
RootedValue trapResult(cx);
483
if (!Call(cx, trap, handler, arg, &trapResult)) {
484
return false;
485
}
486
487
booleanTrapResult = ToBoolean(trapResult);
488
}
489
490
// Steps 8.
491
bool targetResult;
492
if (!IsExtensible(cx, target, &targetResult)) {
493
return false;
494
}
495
496
// Step 9.
497
if (targetResult != booleanTrapResult) {
498
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
499
JSMSG_PROXY_EXTENSIBILITY);
500
return false;
501
}
502
503
// Step 10.
504
*extensible = booleanTrapResult;
505
return true;
506
}
507
508
// ES8 rev 0c1bd3004329336774cbc90de727cd0cf5f11e93
509
// 9.5.5 Proxy.[[GetOwnProperty]](P)
510
bool ScriptedProxyHandler::getOwnPropertyDescriptor(
511
JSContext* cx, HandleObject proxy, HandleId id,
512
MutableHandle<PropertyDescriptor> desc) const {
513
// Steps 2-4.
514
RootedObject handler(cx, ScriptedProxyHandler::handlerObject(proxy));
515
if (!handler) {
516
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
517
JSMSG_PROXY_REVOKED);
518
return false;
519
}
520
521
// Step 5.
522
RootedObject target(cx, proxy->as<ProxyObject>().target());
523
MOZ_ASSERT(target);
524
525
// Step 6.
526
RootedValue trap(cx);
527
if (!GetProxyTrap(cx, handler, cx->names().getOwnPropertyDescriptor, &trap)) {
528
return false;
529
}
530
531
// Step 7.
532
if (trap.isUndefined()) {
533
return GetOwnPropertyDescriptor(cx, target, id, desc);
534
}
535
536
// Step 8.
537
RootedValue propKey(cx);
538
if (!IdToStringOrSymbol(cx, id, &propKey)) {
539
return false;
540
}
541
542
RootedValue trapResult(cx);
543
RootedValue targetVal(cx, ObjectValue(*target));
544
if (!Call(cx, trap, handler, targetVal, propKey, &trapResult)) {
545
return false;
546
}
547
548
// Step 9.
549
if (!trapResult.isUndefined() && !trapResult.isObject()) {
550
return js::Throw(cx, id, JSMSG_PROXY_GETOWN_OBJORUNDEF);
551
}
552
553
// Step 10.
554
Rooted<PropertyDescriptor> targetDesc(cx);
555
if (!GetOwnPropertyDescriptor(cx, target, id, &targetDesc)) {
556
return false;
557
}
558
559
// Step 11.
560
if (trapResult.isUndefined()) {
561
// Step 11a.
562
if (!targetDesc.object()) {
563
desc.object().set(nullptr);
564
return true;
565
}
566
567
// Step 11b.
568
if (!targetDesc.configurable()) {
569
return js::Throw(cx, id, JSMSG_CANT_REPORT_NC_AS_NE);
570
}
571
572
// Steps 11c-d.
573
bool extensibleTarget;
574
if (!IsExtensible(cx, target, &extensibleTarget)) {
575
return false;
576
}
577
578
// Step 11e.
579
if (!extensibleTarget) {
580
return js::Throw(cx, id, JSMSG_CANT_REPORT_E_AS_NE);
581
}
582
583
// Step 11f.
584
desc.object().set(nullptr);
585
return true;
586
}
587
588
// Step 12.
589
bool extensibleTarget;
590
if (!IsExtensible(cx, target, &extensibleTarget)) {
591
return false;
592
}
593
594
// Step 13.
595
Rooted<PropertyDescriptor> resultDesc(cx);
596
if (!ToPropertyDescriptor(cx, trapResult, true, &resultDesc)) {
597
return false;
598
}
599
600
// Step 14.
601
CompletePropertyDescriptor(&resultDesc);
602
603
// Step 15.
604
const char* errorDetails = nullptr;
605
if (!IsCompatiblePropertyDescriptor(cx, extensibleTarget, resultDesc,
606
targetDesc, &errorDetails))
607
return false;
608
609
// Step 16.
610
if (errorDetails) {
611
return js::Throw(cx, id, JSMSG_CANT_REPORT_INVALID, errorDetails);
612
}
613
614
// Step 17.
615
if (!resultDesc.configurable()) {
616
if (!targetDesc.object()) {
617
return js::Throw(cx, id, JSMSG_CANT_REPORT_NE_AS_NC);
618
}
619
620
if (targetDesc.configurable()) {
621
return js::Throw(cx, id, JSMSG_CANT_REPORT_C_AS_NC);
622
}
623
624
if (resultDesc.hasWritable() && !resultDesc.writable()) {
625
if (targetDesc.writable()) {
626
return js::Throw(cx, id, JSMSG_CANT_REPORT_W_AS_NW);
627
}
628
}
629
}
630
631
// Step 18.
632
desc.set(resultDesc);
633
desc.object().set(proxy);
634
return true;
635
}
636
637
// ES8 rev 0c1bd3004329336774cbc90de727cd0cf5f11e93
638
// 9.5.6 Proxy.[[DefineOwnProperty]](P, Desc)
639
bool ScriptedProxyHandler::defineProperty(JSContext* cx, HandleObject proxy,
640
HandleId id,
641
Handle<PropertyDescriptor> desc,
642
ObjectOpResult& result) const {
643
// Steps 2-4.
644
RootedObject handler(cx, ScriptedProxyHandler::handlerObject(proxy));
645
if (!handler) {
646
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
647
JSMSG_PROXY_REVOKED);
648
return false;
649
}
650
651
// Step 5.
652
RootedObject target(cx, proxy->as<ProxyObject>().target());
653
MOZ_ASSERT(target);
654
655
// Step 6.
656
RootedValue trap(cx);
657
if (!GetProxyTrap(cx, handler, cx->names().defineProperty, &trap)) {
658
return false;
659
}
660
661
// Step 7.
662
if (trap.isUndefined()) {
663
return DefineProperty(cx, target, id, desc, result);
664
}
665
666
// Step 8.
667
RootedValue descObj(cx);
668
if (!FromPropertyDescriptorToObject(cx, desc, &descObj)) {
669
return false;
670
}
671
672
// Step 9.
673
RootedValue propKey(cx);
674
if (!IdToStringOrSymbol(cx, id, &propKey)) {
675
return false;
676
}
677
678
RootedValue trapResult(cx);
679
{
680
FixedInvokeArgs<3> args(cx);
681
682
args[0].setObject(*target);
683
args[1].set(propKey);
684
args[2].set(descObj);
685
686
RootedValue thisv(cx, ObjectValue(*handler));
687
if (!Call(cx, trap, thisv, args, &trapResult)) {
688
return false;
689
}
690
}
691
692
// Step 10.
693
if (!ToBoolean(trapResult)) {
694
return result.fail(JSMSG_PROXY_DEFINE_RETURNED_FALSE);
695
}
696
697
// Step 11.
698
Rooted<PropertyDescriptor> targetDesc(cx);
699
if (!GetOwnPropertyDescriptor(cx, target, id, &targetDesc)) {
700
return false;
701
}
702
703
// Step 12.
704
bool extensibleTarget;
705
if (!IsExtensible(cx, target, &extensibleTarget)) {
706
return false;
707
}
708
709
// Steps 13-14.
710
bool settingConfigFalse = desc.hasConfigurable() && !desc.configurable();
711
712
// Steps 15-16.
713
if (!targetDesc.object()) {
714
// Step 15a.
715
if (!extensibleTarget) {
716
return js::Throw(cx, id, JSMSG_CANT_DEFINE_NEW);
717
}
718
719
// Step 15b.
720
if (settingConfigFalse) {
721
return js::Throw(cx, id, JSMSG_CANT_DEFINE_NE_AS_NC);
722
}
723
} else {
724
// Step 16a.
725
const char* errorDetails = nullptr;
726
if (!IsCompatiblePropertyDescriptor(cx, extensibleTarget, desc, targetDesc,
727
&errorDetails))
728
return false;
729
730
if (errorDetails) {
731
return js::Throw(cx, id, JSMSG_CANT_DEFINE_INVALID, errorDetails);
732
}
733
734
// Step 16b.
735
if (settingConfigFalse && targetDesc.configurable()) {
736
static const char DETAILS_CANT_REPORT_C_AS_NC[] =
737
"proxy can't define an existing configurable property as "
738
"non-configurable";
739
return js::Throw(cx, id, JSMSG_CANT_DEFINE_INVALID,
740
DETAILS_CANT_REPORT_C_AS_NC);
741
}
742
743
if (targetDesc.isDataDescriptor() && !targetDesc.configurable() &&
744
targetDesc.writable()) {
745
if (desc.hasWritable() && !desc.writable()) {
746
static const char DETAILS_CANT_DEFINE_NW[] =
747
"proxy can't define an existing non-configurable writable property "
748
"as non-writable";
749
return js::Throw(cx, id, JSMSG_CANT_DEFINE_INVALID,
750
DETAILS_CANT_DEFINE_NW);
751
}
752
}
753
}
754
755
// Step 17.
756
return result.succeed();
757
}
758
759
// ES8 rev 0c1bd3004329336774cbc90de727cd0cf5f11e93
760
// 7.3.17 CreateListFromArrayLike with elementTypes fixed to symbol/string.
761
static bool CreateFilteredListFromArrayLike(JSContext* cx, HandleValue v,
762
MutableHandleIdVector props) {
763
// Step 2.
764
RootedObject obj(cx, RequireObject(cx, JSMSG_OBJECT_REQUIRED_RET_OWNKEYS,
765
JSDVG_IGNORE_STACK, v));
766
if (!obj) {
767
return false;
768
}
769
770
// Step 3.
771
uint32_t len;
772
if (!GetLengthProperty(cx, obj, &len)) {
773
return false;
774
}
775
776
// Steps 4-6.
777
RootedValue next(cx);
778
RootedId id(cx);
779
uint32_t index = 0;
780
while (index < len) {
781
// Steps 6a-b.
782
if (!GetElement(cx, obj, obj, index, &next)) {
783
return false;
784
}
785
786
// Step 6c.
787
if (!next.isString() && !next.isSymbol()) {
788
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
789
JSMSG_OWNKEYS_STR_SYM);
790
return false;
791
}
792
793
if (!ValueToId<CanGC>(cx, next, &id)) {
794
return false;
795
}
796
797
// Step 6d.
798
if (!props.append(id)) {
799
return false;
800
}
801
802
// Step 6e.
803
index++;
804
}
805
806
// Step 7.
807
return true;
808
}
809
810
// ES2018 draft rev aab1ea3bd4d03c85d6f4a91503b4169346ab7271
811
// 9.5.11 Proxy.[[OwnPropertyKeys]]()
812
bool ScriptedProxyHandler::ownPropertyKeys(JSContext* cx, HandleObject proxy,
813
MutableHandleIdVector props) const {
814
// Steps 1-3.
815
RootedObject handler(cx, ScriptedProxyHandler::handlerObject(proxy));
816
if (!handler) {
817
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
818
JSMSG_PROXY_REVOKED);
819
return false;
820
}
821
822
// Step 4.
823
RootedObject target(cx, proxy->as<ProxyObject>().target());
824
MOZ_ASSERT(target);
825
826
// Step 5.
827
RootedValue trap(cx);
828
if (!GetProxyTrap(cx, handler, cx->names().ownKeys, &trap)) {
829
return false;
830
}
831
832
// Step 6.
833
if (trap.isUndefined()) {
834
return GetPropertyKeys(
835
cx, target, JSITER_OWNONLY | JSITER_HIDDEN | JSITER_SYMBOLS, props);
836
}
837
838
// Step 7.
839
RootedValue trapResultArray(cx);
840
RootedValue targetVal(cx, ObjectValue(*target));
841
if (!Call(cx, trap, handler, targetVal, &trapResultArray)) {
842
return false;
843
}
844
845
// Step 8.
846
RootedIdVector trapResult(cx);
847
if (!CreateFilteredListFromArrayLike(cx, trapResultArray, &trapResult)) {
848
return false;
849
}
850
851
// Steps 9, 18.
852
Rooted<GCHashSet<jsid>> uncheckedResultKeys(
853
cx, GCHashSet<jsid>(cx, trapResult.length()));
854
855
for (size_t i = 0, len = trapResult.length(); i < len; i++) {
856
MOZ_ASSERT(!JSID_IS_VOID(trapResult[i]));
857
858
auto ptr = uncheckedResultKeys.lookupForAdd(trapResult[i]);
859
if (ptr) {
860
return js::Throw(cx, trapResult[i], JSMSG_OWNKEYS_DUPLICATE);
861
}
862
863
if (!uncheckedResultKeys.add(ptr, trapResult[i])) {
864
return false;
865
}
866
}
867
868
// Step 10.
869
bool extensibleTarget;
870
if (!IsExtensible(cx, target, &extensibleTarget)) {
871
return false;
872
}
873
874
// Steps 11-13.
875
RootedIdVector targetKeys(cx);
876
if (!GetPropertyKeys(cx, target,
877
JSITER_OWNONLY | JSITER_HIDDEN | JSITER_SYMBOLS,
878
&targetKeys)) {
879
return false;
880
}
881
882
// Steps 14-15.
883
RootedIdVector targetConfigurableKeys(cx);
884
RootedIdVector targetNonconfigurableKeys(cx);
885
886
// Step 16.
887
Rooted<PropertyDescriptor> desc(cx);
888
for (size_t i = 0; i < targetKeys.length(); ++i) {
889
// Step 16.a.
890
if (!GetOwnPropertyDescriptor(cx, target, targetKeys[i], &desc)) {
891
return false;
892
}
893
894
// Steps 16.b-c.
895
if (desc.object() && !desc.configurable()) {
896
if (!targetNonconfigurableKeys.append(targetKeys[i])) {
897
return false;
898
}
899
} else {
900
if (!targetConfigurableKeys.append(targetKeys[i])) {
901
return false;
902
}
903
}
904
}
905
906
// Step 17.
907
if (extensibleTarget && targetNonconfigurableKeys.empty()) {
908
return props.appendAll(trapResult);
909
}
910
911
// Step 19.
912
for (size_t i = 0; i < targetNonconfigurableKeys.length(); ++i) {
913
MOZ_ASSERT(!JSID_IS_VOID(targetNonconfigurableKeys[i]));
914
915
auto ptr = uncheckedResultKeys.lookup(targetNonconfigurableKeys[i]);
916
917
// Step 19.a.
918
if (!ptr) {
919
return js::Throw(cx, targetNonconfigurableKeys[i], JSMSG_CANT_SKIP_NC);
920
}
921
922
// Step 19.b.
923
uncheckedResultKeys.remove(ptr);
924
}
925
926
// Step 20.
927
if (extensibleTarget) {
928
return props.appendAll(trapResult);
929
}
930
931
// Step 21.
932
for (size_t i = 0; i < targetConfigurableKeys.length(); ++i) {
933
MOZ_ASSERT(!JSID_IS_VOID(targetConfigurableKeys[i]));
934
935
auto ptr = uncheckedResultKeys.lookup(targetConfigurableKeys[i]);
936
937
// Step 21.a.
938
if (!ptr) {
939
return js::Throw(cx, targetConfigurableKeys[i],
940
JSMSG_CANT_REPORT_E_AS_NE);
941
}
942
943
// Step 21.b.
944
uncheckedResultKeys.remove(ptr);
945
}
946
947
// Step 22.
948
if (!uncheckedResultKeys.empty()) {
949
RootedId id(cx, uncheckedResultKeys.all().front());
950
return js::Throw(cx, id, JSMSG_CANT_REPORT_NEW);
951
}
952
953
// Step 23.
954
return props.appendAll(trapResult);
955
}
956
957
// ES8 rev 0c1bd3004329336774cbc90de727cd0cf5f11e93
958
// 9.5.10 Proxy.[[Delete]](P)
959
bool ScriptedProxyHandler::delete_(JSContext* cx, HandleObject proxy,
960
HandleId id, ObjectOpResult& result) const {
961
// Steps 2-4.
962
RootedObject handler(cx, ScriptedProxyHandler::handlerObject(proxy));
963
if (!handler) {
964
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
965
JSMSG_PROXY_REVOKED);
966
return false;
967
}
968
969
// Step 5.
970
RootedObject target(cx, proxy->as<ProxyObject>().target());
971
MOZ_ASSERT(target);
972
973
// Step 6.
974
RootedValue trap(cx);
975
if (!GetProxyTrap(cx, handler, cx->names().deleteProperty, &trap)) {
976
return false;
977
}
978
979
// Step 7.
980
if (trap.isUndefined()) {
981
return DeleteProperty(cx, target, id, result);
982
}
983
984
// Step 8.
985
bool booleanTrapResult;
986
{
987
RootedValue value(cx);
988
if (!IdToStringOrSymbol(cx, id, &value)) {
989
return false;
990
}
991
992
RootedValue targetVal(cx, ObjectValue(*target));
993
RootedValue trapResult(cx);
994
if (!Call(cx, trap, handler, targetVal, value, &trapResult)) {
995
return false;
996
}
997
998
booleanTrapResult = ToBoolean(trapResult);
999
}
1000
1001
// Step 9.
1002
if (!booleanTrapResult) {
1003
return result.fail(JSMSG_PROXY_DELETE_RETURNED_FALSE);
1004
}
1005
1006
// Step 10.
1007
Rooted<PropertyDescriptor> desc(cx);
1008
if (!GetOwnPropertyDescriptor(cx, target, id, &desc)) {
1009
return false;
1010
}
1011
1012
// Step 11.
1013
if (!desc.object()) {
1014
return result.succeed();
1015
}
1016
1017
// Step 12.
1018
if (!desc.configurable()) {
1019
return Throw(cx, id, JSMSG_CANT_DELETE);
1020
}
1021
1022
bool extensible;
1023
if (!IsExtensible(cx, target, &extensible)) {
1024
return false;
1025
}
1026
1027
if (!extensible) {
1028
return Throw(cx, id, JSMSG_CANT_DELETE_NON_EXTENSIBLE);
1029
}
1030
1031
// Step 13.
1032
return result.succeed();
1033
}
1034
1035
// ES8 rev 0c1bd3004329336774cbc90de727cd0cf5f11e93
1036
// 9.5.7 Proxy.[[HasProperty]](P)
1037
bool ScriptedProxyHandler::has(JSContext* cx, HandleObject proxy, HandleId id,
1038
bool* bp) const {
1039
// Steps 2-4.
1040
RootedObject handler(cx, ScriptedProxyHandler::handlerObject(proxy));
1041
if (!handler) {
1042
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
1043
JSMSG_PROXY_REVOKED);
1044
return false;
1045
}
1046
1047
// Step 5.
1048
RootedObject target(cx, proxy->as<ProxyObject>().target());
1049
MOZ_ASSERT(target);
1050
1051
// Step 6.
1052
RootedValue trap(cx);
1053
if (!GetProxyTrap(cx, handler, cx->names().has, &trap)) {
1054
return false;
1055
}
1056
1057
// Step 7.
1058
if (trap.isUndefined()) {
1059
return HasProperty(cx, target, id, bp);
1060
}
1061
1062
// Step 8.
1063
RootedValue value(cx);
1064
if (!IdToStringOrSymbol(cx, id, &value)) {
1065
return false;
1066
}
1067
1068
RootedValue trapResult(cx);
1069
RootedValue targetVal(cx, ObjectValue(*target));
1070
if (!Call(cx, trap, handler, targetVal, value, &trapResult)) {
1071
return false;
1072
}
1073
1074
bool booleanTrapResult = ToBoolean(trapResult);
1075
1076
// Step 9.
1077
if (!booleanTrapResult) {
1078
// Step 9a.
1079
Rooted<PropertyDescriptor> desc(cx);
1080
if (!GetOwnPropertyDescriptor(cx, target, id, &desc)) {
1081
return false;
1082
}
1083
1084
// Step 9b.
1085
if (desc.object()) {
1086
// Step 9b(i).
1087
if (!desc.configurable()) {
1088
return js::Throw(cx, id, JSMSG_CANT_REPORT_NC_AS_NE);
1089
}
1090
1091
// Step 9b(ii).
1092
bool extensible;
1093
if (!IsExtensible(cx, target, &extensible)) {
1094
return false;
1095
}
1096
1097
// Step 9b(iii).
1098
if (!extensible) {
1099
return js::Throw(cx, id, JSMSG_CANT_REPORT_E_AS_NE);
1100
}
1101
}
1102
}
1103
1104
// Step 10.
1105
*bp = booleanTrapResult;
1106
return true;
1107
}
1108
1109
// ES8 rev 0c1bd3004329336774cbc90de727cd0cf5f11e93
1110
// 9.5.8 Proxy.[[GetP]](P, Receiver)
1111
bool ScriptedProxyHandler::get(JSContext* cx, HandleObject proxy,
1112
HandleValue receiver, HandleId id,
1113
MutableHandleValue vp) const {
1114
// Steps 2-4.
1115
RootedObject handler(cx, ScriptedProxyHandler::handlerObject(proxy));
1116
if (!handler) {
1117
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
1118
JSMSG_PROXY_REVOKED);
1119
return false;
1120
}
1121
1122
// Step 5.
1123
RootedObject target(cx, proxy->as<ProxyObject>().target());
1124
MOZ_ASSERT(target);
1125
1126
// Steps 6.
1127
RootedValue trap(cx);
1128
if (!GetProxyTrap(cx, handler, cx->names().get, &trap)) {
1129
return false;
1130
}
1131
1132
// Step 7.
1133
if (trap.isUndefined()) {
1134
return GetProperty(cx, target, receiver, id, vp);
1135
}
1136
1137
// Step 8.
1138
RootedValue value(cx);
1139
if (!IdToStringOrSymbol(cx, id, &value)) {
1140
return false;
1141
}
1142
1143
RootedValue trapResult(cx);
1144
{
1145
FixedInvokeArgs<3> args(cx);
1146
1147
args[0].setObject(*target);
1148
args[1].set(value);
1149
args[2].set(receiver);
1150
1151
RootedValue thisv(cx, ObjectValue(*handler));
1152
if (!Call(cx, trap, thisv, args, &trapResult)) {
1153
return false;
1154
}
1155
}
1156
1157
// Step 9.
1158
Rooted<PropertyDescriptor> desc(cx);
1159
if (!GetOwnPropertyDescriptor(cx, target, id, &desc)) {
1160
return false;
1161
}
1162
1163
// Step 10.
1164
if (desc.object()) {
1165
// Step 10a.
1166
if (desc.isDataDescriptor() && !desc.configurable() && !desc.writable()) {
1167
bool same;
1168
if (!SameValue(cx, trapResult, desc.value(), &same)) {
1169
return false;
1170
}
1171
if (!same) {
1172
return js::Throw(cx, id, JSMSG_MUST_REPORT_SAME_VALUE);
1173
}
1174
}
1175
1176
// Step 10b.
1177
if (desc.isAccessorDescriptor() && !desc.configurable() &&
1178
(desc.getterObject() == nullptr) && !trapResult.isUndefined()) {
1179
return js::Throw(cx, id, JSMSG_MUST_REPORT_UNDEFINED);
1180
}
1181
}
1182
1183
// Step 11.
1184
vp.set(trapResult);
1185
return true;
1186
}
1187
1188
// ES8 rev 0c1bd3004329336774cbc90de727cd0cf5f11e93
1189
// 9.5.9 Proxy.[[Set]](P, V, Receiver)
1190
bool ScriptedProxyHandler::set(JSContext* cx, HandleObject proxy, HandleId id,
1191
HandleValue v, HandleValue receiver,
1192
ObjectOpResult& result) const {
1193
// Steps 2-4.
1194
RootedObject handler(cx, ScriptedProxyHandler::handlerObject(proxy));
1195
if (!handler) {
1196
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
1197
JSMSG_PROXY_REVOKED);
1198
return false;
1199
}
1200
1201
// Step 5.
1202
RootedObject target(cx, proxy->as<ProxyObject>().target());
1203
MOZ_ASSERT(target);
1204
1205
// Step 6.
1206
RootedValue trap(cx);
1207
if (!GetProxyTrap(cx, handler, cx->names().set, &trap)) {
1208
return false;
1209
}
1210
1211
// Step 7.
1212
if (trap.isUndefined()) {
1213
return SetProperty(cx, target, id, v, receiver, result);
1214
}
1215
1216
// Step 8.
1217
RootedValue value(cx);
1218
if (!IdToStringOrSymbol(cx, id, &value)) {
1219
return false;
1220
}
1221
1222
RootedValue trapResult(cx);
1223
{
1224
FixedInvokeArgs<4> args(cx);
1225
1226
args[0].setObject(*target);
1227
args[1].set(value);
1228
args[2].set(v);
1229
args[3].set(receiver);
1230
1231
RootedValue thisv(cx, ObjectValue(*handler));
1232
if (!Call(cx, trap, thisv, args, &trapResult)) {
1233
return false;
1234
}
1235
}
1236
1237
// Step 9.
1238
if (!ToBoolean(trapResult)) {
1239
return result.fail(JSMSG_PROXY_SET_RETURNED_FALSE);
1240
}
1241
1242
// Step 10.
1243
Rooted<PropertyDescriptor> desc(cx);
1244
if (!GetOwnPropertyDescriptor(cx, target, id, &desc)) {
1245
return false;
1246
}
1247
1248
// Step 11.
1249
if (desc.object()) {
1250
// Step 11a.
1251
if (desc.isDataDescriptor() && !desc.configurable() && !desc.writable()) {
1252
bool same;
1253
if (!SameValue(cx, v, desc.value(), &same)) {
1254
return false;
1255
}
1256
if (!same) {
1257
return js::Throw(cx, id, JSMSG_CANT_SET_NW_NC);
1258
}
1259
}
1260
1261
// Step 11b.
1262
if (desc.isAccessorDescriptor() && !desc.configurable() &&
1263
desc.setterObject() == nullptr) {
1264
return js::Throw(cx, id, JSMSG_CANT_SET_WO_SETTER);
1265
}
1266
}
1267
1268
// Step 12.
1269
return result.succeed();
1270
}
1271
1272
// ES7 0c1bd3004329336774cbc90de727cd0cf5f11e93 9.5.13 Proxy.[[Call]]
1273
bool ScriptedProxyHandler::call(JSContext* cx, HandleObject proxy,
1274
const CallArgs& args) const {
1275
// Steps 1-3.
1276
RootedObject handler(cx, ScriptedProxyHandler::handlerObject(proxy));
1277
if (!handler) {
1278
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
1279
JSMSG_PROXY_REVOKED);
1280
return false;
1281
}
1282
1283
// Step 4.
1284
RootedObject target(cx, proxy->as<ProxyObject>().target());
1285
MOZ_ASSERT(target);
1286
MOZ_ASSERT(target->isCallable());
1287
1288
// Step 5.
1289
RootedValue trap(cx);
1290
if (!GetProxyTrap(cx, handler, cx->names().apply, &trap)) {
1291
return false;
1292
}
1293
1294
// Step 6.
1295
if (trap.isUndefined()) {
1296
InvokeArgs iargs(cx);
1297
if (!FillArgumentsFromArraylike(cx, iargs, args)) {
1298
return false;
1299
}
1300
1301
RootedValue fval(cx, ObjectValue(*target));
1302
return js::Call(cx, fval, args.thisv(), iargs, args.rval());
1303
}
1304
1305
// Step 7.
1306
RootedObject argArray(cx,
1307
NewDenseCopiedArray(cx, args.length(), args.array()));
1308
if (!argArray) {
1309
return false;
1310
}
1311
1312
// Step 8.
1313
FixedInvokeArgs<3> iargs(cx);
1314
1315
iargs[0].setObject(*target);
1316
iargs[1].set(args.thisv());
1317
iargs[2].setObject(*argArray);
1318
1319
RootedValue thisv(cx, ObjectValue(*handler));
1320
return js::Call(cx, trap, thisv, iargs, args.rval());
1321
}
1322
1323
// ES7 0c1bd3004329336774cbc90de727cd0cf5f11e93 9.5.14 Proxy.[[Construct]]
1324
bool ScriptedProxyHandler::construct(JSContext* cx, HandleObject proxy,
1325
const CallArgs& args) const {
1326
// Steps 1-3.
1327
RootedObject handler(cx, ScriptedProxyHandler::handlerObject(proxy));
1328
if (!handler) {
1329
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
1330
JSMSG_PROXY_REVOKED);
1331
return false;
1332
}
1333
1334
// Step 4.
1335
RootedObject target(cx, proxy->as<ProxyObject>().target());
1336
MOZ_ASSERT(target);
1337
MOZ_ASSERT(target->isConstructor());
1338
1339
// Step 5.
1340
RootedValue trap(cx);
1341
if (!GetProxyTrap(cx, handler, cx->names().construct, &trap)) {
1342
return false;
1343
}
1344
1345
// Step 6.
1346
if (trap.isUndefined()) {
1347
ConstructArgs cargs(cx);
1348
if (!FillArgumentsFromArraylike(cx, cargs, args)) {
1349
return false;
1350
}
1351
1352
RootedValue targetv(cx, ObjectValue(*target));
1353
RootedObject obj(cx);
1354
if (!Construct(cx, targetv, cargs, args.newTarget(), &obj)) {
1355
return false;
1356
}
1357
1358
args.rval().setObject(*obj);
1359
return true;
1360
}
1361
1362
// Step 7.
1363
RootedObject argArray(cx,
1364
NewDenseCopiedArray(cx, args.length(), args.array()));
1365
if (!argArray) {
1366
return false;
1367
}
1368
1369
// Steps 8, 10.
1370
{
1371
FixedInvokeArgs<3> iargs(cx);
1372
1373
iargs[0].setObject(*target);
1374
iargs[1].setObject(*argArray);
1375
iargs[2].set(args.newTarget());
1376
1377
RootedValue thisv(cx, ObjectValue(*handler));
1378
if (!Call(cx, trap, thisv, iargs, args.rval())) {
1379
return false;
1380
}
1381
}
1382
1383
// Step 9.
1384
if (!args.rval().isObject()) {
1385
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
1386
JSMSG_PROXY_CONSTRUCT_OBJECT);
1387
return false;
1388
}
1389
1390
return true;
1391
}
1392
1393
bool ScriptedProxyHandler::nativeCall(JSContext* cx, IsAcceptableThis test,
1394
NativeImpl impl,
1395
const CallArgs& args) const {
1396
ReportIncompatible(cx, args);
1397
return false;
1398
}
1399
1400
bool ScriptedProxyHandler::hasInstance(JSContext* cx, HandleObject proxy,
1401
MutableHandleValue v, bool* bp) const {
1402
return InstanceofOperator(cx, proxy, v, bp);
1403
}
1404
1405
bool ScriptedProxyHandler::getBuiltinClass(JSContext* cx, HandleObject proxy,
1406
ESClass* cls) const {
1407
*cls = ESClass::Other;
1408
return true;
1409
}
1410
1411
bool ScriptedProxyHandler::isArray(JSContext* cx, HandleObject proxy,
1412
IsArrayAnswer* answer) const {
1413
RootedObject target(cx, proxy->as<ProxyObject>().target());
1414
if (target) {
1415
return JS::IsArray(cx, target, answer);
1416
}
1417
1418
*answer = IsArrayAnswer::RevokedProxy;
1419
return true;
1420
}
1421
1422
const char* ScriptedProxyHandler::className(JSContext* cx,
1423
HandleObject proxy) const {
1424
// Right now the caller is not prepared to handle failures.
1425
return BaseProxyHandler::className(cx, proxy);
1426
}
1427
1428
JSString* ScriptedProxyHandler::fun_toString(JSContext* cx, HandleObject proxy,
1429
bool isToSource) const {
1430
// The BaseProxyHandler has the desired behavior: Throw for non-callable,
1431
// otherwise return [native code].
1432
return BaseProxyHandler::fun_toString(cx, proxy, isToSource);
1433
}
1434
1435
RegExpShared* ScriptedProxyHandler::regexp_toShared(JSContext* cx,
1436
HandleObject proxy) const {
1437
MOZ_CRASH("Should not end up in ScriptedProxyHandler::regexp_toShared");
1438
}
1439
1440
bool ScriptedProxyHandler::boxedValue_unbox(JSContext* cx, HandleObject proxy,
1441
MutableHandleValue vp) const {
1442
MOZ_CRASH("Should not end up in ScriptedProxyHandler::boxedValue_unbox");
1443
return false;
1444
}
1445
1446
bool ScriptedProxyHandler::isCallable(JSObject* obj) const {
1447
MOZ_ASSERT(obj->as<ProxyObject>().handler() ==
1448
&ScriptedProxyHandler::singleton);
1449
uint32_t callConstruct = obj->as<ProxyObject>()
1450
.reservedSlot(IS_CALLCONSTRUCT_EXTRA)
1451
.toPrivateUint32();
1452
return !!(callConstruct & IS_CALLABLE);
1453
}
1454
1455
bool ScriptedProxyHandler::isConstructor(JSObject* obj) const {
1456
MOZ_ASSERT(obj->as<ProxyObject>().handler() ==
1457
&ScriptedProxyHandler::singleton);
1458
uint32_t callConstruct = obj->as<ProxyObject>()
1459
.reservedSlot(IS_CALLCONSTRUCT_EXTRA)
1460
.toPrivateUint32();
1461
return !!(callConstruct & IS_CONSTRUCTOR);
1462
}
1463
1464
const char ScriptedProxyHandler::family = 0;
1465
const ScriptedProxyHandler ScriptedProxyHandler::singleton;
1466
1467
bool IsRevokedScriptedProxy(JSObject* obj) {
1468
obj = CheckedUnwrapStatic(obj);
1469
return obj && IsScriptedProxy(obj) && !obj->as<ProxyObject>().target();
1470
}
1471
1472
// ES8 rev 0c1bd3004329336774cbc90de727cd0cf5f11e93
1473
// 9.5.14 ProxyCreate.
1474
static bool ProxyCreate(JSContext* cx, CallArgs& args, const char* callerName) {
1475
if (!args.requireAtLeast(cx, callerName, 2)) {
1476
return false;
1477
}
1478
1479
// Step 1.
1480
RootedObject target(cx,
1481
RequireObjectArg(cx, "`target`", callerName, args[0]));
1482
if (!target) {
1483
return false;
1484
}
1485
1486
// Step 2.
1487
if (IsRevokedScriptedProxy(target)) {
1488
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
1489
JSMSG_PROXY_ARG_REVOKED, "1");
1490
return false;
1491
}
1492
1493
// Step 3.
1494
RootedObject handler(cx,
1495
RequireObjectArg(cx, "`handler`", callerName, args[1]));
1496
if (!handler) {
1497
return false;
1498
}
1499
1500
// Step 4.
1501
if (IsRevokedScriptedProxy(handler)) {
1502
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
1503
JSMSG_PROXY_ARG_REVOKED, "2");
1504
return false;
1505
}
1506
1507
// Steps 5-6, 8.
1508
RootedValue priv(cx, ObjectValue(*target));
1509
JSObject* proxy_ = NewProxyObject(cx, &ScriptedProxyHandler::singleton, priv,
1510
TaggedProto::LazyProto);
1511
if (!proxy_) {
1512
return false;
1513
}
1514
1515
// Step 9 (reordered).
1516
Rooted<ProxyObject*> proxy(cx, &proxy_->as<ProxyObject>());
1517
proxy->setReservedSlot(ScriptedProxyHandler::HANDLER_EXTRA,
1518
ObjectValue(*handler));
1519
1520
// Step 7.
1521
uint32_t callable =
1522
target->isCallable() ? ScriptedProxyHandler::IS_CALLABLE : 0;
1523
uint32_t constructor =
1524
target->isConstructor() ? ScriptedProxyHandler::IS_CONSTRUCTOR : 0;
1525
proxy->setReservedSlot(ScriptedProxyHandler::IS_CALLCONSTRUCT_EXTRA,
1526
PrivateUint32Value(callable | constructor));
1527
1528
// Step 10.
1529
args.rval().setObject(*proxy);
1530
return true;
1531
}
1532
1533
bool js::proxy(JSContext* cx, unsigned argc, Value* vp) {
1534
CallArgs args = CallArgsFromVp(argc, vp);
1535
1536
if (!ThrowIfNotConstructing(cx, args, "Proxy")) {
1537
return false;
1538
}
1539
1540
return ProxyCreate(cx, args, "Proxy");
1541
}
1542
1543
static bool RevokeProxy(JSContext* cx, unsigned argc, Value* vp) {
1544
CallArgs args = CallArgsFromVp(argc, vp);
1545
1546
RootedFunction func(cx, &args.callee().as<JSFunction>());
1547
RootedObject p(cx, func->getExtendedSlot(ScriptedProxyHandler::REVOKE_SLOT)
1548
.toObjectOrNull());
1549
1550
if (p) {
1551
func->setExtendedSlot(ScriptedProxyHandler::REVOKE_SLOT, NullValue());
1552
1553
MOZ_ASSERT(p->is<ProxyObject>());
1554
1555
p->as<ProxyObject>().setSameCompartmentPrivate(NullValue());
1556
p->as<ProxyObject>().setReservedSlot(ScriptedProxyHandler::HANDLER_EXTRA,
1557
NullValue());
1558
}
1559
1560
args.rval().setUndefined();
1561
return true;
1562
}
1563
1564
bool js::proxy_revocable(JSContext* cx, unsigned argc, Value* vp) {
1565
CallArgs args = CallArgsFromVp(argc, vp);
1566
1567
if (!ProxyCreate(cx, args, "Proxy.revocable")) {
1568
return false;
1569
}
1570
1571
RootedValue proxyVal(cx, args.rval());
1572
MOZ_ASSERT(proxyVal.toObject().is<ProxyObject>());
1573
1574
RootedFunction revoker(
1575
cx, NewNativeFunction(cx, RevokeProxy, 0, nullptr,
1576
gc::AllocKind::FUNCTION_EXTENDED, GenericObject));
1577
if (!revoker) {
1578
return false;
1579
}
1580
1581
revoker->initExtendedSlot(ScriptedProxyHandler::REVOKE_SLOT, proxyVal);
1582
1583
RootedPlainObject result(cx, NewBuiltinClassInstance<PlainObject>(cx));
1584
if (!result) {
1585
return false;
1586
}
1587
1588
RootedValue revokeVal(cx, ObjectValue(*revoker));
1589
if (!DefineDataProperty(cx, result, cx->names().proxy, proxyVal) ||
1590
!DefineDataProperty(cx, result, cx->names().revoke, revokeVal)) {
1591
return false;
1592
}
1593
1594
args.rval().setObject(*result);
1595
return true;
1596
}