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