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