/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 | } |