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 "js/Proxy.h"
8
9
#include "mozilla/Attributes.h"
10
11
#include <string.h>
12
13
#include "jsapi.h"
14
15
#include "js/PropertySpec.h"
16
#include "js/StableStringChars.h"
17
#include "js/Wrapper.h"
18
#include "proxy/DeadObjectProxy.h"
19
#include "proxy/ScriptedProxyHandler.h"
20
#include "vm/JSContext.h"
21
#include "vm/JSFunction.h"
22
#include "vm/JSObject.h"
23
#include "vm/WrapperObject.h"
24
25
#include "gc/Marking-inl.h"
26
#include "vm/JSAtom-inl.h"
27
#include "vm/JSObject-inl.h"
28
#include "vm/NativeObject-inl.h"
29
30
using namespace js;
31
32
void js::AutoEnterPolicy::reportErrorIfExceptionIsNotPending(JSContext* cx,
33
HandleId id) {
34
if (JS_IsExceptionPending(cx)) {
35
return;
36
}
37
38
if (JSID_IS_VOID(id)) {
39
ReportAccessDenied(cx);
40
} else {
41
Throw(cx, id, JSMSG_PROPERTY_ACCESS_DENIED);
42
}
43
}
44
45
#ifdef DEBUG
46
void js::AutoEnterPolicy::recordEnter(JSContext* cx, HandleObject proxy,
47
HandleId id, Action act) {
48
if (allowed()) {
49
context = cx;
50
enteredProxy.emplace(proxy);
51
enteredId.emplace(id);
52
enteredAction = act;
53
prev = cx->enteredPolicy;
54
cx->enteredPolicy = this;
55
}
56
}
57
58
void js::AutoEnterPolicy::recordLeave() {
59
if (enteredProxy) {
60
MOZ_ASSERT(context->enteredPolicy == this);
61
context->enteredPolicy = prev;
62
}
63
}
64
65
JS_FRIEND_API void js::assertEnteredPolicy(JSContext* cx, JSObject* proxy,
66
jsid id,
67
BaseProxyHandler::Action act) {
68
MOZ_ASSERT(proxy->is<ProxyObject>());
69
MOZ_ASSERT(cx->enteredPolicy);
70
MOZ_ASSERT(cx->enteredPolicy->enteredProxy->get() == proxy);
71
MOZ_ASSERT(cx->enteredPolicy->enteredId->get() == id);
72
MOZ_ASSERT(cx->enteredPolicy->enteredAction & act);
73
}
74
#endif
75
76
bool Proxy::getOwnPropertyDescriptor(JSContext* cx, HandleObject proxy,
77
HandleId id,
78
MutableHandle<PropertyDescriptor> desc) {
79
if (!CheckRecursionLimit(cx)) {
80
return false;
81
}
82
const BaseProxyHandler* handler = proxy->as<ProxyObject>().handler();
83
desc.object().set(
84
nullptr); // default result if we refuse to perform this action
85
AutoEnterPolicy policy(cx, handler, proxy, id,
86
BaseProxyHandler::GET_PROPERTY_DESCRIPTOR, true);
87
if (!policy.allowed()) {
88
return policy.returnValue();
89
}
90
return handler->getOwnPropertyDescriptor(cx, proxy, id, desc);
91
}
92
93
bool Proxy::defineProperty(JSContext* cx, HandleObject proxy, HandleId id,
94
Handle<PropertyDescriptor> desc,
95
ObjectOpResult& result) {
96
if (!CheckRecursionLimit(cx)) {
97
return false;
98
}
99
const BaseProxyHandler* handler = proxy->as<ProxyObject>().handler();
100
AutoEnterPolicy policy(cx, handler, proxy, id, BaseProxyHandler::SET, true);
101
if (!policy.allowed()) {
102
if (!policy.returnValue()) {
103
return false;
104
}
105
return result.succeed();
106
}
107
return proxy->as<ProxyObject>().handler()->defineProperty(cx, proxy, id, desc,
108
result);
109
}
110
111
bool Proxy::ownPropertyKeys(JSContext* cx, HandleObject proxy,
112
MutableHandleIdVector props) {
113
if (!CheckRecursionLimit(cx)) {
114
return false;
115
}
116
const BaseProxyHandler* handler = proxy->as<ProxyObject>().handler();
117
AutoEnterPolicy policy(cx, handler, proxy, JSID_VOIDHANDLE,
118
BaseProxyHandler::ENUMERATE, true);
119
if (!policy.allowed()) {
120
return policy.returnValue();
121
}
122
return proxy->as<ProxyObject>().handler()->ownPropertyKeys(cx, proxy, props);
123
}
124
125
bool Proxy::delete_(JSContext* cx, HandleObject proxy, HandleId id,
126
ObjectOpResult& result) {
127
if (!CheckRecursionLimit(cx)) {
128
return false;
129
}
130
const BaseProxyHandler* handler = proxy->as<ProxyObject>().handler();
131
AutoEnterPolicy policy(cx, handler, proxy, id, BaseProxyHandler::SET, true);
132
if (!policy.allowed()) {
133
bool ok = policy.returnValue();
134
if (ok) {
135
result.succeed();
136
}
137
return ok;
138
}
139
return proxy->as<ProxyObject>().handler()->delete_(cx, proxy, id, result);
140
}
141
142
JS_FRIEND_API bool js::AppendUnique(JSContext* cx, MutableHandleIdVector base,
143
HandleIdVector others) {
144
RootedIdVector uniqueOthers(cx);
145
if (!uniqueOthers.reserve(others.length())) {
146
return false;
147
}
148
for (size_t i = 0; i < others.length(); ++i) {
149
bool unique = true;
150
for (size_t j = 0; j < base.length(); ++j) {
151
if (others[i].get() == base[j]) {
152
unique = false;
153
break;
154
}
155
}
156
if (unique) {
157
if (!uniqueOthers.append(others[i])) {
158
return false;
159
}
160
}
161
}
162
return base.appendAll(uniqueOthers);
163
}
164
165
/* static */
166
bool Proxy::getPrototype(JSContext* cx, HandleObject proxy,
167
MutableHandleObject proto) {
168
MOZ_ASSERT(proxy->hasDynamicPrototype());
169
if (!CheckRecursionLimit(cx)) {
170
return false;
171
}
172
return proxy->as<ProxyObject>().handler()->getPrototype(cx, proxy, proto);
173
}
174
175
/* static */
176
bool Proxy::setPrototype(JSContext* cx, HandleObject proxy, HandleObject proto,
177
ObjectOpResult& result) {
178
MOZ_ASSERT(proxy->hasDynamicPrototype());
179
if (!CheckRecursionLimit(cx)) {
180
return false;
181
}
182
return proxy->as<ProxyObject>().handler()->setPrototype(cx, proxy, proto,
183
result);
184
}
185
186
/* static */
187
bool Proxy::getPrototypeIfOrdinary(JSContext* cx, HandleObject proxy,
188
bool* isOrdinary,
189
MutableHandleObject proto) {
190
if (!CheckRecursionLimit(cx)) {
191
return false;
192
}
193
return proxy->as<ProxyObject>().handler()->getPrototypeIfOrdinary(
194
cx, proxy, isOrdinary, proto);
195
}
196
197
/* static */
198
bool Proxy::setImmutablePrototype(JSContext* cx, HandleObject proxy,
199
bool* succeeded) {
200
if (!CheckRecursionLimit(cx)) {
201
return false;
202
}
203
const BaseProxyHandler* handler = proxy->as<ProxyObject>().handler();
204
return handler->setImmutablePrototype(cx, proxy, succeeded);
205
}
206
207
/* static */
208
bool Proxy::preventExtensions(JSContext* cx, HandleObject proxy,
209
ObjectOpResult& result) {
210
if (!CheckRecursionLimit(cx)) {
211
return false;
212
}
213
const BaseProxyHandler* handler = proxy->as<ProxyObject>().handler();
214
return handler->preventExtensions(cx, proxy, result);
215
}
216
217
/* static */
218
bool Proxy::isExtensible(JSContext* cx, HandleObject proxy, bool* extensible) {
219
if (!CheckRecursionLimit(cx)) {
220
return false;
221
}
222
return proxy->as<ProxyObject>().handler()->isExtensible(cx, proxy,
223
extensible);
224
}
225
226
bool Proxy::has(JSContext* cx, HandleObject proxy, HandleId id, bool* bp) {
227
if (!CheckRecursionLimit(cx)) {
228
return false;
229
}
230
const BaseProxyHandler* handler = proxy->as<ProxyObject>().handler();
231
*bp = false; // default result if we refuse to perform this action
232
AutoEnterPolicy policy(cx, handler, proxy, id, BaseProxyHandler::GET, true);
233
if (!policy.allowed()) {
234
return policy.returnValue();
235
}
236
237
if (handler->hasPrototype()) {
238
if (!handler->hasOwn(cx, proxy, id, bp)) {
239
return false;
240
}
241
if (*bp) {
242
return true;
243
}
244
245
RootedObject proto(cx);
246
if (!GetPrototype(cx, proxy, &proto)) {
247
return false;
248
}
249
if (!proto) {
250
return true;
251
}
252
253
return HasProperty(cx, proto, id, bp);
254
}
255
256
return handler->has(cx, proxy, id, bp);
257
}
258
259
bool js::ProxyHas(JSContext* cx, HandleObject proxy, HandleValue idVal,
260
MutableHandleValue result) {
261
RootedId id(cx);
262
if (!ValueToId<CanGC>(cx, idVal, &id)) {
263
return false;
264
}
265
266
bool has;
267
if (!Proxy::has(cx, proxy, id, &has)) {
268
return false;
269
}
270
271
result.setBoolean(has);
272
return true;
273
}
274
275
bool Proxy::hasOwn(JSContext* cx, HandleObject proxy, HandleId id, bool* bp) {
276
if (!CheckRecursionLimit(cx)) {
277
return false;
278
}
279
const BaseProxyHandler* handler = proxy->as<ProxyObject>().handler();
280
*bp = false; // default result if we refuse to perform this action
281
AutoEnterPolicy policy(cx, handler, proxy, id, BaseProxyHandler::GET, true);
282
if (!policy.allowed()) {
283
return policy.returnValue();
284
}
285
return handler->hasOwn(cx, proxy, id, bp);
286
}
287
288
bool js::ProxyHasOwn(JSContext* cx, HandleObject proxy, HandleValue idVal,
289
MutableHandleValue result) {
290
RootedId id(cx);
291
if (!ValueToId<CanGC>(cx, idVal, &id)) {
292
return false;
293
}
294
295
bool hasOwn;
296
if (!Proxy::hasOwn(cx, proxy, id, &hasOwn)) {
297
return false;
298
}
299
300
result.setBoolean(hasOwn);
301
return true;
302
}
303
304
static MOZ_ALWAYS_INLINE Value ValueToWindowProxyIfWindow(const Value& v,
305
JSObject* proxy) {
306
if (v.isObject() && v != ObjectValue(*proxy)) {
307
return ObjectValue(*ToWindowProxyIfWindow(&v.toObject()));
308
}
309
return v;
310
}
311
312
MOZ_ALWAYS_INLINE bool Proxy::getInternal(JSContext* cx, HandleObject proxy,
313
HandleValue receiver, HandleId id,
314
MutableHandleValue vp) {
315
MOZ_ASSERT_IF(receiver.isObject(), !IsWindow(&receiver.toObject()));
316
317
if (!CheckRecursionLimit(cx)) {
318
return false;
319
}
320
const BaseProxyHandler* handler = proxy->as<ProxyObject>().handler();
321
vp.setUndefined(); // default result if we refuse to perform this action
322
AutoEnterPolicy policy(cx, handler, proxy, id, BaseProxyHandler::GET, true);
323
if (!policy.allowed()) {
324
return policy.returnValue();
325
}
326
327
if (handler->hasPrototype()) {
328
bool own;
329
if (!handler->hasOwn(cx, proxy, id, &own)) {
330
return false;
331
}
332
if (!own) {
333
RootedObject proto(cx);
334
if (!GetPrototype(cx, proxy, &proto)) {
335
return false;
336
}
337
if (!proto) {
338
return true;
339
}
340
return GetProperty(cx, proto, receiver, id, vp);
341
}
342
}
343
344
return handler->get(cx, proxy, receiver, id, vp);
345
}
346
347
bool Proxy::get(JSContext* cx, HandleObject proxy, HandleValue receiver_,
348
HandleId id, MutableHandleValue vp) {
349
// Use the WindowProxy as receiver if receiver_ is a Window. Proxy handlers
350
// shouldn't have to know about the Window/WindowProxy distinction.
351
RootedValue receiver(cx, ValueToWindowProxyIfWindow(receiver_, proxy));
352
return getInternal(cx, proxy, receiver, id, vp);
353
}
354
355
bool js::ProxyGetProperty(JSContext* cx, HandleObject proxy, HandleId id,
356
MutableHandleValue vp) {
357
RootedValue receiver(cx, ObjectValue(*proxy));
358
return Proxy::getInternal(cx, proxy, receiver, id, vp);
359
}
360
361
bool js::ProxyGetPropertyByValue(JSContext* cx, HandleObject proxy,
362
HandleValue idVal, MutableHandleValue vp) {
363
RootedId id(cx);
364
if (!ValueToId<CanGC>(cx, idVal, &id)) {
365
return false;
366
}
367
368
RootedValue receiver(cx, ObjectValue(*proxy));
369
return Proxy::getInternal(cx, proxy, receiver, id, vp);
370
}
371
372
MOZ_ALWAYS_INLINE bool Proxy::setInternal(JSContext* cx, HandleObject proxy,
373
HandleId id, HandleValue v,
374
HandleValue receiver,
375
ObjectOpResult& result) {
376
MOZ_ASSERT_IF(receiver.isObject(), !IsWindow(&receiver.toObject()));
377
378
if (!CheckRecursionLimit(cx)) {
379
return false;
380
}
381
const BaseProxyHandler* handler = proxy->as<ProxyObject>().handler();
382
AutoEnterPolicy policy(cx, handler, proxy, id, BaseProxyHandler::SET, true);
383
if (!policy.allowed()) {
384
if (!policy.returnValue()) {
385
return false;
386
}
387
return result.succeed();
388
}
389
390
// Special case. See the comment on BaseProxyHandler::mHasPrototype.
391
if (handler->hasPrototype()) {
392
return handler->BaseProxyHandler::set(cx, proxy, id, v, receiver, result);
393
}
394
395
return handler->set(cx, proxy, id, v, receiver, result);
396
}
397
398
bool Proxy::set(JSContext* cx, HandleObject proxy, HandleId id, HandleValue v,
399
HandleValue receiver_, ObjectOpResult& result) {
400
// Use the WindowProxy as receiver if receiver_ is a Window. Proxy handlers
401
// shouldn't have to know about the Window/WindowProxy distinction.
402
RootedValue receiver(cx, ValueToWindowProxyIfWindow(receiver_, proxy));
403
return setInternal(cx, proxy, id, v, receiver, result);
404
}
405
406
bool js::ProxySetProperty(JSContext* cx, HandleObject proxy, HandleId id,
407
HandleValue val, bool strict) {
408
ObjectOpResult result;
409
RootedValue receiver(cx, ObjectValue(*proxy));
410
if (!Proxy::setInternal(cx, proxy, id, val, receiver, result)) {
411
return false;
412
}
413
return result.checkStrictErrorOrWarning(cx, proxy, id, strict);
414
}
415
416
bool js::ProxySetPropertyByValue(JSContext* cx, HandleObject proxy,
417
HandleValue idVal, HandleValue val,
418
bool strict) {
419
RootedId id(cx);
420
if (!ValueToId<CanGC>(cx, idVal, &id)) {
421
return false;
422
}
423
424
ObjectOpResult result;
425
RootedValue receiver(cx, ObjectValue(*proxy));
426
if (!Proxy::setInternal(cx, proxy, id, val, receiver, result)) {
427
return false;
428
}
429
return result.checkStrictErrorOrWarning(cx, proxy, id, strict);
430
}
431
432
bool Proxy::getOwnEnumerablePropertyKeys(JSContext* cx, HandleObject proxy,
433
MutableHandleIdVector props) {
434
if (!CheckRecursionLimit(cx)) {
435
return false;
436
}
437
const BaseProxyHandler* handler = proxy->as<ProxyObject>().handler();
438
AutoEnterPolicy policy(cx, handler, proxy, JSID_VOIDHANDLE,
439
BaseProxyHandler::ENUMERATE, true);
440
if (!policy.allowed()) {
441
return policy.returnValue();
442
}
443
return handler->getOwnEnumerablePropertyKeys(cx, proxy, props);
444
}
445
446
bool Proxy::enumerate(JSContext* cx, HandleObject proxy,
447
MutableHandleIdVector props) {
448
if (!CheckRecursionLimit(cx)) {
449
return false;
450
}
451
452
const BaseProxyHandler* handler = proxy->as<ProxyObject>().handler();
453
if (handler->hasPrototype()) {
454
if (!Proxy::getOwnEnumerablePropertyKeys(cx, proxy, props)) {
455
return false;
456
}
457
458
RootedObject proto(cx);
459
if (!GetPrototype(cx, proxy, &proto)) {
460
return false;
461
}
462
if (!proto) {
463
return true;
464
}
465
466
cx->check(proxy, proto);
467
468
RootedIdVector protoProps(cx);
469
if (!GetPropertyKeys(cx, proto, 0, &protoProps)) {
470
return false;
471
}
472
return AppendUnique(cx, props, protoProps);
473
}
474
475
AutoEnterPolicy policy(cx, handler, proxy, JSID_VOIDHANDLE,
476
BaseProxyHandler::ENUMERATE, true);
477
478
// If the policy denies access but wants us to return true, we need
479
// to return an empty |props| list.
480
if (!policy.allowed()) {
481
MOZ_ASSERT(props.empty());
482
return policy.returnValue();
483
}
484
485
return handler->enumerate(cx, proxy, props);
486
}
487
488
bool Proxy::call(JSContext* cx, HandleObject proxy, const CallArgs& args) {
489
if (!CheckRecursionLimit(cx)) {
490
return false;
491
}
492
const BaseProxyHandler* handler = proxy->as<ProxyObject>().handler();
493
494
// Because vp[0] is JS_CALLEE on the way in and JS_RVAL on the way out, we
495
// can only set our default value once we're sure that we're not calling the
496
// trap.
497
AutoEnterPolicy policy(cx, handler, proxy, JSID_VOIDHANDLE,
498
BaseProxyHandler::CALL, true);
499
if (!policy.allowed()) {
500
args.rval().setUndefined();
501
return policy.returnValue();
502
}
503
504
return handler->call(cx, proxy, args);
505
}
506
507
bool Proxy::construct(JSContext* cx, HandleObject proxy, const CallArgs& args) {
508
if (!CheckRecursionLimit(cx)) {
509
return false;
510
}
511
const BaseProxyHandler* handler = proxy->as<ProxyObject>().handler();
512
513
// Because vp[0] is JS_CALLEE on the way in and JS_RVAL on the way out, we
514
// can only set our default value once we're sure that we're not calling the
515
// trap.
516
AutoEnterPolicy policy(cx, handler, proxy, JSID_VOIDHANDLE,
517
BaseProxyHandler::CALL, true);
518
if (!policy.allowed()) {
519
args.rval().setUndefined();
520
return policy.returnValue();
521
}
522
523
return handler->construct(cx, proxy, args);
524
}
525
526
bool Proxy::nativeCall(JSContext* cx, IsAcceptableThis test, NativeImpl impl,
527
const CallArgs& args) {
528
if (!CheckRecursionLimit(cx)) {
529
return false;
530
}
531
RootedObject proxy(cx, &args.thisv().toObject());
532
// Note - we don't enter a policy here because our security architecture
533
// guards against nativeCall by overriding the trap itself in the right
534
// circumstances.
535
return proxy->as<ProxyObject>().handler()->nativeCall(cx, test, impl, args);
536
}
537
538
bool Proxy::hasInstance(JSContext* cx, HandleObject proxy, MutableHandleValue v,
539
bool* bp) {
540
if (!CheckRecursionLimit(cx)) {
541
return false;
542
}
543
const BaseProxyHandler* handler = proxy->as<ProxyObject>().handler();
544
*bp = false; // default result if we refuse to perform this action
545
AutoEnterPolicy policy(cx, handler, proxy, JSID_VOIDHANDLE,
546
BaseProxyHandler::GET, true);
547
if (!policy.allowed()) {
548
return policy.returnValue();
549
}
550
return proxy->as<ProxyObject>().handler()->hasInstance(cx, proxy, v, bp);
551
}
552
553
bool Proxy::getBuiltinClass(JSContext* cx, HandleObject proxy, ESClass* cls) {
554
if (!CheckRecursionLimit(cx)) {
555
return false;
556
}
557
return proxy->as<ProxyObject>().handler()->getBuiltinClass(cx, proxy, cls);
558
}
559
560
bool Proxy::isArray(JSContext* cx, HandleObject proxy,
561
JS::IsArrayAnswer* answer) {
562
if (!CheckRecursionLimit(cx)) {
563
return false;
564
}
565
return proxy->as<ProxyObject>().handler()->isArray(cx, proxy, answer);
566
}
567
568
const char* Proxy::className(JSContext* cx, HandleObject proxy) {
569
// Check for unbounded recursion, but don't signal an error; className
570
// needs to be infallible.
571
int stackDummy;
572
if (!JS_CHECK_STACK_SIZE(GetNativeStackLimit(cx), &stackDummy)) {
573
return "too much recursion";
574
}
575
576
const BaseProxyHandler* handler = proxy->as<ProxyObject>().handler();
577
AutoEnterPolicy policy(cx, handler, proxy, JSID_VOIDHANDLE,
578
BaseProxyHandler::GET, /* mayThrow = */ false);
579
// Do the safe thing if the policy rejects.
580
if (!policy.allowed()) {
581
return handler->BaseProxyHandler::className(cx, proxy);
582
}
583
return handler->className(cx, proxy);
584
}
585
586
JSString* Proxy::fun_toString(JSContext* cx, HandleObject proxy,
587
bool isToSource) {
588
if (!CheckRecursionLimit(cx)) {
589
return nullptr;
590
}
591
const BaseProxyHandler* handler = proxy->as<ProxyObject>().handler();
592
AutoEnterPolicy policy(cx, handler, proxy, JSID_VOIDHANDLE,
593
BaseProxyHandler::GET, /* mayThrow = */ false);
594
// Do the safe thing if the policy rejects.
595
if (!policy.allowed()) {
596
return handler->BaseProxyHandler::fun_toString(cx, proxy, isToSource);
597
}
598
return handler->fun_toString(cx, proxy, isToSource);
599
}
600
601
RegExpShared* Proxy::regexp_toShared(JSContext* cx, HandleObject proxy) {
602
if (!CheckRecursionLimit(cx)) {
603
return nullptr;
604
}
605
return proxy->as<ProxyObject>().handler()->regexp_toShared(cx, proxy);
606
}
607
608
bool Proxy::boxedValue_unbox(JSContext* cx, HandleObject proxy,
609
MutableHandleValue vp) {
610
if (!CheckRecursionLimit(cx)) {
611
return false;
612
}
613
return proxy->as<ProxyObject>().handler()->boxedValue_unbox(cx, proxy, vp);
614
}
615
616
JSObject* const TaggedProto::LazyProto = reinterpret_cast<JSObject*>(0x1);
617
618
/* static */
619
bool Proxy::getElements(JSContext* cx, HandleObject proxy, uint32_t begin,
620
uint32_t end, ElementAdder* adder) {
621
if (!CheckRecursionLimit(cx)) {
622
return false;
623
}
624
const BaseProxyHandler* handler = proxy->as<ProxyObject>().handler();
625
AutoEnterPolicy policy(cx, handler, proxy, JSID_VOIDHANDLE,
626
BaseProxyHandler::GET,
627
/* mayThrow = */ true);
628
if (!policy.allowed()) {
629
if (policy.returnValue()) {
630
MOZ_ASSERT(!cx->isExceptionPending());
631
return js::GetElementsWithAdder(cx, proxy, proxy, begin, end, adder);
632
}
633
return false;
634
}
635
return handler->getElements(cx, proxy, begin, end, adder);
636
}
637
638
/* static */
639
void Proxy::trace(JSTracer* trc, JSObject* proxy) {
640
const BaseProxyHandler* handler = proxy->as<ProxyObject>().handler();
641
handler->trace(trc, proxy);
642
}
643
644
static bool proxy_LookupProperty(JSContext* cx, HandleObject obj, HandleId id,
645
MutableHandleObject objp,
646
MutableHandle<JS::PropertyResult> propp) {
647
bool found;
648
if (!Proxy::has(cx, obj, id, &found)) {
649
return false;
650
}
651
652
if (found) {
653
propp.setNonNativeProperty();
654
objp.set(obj);
655
} else {
656
propp.setNotFound();
657
objp.set(nullptr);
658
}
659
return true;
660
}
661
662
static bool proxy_DeleteProperty(JSContext* cx, HandleObject obj, HandleId id,
663
ObjectOpResult& result) {
664
if (!Proxy::delete_(cx, obj, id, result)) {
665
return false;
666
}
667
return SuppressDeletedProperty(cx, obj, id); // XXX is this necessary?
668
}
669
670
/* static */
671
void ProxyObject::traceEdgeToTarget(JSTracer* trc, ProxyObject* obj) {
672
TraceCrossCompartmentEdge(trc, obj, obj->slotOfPrivate(), "proxy target");
673
}
674
675
/* static */
676
void ProxyObject::trace(JSTracer* trc, JSObject* obj) {
677
ProxyObject* proxy = &obj->as<ProxyObject>();
678
679
TraceEdge(trc, proxy->shapePtr(), "ProxyObject_shape");
680
681
#ifdef DEBUG
682
if (TlsContext.get()->isStrictProxyCheckingEnabled() &&
683
proxy->is<WrapperObject>()) {
684
JSObject* referent = MaybeForwarded(proxy->target());
685
if (referent->compartment() != proxy->compartment()) {
686
/*
687
* Assert that this proxy is tracked in the wrapper map. We maintain
688
* the invariant that the wrapped object is the key in the wrapper map.
689
*/
690
ObjectWrapperMap::Ptr p = proxy->compartment()->lookupWrapper(referent);
691
MOZ_ASSERT(p);
692
MOZ_ASSERT(*p->value().unsafeGet() == proxy);
693
}
694
}
695
#endif
696
697
// Note: If you add new slots here, make sure to change
698
// nuke() to cope.
699
700
traceEdgeToTarget(trc, proxy);
701
702
size_t nreserved = proxy->numReservedSlots();
703
for (size_t i = 0; i < nreserved; i++) {
704
/*
705
* The GC can use the second reserved slot to link the cross compartment
706
* wrappers into a linked list, in which case we don't want to trace it.
707
*/
708
if (proxy->is<CrossCompartmentWrapperObject>() &&
709
i == CrossCompartmentWrapperObject::GrayLinkReservedSlot) {
710
continue;
711
}
712
TraceEdge(trc, proxy->reservedSlotPtr(i), "proxy_reserved");
713
}
714
715
Proxy::trace(trc, obj);
716
}
717
718
static void proxy_Finalize(JSFreeOp* fop, JSObject* obj) {
719
// Suppress a bogus warning about finalize().
720
JS::AutoSuppressGCAnalysis nogc;
721
722
MOZ_ASSERT(obj->is<ProxyObject>());
723
obj->as<ProxyObject>().handler()->finalize(fop, obj);
724
725
if (!obj->as<ProxyObject>().usingInlineValueArray()) {
726
// Bug 1560019: This allocation is not tracked, but is only present when
727
// objects are swapped which is assumed to be relatively rare.
728
fop->freeUntracked(js::detail::GetProxyDataLayout(obj)->values());
729
}
730
}
731
732
size_t js::proxy_ObjectMoved(JSObject* obj, JSObject* old) {
733
ProxyObject& proxy = obj->as<ProxyObject>();
734
735
if (IsInsideNursery(old)) {
736
// Objects in the nursery are never swapped so the proxy must have an
737
// inline ProxyValueArray.
738
MOZ_ASSERT(old->as<ProxyObject>().usingInlineValueArray());
739
proxy.setInlineValueArray();
740
}
741
742
return proxy.handler()->objectMoved(obj, old);
743
}
744
745
const JSClassOps js::ProxyClassOps = {
746
nullptr, /* addProperty */
747
nullptr, /* delProperty */
748
nullptr, /* enumerate */
749
nullptr, /* newEnumerate */
750
nullptr, /* resolve */
751
nullptr, /* mayResolve */
752
proxy_Finalize, /* finalize */
753
nullptr, /* call */
754
Proxy::hasInstance, /* hasInstance */
755
nullptr, /* construct */
756
ProxyObject::trace, /* trace */
757
};
758
759
const ClassExtension js::ProxyClassExtension = {proxy_ObjectMoved};
760
761
const ObjectOps js::ProxyObjectOps = {
762
proxy_LookupProperty, Proxy::defineProperty,
763
Proxy::has, Proxy::get,
764
Proxy::set, Proxy::getOwnPropertyDescriptor,
765
proxy_DeleteProperty, Proxy::getElements,
766
Proxy::fun_toString};
767
768
const JSClass js::ProxyClass =
769
PROXY_CLASS_DEF("Proxy", JSCLASS_HAS_CACHED_PROTO(JSProto_Proxy) |
770
JSCLASS_HAS_RESERVED_SLOTS(2));
771
772
JS_FRIEND_API JSObject* js::NewProxyObject(JSContext* cx,
773
const BaseProxyHandler* handler,
774
HandleValue priv, JSObject* proto_,
775
const ProxyOptions& options) {
776
AssertHeapIsIdle();
777
CHECK_THREAD(cx);
778
if (proto_ != TaggedProto::LazyProto) {
779
cx->check(proto_); // |priv| might be cross-compartment.
780
}
781
782
if (options.lazyProto()) {
783
MOZ_ASSERT(!proto_);
784
proto_ = TaggedProto::LazyProto;
785
}
786
787
return ProxyObject::New(cx, handler, priv, TaggedProto(proto_), options);
788
}
789
790
void ProxyObject::renew(const BaseProxyHandler* handler, const Value& priv) {
791
MOZ_ASSERT(!IsInsideNursery(this));
792
MOZ_ASSERT_IF(IsCrossCompartmentWrapper(this), IsDeadProxyObject(this));
793
MOZ_ASSERT(getClass() == &ProxyClass);
794
MOZ_ASSERT(!IsWindowProxy(this));
795
MOZ_ASSERT(hasDynamicPrototype());
796
797
setHandler(handler);
798
setCrossCompartmentPrivate(priv);
799
for (size_t i = 0; i < numReservedSlots(); i++) {
800
setReservedSlot(i, UndefinedValue());
801
}
802
}
803
804
JSObject* js::InitProxyClass(JSContext* cx, Handle<GlobalObject*> global) {
805
static const JSFunctionSpec static_methods[] = {
806
JS_FN("revocable", proxy_revocable, 2, 0), JS_FS_END};
807
808
RootedFunction ctor(cx);
809
ctor = GlobalObject::createConstructor(cx, proxy, cx->names().Proxy, 2);
810
if (!ctor) {
811
return nullptr;
812
}
813
814
if (!JS_DefineFunctions(cx, ctor, static_methods)) {
815
return nullptr;
816
}
817
if (!JS_DefineProperty(cx, global, "Proxy", ctor, JSPROP_RESOLVING)) {
818
return nullptr;
819
}
820
821
global->setConstructor(JSProto_Proxy, ObjectValue(*ctor));
822
return ctor;
823
}