Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/js/src/proxy/Wrapper.cpp
Line
Count
Source (jump to first uncovered line)
1
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2
 * vim: set ts=8 sts=4 et sw=4 tw=99:
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/Wrapper.h"
8
9
#include "jsexn.h"
10
11
#include "js/Proxy.h"
12
#include "vm/ErrorObject.h"
13
#include "vm/JSContext.h"
14
#include "vm/ProxyObject.h"
15
#include "vm/Realm.h"
16
#include "vm/RegExpObject.h"
17
#include "vm/WrapperObject.h"
18
19
#include "gc/Marking-inl.h"
20
#include "vm/JSObject-inl.h"
21
#include "vm/NativeObject-inl.h"
22
23
using namespace js;
24
25
bool
26
Wrapper::finalizeInBackground(const Value& priv) const
27
0
{
28
0
    if (!priv.isObject()) {
29
0
        return true;
30
0
    }
31
0
32
0
    /*
33
0
     * Make the 'background-finalized-ness' of the wrapper the same as the
34
0
     * wrapped object, to allow transplanting between them.
35
0
     */
36
0
    JSObject* wrapped = MaybeForwarded(&priv.toObject());
37
0
    gc::AllocKind wrappedKind;
38
0
    if (IsInsideNursery(wrapped)) {
39
0
        JSRuntime* rt = wrapped->runtimeFromMainThread();
40
0
        wrappedKind = wrapped->allocKindForTenure(rt->gc.nursery());
41
0
    } else {
42
0
        wrappedKind = wrapped->asTenured().getAllocKind();
43
0
    }
44
0
    return IsBackgroundFinalized(wrappedKind);
45
0
}
46
47
bool
48
ForwardingProxyHandler::getOwnPropertyDescriptor(JSContext* cx, HandleObject proxy, HandleId id,
49
                                                 MutableHandle<PropertyDescriptor> desc) const
50
0
{
51
0
    assertEnteredPolicy(cx, proxy, id, GET | SET | GET_PROPERTY_DESCRIPTOR);
52
0
    RootedObject target(cx, proxy->as<ProxyObject>().target());
53
0
    return GetOwnPropertyDescriptor(cx, target, id, desc);
54
0
}
55
56
bool
57
ForwardingProxyHandler::defineProperty(JSContext* cx, HandleObject proxy, HandleId id,
58
                                       Handle<PropertyDescriptor> desc,
59
                                       ObjectOpResult& result) const
60
0
{
61
0
    assertEnteredPolicy(cx, proxy, id, SET);
62
0
    RootedObject target(cx, proxy->as<ProxyObject>().target());
63
0
    return DefineProperty(cx, target, id, desc, result);
64
0
}
65
66
bool
67
ForwardingProxyHandler::ownPropertyKeys(JSContext* cx, HandleObject proxy,
68
                                        AutoIdVector& props) const
69
0
{
70
0
    assertEnteredPolicy(cx, proxy, JSID_VOID, ENUMERATE);
71
0
    RootedObject target(cx, proxy->as<ProxyObject>().target());
72
0
    return GetPropertyKeys(cx, target, JSITER_OWNONLY | JSITER_HIDDEN | JSITER_SYMBOLS, &props);
73
0
}
74
75
bool
76
ForwardingProxyHandler::delete_(JSContext* cx, HandleObject proxy, HandleId id,
77
                                ObjectOpResult& result) const
78
0
{
79
0
    assertEnteredPolicy(cx, proxy, id, SET);
80
0
    RootedObject target(cx, proxy->as<ProxyObject>().target());
81
0
    return DeleteProperty(cx, target, id, result);
82
0
}
83
84
JSObject*
85
ForwardingProxyHandler::enumerate(JSContext* cx, HandleObject proxy) const
86
0
{
87
0
    assertEnteredPolicy(cx, proxy, JSID_VOID, ENUMERATE);
88
0
    MOZ_ASSERT(!hasPrototype()); // Should never be called if there's a prototype.
89
0
    RootedObject target(cx, proxy->as<ProxyObject>().target());
90
0
    return GetIterator(cx, target);
91
0
}
92
93
bool
94
ForwardingProxyHandler::getPrototype(JSContext* cx, HandleObject proxy,
95
                                     MutableHandleObject protop) const
96
0
{
97
0
    RootedObject target(cx, proxy->as<ProxyObject>().target());
98
0
    return GetPrototype(cx, target, protop);
99
0
}
100
101
bool
102
ForwardingProxyHandler::setPrototype(JSContext* cx, HandleObject proxy, HandleObject proto,
103
                                     ObjectOpResult& result) const
104
0
{
105
0
    RootedObject target(cx, proxy->as<ProxyObject>().target());
106
0
    return SetPrototype(cx, target, proto, result);
107
0
}
108
109
bool
110
ForwardingProxyHandler::getPrototypeIfOrdinary(JSContext* cx, HandleObject proxy,
111
                                               bool* isOrdinary, MutableHandleObject protop) const
112
0
{
113
0
    RootedObject target(cx, proxy->as<ProxyObject>().target());
114
0
    return GetPrototypeIfOrdinary(cx, target, isOrdinary, protop);
115
0
}
116
117
bool
118
ForwardingProxyHandler::setImmutablePrototype(JSContext* cx, HandleObject proxy,
119
                                              bool* succeeded) const
120
0
{
121
0
    RootedObject target(cx, proxy->as<ProxyObject>().target());
122
0
    return SetImmutablePrototype(cx, target, succeeded);
123
0
}
124
125
bool
126
ForwardingProxyHandler::preventExtensions(JSContext* cx, HandleObject proxy,
127
                                          ObjectOpResult& result) const
128
0
{
129
0
    RootedObject target(cx, proxy->as<ProxyObject>().target());
130
0
    return PreventExtensions(cx, target, result);
131
0
}
132
133
bool
134
ForwardingProxyHandler::isExtensible(JSContext* cx, HandleObject proxy, bool* extensible) const
135
0
{
136
0
    RootedObject target(cx, proxy->as<ProxyObject>().target());
137
0
    return IsExtensible(cx, target, extensible);
138
0
}
139
140
bool
141
ForwardingProxyHandler::has(JSContext* cx, HandleObject proxy, HandleId id, bool* bp) const
142
0
{
143
0
    assertEnteredPolicy(cx, proxy, id, GET);
144
0
    MOZ_ASSERT(!hasPrototype()); // Should never be called if there's a prototype.
145
0
    RootedObject target(cx, proxy->as<ProxyObject>().target());
146
0
    return HasProperty(cx, target, id, bp);
147
0
}
148
149
bool
150
ForwardingProxyHandler::get(JSContext* cx, HandleObject proxy, HandleValue receiver, HandleId id,
151
                            MutableHandleValue vp) const
152
0
{
153
0
    assertEnteredPolicy(cx, proxy, id, GET);
154
0
    RootedObject target(cx, proxy->as<ProxyObject>().target());
155
0
    return GetProperty(cx, target, receiver, id, vp);
156
0
}
157
158
bool
159
ForwardingProxyHandler::set(JSContext* cx, HandleObject proxy, HandleId id, HandleValue v,
160
                            HandleValue receiver, ObjectOpResult& result) const
161
0
{
162
0
    assertEnteredPolicy(cx, proxy, id, SET);
163
0
    RootedObject target(cx, proxy->as<ProxyObject>().target());
164
0
    return SetProperty(cx, target, id, v, receiver, result);
165
0
}
166
167
bool
168
ForwardingProxyHandler::call(JSContext* cx, HandleObject proxy, const CallArgs& args) const
169
0
{
170
0
    assertEnteredPolicy(cx, proxy, JSID_VOID, CALL);
171
0
    RootedValue target(cx, proxy->as<ProxyObject>().private_());
172
0
173
0
    InvokeArgs iargs(cx);
174
0
    if (!FillArgumentsFromArraylike(cx, iargs, args)) {
175
0
        return false;
176
0
    }
177
0
178
0
    return js::Call(cx, target, args.thisv(), iargs, args.rval());
179
0
}
180
181
bool
182
ForwardingProxyHandler::construct(JSContext* cx, HandleObject proxy, const CallArgs& args) const
183
0
{
184
0
    assertEnteredPolicy(cx, proxy, JSID_VOID, CALL);
185
0
186
0
    RootedValue target(cx, proxy->as<ProxyObject>().private_());
187
0
    if (!IsConstructor(target)) {
188
0
        ReportValueError(cx, JSMSG_NOT_CONSTRUCTOR, JSDVG_IGNORE_STACK, target, nullptr);
189
0
        return false;
190
0
    }
191
0
192
0
    ConstructArgs cargs(cx);
193
0
    if (!FillArgumentsFromArraylike(cx, cargs, args)) {
194
0
        return false;
195
0
    }
196
0
197
0
    RootedObject obj(cx);
198
0
    if (!Construct(cx, target, cargs, args.newTarget(), &obj)) {
199
0
        return false;
200
0
    }
201
0
202
0
    args.rval().setObject(*obj);
203
0
    return true;
204
0
}
205
206
bool
207
ForwardingProxyHandler::getPropertyDescriptor(JSContext* cx, HandleObject proxy, HandleId id,
208
                                              MutableHandle<PropertyDescriptor> desc) const
209
0
{
210
0
    assertEnteredPolicy(cx, proxy, id, GET | SET | GET_PROPERTY_DESCRIPTOR);
211
0
    MOZ_ASSERT(!hasPrototype()); // Should never be called if there's a prototype.
212
0
    RootedObject target(cx, proxy->as<ProxyObject>().target());
213
0
    return GetPropertyDescriptor(cx, target, id, desc);
214
0
}
215
216
bool
217
ForwardingProxyHandler::hasOwn(JSContext* cx, HandleObject proxy, HandleId id, bool* bp) const
218
0
{
219
0
    assertEnteredPolicy(cx, proxy, id, GET);
220
0
    RootedObject target(cx, proxy->as<ProxyObject>().target());
221
0
    return HasOwnProperty(cx, target, id, bp);
222
0
}
223
224
bool
225
ForwardingProxyHandler::getOwnEnumerablePropertyKeys(JSContext* cx, HandleObject proxy,
226
                                                     AutoIdVector& props) const
227
0
{
228
0
    assertEnteredPolicy(cx, proxy, JSID_VOID, ENUMERATE);
229
0
    RootedObject target(cx, proxy->as<ProxyObject>().target());
230
0
    return GetPropertyKeys(cx, target, JSITER_OWNONLY, &props);
231
0
}
232
233
bool
234
ForwardingProxyHandler::nativeCall(JSContext* cx, IsAcceptableThis test, NativeImpl impl,
235
                                   const CallArgs& args) const
236
0
{
237
0
    args.setThis(ObjectValue(*args.thisv().toObject().as<ProxyObject>().target()));
238
0
    if (!test(args.thisv())) {
239
0
        ReportIncompatible(cx, args);
240
0
        return false;
241
0
    }
242
0
243
0
    return CallNativeImpl(cx, impl, args);
244
0
}
245
246
bool
247
ForwardingProxyHandler::hasInstance(JSContext* cx, HandleObject proxy, MutableHandleValue v,
248
                                    bool* bp) const
249
0
{
250
0
    assertEnteredPolicy(cx, proxy, JSID_VOID, GET);
251
0
    RootedObject target(cx, proxy->as<ProxyObject>().target());
252
0
    return HasInstance(cx, target, v, bp);
253
0
}
254
255
bool
256
ForwardingProxyHandler::getBuiltinClass(JSContext* cx, HandleObject proxy, ESClass* cls) const
257
0
{
258
0
    RootedObject target(cx, proxy->as<ProxyObject>().target());
259
0
    return GetBuiltinClass(cx, target, cls);
260
0
}
261
262
bool
263
ForwardingProxyHandler::isArray(JSContext* cx, HandleObject proxy, JS::IsArrayAnswer* answer) const
264
0
{
265
0
    RootedObject target(cx, proxy->as<ProxyObject>().target());
266
0
    return IsArray(cx, target, answer);
267
0
}
268
269
const char*
270
ForwardingProxyHandler::className(JSContext* cx, HandleObject proxy) const
271
0
{
272
0
    assertEnteredPolicy(cx, proxy, JSID_VOID, GET);
273
0
    RootedObject target(cx, proxy->as<ProxyObject>().target());
274
0
    return GetObjectClassName(cx, target);
275
0
}
276
277
JSString*
278
ForwardingProxyHandler::fun_toString(JSContext* cx, HandleObject proxy, bool isToSource) const
279
0
{
280
0
    assertEnteredPolicy(cx, proxy, JSID_VOID, GET);
281
0
    RootedObject target(cx, proxy->as<ProxyObject>().target());
282
0
    return fun_toStringHelper(cx, target, isToSource);
283
0
}
284
285
RegExpShared*
286
ForwardingProxyHandler::regexp_toShared(JSContext* cx, HandleObject proxy) const
287
0
{
288
0
    RootedObject target(cx, proxy->as<ProxyObject>().target());
289
0
    return RegExpToShared(cx, target);
290
0
}
291
292
bool
293
ForwardingProxyHandler::boxedValue_unbox(JSContext* cx, HandleObject proxy,
294
                                         MutableHandleValue vp) const
295
0
{
296
0
    RootedObject target(cx, proxy->as<ProxyObject>().target());
297
0
    return Unbox(cx, target, vp);
298
0
}
299
300
bool
301
ForwardingProxyHandler::isCallable(JSObject* obj) const
302
0
{
303
0
    JSObject* target = obj->as<ProxyObject>().target();
304
0
    return target->isCallable();
305
0
}
306
307
bool
308
ForwardingProxyHandler::isConstructor(JSObject* obj) const
309
0
{
310
0
    JSObject* target = obj->as<ProxyObject>().target();
311
0
    return target->isConstructor();
312
0
}
313
314
JSObject*
315
Wrapper::weakmapKeyDelegate(JSObject* proxy) const
316
0
{
317
0
    // This may be called during GC.
318
0
    return UncheckedUnwrapWithoutExpose(proxy);
319
0
}
320
321
JSObject*
322
Wrapper::New(JSContext* cx, JSObject* obj, const Wrapper* handler,
323
             const WrapperOptions& options)
324
0
{
325
0
    RootedValue priv(cx, ObjectValue(*obj));
326
0
    return NewProxyObject(cx, handler, priv, options.proto(), options);
327
0
}
328
329
JSObject*
330
Wrapper::Renew(JSObject* existing, JSObject* obj, const Wrapper* handler)
331
0
{
332
0
    existing->as<ProxyObject>().renew(handler, ObjectValue(*obj));
333
0
    return existing;
334
0
}
335
336
JSObject*
337
Wrapper::wrappedObject(JSObject* wrapper)
338
0
{
339
0
    MOZ_ASSERT(wrapper->is<WrapperObject>());
340
0
    JSObject* target = wrapper->as<ProxyObject>().target();
341
0
342
0
    // Eagerly unmark gray wrapper targets so we can assert that we don't create
343
0
    // black to gray edges. An incremental GC will eventually mark the targets
344
0
    // of black wrappers black but while it is in progress we can observe gray
345
0
    // targets. Expose rather than returning a gray object in this case.
346
0
    if (target) {
347
0
        // A cross-compartment wrapper should never wrap a CCW. We rely on this
348
0
        // in the wrapper handlers (we use AutoRealm on our return value, and
349
0
        // AutoRealm cannot be used with CCWs).
350
0
        MOZ_ASSERT_IF(IsCrossCompartmentWrapper(wrapper),
351
0
                      !IsCrossCompartmentWrapper(target));
352
0
        if (wrapper->isMarkedBlack()) {
353
0
            MOZ_ASSERT(JS::ObjectIsNotGray(target));
354
0
        }
355
0
        if (!wrapper->isMarkedGray()) {
356
0
            JS::ExposeObjectToActiveJS(target);
357
0
        }
358
0
    }
359
0
360
0
    return target;
361
0
}
362
363
JS_FRIEND_API(JSObject*)
364
js::UncheckedUnwrapWithoutExpose(JSObject* wrapped)
365
112
{
366
112
    while (true) {
367
112
        if (!wrapped->is<WrapperObject>() || MOZ_UNLIKELY(IsWindowProxy(wrapped))) {
368
112
            break;
369
112
        }
370
0
        wrapped = wrapped->as<WrapperObject>().target();
371
0
372
0
        // This can be called from Wrapper::weakmapKeyDelegate() on a wrapper
373
0
        // whose referent has been moved while it is still unmarked.
374
0
        if (wrapped) {
375
0
            wrapped = MaybeForwarded(wrapped);
376
0
        }
377
0
    }
378
112
    return wrapped;
379
112
}
380
381
JS_FRIEND_API(JSObject*)
382
js::UncheckedUnwrap(JSObject* wrapped, bool stopAtWindowProxy, unsigned* flagsp)
383
9.74M
{
384
9.74M
    MOZ_ASSERT(!JS::RuntimeHeapIsCollecting());
385
9.74M
    MOZ_ASSERT(CurrentThreadCanAccessRuntime(wrapped->runtimeFromAnyThread()));
386
9.74M
387
9.74M
    unsigned flags = 0;
388
9.74M
    while (true) {
389
9.74M
        if (!wrapped->is<WrapperObject>() ||
390
9.74M
            MOZ_UNLIKELY(stopAtWindowProxy && IsWindowProxy(wrapped)))
391
9.74M
        {
392
9.74M
            break;
393
9.74M
        }
394
0
        flags |= Wrapper::wrapperHandler(wrapped)->flags();
395
0
        wrapped = Wrapper::wrappedObject(wrapped);
396
0
    }
397
9.74M
    if (flagsp) {
398
0
        *flagsp = flags;
399
0
    }
400
9.74M
    return wrapped;
401
9.74M
}
402
403
JS_FRIEND_API(JSObject*)
404
js::CheckedUnwrap(JSObject* obj, bool stopAtWindowProxy)
405
17.8M
{
406
17.8M
    while (true) {
407
17.8M
        JSObject* wrapper = obj;
408
17.8M
        obj = UnwrapOneChecked(obj, stopAtWindowProxy);
409
17.8M
        if (!obj || obj == wrapper) {
410
17.8M
            return obj;
411
17.8M
        }
412
17.8M
    }
413
17.8M
}
414
415
JS_FRIEND_API(JSObject*)
416
js::UnwrapOneChecked(JSObject* obj, bool stopAtWindowProxy)
417
17.8M
{
418
17.8M
    MOZ_ASSERT(!JS::RuntimeHeapIsCollecting());
419
17.8M
    MOZ_ASSERT(CurrentThreadCanAccessRuntime(obj->runtimeFromAnyThread()));
420
17.8M
421
17.8M
    if (!obj->is<WrapperObject>() ||
422
17.8M
        MOZ_UNLIKELY(stopAtWindowProxy && IsWindowProxy(obj)))
423
17.8M
    {
424
17.8M
        return obj;
425
17.8M
    }
426
0
427
0
    const Wrapper* handler = Wrapper::wrapperHandler(obj);
428
0
    return handler->hasSecurityPolicy() ? nullptr : Wrapper::wrappedObject(obj);
429
0
}
430
431
void
432
js::ReportAccessDenied(JSContext* cx)
433
0
{
434
0
    JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_OBJECT_ACCESS_DENIED);
435
0
}
436
437
const char Wrapper::family = 0;
438
const Wrapper Wrapper::singleton((unsigned)0);
439
const Wrapper Wrapper::singletonWithPrototype((unsigned)0, true);
440
JSObject* const Wrapper::defaultProto = TaggedProto::LazyProto;
441
442
/* Compartments. */
443
444
JSObject*
445
js::TransparentObjectWrapper(JSContext* cx, HandleObject existing, HandleObject obj)
446
0
{
447
0
    // Allow wrapping outer window proxies.
448
0
    MOZ_ASSERT(!obj->is<WrapperObject>() || IsWindowProxy(obj));
449
0
    return Wrapper::New(cx, obj, &CrossCompartmentWrapper::singleton);
450
0
}
451
452
ErrorCopier::~ErrorCopier()
453
0
{
454
0
    JSContext* cx = ar->context();
455
0
456
0
    // The provenance of Debugger.DebuggeeWouldRun is the topmost locking
457
0
    // debugger compartment; it should not be copied around.
458
0
    if (ar->origin()->compartment() != cx->compartment() &&
459
0
        cx->isExceptionPending() &&
460
0
        !cx->isThrowingDebuggeeWouldRun())
461
0
    {
462
0
        RootedValue exc(cx);
463
0
        if (cx->getPendingException(&exc) && exc.isObject() && exc.toObject().is<ErrorObject>()) {
464
0
            cx->clearPendingException();
465
0
            ar.reset();
466
0
            Rooted<ErrorObject*> errObj(cx, &exc.toObject().as<ErrorObject>());
467
0
            if (JSObject* copyobj = CopyErrorObject(cx, errObj)) {
468
0
                RootedValue rootedCopy(cx, ObjectValue(*copyobj));
469
0
                cx->setPendingException(rootedCopy);
470
0
            }
471
0
        }
472
0
    }
473
0
}