1 : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 : * vim: set ts=4 sw=4 et tw=99:
3 : *
4 : * ***** BEGIN LICENSE BLOCK *****
5 : * Version: MPL 1.1/GPL 2.0/LGPL 2.1
6 : *
7 : * The contents of this file are subject to the Mozilla Public License Version
8 : * 1.1 (the "License"); you may not use this file except in compliance with
9 : * the License. You may obtain a copy of the License at
10 : * http://www.mozilla.org/MPL/
11 : *
12 : * Software distributed under the License is distributed on an "AS IS" basis,
13 : * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
14 : * for the specific language governing rights and limitations under the
15 : * License.
16 : *
17 : * The Original Code is Mozilla SpiderMonkey JavaScript 1.9 code, released
18 : * May 28, 2008.
19 : *
20 : * The Initial Developer of the Original Code is
21 : * Mozilla Foundation
22 : * Portions created by the Initial Developer are Copyright (C) 2009
23 : * the Initial Developer. All Rights Reserved.
24 : *
25 : * Contributor(s):
26 : * Andreas Gal <gal@mozilla.com>
27 : *
28 : * Alternatively, the contents of this file may be used under the terms of
29 : * either of the GNU General Public License Version 2 or later (the "GPL"),
30 : * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
31 : * in which case the provisions of the GPL or the LGPL are applicable instead
32 : * of those above. If you wish to allow use of your version of this file only
33 : * under the terms of either the GPL or the LGPL, and not to allow others to
34 : * use your version of this file under the terms of the MPL, indicate your
35 : * decision by deleting the provisions above and replace them with the notice
36 : * and other provisions required by the GPL or the LGPL. If you do not delete
37 : * the provisions above, a recipient may use your version of this file under
38 : * the terms of any one of the MPL, the GPL or the LGPL.
39 : *
40 : * ***** END LICENSE BLOCK ***** */
41 :
42 : #include <string.h>
43 : #include "jsapi.h"
44 : #include "jscntxt.h"
45 : #include "jsgc.h"
46 : #include "jsgcmark.h"
47 : #include "jsprvtd.h"
48 : #include "jsnum.h"
49 : #include "jsobj.h"
50 : #include "jsproxy.h"
51 : #include "jsscope.h"
52 :
53 : #include "vm/MethodGuard.h"
54 :
55 : #include "jsatominlines.h"
56 : #include "jsinferinlines.h"
57 : #include "jsobjinlines.h"
58 :
59 : using namespace js;
60 : using namespace js::gc;
61 :
62 : static inline HeapSlot &
63 35306 : GetCall(JSObject *proxy)
64 : {
65 35306 : JS_ASSERT(IsFunctionProxy(proxy));
66 35306 : return proxy->getSlotRef(JSSLOT_PROXY_CALL);
67 : }
68 :
69 : static inline Value
70 207 : GetConstruct(JSObject *proxy)
71 : {
72 207 : if (proxy->slotSpan() <= JSSLOT_PROXY_CONSTRUCT)
73 0 : return UndefinedValue();
74 207 : return proxy->getSlot(JSSLOT_PROXY_CONSTRUCT);
75 : }
76 :
77 : static inline HeapSlot &
78 11009 : GetFunctionProxyConstruct(JSObject *proxy)
79 : {
80 11009 : JS_ASSERT(IsFunctionProxy(proxy));
81 11009 : JS_ASSERT(proxy->slotSpan() > JSSLOT_PROXY_CONSTRUCT);
82 11009 : return proxy->getSlotRef(JSSLOT_PROXY_CONSTRUCT);
83 : }
84 :
85 : static bool
86 31555 : OperationInProgress(JSContext *cx, JSObject *proxy)
87 : {
88 31555 : PendingProxyOperation *op = cx->runtime->pendingProxyOperation;
89 63110 : while (op) {
90 31519 : if (op->object == proxy)
91 31519 : return true;
92 0 : op = op->next;
93 : }
94 36 : return false;
95 : }
96 :
97 : static bool
98 : FixProxy(JSContext *cx, JSObject *proxy, JSBool *bp);
99 :
100 102067 : ProxyHandler::ProxyHandler(void *family) : mFamily(family)
101 : {
102 102067 : }
103 :
104 102803 : ProxyHandler::~ProxyHandler()
105 : {
106 102803 : }
107 :
108 : bool
109 14 : ProxyHandler::has(JSContext *cx, JSObject *proxy, jsid id, bool *bp)
110 : {
111 14 : JS_ASSERT(OperationInProgress(cx, proxy));
112 28 : AutoPropertyDescriptorRooter desc(cx);
113 14 : if (!getPropertyDescriptor(cx, proxy, id, false, &desc))
114 0 : return false;
115 14 : *bp = !!desc.obj;
116 14 : return true;
117 : }
118 :
119 : bool
120 0 : ProxyHandler::hasOwn(JSContext *cx, JSObject *proxy, jsid id, bool *bp)
121 : {
122 0 : JS_ASSERT(OperationInProgress(cx, proxy));
123 0 : AutoPropertyDescriptorRooter desc(cx);
124 0 : if (!getOwnPropertyDescriptor(cx, proxy, id, false, &desc))
125 0 : return false;
126 0 : *bp = !!desc.obj;
127 0 : return true;
128 : }
129 :
130 : bool
131 155 : ProxyHandler::get(JSContext *cx, JSObject *proxy, JSObject *receiver, jsid id, Value *vp)
132 : {
133 155 : JS_ASSERT(OperationInProgress(cx, proxy));
134 310 : AutoPropertyDescriptorRooter desc(cx);
135 155 : if (!getPropertyDescriptor(cx, proxy, id, false, &desc))
136 135 : return false;
137 20 : if (!desc.obj) {
138 18 : vp->setUndefined();
139 18 : return true;
140 : }
141 2 : if (!desc.getter ||
142 0 : (!(desc.attrs & JSPROP_GETTER) && desc.getter == JS_PropertyStub)) {
143 2 : *vp = desc.value;
144 2 : return true;
145 : }
146 0 : if (desc.attrs & JSPROP_GETTER)
147 0 : return InvokeGetterOrSetter(cx, receiver, CastAsObjectJsval(desc.getter), 0, NULL, vp);
148 0 : if (!(desc.attrs & JSPROP_SHARED))
149 0 : *vp = desc.value;
150 : else
151 0 : vp->setUndefined();
152 0 : if (desc.attrs & JSPROP_SHORTID)
153 0 : id = INT_TO_JSID(desc.shortid);
154 0 : return CallJSPropertyOp(cx, desc.getter, receiver, id, vp);
155 : }
156 :
157 : bool
158 513 : ProxyHandler::getElementIfPresent(JSContext *cx, JSObject *proxy, JSObject *receiver, uint32_t index, Value *vp, bool *present)
159 : {
160 : jsid id;
161 513 : if (!IndexToId(cx, index, &id))
162 0 : return false;
163 :
164 513 : if (!has(cx, proxy, id, present))
165 18 : return false;
166 :
167 495 : if (!*present) {
168 9 : Debug_SetValueRangeToCrashOnTouch(vp, 1);
169 9 : return true;
170 : }
171 :
172 486 : return get(cx, proxy, receiver, id, vp);
173 : }
174 :
175 : bool
176 2 : ProxyHandler::set(JSContext *cx, JSObject *proxy, JSObject *receiver, jsid id, bool strict,
177 : Value *vp)
178 : {
179 2 : JS_ASSERT(OperationInProgress(cx, proxy));
180 4 : AutoPropertyDescriptorRooter desc(cx);
181 2 : if (!getOwnPropertyDescriptor(cx, proxy, id, true, &desc))
182 0 : return false;
183 : /* The control-flow here differs from ::get() because of the fall-through case below. */
184 2 : if (desc.obj) {
185 0 : if (desc.attrs & JSPROP_READONLY)
186 0 : return true;
187 0 : if (!desc.setter) {
188 : // Be wary of the odd explicit undefined setter case possible through
189 : // Object.defineProperty.
190 0 : if (!(desc.attrs & JSPROP_SETTER))
191 0 : desc.setter = JS_StrictPropertyStub;
192 0 : } else if ((desc.attrs & JSPROP_SETTER) || desc.setter != JS_StrictPropertyStub) {
193 0 : if (!CallSetter(cx, receiver, id, desc.setter, desc.attrs, desc.shortid, strict, vp))
194 0 : return false;
195 0 : if (!proxy->isProxy() || GetProxyHandler(proxy) != this)
196 0 : return true;
197 0 : if (desc.attrs & JSPROP_SHARED)
198 0 : return true;
199 : }
200 0 : if (!desc.getter) {
201 : // Same as above for the null setter case.
202 0 : if (!(desc.attrs & JSPROP_GETTER))
203 0 : desc.getter = JS_PropertyStub;
204 : }
205 0 : desc.value = *vp;
206 0 : return defineProperty(cx, receiver, id, &desc);
207 : }
208 2 : if (!getPropertyDescriptor(cx, proxy, id, true, &desc))
209 0 : return false;
210 2 : if (desc.obj) {
211 0 : if (desc.attrs & JSPROP_READONLY)
212 0 : return true;
213 0 : if (!desc.setter) {
214 : // Be wary of the odd explicit undefined setter case possible through
215 : // Object.defineProperty.
216 0 : if (!(desc.attrs & JSPROP_SETTER))
217 0 : desc.setter = JS_StrictPropertyStub;
218 0 : } else if ((desc.attrs & JSPROP_SETTER) || desc.setter != JS_StrictPropertyStub) {
219 0 : if (!CallSetter(cx, receiver, id, desc.setter, desc.attrs, desc.shortid, strict, vp))
220 0 : return false;
221 0 : if (!proxy->isProxy() || GetProxyHandler(proxy) != this)
222 0 : return true;
223 0 : if (desc.attrs & JSPROP_SHARED)
224 0 : return true;
225 : }
226 0 : if (!desc.getter) {
227 : // Same as above for the null setter case.
228 0 : if (!(desc.attrs & JSPROP_GETTER))
229 0 : desc.getter = JS_PropertyStub;
230 : }
231 0 : return defineProperty(cx, receiver, id, &desc);
232 : }
233 :
234 2 : desc.obj = receiver;
235 2 : desc.value = *vp;
236 2 : desc.attrs = JSPROP_ENUMERATE;
237 2 : desc.shortid = 0;
238 2 : desc.getter = NULL;
239 2 : desc.setter = NULL; // Pick up the class getter/setter.
240 2 : return defineProperty(cx, receiver, id, &desc);
241 : }
242 :
243 : bool
244 0 : ProxyHandler::keys(JSContext *cx, JSObject *proxy, AutoIdVector &props)
245 : {
246 0 : JS_ASSERT(OperationInProgress(cx, proxy));
247 0 : JS_ASSERT(props.length() == 0);
248 :
249 0 : if (!getOwnPropertyNames(cx, proxy, props))
250 0 : return false;
251 :
252 : /* Select only the enumerable properties through in-place iteration. */
253 0 : AutoPropertyDescriptorRooter desc(cx);
254 0 : size_t i = 0;
255 0 : for (size_t j = 0, len = props.length(); j < len; j++) {
256 0 : JS_ASSERT(i <= j);
257 0 : jsid id = props[j];
258 0 : if (!getOwnPropertyDescriptor(cx, proxy, id, false, &desc))
259 0 : return false;
260 0 : if (desc.obj && (desc.attrs & JSPROP_ENUMERATE))
261 0 : props[i++] = id;
262 : }
263 :
264 0 : JS_ASSERT(i <= props.length());
265 0 : props.resize(i);
266 :
267 0 : return true;
268 : }
269 :
270 : bool
271 0 : ProxyHandler::iterate(JSContext *cx, JSObject *proxy, unsigned flags, Value *vp)
272 : {
273 0 : JS_ASSERT(OperationInProgress(cx, proxy));
274 0 : AutoIdVector props(cx);
275 0 : if ((flags & JSITER_OWNONLY)
276 0 : ? !keys(cx, proxy, props)
277 0 : : !enumerate(cx, proxy, props)) {
278 0 : return false;
279 : }
280 0 : return EnumeratedIdVectorToIterator(cx, proxy, flags, props, vp);
281 : }
282 :
283 : JSString *
284 0 : ProxyHandler::obj_toString(JSContext *cx, JSObject *proxy)
285 : {
286 0 : JS_ASSERT(proxy->isProxy());
287 :
288 0 : return JS_NewStringCopyZ(cx, IsFunctionProxy(proxy)
289 : ? "[object Function]"
290 0 : : "[object Object]");
291 : }
292 :
293 : JSString *
294 9 : ProxyHandler::fun_toString(JSContext *cx, JSObject *proxy, unsigned indent)
295 : {
296 9 : JS_ASSERT(proxy->isProxy());
297 9 : Value fval = GetCall(proxy);
298 27 : if (IsFunctionProxy(proxy) &&
299 18 : (fval.isPrimitive() || !fval.toObject().isFunction())) {
300 : JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
301 : JSMSG_INCOMPATIBLE_PROTO,
302 : js_Function_str, js_toString_str,
303 0 : "object");
304 0 : return NULL;
305 : }
306 9 : return fun_toStringHelper(cx, &fval.toObject(), indent);
307 : }
308 :
309 : bool
310 0 : ProxyHandler::regexp_toShared(JSContext *cx, JSObject *proxy, RegExpGuard *g)
311 : {
312 0 : JS_NOT_REACHED("This should have been a wrapped regexp");
313 : return false;
314 : }
315 :
316 : bool
317 157 : ProxyHandler::defaultValue(JSContext *cx, JSObject *proxy, JSType hint, Value *vp)
318 : {
319 157 : return DefaultValue(cx, proxy, hint, vp);
320 : }
321 :
322 : bool
323 0 : ProxyHandler::iteratorNext(JSContext *cx, JSObject *proxy, Value *vp)
324 : {
325 0 : vp->setMagic(JS_NO_ITER_VALUE);
326 0 : return true;
327 : }
328 :
329 : bool
330 24108 : ProxyHandler::call(JSContext *cx, JSObject *proxy, unsigned argc, Value *vp)
331 : {
332 24108 : JS_ASSERT(OperationInProgress(cx, proxy));
333 48216 : AutoValueRooter rval(cx);
334 24108 : JSBool ok = Invoke(cx, vp[1], GetCall(proxy), argc, JS_ARGV(cx, vp), rval.addr());
335 24108 : if (ok)
336 21525 : JS_SET_RVAL(cx, vp, rval.value());
337 24108 : return ok;
338 : }
339 :
340 : bool
341 189 : ProxyHandler::construct(JSContext *cx, JSObject *proxy,
342 : unsigned argc, Value *argv, Value *rval)
343 : {
344 189 : JS_ASSERT(OperationInProgress(cx, proxy));
345 189 : Value fval = GetConstruct(proxy);
346 189 : if (fval.isUndefined())
347 162 : return InvokeConstructor(cx, GetCall(proxy), argc, argv, rval);
348 27 : return Invoke(cx, UndefinedValue(), fval, argc, argv, rval);
349 : }
350 :
351 : bool
352 1800 : ProxyHandler::nativeCall(JSContext *cx, JSObject *proxy, Class *clasp, Native native, CallArgs args)
353 : {
354 1800 : JS_ASSERT(OperationInProgress(cx, proxy));
355 1800 : ReportIncompatibleMethod(cx, args, clasp);
356 1800 : return false;
357 : }
358 :
359 : bool
360 0 : ProxyHandler::hasInstance(JSContext *cx, JSObject *proxy, const Value *vp, bool *bp)
361 : {
362 0 : JS_ASSERT(OperationInProgress(cx, proxy));
363 0 : js_ReportValueError(cx, JSMSG_BAD_INSTANCEOF_RHS,
364 0 : JSDVG_SEARCH_STACK, ObjectValue(*proxy), NULL);
365 0 : return false;
366 : }
367 :
368 : JSType
369 0 : ProxyHandler::typeOf(JSContext *cx, JSObject *proxy)
370 : {
371 0 : JS_ASSERT(OperationInProgress(cx, proxy));
372 0 : return IsFunctionProxy(proxy) ? JSTYPE_FUNCTION : JSTYPE_OBJECT;
373 : }
374 :
375 : bool
376 153 : ProxyHandler::objectClassIs(JSObject *proxy, ESClassValue classValue, JSContext *cx)
377 : {
378 153 : JS_ASSERT(OperationInProgress(cx, proxy));
379 153 : return false;
380 : }
381 :
382 : void
383 23749 : ProxyHandler::finalize(JSContext *cx, JSObject *proxy)
384 : {
385 23749 : }
386 :
387 : void
388 2824 : ProxyHandler::trace(JSTracer *trc, JSObject *proxy)
389 : {
390 2824 : }
391 :
392 : static bool
393 5098 : GetTrap(JSContext *cx, JSObject *handler, JSAtom *atom, Value *fvalp)
394 : {
395 5098 : JS_CHECK_RECURSION(cx, return false);
396 :
397 5096 : return handler->getGeneric(cx, ATOM_TO_JSID(atom), fvalp);
398 : }
399 :
400 : static bool
401 2075 : GetFundamentalTrap(JSContext *cx, JSObject *handler, JSAtom *atom, Value *fvalp)
402 : {
403 2075 : if (!GetTrap(cx, handler, atom, fvalp))
404 2 : return false;
405 :
406 2073 : if (!js_IsCallable(*fvalp)) {
407 18 : JSAutoByteString bytes;
408 9 : if (js_AtomToPrintableString(cx, atom, &bytes))
409 9 : JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_NOT_FUNCTION, bytes.ptr());
410 9 : return false;
411 : }
412 :
413 2064 : return true;
414 : }
415 :
416 : static bool
417 3023 : GetDerivedTrap(JSContext *cx, JSObject *handler, JSAtom *atom, Value *fvalp)
418 : {
419 0 : JS_ASSERT(atom == ATOM(has) ||
420 : atom == ATOM(hasOwn) ||
421 : atom == ATOM(get) ||
422 : atom == ATOM(set) ||
423 : atom == ATOM(keys) ||
424 3023 : atom == ATOM(iterate));
425 :
426 3023 : return GetTrap(cx, handler, atom, fvalp);
427 : }
428 :
429 : static bool
430 4916 : Trap(JSContext *cx, JSObject *handler, Value fval, unsigned argc, Value* argv, Value *rval)
431 : {
432 4916 : return Invoke(cx, ObjectValue(*handler), fval, argc, argv, rval);
433 : }
434 :
435 : static bool
436 989 : Trap1(JSContext *cx, JSObject *handler, Value fval, jsid id, Value *rval)
437 : {
438 989 : JSString *str = ToString(cx, IdToValue(id));
439 989 : if (!str)
440 0 : return false;
441 989 : rval->setString(str);
442 989 : return Trap(cx, handler, fval, 1, rval, rval);
443 : }
444 :
445 : static bool
446 1763 : Trap2(JSContext *cx, JSObject *handler, Value fval, jsid id, Value v, Value *rval)
447 : {
448 1763 : JSString *str = ToString(cx, IdToValue(id));
449 1763 : if (!str)
450 0 : return false;
451 1763 : rval->setString(str);
452 1763 : Value argv[2] = { *rval, v };
453 1763 : return Trap(cx, handler, fval, 2, argv, rval);
454 : }
455 :
456 : static bool
457 1815 : ParsePropertyDescriptorObject(JSContext *cx, JSObject *obj, jsid id, const Value &v,
458 : PropertyDescriptor *desc)
459 : {
460 3630 : AutoPropDescArrayRooter descs(cx);
461 1815 : PropDesc *d = descs.append();
462 1815 : if (!d || !d->initialize(cx, v))
463 0 : return false;
464 1815 : desc->obj = obj;
465 1815 : desc->value = d->value;
466 1815 : JS_ASSERT(!(d->attrs & JSPROP_SHORTID));
467 1815 : desc->attrs = d->attrs;
468 1815 : desc->getter = d->getter();
469 1815 : desc->setter = d->setter();
470 1815 : desc->shortid = 0;
471 1815 : return true;
472 : }
473 :
474 : static bool
475 32 : IndicatePropertyNotFound(JSContext *cx, PropertyDescriptor *desc)
476 : {
477 32 : desc->obj = NULL;
478 32 : return true;
479 : }
480 :
481 : static bool
482 753 : ValueToBool(JSContext *cx, const Value &v, bool *bp)
483 : {
484 753 : *bp = !!js_ValueToBoolean(v);
485 753 : return true;
486 : }
487 :
488 : static bool
489 9 : ArrayToIdVector(JSContext *cx, const Value &array, AutoIdVector &props)
490 : {
491 9 : JS_ASSERT(props.length() == 0);
492 :
493 9 : if (array.isPrimitive())
494 0 : return true;
495 :
496 9 : JSObject *obj = &array.toObject();
497 : uint32_t length;
498 9 : if (!js_GetLengthProperty(cx, obj, &length))
499 0 : return false;
500 :
501 18 : for (uint32_t n = 0; n < length; ++n) {
502 9 : if (!JS_CHECK_OPERATION_LIMIT(cx))
503 0 : return false;
504 : Value v;
505 9 : if (!obj->getElement(cx, n, &v))
506 0 : return false;
507 : jsid id;
508 9 : if (!ValueToId(cx, v, &id))
509 0 : return false;
510 9 : if (!props.append(js_CheckForStringIndex(id)))
511 0 : return false;
512 : }
513 :
514 9 : return true;
515 : }
516 :
517 : /* Derived class for all scripted proxy handlers. */
518 : class ScriptedProxyHandler : public ProxyHandler {
519 : public:
520 : ScriptedProxyHandler();
521 : virtual ~ScriptedProxyHandler();
522 :
523 : /* ES5 Harmony fundamental proxy traps. */
524 : virtual bool getPropertyDescriptor(JSContext *cx, JSObject *proxy, jsid id, bool set,
525 : PropertyDescriptor *desc);
526 : virtual bool getOwnPropertyDescriptor(JSContext *cx, JSObject *proxy, jsid id, bool set,
527 : PropertyDescriptor *desc);
528 : virtual bool defineProperty(JSContext *cx, JSObject *proxy, jsid id,
529 : PropertyDescriptor *desc);
530 : virtual bool getOwnPropertyNames(JSContext *cx, JSObject *proxy, AutoIdVector &props);
531 : virtual bool delete_(JSContext *cx, JSObject *proxy, jsid id, bool *bp);
532 : virtual bool enumerate(JSContext *cx, JSObject *proxy, AutoIdVector &props);
533 : virtual bool fix(JSContext *cx, JSObject *proxy, Value *vp);
534 :
535 : /* ES5 Harmony derived proxy traps. */
536 : virtual bool has(JSContext *cx, JSObject *proxy, jsid id, bool *bp);
537 : virtual bool hasOwn(JSContext *cx, JSObject *proxy, jsid id, bool *bp);
538 : virtual bool get(JSContext *cx, JSObject *proxy, JSObject *receiver, jsid id, Value *vp);
539 : virtual bool set(JSContext *cx, JSObject *proxy, JSObject *receiver, jsid id, bool strict,
540 : Value *vp);
541 : virtual bool keys(JSContext *cx, JSObject *proxy, AutoIdVector &props);
542 : virtual bool iterate(JSContext *cx, JSObject *proxy, unsigned flags, Value *vp);
543 :
544 : static ScriptedProxyHandler singleton;
545 : };
546 :
547 : static int sScriptedProxyHandlerFamily = 0;
548 :
549 19870 : ScriptedProxyHandler::ScriptedProxyHandler() : ProxyHandler(&sScriptedProxyHandlerFamily)
550 : {
551 19870 : }
552 :
553 19893 : ScriptedProxyHandler::~ScriptedProxyHandler()
554 : {
555 19893 : }
556 :
557 : static bool
558 51 : ReturnedValueMustNotBePrimitive(JSContext *cx, JSObject *proxy, JSAtom *atom, const Value &v)
559 : {
560 51 : if (v.isPrimitive()) {
561 0 : JSAutoByteString bytes;
562 0 : if (js_AtomToPrintableString(cx, atom, &bytes)) {
563 0 : js_ReportValueError2(cx, JSMSG_BAD_TRAP_RETURN_VALUE,
564 0 : JSDVG_SEARCH_STACK, ObjectOrNullValue(proxy), NULL, bytes.ptr());
565 : }
566 0 : return false;
567 : }
568 51 : return true;
569 : }
570 :
571 : static JSObject *
572 5098 : GetProxyHandlerObject(JSContext *cx, JSObject *proxy)
573 : {
574 5098 : JS_ASSERT(OperationInProgress(cx, proxy));
575 5098 : return GetProxyPrivate(proxy).toObjectOrNull();
576 : }
577 :
578 : bool
579 171 : ScriptedProxyHandler::getPropertyDescriptor(JSContext *cx, JSObject *proxy, jsid id, bool set,
580 : PropertyDescriptor *desc)
581 : {
582 171 : JSObject *handler = GetProxyHandlerObject(cx, proxy);
583 342 : AutoValueRooter tvr(cx);
584 171 : return GetFundamentalTrap(cx, handler, ATOM(getPropertyDescriptor), tvr.addr()) &&
585 162 : Trap1(cx, handler, tvr.value(), id, tvr.addr()) &&
586 66 : ((tvr.value().isUndefined() && IndicatePropertyNotFound(cx, desc)) ||
587 6 : (ReturnedValueMustNotBePrimitive(cx, proxy, ATOM(getPropertyDescriptor), tvr.value()) &&
588 405 : ParsePropertyDescriptorObject(cx, proxy, id, tvr.value(), desc)));
589 : }
590 :
591 : bool
592 11 : ScriptedProxyHandler::getOwnPropertyDescriptor(JSContext *cx, JSObject *proxy, jsid id, bool set,
593 : PropertyDescriptor *desc)
594 : {
595 11 : JSObject *handler = GetProxyHandlerObject(cx, proxy);
596 22 : AutoValueRooter tvr(cx);
597 11 : return GetFundamentalTrap(cx, handler, ATOM(getOwnPropertyDescriptor), tvr.addr()) &&
598 11 : Trap1(cx, handler, tvr.value(), id, tvr.addr()) &&
599 13 : ((tvr.value().isUndefined() && IndicatePropertyNotFound(cx, desc)) ||
600 9 : (ReturnedValueMustNotBePrimitive(cx, proxy, ATOM(getPropertyDescriptor), tvr.value()) &&
601 44 : ParsePropertyDescriptorObject(cx, proxy, id, tvr.value(), desc)));
602 : }
603 :
604 : bool
605 1765 : ScriptedProxyHandler::defineProperty(JSContext *cx, JSObject *proxy, jsid id,
606 : PropertyDescriptor *desc)
607 : {
608 1765 : JSObject *handler = GetProxyHandlerObject(cx, proxy);
609 3530 : AutoValueRooter tvr(cx);
610 3530 : AutoValueRooter fval(cx);
611 1765 : return GetFundamentalTrap(cx, handler, ATOM(defineProperty), fval.addr()) &&
612 1763 : NewPropertyDescriptorObject(cx, desc, tvr.addr()) &&
613 3528 : Trap2(cx, handler, fval.value(), id, tvr.value(), tvr.addr());
614 : }
615 :
616 : bool
617 9 : ScriptedProxyHandler::getOwnPropertyNames(JSContext *cx, JSObject *proxy, AutoIdVector &props)
618 : {
619 9 : JSObject *handler = GetProxyHandlerObject(cx, proxy);
620 18 : AutoValueRooter tvr(cx);
621 9 : return GetFundamentalTrap(cx, handler, ATOM(getOwnPropertyNames), tvr.addr()) &&
622 9 : Trap(cx, handler, tvr.value(), 0, NULL, tvr.addr()) &&
623 18 : ArrayToIdVector(cx, tvr.value(), props);
624 : }
625 :
626 : bool
627 83 : ScriptedProxyHandler::delete_(JSContext *cx, JSObject *proxy, jsid id, bool *bp)
628 : {
629 83 : JSObject *handler = GetProxyHandlerObject(cx, proxy);
630 166 : AutoValueRooter tvr(cx);
631 83 : return GetFundamentalTrap(cx, handler, ATOM(delete), tvr.addr()) &&
632 83 : Trap1(cx, handler, tvr.value(), id, tvr.addr()) &&
633 166 : ValueToBool(cx, tvr.value(), bp);
634 : }
635 :
636 : bool
637 0 : ScriptedProxyHandler::enumerate(JSContext *cx, JSObject *proxy, AutoIdVector &props)
638 : {
639 0 : JSObject *handler = GetProxyHandlerObject(cx, proxy);
640 0 : AutoValueRooter tvr(cx);
641 0 : return GetFundamentalTrap(cx, handler, ATOM(enumerate), tvr.addr()) &&
642 0 : Trap(cx, handler, tvr.value(), 0, NULL, tvr.addr()) &&
643 0 : ArrayToIdVector(cx, tvr.value(), props);
644 : }
645 :
646 : bool
647 36 : ScriptedProxyHandler::fix(JSContext *cx, JSObject *proxy, Value *vp)
648 : {
649 36 : JSObject *handler = GetProxyHandlerObject(cx, proxy);
650 36 : return GetFundamentalTrap(cx, handler, ATOM(fix), vp) &&
651 36 : Trap(cx, handler, *vp, 0, NULL, vp);
652 : }
653 :
654 : bool
655 747 : ScriptedProxyHandler::has(JSContext *cx, JSObject *proxy, jsid id, bool *bp)
656 : {
657 747 : JSObject *handler = GetProxyHandlerObject(cx, proxy);
658 1494 : AutoValueRooter tvr(cx);
659 747 : if (!GetDerivedTrap(cx, handler, ATOM(has), tvr.addr()))
660 0 : return false;
661 747 : if (!js_IsCallable(tvr.value()))
662 14 : return ProxyHandler::has(cx, proxy, id, bp);
663 733 : return Trap1(cx, handler, tvr.value(), id, tvr.addr()) &&
664 733 : ValueToBool(cx, tvr.value(), bp);
665 : }
666 :
667 : bool
668 0 : ScriptedProxyHandler::hasOwn(JSContext *cx, JSObject *proxy, jsid id, bool *bp)
669 : {
670 0 : JSObject *handler = GetProxyHandlerObject(cx, proxy);
671 0 : AutoValueRooter tvr(cx);
672 0 : if (!GetDerivedTrap(cx, handler, ATOM(hasOwn), tvr.addr()))
673 0 : return false;
674 0 : if (!js_IsCallable(tvr.value()))
675 0 : return ProxyHandler::hasOwn(cx, proxy, id, bp);
676 0 : return Trap1(cx, handler, tvr.value(), id, tvr.addr()) &&
677 0 : ValueToBool(cx, tvr.value(), bp);
678 : }
679 :
680 : bool
681 1887 : ScriptedProxyHandler::get(JSContext *cx, JSObject *proxy, JSObject *receiver, jsid id, Value *vp)
682 : {
683 1887 : JSObject *handler = GetProxyHandlerObject(cx, proxy);
684 1887 : JSString *str = ToString(cx, IdToValue(id));
685 1887 : if (!str)
686 0 : return false;
687 3774 : AutoValueRooter tvr(cx, StringValue(str));
688 1887 : Value argv[] = { ObjectOrNullValue(receiver), tvr.value() };
689 3774 : AutoValueRooter fval(cx);
690 1887 : if (!GetDerivedTrap(cx, handler, ATOM(get), fval.addr()))
691 0 : return false;
692 1887 : if (!js_IsCallable(fval.value()))
693 155 : return ProxyHandler::get(cx, proxy, receiver, id, vp);
694 1732 : return Trap(cx, handler, fval.value(), 2, argv, vp);
695 : }
696 :
697 : bool
698 344 : ScriptedProxyHandler::set(JSContext *cx, JSObject *proxy, JSObject *receiver, jsid id, bool strict,
699 : Value *vp)
700 : {
701 344 : JSObject *handler = GetProxyHandlerObject(cx, proxy);
702 344 : JSString *str = ToString(cx, IdToValue(id));
703 344 : if (!str)
704 0 : return false;
705 688 : AutoValueRooter tvr(cx, StringValue(str));
706 344 : Value argv[] = { ObjectOrNullValue(receiver), tvr.value(), *vp };
707 688 : AutoValueRooter fval(cx);
708 344 : if (!GetDerivedTrap(cx, handler, ATOM(set), fval.addr()))
709 0 : return false;
710 344 : if (!js_IsCallable(fval.value()))
711 2 : return ProxyHandler::set(cx, proxy, receiver, id, strict, vp);
712 342 : return Trap(cx, handler, fval.value(), 3, argv, tvr.addr());
713 : }
714 :
715 : bool
716 0 : ScriptedProxyHandler::keys(JSContext *cx, JSObject *proxy, AutoIdVector &props)
717 : {
718 0 : JSObject *handler = GetProxyHandlerObject(cx, proxy);
719 0 : AutoValueRooter tvr(cx);
720 0 : if (!GetDerivedTrap(cx, handler, ATOM(keys), tvr.addr()))
721 0 : return false;
722 0 : if (!js_IsCallable(tvr.value()))
723 0 : return ProxyHandler::keys(cx, proxy, props);
724 0 : return Trap(cx, handler, tvr.value(), 0, NULL, tvr.addr()) &&
725 0 : ArrayToIdVector(cx, tvr.value(), props);
726 : }
727 :
728 : bool
729 45 : ScriptedProxyHandler::iterate(JSContext *cx, JSObject *proxy, unsigned flags, Value *vp)
730 : {
731 45 : JSObject *handler = GetProxyHandlerObject(cx, proxy);
732 90 : AutoValueRooter tvr(cx);
733 45 : if (!GetDerivedTrap(cx, handler, ATOM(iterate), tvr.addr()))
734 0 : return false;
735 45 : if (!js_IsCallable(tvr.value()))
736 0 : return ProxyHandler::iterate(cx, proxy, flags, vp);
737 45 : return Trap(cx, handler, tvr.value(), 0, NULL, vp) &&
738 45 : ReturnedValueMustNotBePrimitive(cx, proxy, ATOM(iterate), *vp);
739 : }
740 :
741 19870 : ScriptedProxyHandler ScriptedProxyHandler::singleton;
742 :
743 : class AutoPendingProxyOperation {
744 : JSRuntime *rt;
745 : PendingProxyOperation op;
746 : public:
747 102713 : AutoPendingProxyOperation(JSContext *cx, JSObject *proxy) : rt(cx->runtime) {
748 102713 : op.next = rt->pendingProxyOperation;
749 102713 : op.object = proxy;
750 102713 : rt->pendingProxyOperation = &op;
751 102713 : }
752 :
753 102713 : ~AutoPendingProxyOperation() {
754 102713 : JS_ASSERT(rt->pendingProxyOperation == &op);
755 102713 : rt->pendingProxyOperation = op.next;
756 102713 : }
757 : };
758 :
759 : bool
760 0 : Proxy::getPropertyDescriptor(JSContext *cx, JSObject *proxy, jsid id, bool set,
761 : PropertyDescriptor *desc)
762 : {
763 0 : JS_CHECK_RECURSION(cx, return false);
764 0 : AutoPendingProxyOperation pending(cx, proxy);
765 0 : return GetProxyHandler(proxy)->getPropertyDescriptor(cx, proxy, id, set, desc);
766 : }
767 :
768 : bool
769 0 : Proxy::getPropertyDescriptor(JSContext *cx, JSObject *proxy, jsid id, bool set, Value *vp)
770 : {
771 0 : JS_CHECK_RECURSION(cx, return false);
772 0 : AutoPendingProxyOperation pending(cx, proxy);
773 0 : AutoPropertyDescriptorRooter desc(cx);
774 0 : return Proxy::getPropertyDescriptor(cx, proxy, id, set, &desc) &&
775 0 : NewPropertyDescriptorObject(cx, &desc, vp);
776 : }
777 :
778 : bool
779 45 : Proxy::getOwnPropertyDescriptor(JSContext *cx, JSObject *proxy, jsid id, bool set,
780 : PropertyDescriptor *desc)
781 : {
782 45 : JS_CHECK_RECURSION(cx, return false);
783 90 : AutoPendingProxyOperation pending(cx, proxy);
784 45 : return GetProxyHandler(proxy)->getOwnPropertyDescriptor(cx, proxy, id, set, desc);
785 : }
786 :
787 : bool
788 0 : Proxy::getOwnPropertyDescriptor(JSContext *cx, JSObject *proxy, jsid id, bool set, Value *vp)
789 : {
790 0 : JS_CHECK_RECURSION(cx, return false);
791 0 : AutoPendingProxyOperation pending(cx, proxy);
792 0 : AutoPropertyDescriptorRooter desc(cx);
793 0 : return Proxy::getOwnPropertyDescriptor(cx, proxy, id, set, &desc) &&
794 0 : NewPropertyDescriptorObject(cx, &desc, vp);
795 : }
796 :
797 : bool
798 1800 : Proxy::defineProperty(JSContext *cx, JSObject *proxy, jsid id, PropertyDescriptor *desc)
799 : {
800 1800 : JS_CHECK_RECURSION(cx, return false);
801 3598 : AutoPendingProxyOperation pending(cx, proxy);
802 1799 : return GetProxyHandler(proxy)->defineProperty(cx, proxy, id, desc);
803 : }
804 :
805 : bool
806 1804 : Proxy::defineProperty(JSContext *cx, JSObject *proxy, jsid id, const Value &v)
807 : {
808 1804 : JS_CHECK_RECURSION(cx, return false);
809 3600 : AutoPendingProxyOperation pending(cx, proxy);
810 3600 : AutoPropertyDescriptorRooter desc(cx);
811 1800 : return ParsePropertyDescriptorObject(cx, proxy, id, v, &desc) &&
812 1800 : Proxy::defineProperty(cx, proxy, id, &desc);
813 : }
814 :
815 : bool
816 9 : Proxy::getOwnPropertyNames(JSContext *cx, JSObject *proxy, AutoIdVector &props)
817 : {
818 9 : JS_CHECK_RECURSION(cx, return false);
819 18 : AutoPendingProxyOperation pending(cx, proxy);
820 9 : return GetProxyHandler(proxy)->getOwnPropertyNames(cx, proxy, props);
821 : }
822 :
823 : bool
824 92 : Proxy::delete_(JSContext *cx, JSObject *proxy, jsid id, bool *bp)
825 : {
826 92 : JS_CHECK_RECURSION(cx, return false);
827 184 : AutoPendingProxyOperation pending(cx, proxy);
828 92 : return GetProxyHandler(proxy)->delete_(cx, proxy, id, bp);
829 : }
830 :
831 : bool
832 0 : Proxy::enumerate(JSContext *cx, JSObject *proxy, AutoIdVector &props)
833 : {
834 0 : JS_CHECK_RECURSION(cx, return false);
835 0 : AutoPendingProxyOperation pending(cx, proxy);
836 0 : return GetProxyHandler(proxy)->enumerate(cx, proxy, props);
837 : }
838 :
839 : bool
840 36 : Proxy::fix(JSContext *cx, JSObject *proxy, Value *vp)
841 : {
842 36 : JS_CHECK_RECURSION(cx, return false);
843 72 : AutoPendingProxyOperation pending(cx, proxy);
844 36 : return GetProxyHandler(proxy)->fix(cx, proxy, vp);
845 : }
846 :
847 : bool
848 1119 : Proxy::has(JSContext *cx, JSObject *proxy, jsid id, bool *bp)
849 : {
850 1119 : JS_CHECK_RECURSION(cx, return false);
851 2238 : AutoPendingProxyOperation pending(cx, proxy);
852 1119 : return GetProxyHandler(proxy)->has(cx, proxy, id, bp);
853 : }
854 :
855 : bool
856 0 : Proxy::hasOwn(JSContext *cx, JSObject *proxy, jsid id, bool *bp)
857 : {
858 0 : JS_CHECK_RECURSION(cx, return false);
859 0 : AutoPendingProxyOperation pending(cx, proxy);
860 0 : return GetProxyHandler(proxy)->hasOwn(cx, proxy, id, bp);
861 : }
862 :
863 : bool
864 52648 : Proxy::get(JSContext *cx, JSObject *proxy, JSObject *receiver, jsid id, Value *vp)
865 : {
866 52648 : JS_CHECK_RECURSION(cx, return false);
867 105296 : AutoPendingProxyOperation pending(cx, proxy);
868 52648 : return GetProxyHandler(proxy)->get(cx, proxy, receiver, id, vp);
869 : }
870 :
871 : bool
872 1648 : Proxy::getElementIfPresent(JSContext *cx, JSObject *proxy, JSObject *receiver, uint32_t index,
873 : Value *vp, bool *present)
874 : {
875 1648 : JS_CHECK_RECURSION(cx, return false);
876 3296 : AutoPendingProxyOperation pending(cx, proxy);
877 1648 : return GetProxyHandler(proxy)->getElementIfPresent(cx, proxy, receiver, index, vp, present);
878 : }
879 :
880 : bool
881 10887 : Proxy::set(JSContext *cx, JSObject *proxy, JSObject *receiver, jsid id, bool strict, Value *vp)
882 : {
883 10887 : JS_CHECK_RECURSION(cx, return false);
884 21774 : AutoPendingProxyOperation pending(cx, proxy);
885 10887 : return GetProxyHandler(proxy)->set(cx, proxy, receiver, id, strict, vp);
886 : }
887 :
888 : bool
889 9 : Proxy::keys(JSContext *cx, JSObject *proxy, AutoIdVector &props)
890 : {
891 9 : JS_CHECK_RECURSION(cx, return false);
892 18 : AutoPendingProxyOperation pending(cx, proxy);
893 9 : return GetProxyHandler(proxy)->keys(cx, proxy, props);
894 : }
895 :
896 : bool
897 81 : Proxy::iterate(JSContext *cx, JSObject *proxy, unsigned flags, Value *vp)
898 : {
899 81 : JS_CHECK_RECURSION(cx, return false);
900 162 : AutoPendingProxyOperation pending(cx, proxy);
901 81 : return GetProxyHandler(proxy)->iterate(cx, proxy, flags, vp);
902 : }
903 :
904 : bool
905 24115 : Proxy::call(JSContext *cx, JSObject *proxy, unsigned argc, Value *vp)
906 : {
907 24115 : JS_CHECK_RECURSION(cx, return false);
908 48222 : AutoPendingProxyOperation pending(cx, proxy);
909 24111 : return GetProxyHandler(proxy)->call(cx, proxy, argc, vp);
910 : }
911 :
912 : bool
913 189 : Proxy::construct(JSContext *cx, JSObject *proxy, unsigned argc, Value *argv, Value *rval)
914 : {
915 189 : JS_CHECK_RECURSION(cx, return false);
916 378 : AutoPendingProxyOperation pending(cx, proxy);
917 189 : return GetProxyHandler(proxy)->construct(cx, proxy, argc, argv, rval);
918 : }
919 :
920 : bool
921 4500 : Proxy::nativeCall(JSContext *cx, JSObject *proxy, Class *clasp, Native native, CallArgs args)
922 : {
923 4500 : JS_CHECK_RECURSION(cx, return false);
924 9000 : AutoPendingProxyOperation pending(cx, proxy);
925 4500 : return GetProxyHandler(proxy)->nativeCall(cx, proxy, clasp, native, args);
926 : }
927 :
928 : bool
929 0 : Proxy::hasInstance(JSContext *cx, JSObject *proxy, const js::Value *vp, bool *bp)
930 : {
931 0 : JS_CHECK_RECURSION(cx, return false);
932 0 : AutoPendingProxyOperation pending(cx, proxy);
933 0 : return GetProxyHandler(proxy)->hasInstance(cx, proxy, vp, bp);
934 : }
935 :
936 : JSType
937 235 : Proxy::typeOf(JSContext *cx, JSObject *proxy)
938 : {
939 : // FIXME: API doesn't allow us to report error (bug 618906).
940 235 : JS_CHECK_RECURSION(cx, return JSTYPE_OBJECT);
941 470 : AutoPendingProxyOperation pending(cx, proxy);
942 235 : return GetProxyHandler(proxy)->typeOf(cx, proxy);
943 : }
944 :
945 : bool
946 595 : Proxy::objectClassIs(JSObject *proxy, ESClassValue classValue, JSContext *cx)
947 : {
948 1190 : AutoPendingProxyOperation pending(cx, proxy);
949 595 : return GetProxyHandler(proxy)->objectClassIs(proxy, classValue, cx);
950 : }
951 :
952 : JSString *
953 1948 : Proxy::obj_toString(JSContext *cx, JSObject *proxy)
954 : {
955 1948 : JS_CHECK_RECURSION(cx, return NULL);
956 3896 : AutoPendingProxyOperation pending(cx, proxy);
957 1948 : return GetProxyHandler(proxy)->obj_toString(cx, proxy);
958 : }
959 :
960 : JSString *
961 9 : Proxy::fun_toString(JSContext *cx, JSObject *proxy, unsigned indent)
962 : {
963 9 : JS_CHECK_RECURSION(cx, return NULL);
964 18 : AutoPendingProxyOperation pending(cx, proxy);
965 9 : return GetProxyHandler(proxy)->fun_toString(cx, proxy, indent);
966 : }
967 :
968 : bool
969 54 : Proxy::regexp_toShared(JSContext *cx, JSObject *proxy, RegExpGuard *g)
970 : {
971 54 : JS_CHECK_RECURSION(cx, return NULL);
972 108 : AutoPendingProxyOperation pending(cx, proxy);
973 54 : return GetProxyHandler(proxy)->regexp_toShared(cx, proxy, g);
974 : }
975 :
976 : bool
977 809 : Proxy::defaultValue(JSContext *cx, JSObject *proxy, JSType hint, Value *vp)
978 : {
979 809 : JS_CHECK_RECURSION(cx, return NULL);
980 1618 : AutoPendingProxyOperation pending(cx, proxy);
981 809 : return GetProxyHandler(proxy)->defaultValue(cx, proxy, hint, vp);
982 : }
983 :
984 : bool
985 54 : Proxy::iteratorNext(JSContext *cx, JSObject *proxy, Value *vp)
986 : {
987 54 : JS_CHECK_RECURSION(cx, return NULL);
988 108 : AutoPendingProxyOperation pending(cx, proxy);
989 54 : return GetProxyHandler(proxy)->iteratorNext(cx, proxy, vp);
990 : }
991 :
992 : static JSObject *
993 0 : proxy_innerObject(JSContext *cx, JSObject *obj)
994 : {
995 0 : return GetProxyPrivate(obj).toObjectOrNull();
996 : }
997 :
998 : static JSBool
999 1119 : proxy_LookupGeneric(JSContext *cx, JSObject *obj, jsid id, JSObject **objp,
1000 : JSProperty **propp)
1001 : {
1002 1119 : id = js_CheckForStringIndex(id);
1003 :
1004 : bool found;
1005 1119 : if (!Proxy::has(cx, obj, id, &found))
1006 9 : return false;
1007 :
1008 1110 : if (found) {
1009 746 : *propp = (JSProperty *)0x1;
1010 746 : *objp = obj;
1011 : } else {
1012 364 : *objp = NULL;
1013 364 : *propp = NULL;
1014 : }
1015 1110 : return true;
1016 : }
1017 :
1018 : static JSBool
1019 0 : proxy_LookupProperty(JSContext *cx, JSObject *obj, PropertyName *name, JSObject **objp,
1020 : JSProperty **propp)
1021 : {
1022 0 : return proxy_LookupGeneric(cx, obj, ATOM_TO_JSID(name), objp, propp);
1023 : }
1024 :
1025 : static JSBool
1026 0 : proxy_LookupElement(JSContext *cx, JSObject *obj, uint32_t index, JSObject **objp,
1027 : JSProperty **propp)
1028 : {
1029 : jsid id;
1030 0 : if (!IndexToId(cx, index, &id))
1031 0 : return false;
1032 0 : return proxy_LookupGeneric(cx, obj, id, objp, propp);
1033 : }
1034 :
1035 : static JSBool
1036 0 : proxy_LookupSpecial(JSContext *cx, JSObject *obj, SpecialId sid, JSObject **objp, JSProperty **propp)
1037 : {
1038 0 : return proxy_LookupGeneric(cx, obj, SPECIALID_TO_JSID(sid), objp, propp);
1039 : }
1040 :
1041 : static JSBool
1042 0 : proxy_DefineGeneric(JSContext *cx, JSObject *obj, jsid id, const Value *value,
1043 : PropertyOp getter, StrictPropertyOp setter, unsigned attrs)
1044 : {
1045 0 : id = js_CheckForStringIndex(id);
1046 :
1047 0 : AutoPropertyDescriptorRooter desc(cx);
1048 0 : desc.obj = obj;
1049 0 : desc.value = *value;
1050 0 : desc.attrs = (attrs & (~JSPROP_SHORTID));
1051 0 : desc.getter = getter;
1052 0 : desc.setter = setter;
1053 0 : desc.shortid = 0;
1054 0 : return Proxy::defineProperty(cx, obj, id, &desc);
1055 : }
1056 :
1057 : static JSBool
1058 0 : proxy_DefineProperty(JSContext *cx, JSObject *obj, PropertyName *name, const Value *value,
1059 : PropertyOp getter, StrictPropertyOp setter, unsigned attrs)
1060 : {
1061 0 : return proxy_DefineGeneric(cx, obj, ATOM_TO_JSID(name), value, getter, setter, attrs);
1062 : }
1063 :
1064 : static JSBool
1065 0 : proxy_DefineElement(JSContext *cx, JSObject *obj, uint32_t index, const Value *value,
1066 : PropertyOp getter, StrictPropertyOp setter, unsigned attrs)
1067 : {
1068 : jsid id;
1069 0 : if (!IndexToId(cx, index, &id))
1070 0 : return false;
1071 0 : return proxy_DefineGeneric(cx, obj, id, value, getter, setter, attrs);
1072 : }
1073 :
1074 : static JSBool
1075 0 : proxy_DefineSpecial(JSContext *cx, JSObject *obj, SpecialId sid, const Value *value,
1076 : PropertyOp getter, StrictPropertyOp setter, unsigned attrs)
1077 : {
1078 0 : return proxy_DefineGeneric(cx, obj, SPECIALID_TO_JSID(sid), value, getter, setter, attrs);
1079 : }
1080 :
1081 : static JSBool
1082 52590 : proxy_GetGeneric(JSContext *cx, JSObject *obj, JSObject *receiver, jsid id, Value *vp)
1083 : {
1084 52590 : id = js_CheckForStringIndex(id);
1085 :
1086 52590 : return Proxy::get(cx, obj, receiver, id, vp);
1087 : }
1088 :
1089 : static JSBool
1090 0 : proxy_GetProperty(JSContext *cx, JSObject *obj, JSObject *receiver, PropertyName *name, Value *vp)
1091 : {
1092 0 : return proxy_GetGeneric(cx, obj, receiver, ATOM_TO_JSID(name), vp);
1093 : }
1094 :
1095 : static JSBool
1096 3265 : proxy_GetElement(JSContext *cx, JSObject *obj, JSObject *receiver, uint32_t index, Value *vp)
1097 : {
1098 : jsid id;
1099 3265 : if (!IndexToId(cx, index, &id))
1100 0 : return false;
1101 3265 : return proxy_GetGeneric(cx, obj, receiver, id, vp);
1102 : }
1103 :
1104 : static JSBool
1105 1648 : proxy_GetElementIfPresent(JSContext *cx, JSObject *obj, JSObject *receiver, uint32_t index,
1106 : Value *vp, bool *present)
1107 : {
1108 1648 : return Proxy::getElementIfPresent(cx, obj, receiver, index, vp, present);
1109 : }
1110 :
1111 : static JSBool
1112 0 : proxy_GetSpecial(JSContext *cx, JSObject *obj, JSObject *receiver, SpecialId sid, Value *vp)
1113 : {
1114 0 : return proxy_GetGeneric(cx, obj, receiver, SPECIALID_TO_JSID(sid), vp);
1115 : }
1116 :
1117 : static JSBool
1118 10887 : proxy_SetGeneric(JSContext *cx, JSObject *obj, jsid id, Value *vp, JSBool strict)
1119 : {
1120 10887 : id = js_CheckForStringIndex(id);
1121 :
1122 10887 : return Proxy::set(cx, obj, obj, id, strict, vp);
1123 : }
1124 :
1125 : static JSBool
1126 0 : proxy_SetProperty(JSContext *cx, JSObject *obj, PropertyName *name, Value *vp, JSBool strict)
1127 : {
1128 0 : return proxy_SetGeneric(cx, obj, ATOM_TO_JSID(name), vp, strict);
1129 : }
1130 :
1131 : static JSBool
1132 0 : proxy_SetElement(JSContext *cx, JSObject *obj, uint32_t index, Value *vp, JSBool strict)
1133 : {
1134 : jsid id;
1135 0 : if (!IndexToId(cx, index, &id))
1136 0 : return false;
1137 0 : return proxy_SetGeneric(cx, obj, id, vp, strict);
1138 : }
1139 :
1140 : static JSBool
1141 0 : proxy_SetSpecial(JSContext *cx, JSObject *obj, SpecialId sid, Value *vp, JSBool strict)
1142 : {
1143 0 : return proxy_SetGeneric(cx, obj, SPECIALID_TO_JSID(sid), vp, strict);
1144 : }
1145 :
1146 : static JSBool
1147 0 : proxy_GetGenericAttributes(JSContext *cx, JSObject *obj, jsid id, unsigned *attrsp)
1148 : {
1149 0 : id = js_CheckForStringIndex(id);
1150 :
1151 0 : AutoPropertyDescriptorRooter desc(cx);
1152 0 : if (!Proxy::getOwnPropertyDescriptor(cx, obj, id, false, &desc))
1153 0 : return false;
1154 0 : *attrsp = desc.attrs;
1155 0 : return true;
1156 : }
1157 :
1158 : static JSBool
1159 0 : proxy_GetPropertyAttributes(JSContext *cx, JSObject *obj, PropertyName *name, unsigned *attrsp)
1160 : {
1161 0 : return proxy_GetGenericAttributes(cx, obj, ATOM_TO_JSID(name), attrsp);
1162 : }
1163 :
1164 : static JSBool
1165 0 : proxy_GetElementAttributes(JSContext *cx, JSObject *obj, uint32_t index, unsigned *attrsp)
1166 : {
1167 : jsid id;
1168 0 : if (!IndexToId(cx, index, &id))
1169 0 : return false;
1170 0 : return proxy_GetGenericAttributes(cx, obj, id, attrsp);
1171 : }
1172 :
1173 : static JSBool
1174 0 : proxy_GetSpecialAttributes(JSContext *cx, JSObject *obj, SpecialId sid, unsigned *attrsp)
1175 : {
1176 0 : return proxy_GetGenericAttributes(cx, obj, SPECIALID_TO_JSID(sid), attrsp);
1177 : }
1178 :
1179 : static JSBool
1180 0 : proxy_SetGenericAttributes(JSContext *cx, JSObject *obj, jsid id, unsigned *attrsp)
1181 : {
1182 0 : id = js_CheckForStringIndex(id);
1183 :
1184 : /* Lookup the current property descriptor so we have setter/getter/value. */
1185 0 : AutoPropertyDescriptorRooter desc(cx);
1186 0 : if (!Proxy::getOwnPropertyDescriptor(cx, obj, id, true, &desc))
1187 0 : return false;
1188 0 : desc.attrs = (*attrsp & (~JSPROP_SHORTID));
1189 0 : return Proxy::defineProperty(cx, obj, id, &desc);
1190 : }
1191 :
1192 : static JSBool
1193 0 : proxy_SetPropertyAttributes(JSContext *cx, JSObject *obj, PropertyName *name, unsigned *attrsp)
1194 : {
1195 0 : return proxy_SetGenericAttributes(cx, obj, ATOM_TO_JSID(name), attrsp);
1196 : }
1197 :
1198 : static JSBool
1199 0 : proxy_SetElementAttributes(JSContext *cx, JSObject *obj, uint32_t index, unsigned *attrsp)
1200 : {
1201 : jsid id;
1202 0 : if (!IndexToId(cx, index, &id))
1203 0 : return false;
1204 0 : return proxy_SetGenericAttributes(cx, obj, id, attrsp);
1205 : }
1206 :
1207 : static JSBool
1208 0 : proxy_SetSpecialAttributes(JSContext *cx, JSObject *obj, SpecialId sid, unsigned *attrsp)
1209 : {
1210 0 : return proxy_SetGenericAttributes(cx, obj, SPECIALID_TO_JSID(sid), attrsp);
1211 : }
1212 :
1213 : static JSBool
1214 92 : proxy_DeleteGeneric(JSContext *cx, JSObject *obj, jsid id, Value *rval, JSBool strict)
1215 : {
1216 92 : JS_ASSERT(id == js_CheckForStringIndex(id));
1217 :
1218 : // TODO: throwing away strict
1219 : bool deleted;
1220 92 : if (!Proxy::delete_(cx, obj, id, &deleted) || !js_SuppressDeletedProperty(cx, obj, id))
1221 36 : return false;
1222 56 : rval->setBoolean(deleted);
1223 56 : return true;
1224 : }
1225 :
1226 : static JSBool
1227 29 : proxy_DeleteProperty(JSContext *cx, JSObject *obj, PropertyName *name, Value *rval, JSBool strict)
1228 : {
1229 29 : return proxy_DeleteGeneric(cx, obj, js_CheckForStringIndex(ATOM_TO_JSID(name)), rval, strict);
1230 : }
1231 :
1232 : static JSBool
1233 63 : proxy_DeleteElement(JSContext *cx, JSObject *obj, uint32_t index, Value *rval, JSBool strict)
1234 : {
1235 : jsid id;
1236 63 : if (!IndexToId(cx, index, &id))
1237 0 : return false;
1238 63 : return proxy_DeleteGeneric(cx, obj, id, rval, strict);
1239 : }
1240 :
1241 : static JSBool
1242 0 : proxy_DeleteSpecial(JSContext *cx, JSObject *obj, SpecialId sid, Value *rval, JSBool strict)
1243 : {
1244 0 : return proxy_DeleteGeneric(cx, obj, SPECIALID_TO_JSID(sid), rval, strict);
1245 : }
1246 :
1247 : static void
1248 37617 : proxy_TraceObject(JSTracer *trc, JSObject *obj)
1249 : {
1250 37617 : GetProxyHandler(obj)->trace(trc, obj);
1251 37617 : MarkCrossCompartmentSlot(trc, &obj->getReservedSlotRef(JSSLOT_PROXY_PRIVATE), "private");
1252 37617 : MarkCrossCompartmentSlot(trc, &obj->getReservedSlotRef(JSSLOT_PROXY_EXTRA + 0), "extra0");
1253 37617 : MarkCrossCompartmentSlot(trc, &obj->getReservedSlotRef(JSSLOT_PROXY_EXTRA + 1), "extra1");
1254 37617 : }
1255 :
1256 : static void
1257 11009 : proxy_TraceFunction(JSTracer *trc, JSObject *obj)
1258 : {
1259 11009 : MarkCrossCompartmentSlot(trc, &GetCall(obj), "call");
1260 11009 : MarkCrossCompartmentSlot(trc, &GetFunctionProxyConstruct(obj), "construct");
1261 11009 : proxy_TraceObject(trc, obj);
1262 11009 : }
1263 :
1264 : static JSBool
1265 809 : proxy_Convert(JSContext *cx, JSObject *proxy, JSType hint, Value *vp)
1266 : {
1267 809 : JS_ASSERT(proxy->isProxy());
1268 809 : return Proxy::defaultValue(cx, proxy, hint, vp);
1269 : }
1270 :
1271 : static JSBool
1272 27 : proxy_Fix(JSContext *cx, JSObject *obj, bool *fixed, AutoIdVector *props)
1273 : {
1274 27 : JS_ASSERT(obj->isProxy());
1275 : JSBool isFixed;
1276 27 : bool ok = FixProxy(cx, obj, &isFixed);
1277 27 : if (ok) {
1278 27 : *fixed = isFixed;
1279 27 : return GetPropertyNames(cx, obj, JSITER_OWNONLY | JSITER_HIDDEN, props);
1280 : }
1281 0 : return false;
1282 : }
1283 :
1284 : static void
1285 28070 : proxy_Finalize(JSContext *cx, JSObject *obj)
1286 : {
1287 28070 : JS_ASSERT(obj->isProxy());
1288 28070 : if (!obj->getSlot(JSSLOT_PROXY_HANDLER).isUndefined())
1289 28070 : GetProxyHandler(obj)->finalize(cx, obj);
1290 28070 : }
1291 :
1292 : static JSBool
1293 0 : proxy_HasInstance(JSContext *cx, JSObject *proxy, const Value *v, JSBool *bp)
1294 : {
1295 0 : AutoPendingProxyOperation pending(cx, proxy);
1296 : bool b;
1297 0 : if (!Proxy::hasInstance(cx, proxy, v, &b))
1298 0 : return false;
1299 0 : *bp = !!b;
1300 0 : return true;
1301 : }
1302 :
1303 : static JSType
1304 235 : proxy_TypeOf(JSContext *cx, JSObject *proxy)
1305 : {
1306 235 : JS_ASSERT(proxy->isProxy());
1307 235 : return Proxy::typeOf(cx, proxy);
1308 : }
1309 :
1310 : JS_FRIEND_DATA(Class) js::ObjectProxyClass = {
1311 : "Proxy",
1312 : Class::NON_NATIVE | JSCLASS_IMPLEMENTS_BARRIERS | JSCLASS_HAS_RESERVED_SLOTS(4),
1313 : JS_PropertyStub, /* addProperty */
1314 : JS_PropertyStub, /* delProperty */
1315 : JS_PropertyStub, /* getProperty */
1316 : JS_StrictPropertyStub, /* setProperty */
1317 : JS_EnumerateStub,
1318 : JS_ResolveStub,
1319 : proxy_Convert,
1320 : proxy_Finalize, /* finalize */
1321 : NULL, /* checkAccess */
1322 : NULL, /* call */
1323 : NULL, /* construct */
1324 : proxy_HasInstance, /* hasInstance */
1325 : proxy_TraceObject, /* trace */
1326 : JS_NULL_CLASS_EXT,
1327 : {
1328 : proxy_LookupGeneric,
1329 : proxy_LookupProperty,
1330 : proxy_LookupElement,
1331 : proxy_LookupSpecial,
1332 : proxy_DefineGeneric,
1333 : proxy_DefineProperty,
1334 : proxy_DefineElement,
1335 : proxy_DefineSpecial,
1336 : proxy_GetGeneric,
1337 : proxy_GetProperty,
1338 : proxy_GetElement,
1339 : proxy_GetElementIfPresent,
1340 : proxy_GetSpecial,
1341 : proxy_SetGeneric,
1342 : proxy_SetProperty,
1343 : proxy_SetElement,
1344 : proxy_SetSpecial,
1345 : proxy_GetGenericAttributes,
1346 : proxy_GetPropertyAttributes,
1347 : proxy_GetElementAttributes,
1348 : proxy_GetSpecialAttributes,
1349 : proxy_SetGenericAttributes,
1350 : proxy_SetPropertyAttributes,
1351 : proxy_SetElementAttributes,
1352 : proxy_SetSpecialAttributes,
1353 : proxy_DeleteProperty,
1354 : proxy_DeleteElement,
1355 : proxy_DeleteSpecial,
1356 : NULL, /* enumerate */
1357 : proxy_TypeOf,
1358 : proxy_Fix, /* fix */
1359 : NULL, /* thisObject */
1360 : NULL, /* clear */
1361 : }
1362 : };
1363 :
1364 : JS_FRIEND_DATA(Class) js::OuterWindowProxyClass = {
1365 : "Proxy",
1366 : Class::NON_NATIVE | JSCLASS_IMPLEMENTS_BARRIERS | JSCLASS_HAS_RESERVED_SLOTS(4),
1367 : JS_PropertyStub, /* addProperty */
1368 : JS_PropertyStub, /* delProperty */
1369 : JS_PropertyStub, /* getProperty */
1370 : JS_StrictPropertyStub, /* setProperty */
1371 : JS_EnumerateStub,
1372 : JS_ResolveStub,
1373 : JS_ConvertStub,
1374 : proxy_Finalize, /* finalize */
1375 : NULL, /* checkAccess */
1376 : NULL, /* call */
1377 : NULL, /* construct */
1378 : NULL, /* hasInstance */
1379 : proxy_TraceObject, /* trace */
1380 : {
1381 : NULL, /* equality */
1382 : NULL, /* outerObject */
1383 : proxy_innerObject,
1384 : NULL /* unused */
1385 : },
1386 : {
1387 : proxy_LookupGeneric,
1388 : proxy_LookupProperty,
1389 : proxy_LookupElement,
1390 : proxy_LookupSpecial,
1391 : proxy_DefineGeneric,
1392 : proxy_DefineProperty,
1393 : proxy_DefineElement,
1394 : proxy_DefineSpecial,
1395 : proxy_GetGeneric,
1396 : proxy_GetProperty,
1397 : proxy_GetElement,
1398 : proxy_GetElementIfPresent,
1399 : proxy_GetSpecial,
1400 : proxy_SetGeneric,
1401 : proxy_SetProperty,
1402 : proxy_SetElement,
1403 : proxy_SetSpecial,
1404 : proxy_GetGenericAttributes,
1405 : proxy_GetPropertyAttributes,
1406 : proxy_GetElementAttributes,
1407 : proxy_GetSpecialAttributes,
1408 : proxy_SetGenericAttributes,
1409 : proxy_SetPropertyAttributes,
1410 : proxy_SetElementAttributes,
1411 : proxy_SetSpecialAttributes,
1412 : proxy_DeleteProperty,
1413 : proxy_DeleteElement,
1414 : proxy_DeleteSpecial,
1415 : NULL, /* enumerate */
1416 : NULL, /* typeof */
1417 : NULL, /* fix */
1418 : NULL, /* thisObject */
1419 : NULL, /* clear */
1420 : }
1421 : };
1422 :
1423 : static JSBool
1424 24115 : proxy_Call(JSContext *cx, unsigned argc, Value *vp)
1425 : {
1426 24115 : JSObject *proxy = &JS_CALLEE(cx, vp).toObject();
1427 24115 : JS_ASSERT(proxy->isProxy());
1428 24115 : return Proxy::call(cx, proxy, argc, vp);
1429 : }
1430 :
1431 : static JSBool
1432 189 : proxy_Construct(JSContext *cx, unsigned argc, Value *vp)
1433 : {
1434 189 : JSObject *proxy = &JS_CALLEE(cx, vp).toObject();
1435 189 : JS_ASSERT(proxy->isProxy());
1436 189 : bool ok = Proxy::construct(cx, proxy, argc, JS_ARGV(cx, vp), vp);
1437 189 : return ok;
1438 : }
1439 :
1440 : JS_FRIEND_DATA(Class) js::FunctionProxyClass = {
1441 : "Proxy",
1442 : Class::NON_NATIVE | JSCLASS_IMPLEMENTS_BARRIERS | JSCLASS_HAS_RESERVED_SLOTS(6),
1443 : JS_PropertyStub, /* addProperty */
1444 : JS_PropertyStub, /* delProperty */
1445 : JS_PropertyStub, /* getProperty */
1446 : JS_StrictPropertyStub, /* setProperty */
1447 : JS_EnumerateStub,
1448 : JS_ResolveStub,
1449 : JS_ConvertStub,
1450 : NULL, /* finalize */
1451 : NULL, /* checkAccess */
1452 : proxy_Call,
1453 : proxy_Construct,
1454 : FunctionClass.hasInstance,
1455 : proxy_TraceFunction, /* trace */
1456 : JS_NULL_CLASS_EXT,
1457 : {
1458 : proxy_LookupGeneric,
1459 : proxy_LookupProperty,
1460 : proxy_LookupElement,
1461 : proxy_LookupSpecial,
1462 : proxy_DefineGeneric,
1463 : proxy_DefineProperty,
1464 : proxy_DefineElement,
1465 : proxy_DefineSpecial,
1466 : proxy_GetGeneric,
1467 : proxy_GetProperty,
1468 : proxy_GetElement,
1469 : proxy_GetElementIfPresent,
1470 : proxy_GetSpecial,
1471 : proxy_SetGeneric,
1472 : proxy_SetProperty,
1473 : proxy_SetElement,
1474 : proxy_SetSpecial,
1475 : proxy_GetGenericAttributes,
1476 : proxy_GetPropertyAttributes,
1477 : proxy_GetElementAttributes,
1478 : proxy_GetSpecialAttributes,
1479 : proxy_SetGenericAttributes,
1480 : proxy_SetPropertyAttributes,
1481 : proxy_SetElementAttributes,
1482 : proxy_SetSpecialAttributes,
1483 : proxy_DeleteProperty,
1484 : proxy_DeleteElement,
1485 : proxy_DeleteSpecial,
1486 : NULL, /* enumerate */
1487 : proxy_TypeOf,
1488 : proxy_Fix, /* fix */
1489 : NULL, /* thisObject */
1490 : NULL, /* clear */
1491 : }
1492 19870 : };
1493 :
1494 : JS_FRIEND_API(JSObject *)
1495 46266 : js::NewProxyObject(JSContext *cx, ProxyHandler *handler, const Value &priv, JSObject *proto,
1496 : JSObject *parent, JSObject *call, JSObject *construct)
1497 : {
1498 46266 : JS_ASSERT_IF(proto, cx->compartment == proto->compartment());
1499 46266 : JS_ASSERT_IF(parent, cx->compartment == parent->compartment());
1500 46266 : bool fun = call || construct;
1501 : Class *clasp;
1502 46266 : if (fun)
1503 18196 : clasp = &FunctionProxyClass;
1504 : else
1505 28070 : clasp = handler->isOuterWindow() ? &OuterWindowProxyClass : &ObjectProxyClass;
1506 :
1507 : /*
1508 : * Eagerly mark properties unknown for proxies, so we don't try to track
1509 : * their properties and so that we don't need to walk the compartment if
1510 : * their prototype changes later.
1511 : */
1512 46266 : if (proto && !proto->setNewTypeUnknown(cx))
1513 0 : return NULL;
1514 :
1515 46266 : JSObject *obj = NewObjectWithGivenProto(cx, clasp, proto, parent);
1516 46266 : if (!obj)
1517 0 : return NULL;
1518 46266 : obj->setSlot(JSSLOT_PROXY_HANDLER, PrivateValue(handler));
1519 46266 : obj->setSlot(JSSLOT_PROXY_PRIVATE, priv);
1520 46266 : if (fun) {
1521 18196 : obj->setSlot(JSSLOT_PROXY_CALL, call ? ObjectValue(*call) : UndefinedValue());
1522 18196 : if (construct) {
1523 45 : obj->setSlot(JSSLOT_PROXY_CONSTRUCT, ObjectValue(*construct));
1524 : }
1525 : }
1526 :
1527 : /* Don't track types of properties of proxies. */
1528 46266 : MarkTypeObjectUnknownProperties(cx, obj->type());
1529 :
1530 46266 : return obj;
1531 : }
1532 :
1533 : static JSBool
1534 669 : proxy_create(JSContext *cx, unsigned argc, Value *vp)
1535 : {
1536 669 : if (argc < 1) {
1537 : JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_MORE_ARGS_NEEDED,
1538 0 : "create", "0", "s");
1539 0 : return false;
1540 : }
1541 669 : JSObject *handler = NonNullObject(cx, vp[2]);
1542 669 : if (!handler)
1543 0 : return false;
1544 669 : JSObject *proto, *parent = NULL;
1545 669 : if (argc > 1 && vp[3].isObject()) {
1546 27 : proto = &vp[3].toObject();
1547 27 : parent = proto->getParent();
1548 : } else {
1549 642 : JS_ASSERT(IsFunctionObject(vp[0]));
1550 642 : proto = NULL;
1551 : }
1552 669 : if (!parent)
1553 642 : parent = vp[0].toObject().getParent();
1554 : JSObject *proxy = NewProxyObject(cx, &ScriptedProxyHandler::singleton, ObjectValue(*handler),
1555 669 : proto, parent);
1556 669 : if (!proxy)
1557 0 : return false;
1558 :
1559 669 : vp->setObject(*proxy);
1560 669 : return true;
1561 : }
1562 :
1563 : static JSBool
1564 135 : proxy_createFunction(JSContext *cx, unsigned argc, Value *vp)
1565 : {
1566 135 : if (argc < 2) {
1567 : JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_MORE_ARGS_NEEDED,
1568 0 : "createFunction", "1", "");
1569 0 : return false;
1570 : }
1571 135 : JSObject *handler = NonNullObject(cx, vp[2]);
1572 135 : if (!handler)
1573 0 : return false;
1574 : JSObject *proto, *parent;
1575 135 : parent = vp[0].toObject().getParent();
1576 135 : proto = parent->global().getOrCreateFunctionPrototype(cx);
1577 135 : if (!proto)
1578 0 : return false;
1579 135 : parent = proto->getParent();
1580 :
1581 135 : JSObject *call = js_ValueToCallableObject(cx, &vp[3], JSV2F_SEARCH_STACK);
1582 135 : if (!call)
1583 0 : return false;
1584 135 : JSObject *construct = NULL;
1585 135 : if (argc > 2) {
1586 45 : construct = js_ValueToCallableObject(cx, &vp[4], JSV2F_SEARCH_STACK);
1587 45 : if (!construct)
1588 0 : return false;
1589 : }
1590 :
1591 : JSObject *proxy = NewProxyObject(cx, &ScriptedProxyHandler::singleton,
1592 : ObjectValue(*handler),
1593 135 : proto, parent, call, construct);
1594 135 : if (!proxy)
1595 0 : return false;
1596 :
1597 135 : vp->setObject(*proxy);
1598 135 : return true;
1599 : }
1600 :
1601 : #ifdef DEBUG
1602 :
1603 : static JSBool
1604 0 : proxy_isTrapping(JSContext *cx, unsigned argc, Value *vp)
1605 : {
1606 0 : if (argc < 1) {
1607 : JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_MORE_ARGS_NEEDED,
1608 0 : "isTrapping", "0", "s");
1609 0 : return false;
1610 : }
1611 0 : JSObject *obj = NonNullObject(cx, vp[2]);
1612 0 : if (!obj)
1613 0 : return false;
1614 0 : vp->setBoolean(obj->isProxy());
1615 0 : return true;
1616 : }
1617 :
1618 : static JSBool
1619 9 : proxy_fix(JSContext *cx, unsigned argc, Value *vp)
1620 : {
1621 9 : if (argc < 1) {
1622 : JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_MORE_ARGS_NEEDED,
1623 0 : "fix", "0", "s");
1624 0 : return false;
1625 : }
1626 9 : JSObject *obj = NonNullObject(cx, vp[2]);
1627 9 : if (!obj)
1628 0 : return false;
1629 9 : if (obj->isProxy()) {
1630 : JSBool flag;
1631 9 : if (!FixProxy(cx, obj, &flag))
1632 0 : return false;
1633 9 : vp->setBoolean(flag);
1634 : } else {
1635 0 : vp->setBoolean(true);
1636 : }
1637 9 : return true;
1638 : }
1639 :
1640 : #endif
1641 :
1642 : static JSFunctionSpec static_methods[] = {
1643 : JS_FN("create", proxy_create, 2, 0),
1644 : JS_FN("createFunction", proxy_createFunction, 3, 0),
1645 : #ifdef DEBUG
1646 : JS_FN("isTrapping", proxy_isTrapping, 1, 0),
1647 : JS_FN("fix", proxy_fix, 1, 0),
1648 : #endif
1649 : JS_FS_END
1650 : };
1651 :
1652 : static const uint32_t JSSLOT_CALLABLE_CALL = 0;
1653 : static const uint32_t JSSLOT_CALLABLE_CONSTRUCT = 1;
1654 :
1655 : static JSBool
1656 9 : callable_Call(JSContext *cx, unsigned argc, Value *vp)
1657 : {
1658 9 : JSObject *callable = &JS_CALLEE(cx, vp).toObject();
1659 9 : JS_ASSERT(callable->getClass() == &CallableObjectClass);
1660 9 : const Value &fval = callable->getSlot(JSSLOT_CALLABLE_CALL);
1661 9 : const Value &thisval = vp[1];
1662 9 : bool ok = Invoke(cx, thisval, fval, argc, JS_ARGV(cx, vp), vp);
1663 9 : return ok;
1664 : }
1665 :
1666 : JSBool
1667 18 : callable_Construct(JSContext *cx, unsigned argc, Value *vp)
1668 : {
1669 18 : JSObject *thisobj = js_CreateThis(cx, &JS_CALLEE(cx, vp).toObject());
1670 18 : if (!thisobj)
1671 0 : return false;
1672 :
1673 18 : JSObject *callable = &vp[0].toObject();
1674 18 : JS_ASSERT(callable->getClass() == &CallableObjectClass);
1675 18 : Value fval = callable->getSlot(JSSLOT_CALLABLE_CONSTRUCT);
1676 18 : if (fval.isUndefined()) {
1677 : /* We don't have an explicit constructor so allocate a new object and use the call. */
1678 0 : fval = callable->getSlot(JSSLOT_CALLABLE_CALL);
1679 0 : JS_ASSERT(fval.isObject());
1680 :
1681 : /* callable is the constructor, so get callable.prototype is the proto of the new object. */
1682 : Value protov;
1683 0 : if (!callable->getProperty(cx, ATOM(classPrototype), &protov))
1684 0 : return false;
1685 :
1686 : JSObject *proto;
1687 0 : if (protov.isObject()) {
1688 0 : proto = &protov.toObject();
1689 : } else {
1690 0 : proto = callable->global().getOrCreateObjectPrototype(cx);
1691 0 : if (!proto)
1692 0 : return false;
1693 : }
1694 :
1695 0 : JSObject *newobj = NewObjectWithGivenProto(cx, &ObjectClass, proto, NULL);
1696 0 : if (!newobj)
1697 0 : return false;
1698 :
1699 : /* If the call returns an object, return that, otherwise the original newobj. */
1700 : Value rval;
1701 0 : if (!Invoke(cx, ObjectValue(*newobj), callable->getSlot(JSSLOT_CALLABLE_CALL),
1702 0 : argc, vp + 2, &rval)) {
1703 0 : return false;
1704 : }
1705 0 : if (rval.isPrimitive())
1706 0 : vp->setObject(*newobj);
1707 : else
1708 0 : *vp = rval;
1709 0 : return true;
1710 : }
1711 :
1712 18 : bool ok = Invoke(cx, ObjectValue(*thisobj), fval, argc, vp + 2, vp);
1713 18 : return ok;
1714 : }
1715 :
1716 : Class js::CallableObjectClass = {
1717 : "Function",
1718 : JSCLASS_HAS_RESERVED_SLOTS(2),
1719 : JS_PropertyStub, /* addProperty */
1720 : JS_PropertyStub, /* delProperty */
1721 : JS_PropertyStub, /* getProperty */
1722 : JS_StrictPropertyStub, /* setProperty */
1723 : JS_EnumerateStub,
1724 : JS_ResolveStub,
1725 : JS_ConvertStub,
1726 : NULL, /* finalize */
1727 : NULL, /* checkAccess */
1728 : callable_Call,
1729 : callable_Construct,
1730 : };
1731 :
1732 : static bool
1733 36 : FixProxy(JSContext *cx, JSObject *proxy, JSBool *bp)
1734 : {
1735 36 : if (OperationInProgress(cx, proxy)) {
1736 0 : JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_BAD_PROXY_FIX);
1737 0 : return false;
1738 : }
1739 :
1740 72 : AutoValueRooter tvr(cx);
1741 36 : if (!Proxy::fix(cx, proxy, tvr.addr()))
1742 0 : return false;
1743 36 : if (tvr.value().isUndefined()) {
1744 0 : *bp = false;
1745 0 : return true;
1746 : }
1747 :
1748 36 : JSObject *props = NonNullObject(cx, tvr.value());
1749 36 : if (!props)
1750 0 : return false;
1751 :
1752 36 : JSObject *proto = proxy->getProto();
1753 36 : JSObject *parent = proxy->getParent();
1754 36 : Class *clasp = IsFunctionProxy(proxy) ? &CallableObjectClass : &ObjectClass;
1755 :
1756 : /*
1757 : * Make a blank object from the recipe fix provided to us. This must have
1758 : * number of fixed slots as the proxy so that we can swap their contents.
1759 : */
1760 36 : gc::AllocKind kind = proxy->getAllocKind();
1761 36 : JSObject *newborn = NewObjectWithGivenProto(cx, clasp, proto, parent, kind);
1762 36 : if (!newborn)
1763 0 : return false;
1764 :
1765 36 : if (clasp == &CallableObjectClass) {
1766 18 : newborn->setSlot(JSSLOT_CALLABLE_CALL, GetCall(proxy));
1767 18 : newborn->setSlot(JSSLOT_CALLABLE_CONSTRUCT, GetConstruct(proxy));
1768 : }
1769 :
1770 : {
1771 72 : AutoPendingProxyOperation pending(cx, proxy);
1772 36 : if (!js_PopulateObject(cx, newborn, props))
1773 0 : return false;
1774 : }
1775 :
1776 : /* Trade contents between the newborn object and the proxy. */
1777 36 : if (!proxy->swap(cx, newborn))
1778 0 : return false;
1779 :
1780 : /* The GC will dispose of the proxy object. */
1781 :
1782 36 : *bp = true;
1783 36 : return true;
1784 : }
1785 :
1786 : Class js::ProxyClass = {
1787 : "Proxy",
1788 : JSCLASS_HAS_CACHED_PROTO(JSProto_Proxy),
1789 : JS_PropertyStub, /* addProperty */
1790 : JS_PropertyStub, /* delProperty */
1791 : JS_PropertyStub, /* getProperty */
1792 : JS_StrictPropertyStub, /* setProperty */
1793 : JS_EnumerateStub,
1794 : JS_ResolveStub,
1795 : JS_ConvertStub
1796 : };
1797 :
1798 : JS_FRIEND_API(JSObject *)
1799 1699 : js_InitProxyClass(JSContext *cx, JSObject *obj)
1800 : {
1801 1699 : JSObject *module = NewObjectWithClassProto(cx, &ProxyClass, NULL, obj);
1802 1699 : if (!module || !module->setSingletonType(cx))
1803 0 : return NULL;
1804 :
1805 1699 : if (!JS_DefineProperty(cx, obj, "Proxy", OBJECT_TO_JSVAL(module),
1806 1699 : JS_PropertyStub, JS_StrictPropertyStub, 0)) {
1807 0 : return NULL;
1808 : }
1809 1699 : if (!JS_DefineFunctions(cx, module, static_methods))
1810 0 : return NULL;
1811 :
1812 1699 : MarkStandardClassInitializedNoProto(obj, &ProxyClass);
1813 :
1814 1699 : return module;
1815 59610 : }
|