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