Line data Source code
1 : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 : * vim: set ts=8 sts=4 et sw=4 tw=99:
3 : * This Source Code Form is subject to the terms of the Mozilla Public
4 : * License, v. 2.0. If a copy of the MPL was not distributed with this
5 : * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 :
7 : #include "proxy/ScriptedProxyHandler.h"
8 :
9 : #include "jsapi.h"
10 :
11 : #include "vm/Interpreter.h" // For InstanceOfOperator
12 :
13 : #include "jsobjinlines.h"
14 : #include "vm/NativeObject-inl.h"
15 :
16 : using namespace js;
17 :
18 : using JS::IsArrayAnswer;
19 : using mozilla::ArrayLength;
20 :
21 : // ES8 rev 0c1bd3004329336774cbc90de727cd0cf5f11e93
22 : // 9.1.6.2 IsCompatiblePropertyDescriptor. BUT that method just calls
23 : // 9.1.6.3 ValidateAndApplyPropertyDescriptor with two additional constant
24 : // arguments. Therefore step numbering is from the latter method, and
25 : // resulting dead code has been removed.
26 : static bool
27 0 : IsCompatiblePropertyDescriptor(JSContext* cx, bool extensible, Handle<PropertyDescriptor> desc,
28 : Handle<PropertyDescriptor> current, bool* bp)
29 : {
30 : // Step 2.
31 0 : if (!current.object()) {
32 : // Step 2a-b,e. As |O| is always undefined, steps 2c-d fall away.
33 0 : *bp = extensible;
34 0 : return true;
35 : }
36 :
37 : // Step 3.
38 0 : if (!desc.hasValue() && !desc.hasWritable() &&
39 0 : !desc.hasGetterObject() && !desc.hasSetterObject() &&
40 0 : !desc.hasEnumerable() && !desc.hasConfigurable())
41 : {
42 0 : *bp = true;
43 0 : return true;
44 : }
45 :
46 : // Step 4.
47 0 : if ((!desc.hasWritable() ||
48 0 : (current.hasWritable() && desc.writable() == current.writable())) &&
49 0 : (!desc.hasGetterObject() || desc.getter() == current.getter()) &&
50 0 : (!desc.hasSetterObject() || desc.setter() == current.setter()) &&
51 0 : (!desc.hasEnumerable() || desc.enumerable() == current.enumerable()) &&
52 0 : (!desc.hasConfigurable() || desc.configurable() == current.configurable()))
53 : {
54 0 : if (!desc.hasValue()) {
55 0 : *bp = true;
56 0 : return true;
57 : }
58 0 : bool same = false;
59 0 : if (!SameValue(cx, desc.value(), current.value(), &same))
60 0 : return false;
61 0 : if (same) {
62 0 : *bp = true;
63 0 : return true;
64 : }
65 : }
66 :
67 : // Step 5.
68 0 : if (!current.configurable()) {
69 : // Step 5a.
70 0 : if (desc.hasConfigurable() && desc.configurable()) {
71 0 : *bp = false;
72 0 : return true;
73 : }
74 :
75 : // Step 5b.
76 0 : if (desc.hasEnumerable() && desc.enumerable() != current.enumerable()) {
77 0 : *bp = false;
78 0 : return true;
79 : }
80 : }
81 :
82 : // Step 6.
83 0 : if (desc.isGenericDescriptor()) {
84 0 : *bp = true;
85 0 : return true;
86 : }
87 :
88 : // Step 7.
89 0 : if (current.isDataDescriptor() != desc.isDataDescriptor()) {
90 : // Steps 7a, 11. As |O| is always undefined, steps 2b-c fall away.
91 0 : *bp = current.configurable();
92 0 : return true;
93 : }
94 :
95 : // Step 8.
96 0 : if (current.isDataDescriptor()) {
97 0 : MOZ_ASSERT(desc.isDataDescriptor()); // by step 7
98 0 : if (!current.configurable() && !current.writable()) {
99 0 : if (desc.hasWritable() && desc.writable()) {
100 0 : *bp = false;
101 0 : return true;
102 : }
103 :
104 0 : if (desc.hasValue()) {
105 : bool same;
106 0 : if (!SameValue(cx, desc.value(), current.value(), &same))
107 0 : return false;
108 0 : if (!same) {
109 0 : *bp = false;
110 0 : return true;
111 : }
112 : }
113 : }
114 :
115 0 : *bp = true;
116 0 : return true;
117 : }
118 :
119 : // Step 9.
120 0 : MOZ_ASSERT(current.isAccessorDescriptor()); // by step 8
121 0 : MOZ_ASSERT(desc.isAccessorDescriptor()); // by step 7
122 0 : *bp = (current.configurable() ||
123 0 : ((!desc.hasSetterObject() || desc.setter() == current.setter()) &&
124 0 : (!desc.hasGetterObject() || desc.getter() == current.getter())));
125 0 : return true;
126 : }
127 :
128 : // Get the [[ProxyHandler]] of a scripted proxy.
129 : /* static */ JSObject*
130 71 : ScriptedProxyHandler::handlerObject(const JSObject* proxy)
131 : {
132 71 : MOZ_ASSERT(proxy->as<ProxyObject>().handler() == &ScriptedProxyHandler::singleton);
133 71 : return proxy->as<ProxyObject>().reservedSlot(ScriptedProxyHandler::HANDLER_EXTRA).toObjectOrNull();
134 : }
135 :
136 : // ES8 rev 0c1bd3004329336774cbc90de727cd0cf5f11e93 7.3.9 GetMethod,
137 : // reimplemented for proxy handler trap-getting to produce better error
138 : // messages.
139 : static bool
140 71 : GetProxyTrap(JSContext* cx, HandleObject handler, HandlePropertyName name, MutableHandleValue func)
141 : {
142 : // Steps 2, 5.
143 71 : if (!GetProperty(cx, handler, handler, name, func))
144 0 : return false;
145 :
146 : // Step 3.
147 71 : if (func.isUndefined())
148 56 : return true;
149 :
150 15 : if (func.isNull()) {
151 0 : func.setUndefined();
152 0 : return true;
153 : }
154 :
155 : // Step 4.
156 15 : if (!IsCallable(func)) {
157 0 : JSAutoByteString bytes(cx, name);
158 0 : if (!bytes)
159 0 : return false;
160 :
161 0 : JS_ReportErrorNumberLatin1(cx, GetErrorMessage, nullptr, JSMSG_BAD_TRAP, bytes.ptr());
162 0 : return false;
163 : }
164 :
165 15 : return true;
166 : }
167 :
168 : // ES8 rev 0c1bd3004329336774cbc90de727cd0cf5f11e93 9.5.1 Proxy.[[GetPrototypeOf]].
169 : bool
170 0 : ScriptedProxyHandler::getPrototype(JSContext* cx, HandleObject proxy,
171 : MutableHandleObject protop) const
172 : {
173 : // Steps 1-3.
174 0 : RootedObject handler(cx, ScriptedProxyHandler::handlerObject(proxy));
175 0 : if (!handler) {
176 0 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_PROXY_REVOKED);
177 0 : return false;
178 : }
179 :
180 : // Step 4.
181 0 : RootedObject target(cx, proxy->as<ProxyObject>().target());
182 0 : MOZ_ASSERT(target);
183 :
184 : // Step 5.
185 0 : RootedValue trap(cx);
186 0 : if (!GetProxyTrap(cx, handler, cx->names().getPrototypeOf, &trap))
187 0 : return false;
188 :
189 : // Step 6.
190 0 : if (trap.isUndefined())
191 0 : return GetPrototype(cx, target, protop);
192 :
193 : // Step 7.
194 0 : RootedValue handlerProto(cx);
195 : {
196 0 : FixedInvokeArgs<1> args(cx);
197 :
198 0 : args[0].setObject(*target);
199 :
200 0 : handlerProto.setObject(*handler);
201 :
202 0 : if (!js::Call(cx, trap, handlerProto, args, &handlerProto))
203 0 : return false;
204 : }
205 :
206 : // Step 8.
207 0 : if (!handlerProto.isObjectOrNull()) {
208 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
209 0 : JSMSG_BAD_GETPROTOTYPEOF_TRAP_RETURN);
210 0 : return false;
211 : }
212 :
213 : // Step 9.
214 : bool extensibleTarget;
215 0 : if (!IsExtensible(cx, target, &extensibleTarget))
216 0 : return false;
217 :
218 : // Step 10.
219 0 : if (extensibleTarget) {
220 0 : protop.set(handlerProto.toObjectOrNull());
221 0 : return true;
222 : }
223 :
224 : // Step 11.
225 0 : RootedObject targetProto(cx);
226 0 : if (!GetPrototype(cx, target, &targetProto))
227 0 : return false;
228 :
229 : // Step 12.
230 0 : if (handlerProto.toObjectOrNull() != targetProto) {
231 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
232 0 : JSMSG_INCONSISTENT_GETPROTOTYPEOF_TRAP);
233 0 : return false;
234 : }
235 :
236 : // Step 13.
237 0 : protop.set(handlerProto.toObjectOrNull());
238 0 : return true;
239 : }
240 :
241 : // ES8 rev 0c1bd3004329336774cbc90de727cd0cf5f11e93 9.5.2 Proxy.[[SetPrototypeOf]].
242 : bool
243 0 : ScriptedProxyHandler::setPrototype(JSContext* cx, HandleObject proxy, HandleObject proto,
244 : ObjectOpResult& result) const
245 : {
246 : // Steps 1-4.
247 0 : RootedObject handler(cx, ScriptedProxyHandler::handlerObject(proxy));
248 0 : if (!handler) {
249 0 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_PROXY_REVOKED);
250 0 : return false;
251 : }
252 :
253 : // Step 5.
254 0 : RootedObject target(cx, proxy->as<ProxyObject>().target());
255 0 : MOZ_ASSERT(target);
256 :
257 : // Step 6.
258 0 : RootedValue trap(cx);
259 0 : if (!GetProxyTrap(cx, handler, cx->names().setPrototypeOf, &trap))
260 0 : return false;
261 :
262 : // Step 7.
263 0 : if (trap.isUndefined())
264 0 : return SetPrototype(cx, target, proto, result);
265 :
266 : // Step 8.
267 : bool booleanTrapResult;
268 : {
269 0 : FixedInvokeArgs<2> args(cx);
270 :
271 0 : args[0].setObject(*target);
272 0 : args[1].setObjectOrNull(proto);
273 :
274 0 : RootedValue hval(cx, ObjectValue(*handler));
275 0 : if (!js::Call(cx, trap, hval, args, &hval))
276 0 : return false;
277 :
278 0 : booleanTrapResult = ToBoolean(hval);
279 : }
280 :
281 : // Step 9.
282 0 : if (!booleanTrapResult)
283 0 : return result.fail(JSMSG_PROXY_SETPROTOTYPEOF_RETURNED_FALSE);
284 :
285 : // Step 10.
286 : bool extensibleTarget;
287 0 : if (!IsExtensible(cx, target, &extensibleTarget))
288 0 : return false;
289 :
290 : // Step 11.
291 0 : if (extensibleTarget)
292 0 : return result.succeed();
293 :
294 : // Step 12.
295 0 : RootedObject targetProto(cx);
296 0 : if (!GetPrototype(cx, target, &targetProto))
297 0 : return false;
298 :
299 : // Step 13.
300 0 : if (proto != targetProto) {
301 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
302 0 : JSMSG_INCONSISTENT_SETPROTOTYPEOF_TRAP);
303 0 : return false;
304 : }
305 :
306 : // Step 14.
307 0 : return result.succeed();
308 : }
309 :
310 : bool
311 0 : ScriptedProxyHandler::getPrototypeIfOrdinary(JSContext* cx, HandleObject proxy, bool* isOrdinary,
312 : MutableHandleObject protop) const
313 : {
314 0 : *isOrdinary = false;
315 0 : return true;
316 : }
317 :
318 : // Not yet part of ES6, but hopefully to be standards-tracked -- and needed to
319 : // handle revoked proxies in any event.
320 : bool
321 0 : ScriptedProxyHandler::setImmutablePrototype(JSContext* cx, HandleObject proxy,
322 : bool* succeeded) const
323 : {
324 0 : RootedObject target(cx, proxy->as<ProxyObject>().target());
325 0 : if (!target) {
326 0 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_PROXY_REVOKED);
327 0 : return false;
328 : }
329 :
330 0 : return SetImmutablePrototype(cx, target, succeeded);
331 : }
332 :
333 : // ES8 rev 0c1bd3004329336774cbc90de727cd0cf5f11e93 9.5.4 Proxy.[[PreventExtensions]]()
334 : bool
335 2 : ScriptedProxyHandler::preventExtensions(JSContext* cx, HandleObject proxy,
336 : ObjectOpResult& result) const
337 : {
338 : // Steps 1-3.
339 4 : RootedObject handler(cx, ScriptedProxyHandler::handlerObject(proxy));
340 2 : if (!handler) {
341 0 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_PROXY_REVOKED);
342 0 : return false;
343 : }
344 :
345 : // Step 4.
346 4 : RootedObject target(cx, proxy->as<ProxyObject>().target());
347 2 : MOZ_ASSERT(target);
348 :
349 : // Step 5.
350 4 : RootedValue trap(cx);
351 2 : if (!GetProxyTrap(cx, handler, cx->names().preventExtensions, &trap))
352 0 : return false;
353 :
354 : // Step 6.
355 2 : if (trap.isUndefined())
356 2 : return PreventExtensions(cx, target, result);
357 :
358 : // Step 7.
359 : bool booleanTrapResult;
360 : {
361 0 : RootedValue arg(cx, ObjectValue(*target));
362 0 : RootedValue trapResult(cx);
363 0 : if (!Call(cx, trap, handler, arg, &trapResult))
364 0 : return false;
365 :
366 0 : booleanTrapResult = ToBoolean(trapResult);
367 : }
368 :
369 : // Step 8.
370 0 : if (booleanTrapResult) {
371 : // Step 8a.
372 : bool targetIsExtensible;
373 0 : if (!IsExtensible(cx, target, &targetIsExtensible))
374 0 : return false;
375 :
376 0 : if (targetIsExtensible) {
377 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
378 0 : JSMSG_CANT_REPORT_AS_NON_EXTENSIBLE);
379 0 : return false;
380 : }
381 :
382 : // Step 9.
383 0 : return result.succeed();
384 : }
385 :
386 : // Also step 9.
387 0 : return result.fail(JSMSG_PROXY_PREVENTEXTENSIONS_RETURNED_FALSE);
388 : }
389 :
390 : // ES8 rev 0c1bd3004329336774cbc90de727cd0cf5f11e93 9.5.3 Proxy.[[IsExtensible]]()
391 : bool
392 0 : ScriptedProxyHandler::isExtensible(JSContext* cx, HandleObject proxy, bool* extensible) const
393 : {
394 : // Steps 1-3.
395 0 : RootedObject handler(cx, ScriptedProxyHandler::handlerObject(proxy));
396 0 : if (!handler) {
397 0 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_PROXY_REVOKED);
398 0 : return false;
399 : }
400 :
401 : // Step 4.
402 0 : RootedObject target(cx, proxy->as<ProxyObject>().target());
403 0 : MOZ_ASSERT(target);
404 :
405 : // Step 5.
406 0 : RootedValue trap(cx);
407 0 : if (!GetProxyTrap(cx, handler, cx->names().isExtensible, &trap))
408 0 : return false;
409 :
410 : // Step 6.
411 0 : if (trap.isUndefined())
412 0 : return IsExtensible(cx, target, extensible);
413 :
414 : // Step 7.
415 : bool booleanTrapResult;
416 : {
417 0 : RootedValue arg(cx, ObjectValue(*target));
418 0 : RootedValue trapResult(cx);
419 0 : if (!Call(cx, trap, handler, arg, &trapResult))
420 0 : return false;
421 :
422 0 : booleanTrapResult = ToBoolean(trapResult);
423 : }
424 :
425 : // Steps 8.
426 : bool targetResult;
427 0 : if (!IsExtensible(cx, target, &targetResult))
428 0 : return false;
429 :
430 : // Step 9.
431 0 : if (targetResult != booleanTrapResult) {
432 0 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_PROXY_EXTENSIBILITY);
433 0 : return false;
434 : }
435 :
436 : // Step 10.
437 0 : *extensible = booleanTrapResult;
438 0 : return true;
439 : }
440 :
441 : // ES8 rev 0c1bd3004329336774cbc90de727cd0cf5f11e93 9.5.5 Proxy.[[GetOwnProperty]](P)
442 : bool
443 26 : ScriptedProxyHandler::getOwnPropertyDescriptor(JSContext* cx, HandleObject proxy, HandleId id,
444 : MutableHandle<PropertyDescriptor> desc) const
445 : {
446 : // Steps 2-4.
447 52 : RootedObject handler(cx, ScriptedProxyHandler::handlerObject(proxy));
448 26 : if (!handler) {
449 0 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_PROXY_REVOKED);
450 0 : return false;
451 : }
452 :
453 : // Step 5.
454 52 : RootedObject target(cx, proxy->as<ProxyObject>().target());
455 26 : MOZ_ASSERT(target);
456 :
457 : // Step 6.
458 52 : RootedValue trap(cx);
459 26 : if (!GetProxyTrap(cx, handler, cx->names().getOwnPropertyDescriptor, &trap))
460 0 : return false;
461 :
462 : // Step 7.
463 26 : if (trap.isUndefined())
464 26 : return GetOwnPropertyDescriptor(cx, target, id, desc);
465 :
466 : // Step 8.
467 0 : RootedValue propKey(cx);
468 0 : if (!IdToStringOrSymbol(cx, id, &propKey))
469 0 : return false;
470 :
471 0 : RootedValue trapResult(cx);
472 0 : RootedValue targetVal(cx, ObjectValue(*target));
473 0 : if (!Call(cx, trap, handler, targetVal, propKey, &trapResult))
474 0 : return false;
475 :
476 : // Step 9.
477 0 : if (!trapResult.isUndefined() && !trapResult.isObject()) {
478 0 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_PROXY_GETOWN_OBJORUNDEF);
479 0 : return false;
480 : }
481 :
482 : // Step 10.
483 0 : Rooted<PropertyDescriptor> targetDesc(cx);
484 0 : if (!GetOwnPropertyDescriptor(cx, target, id, &targetDesc))
485 0 : return false;
486 :
487 : // Step 11.
488 0 : if (trapResult.isUndefined()) {
489 : // Step 11a.
490 0 : if (!targetDesc.object()) {
491 0 : desc.object().set(nullptr);
492 0 : return true;
493 : }
494 :
495 : // Step 11b.
496 0 : if (!targetDesc.configurable()) {
497 0 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_CANT_REPORT_NC_AS_NE);
498 0 : return false;
499 : }
500 :
501 : // Steps 11c-d.
502 : bool extensibleTarget;
503 0 : if (!IsExtensible(cx, target, &extensibleTarget))
504 0 : return false;
505 :
506 : // Step 11e.
507 0 : if (!extensibleTarget) {
508 0 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_CANT_REPORT_E_AS_NE);
509 0 : return false;
510 : }
511 :
512 : // Step 11f.
513 0 : desc.object().set(nullptr);
514 0 : return true;
515 : }
516 :
517 : // Step 12.
518 : bool extensibleTarget;
519 0 : if (!IsExtensible(cx, target, &extensibleTarget))
520 0 : return false;
521 :
522 : // Step 13.
523 0 : Rooted<PropertyDescriptor> resultDesc(cx);
524 0 : if (!ToPropertyDescriptor(cx, trapResult, true, &resultDesc))
525 0 : return false;
526 :
527 : // Step 14.
528 0 : CompletePropertyDescriptor(&resultDesc);
529 :
530 : // Step 15.
531 : bool valid;
532 0 : if (!IsCompatiblePropertyDescriptor(cx, extensibleTarget, resultDesc, targetDesc, &valid))
533 0 : return false;
534 :
535 : // Step 16.
536 0 : if (!valid) {
537 0 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_CANT_REPORT_INVALID);
538 0 : return false;
539 : }
540 :
541 : // Step 17.
542 0 : if (!resultDesc.configurable()) {
543 0 : if (!targetDesc.object()) {
544 0 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_CANT_REPORT_NE_AS_NC);
545 0 : return false;
546 : }
547 :
548 0 : if (targetDesc.configurable()) {
549 0 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_CANT_REPORT_C_AS_NC);
550 0 : return false;
551 : }
552 : }
553 :
554 : // Step 18.
555 0 : desc.set(resultDesc);
556 0 : desc.object().set(proxy);
557 0 : return true;
558 : }
559 :
560 : // ES8 rev 0c1bd3004329336774cbc90de727cd0cf5f11e93 9.5.6 Proxy.[[DefineOwnProperty]](P, Desc)
561 : bool
562 26 : ScriptedProxyHandler::defineProperty(JSContext* cx, HandleObject proxy, HandleId id,
563 : Handle<PropertyDescriptor> desc, ObjectOpResult& result) const
564 : {
565 : // Steps 2-4.
566 52 : RootedObject handler(cx, ScriptedProxyHandler::handlerObject(proxy));
567 26 : if (!handler) {
568 0 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_PROXY_REVOKED);
569 0 : return false;
570 : }
571 :
572 : // Step 5.
573 52 : RootedObject target(cx, proxy->as<ProxyObject>().target());
574 26 : MOZ_ASSERT(target);
575 :
576 : // Step 6.
577 52 : RootedValue trap(cx);
578 26 : if (!GetProxyTrap(cx, handler, cx->names().defineProperty, &trap))
579 0 : return false;
580 :
581 : // Step 7.
582 26 : if (trap.isUndefined())
583 26 : return DefineProperty(cx, target, id, desc, result);
584 :
585 : // Step 8.
586 0 : RootedValue descObj(cx);
587 0 : if (!FromPropertyDescriptorToObject(cx, desc, &descObj))
588 0 : return false;
589 :
590 : // Step 9.
591 0 : RootedValue propKey(cx);
592 0 : if (!IdToStringOrSymbol(cx, id, &propKey))
593 0 : return false;
594 :
595 0 : RootedValue trapResult(cx);
596 : {
597 0 : FixedInvokeArgs<3> args(cx);
598 :
599 0 : args[0].setObject(*target);
600 0 : args[1].set(propKey);
601 0 : args[2].set(descObj);
602 :
603 0 : RootedValue thisv(cx, ObjectValue(*handler));
604 0 : if (!Call(cx, trap, thisv, args, &trapResult))
605 0 : return false;
606 : }
607 :
608 : // Step 10.
609 0 : if (!ToBoolean(trapResult))
610 0 : return result.fail(JSMSG_PROXY_DEFINE_RETURNED_FALSE);
611 :
612 : // Step 11.
613 0 : Rooted<PropertyDescriptor> targetDesc(cx);
614 0 : if (!GetOwnPropertyDescriptor(cx, target, id, &targetDesc))
615 0 : return false;
616 :
617 : // Step 12.
618 : bool extensibleTarget;
619 0 : if (!IsExtensible(cx, target, &extensibleTarget))
620 0 : return false;
621 :
622 : // Steps 13-14.
623 0 : bool settingConfigFalse = desc.hasConfigurable() && !desc.configurable();
624 :
625 : // Steps 15-16.
626 0 : if (!targetDesc.object()) {
627 : // Step 15a.
628 0 : if (!extensibleTarget) {
629 0 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_CANT_DEFINE_NEW);
630 0 : return false;
631 : }
632 :
633 : // Step 15b.
634 0 : if (settingConfigFalse) {
635 0 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_CANT_DEFINE_NE_AS_NC);
636 0 : return false;
637 : }
638 : } else {
639 : // Steps 16a-b.
640 : bool valid;
641 0 : if (!IsCompatiblePropertyDescriptor(cx, extensibleTarget, desc, targetDesc, &valid))
642 0 : return false;
643 :
644 0 : if (!valid || (settingConfigFalse && targetDesc.configurable())) {
645 0 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_CANT_DEFINE_INVALID);
646 0 : return false;
647 : }
648 : }
649 :
650 : // Step 17.
651 0 : return result.succeed();
652 : }
653 :
654 : // ES8 rev 0c1bd3004329336774cbc90de727cd0cf5f11e93
655 : // 7.3.17 CreateListFromArrayLike with elementTypes fixed to symbol/string.
656 : static bool
657 0 : CreateFilteredListFromArrayLike(JSContext* cx, HandleValue v, AutoIdVector& props)
658 : {
659 : // Step 2.
660 0 : RootedObject obj(cx, NonNullObjectWithName(cx, "return value of the ownKeys trap", v));
661 0 : if (!obj)
662 0 : return false;
663 :
664 : // Step 3.
665 : uint32_t len;
666 0 : if (!GetLengthProperty(cx, obj, &len))
667 0 : return false;
668 :
669 : // Steps 4-6.
670 0 : RootedValue next(cx);
671 0 : RootedId id(cx);
672 0 : uint32_t index = 0;
673 0 : while (index < len) {
674 : // Steps 6a-b.
675 0 : if (!GetElement(cx, obj, obj, index, &next))
676 0 : return false;
677 :
678 : // Step 6c.
679 0 : if (!next.isString() && !next.isSymbol()) {
680 0 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_ONWKEYS_STR_SYM);
681 0 : return false;
682 : }
683 :
684 0 : if (!ValueToId<CanGC>(cx, next, &id))
685 0 : return false;
686 :
687 : // Step 6d.
688 0 : if (!props.append(id))
689 0 : return false;
690 :
691 : // Step 6e.
692 0 : index++;
693 : }
694 :
695 : // Step 7.
696 0 : return true;
697 : }
698 :
699 :
700 : // ES8 rev 0c1bd3004329336774cbc90de727cd0cf5f11e93 9.5.11 Proxy.[[OwnPropertyKeys]]()
701 : bool
702 2 : ScriptedProxyHandler::ownPropertyKeys(JSContext* cx, HandleObject proxy, AutoIdVector& props) const
703 : {
704 : // Steps 1-3.
705 4 : RootedObject handler(cx, ScriptedProxyHandler::handlerObject(proxy));
706 2 : if (!handler) {
707 0 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_PROXY_REVOKED);
708 0 : return false;
709 : }
710 :
711 : // Step 4.
712 4 : RootedObject target(cx, proxy->as<ProxyObject>().target());
713 2 : MOZ_ASSERT(target);
714 :
715 : // Step 5.
716 4 : RootedValue trap(cx);
717 2 : if (!GetProxyTrap(cx, handler, cx->names().ownKeys, &trap))
718 0 : return false;
719 :
720 : // Step 6.
721 2 : if (trap.isUndefined())
722 2 : return GetPropertyKeys(cx, target, JSITER_OWNONLY | JSITER_HIDDEN | JSITER_SYMBOLS, &props);
723 :
724 : // Step 7.
725 0 : RootedValue trapResultArray(cx);
726 0 : RootedValue targetVal(cx, ObjectValue(*target));
727 0 : if (!Call(cx, trap, handler, targetVal, &trapResultArray))
728 0 : return false;
729 :
730 : // Step 8.
731 0 : AutoIdVector trapResult(cx);
732 0 : if (!CreateFilteredListFromArrayLike(cx, trapResultArray, trapResult))
733 0 : return false;
734 :
735 : // Step 9.
736 : bool extensibleTarget;
737 0 : if (!IsExtensible(cx, target, &extensibleTarget))
738 0 : return false;
739 :
740 : // Steps 10-11.
741 0 : AutoIdVector targetKeys(cx);
742 0 : if (!GetPropertyKeys(cx, target, JSITER_OWNONLY | JSITER_HIDDEN | JSITER_SYMBOLS, &targetKeys))
743 0 : return false;
744 :
745 : // Steps 12-13.
746 0 : AutoIdVector targetConfigurableKeys(cx);
747 0 : AutoIdVector targetNonconfigurableKeys(cx);
748 :
749 : // Step 14.
750 0 : Rooted<PropertyDescriptor> desc(cx);
751 0 : for (size_t i = 0; i < targetKeys.length(); ++i) {
752 : // Step 14a.
753 0 : if (!GetOwnPropertyDescriptor(cx, target, targetKeys[i], &desc))
754 0 : return false;
755 :
756 : // Steps 14b-c.
757 0 : if (desc.object() && !desc.configurable()) {
758 0 : if (!targetNonconfigurableKeys.append(targetKeys[i]))
759 0 : return false;
760 : } else {
761 0 : if (!targetConfigurableKeys.append(targetKeys[i]))
762 0 : return false;
763 : }
764 : }
765 :
766 : // Step 15.
767 0 : if (extensibleTarget && targetNonconfigurableKeys.empty())
768 0 : return props.appendAll(trapResult);
769 :
770 : // Step 16.
771 : // The algorithm below always removes all occurences of the same key
772 : // at once, so we can use a set here.
773 0 : Rooted<GCHashSet<jsid>> uncheckedResultKeys(cx, GCHashSet<jsid>(cx));
774 0 : if (!uncheckedResultKeys.init(trapResult.length()))
775 0 : return false;
776 :
777 0 : for (size_t i = 0, len = trapResult.length(); i < len; i++) {
778 0 : MOZ_ASSERT(!JSID_IS_VOID(trapResult[i]));
779 :
780 0 : if (!uncheckedResultKeys.put(trapResult[i]))
781 0 : return false;
782 : }
783 :
784 : // Step 17.
785 0 : for (size_t i = 0; i < targetNonconfigurableKeys.length(); ++i) {
786 0 : MOZ_ASSERT(!JSID_IS_VOID(targetNonconfigurableKeys[i]));
787 :
788 0 : auto ptr = uncheckedResultKeys.lookup(targetNonconfigurableKeys[i]);
789 :
790 : // Step 17a.
791 0 : if (!ptr) {
792 0 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_CANT_SKIP_NC);
793 0 : return false;
794 : }
795 :
796 : // Step 17b.
797 0 : uncheckedResultKeys.remove(ptr);
798 : }
799 :
800 : // Step 18.
801 0 : if (extensibleTarget)
802 0 : return props.appendAll(trapResult);
803 :
804 : // Step 19.
805 0 : for (size_t i = 0; i < targetConfigurableKeys.length(); ++i) {
806 0 : MOZ_ASSERT(!JSID_IS_VOID(targetConfigurableKeys[i]));
807 :
808 0 : auto ptr = uncheckedResultKeys.lookup(targetConfigurableKeys[i]);
809 :
810 : // Step 19a.
811 0 : if (!ptr) {
812 0 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_CANT_REPORT_E_AS_NE);
813 0 : return false;
814 : }
815 :
816 : // Step 19b.
817 0 : uncheckedResultKeys.remove(ptr);
818 : }
819 :
820 : // Step 20.
821 0 : if (!uncheckedResultKeys.empty()) {
822 0 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_CANT_REPORT_NEW);
823 0 : return false;
824 : }
825 :
826 : // Step 21.
827 0 : return props.appendAll(trapResult);
828 : }
829 :
830 : // ES8 rev 0c1bd3004329336774cbc90de727cd0cf5f11e93 9.5.10 Proxy.[[Delete]](P)
831 : bool
832 0 : ScriptedProxyHandler::delete_(JSContext* cx, HandleObject proxy, HandleId id,
833 : ObjectOpResult& result) const
834 : {
835 : // Steps 2-4.
836 0 : RootedObject handler(cx, ScriptedProxyHandler::handlerObject(proxy));
837 0 : if (!handler) {
838 0 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_PROXY_REVOKED);
839 0 : return false;
840 : }
841 :
842 : // Step 5.
843 0 : RootedObject target(cx, proxy->as<ProxyObject>().target());
844 0 : MOZ_ASSERT(target);
845 :
846 : // Step 6.
847 0 : RootedValue trap(cx);
848 0 : if (!GetProxyTrap(cx, handler, cx->names().deleteProperty, &trap))
849 0 : return false;
850 :
851 : // Step 7.
852 0 : if (trap.isUndefined())
853 0 : return DeleteProperty(cx, target, id, result);
854 :
855 : // Step 8.
856 : bool booleanTrapResult;
857 : {
858 0 : RootedValue value(cx);
859 0 : if (!IdToStringOrSymbol(cx, id, &value))
860 0 : return false;
861 :
862 0 : RootedValue targetVal(cx, ObjectValue(*target));
863 0 : RootedValue trapResult(cx);
864 0 : if (!Call(cx, trap, handler, targetVal, value, &trapResult))
865 0 : return false;
866 :
867 0 : booleanTrapResult = ToBoolean(trapResult);
868 : }
869 :
870 : // Step 9.
871 0 : if (!booleanTrapResult)
872 0 : return result.fail(JSMSG_PROXY_DELETE_RETURNED_FALSE);
873 :
874 : // Step 10.
875 0 : Rooted<PropertyDescriptor> desc(cx);
876 0 : if (!GetOwnPropertyDescriptor(cx, target, id, &desc))
877 0 : return false;
878 :
879 : // Step 12.
880 0 : if (desc.object() && !desc.configurable()) {
881 0 : RootedValue v(cx, IdToValue(id));
882 0 : ReportValueError(cx, JSMSG_CANT_DELETE, JSDVG_IGNORE_STACK, v, nullptr);
883 0 : return false;
884 : }
885 :
886 : // Steps 11,13.
887 0 : return result.succeed();
888 : }
889 :
890 : // ES8 rev 0c1bd3004329336774cbc90de727cd0cf5f11e93 9.5.7 Proxy.[[HasProperty]](P)
891 : bool
892 0 : ScriptedProxyHandler::has(JSContext* cx, HandleObject proxy, HandleId id, bool* bp) const
893 : {
894 : // Steps 2-4.
895 0 : RootedObject handler(cx, ScriptedProxyHandler::handlerObject(proxy));
896 0 : if (!handler) {
897 0 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_PROXY_REVOKED);
898 0 : return false;
899 : }
900 :
901 : // Step 5.
902 0 : RootedObject target(cx, proxy->as<ProxyObject>().target());
903 0 : MOZ_ASSERT(target);
904 :
905 : // Step 6.
906 0 : RootedValue trap(cx);
907 0 : if (!GetProxyTrap(cx, handler, cx->names().has, &trap))
908 0 : return false;
909 :
910 : // Step 7.
911 0 : if (trap.isUndefined())
912 0 : return HasProperty(cx, target, id, bp);
913 :
914 : // Step 8.
915 0 : RootedValue value(cx);
916 0 : if (!IdToStringOrSymbol(cx, id, &value))
917 0 : return false;
918 :
919 0 : RootedValue trapResult(cx);
920 0 : RootedValue targetVal(cx, ObjectValue(*target));
921 0 : if (!Call(cx, trap, handler, targetVal, value, &trapResult))
922 0 : return false;
923 :
924 0 : bool booleanTrapResult = ToBoolean(trapResult);
925 :
926 : // Step 9.
927 0 : if (!booleanTrapResult) {
928 : // Step 9a.
929 0 : Rooted<PropertyDescriptor> desc(cx);
930 0 : if (!GetOwnPropertyDescriptor(cx, target, id, &desc))
931 0 : return false;
932 :
933 : // Step 9b.
934 0 : if (desc.object()) {
935 : // Step 9b(i).
936 0 : if (!desc.configurable()) {
937 0 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_CANT_REPORT_NC_AS_NE);
938 0 : return false;
939 : }
940 :
941 : // Step 9b(ii).
942 : bool extensible;
943 0 : if (!IsExtensible(cx, target, &extensible))
944 0 : return false;
945 :
946 : // Step 9b(iii).
947 0 : if (!extensible) {
948 0 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_CANT_REPORT_E_AS_NE);
949 0 : return false;
950 : }
951 : }
952 : }
953 :
954 : // Step 10.
955 0 : *bp = booleanTrapResult;
956 0 : return true;
957 : }
958 :
959 : // ES8 rev 0c1bd3004329336774cbc90de727cd0cf5f11e93 9.5.8 Proxy.[[GetP]](P, Receiver)
960 : bool
961 15 : ScriptedProxyHandler::get(JSContext* cx, HandleObject proxy, HandleValue receiver, HandleId id,
962 : MutableHandleValue vp) const
963 : {
964 : // Steps 2-4.
965 30 : RootedObject handler(cx, ScriptedProxyHandler::handlerObject(proxy));
966 15 : if (!handler) {
967 0 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_PROXY_REVOKED);
968 0 : return false;
969 : }
970 :
971 : // Step 5.
972 30 : RootedObject target(cx, proxy->as<ProxyObject>().target());
973 15 : MOZ_ASSERT(target);
974 :
975 : // Steps 6.
976 30 : RootedValue trap(cx);
977 15 : if (!GetProxyTrap(cx, handler, cx->names().get, &trap))
978 0 : return false;
979 :
980 : // Step 7.
981 15 : if (trap.isUndefined())
982 0 : return GetProperty(cx, target, receiver, id, vp);
983 :
984 : // Step 8.
985 30 : RootedValue value(cx);
986 15 : if (!IdToStringOrSymbol(cx, id, &value))
987 0 : return false;
988 :
989 30 : RootedValue trapResult(cx);
990 : {
991 30 : FixedInvokeArgs<3> args(cx);
992 :
993 15 : args[0].setObject(*target);
994 15 : args[1].set(value);
995 15 : args[2].set(receiver);
996 :
997 30 : RootedValue thisv(cx, ObjectValue(*handler));
998 15 : if (!Call(cx, trap, thisv, args, &trapResult))
999 0 : return false;
1000 : }
1001 :
1002 : // Step 9.
1003 30 : Rooted<PropertyDescriptor> desc(cx);
1004 15 : if (!GetOwnPropertyDescriptor(cx, target, id, &desc))
1005 0 : return false;
1006 :
1007 : // Step 10.
1008 15 : if (desc.object()) {
1009 : // Step 10a.
1010 5 : if (desc.isDataDescriptor() && !desc.configurable() && !desc.writable()) {
1011 : bool same;
1012 1 : if (!SameValue(cx, trapResult, desc.value(), &same))
1013 0 : return false;
1014 1 : if (!same) {
1015 0 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_MUST_REPORT_SAME_VALUE);
1016 0 : return false;
1017 : }
1018 : }
1019 :
1020 : // Step 10b.
1021 5 : if (desc.isAccessorDescriptor() && !desc.configurable() && desc.getterObject() == nullptr) {
1022 0 : if (!trapResult.isUndefined()) {
1023 0 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_MUST_REPORT_UNDEFINED);
1024 0 : return false;
1025 : }
1026 : }
1027 : }
1028 :
1029 : // Step 11.
1030 15 : vp.set(trapResult);
1031 15 : return true;
1032 : }
1033 :
1034 : // ES8 rev 0c1bd3004329336774cbc90de727cd0cf5f11e93 9.5.9 Proxy.[[Set]](P, V, Receiver)
1035 : bool
1036 0 : ScriptedProxyHandler::set(JSContext* cx, HandleObject proxy, HandleId id, HandleValue v,
1037 : HandleValue receiver, ObjectOpResult& result) const
1038 : {
1039 : // Steps 2-4.
1040 0 : RootedObject handler(cx, ScriptedProxyHandler::handlerObject(proxy));
1041 0 : if (!handler) {
1042 0 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_PROXY_REVOKED);
1043 0 : return false;
1044 : }
1045 :
1046 : // Step 5.
1047 0 : RootedObject target(cx, proxy->as<ProxyObject>().target());
1048 0 : MOZ_ASSERT(target);
1049 :
1050 : // Step 6.
1051 0 : RootedValue trap(cx);
1052 0 : if (!GetProxyTrap(cx, handler, cx->names().set, &trap))
1053 0 : return false;
1054 :
1055 : // Step 7.
1056 0 : if (trap.isUndefined())
1057 0 : return SetProperty(cx, target, id, v, receiver, result);
1058 :
1059 : // Step 8.
1060 0 : RootedValue value(cx);
1061 0 : if (!IdToStringOrSymbol(cx, id, &value))
1062 0 : return false;
1063 :
1064 0 : RootedValue trapResult(cx);
1065 : {
1066 0 : FixedInvokeArgs<4> args(cx);
1067 :
1068 0 : args[0].setObject(*target);
1069 0 : args[1].set(value);
1070 0 : args[2].set(v);
1071 0 : args[3].set(receiver);
1072 :
1073 0 : RootedValue thisv(cx, ObjectValue(*handler));
1074 0 : if (!Call(cx, trap, thisv, args, &trapResult))
1075 0 : return false;
1076 : }
1077 :
1078 : // Step 9.
1079 0 : if (!ToBoolean(trapResult))
1080 0 : return result.fail(JSMSG_PROXY_SET_RETURNED_FALSE);
1081 :
1082 : // Step 10.
1083 0 : Rooted<PropertyDescriptor> desc(cx);
1084 0 : if (!GetOwnPropertyDescriptor(cx, target, id, &desc))
1085 0 : return false;
1086 :
1087 : // Step 11.
1088 0 : if (desc.object()) {
1089 : // Step 11a.
1090 0 : if (desc.isDataDescriptor() && !desc.configurable() && !desc.writable()) {
1091 : bool same;
1092 0 : if (!SameValue(cx, v, desc.value(), &same))
1093 0 : return false;
1094 0 : if (!same) {
1095 0 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_CANT_SET_NW_NC);
1096 0 : return false;
1097 : }
1098 : }
1099 :
1100 : // Step 11b.
1101 0 : if (desc.isAccessorDescriptor() && !desc.configurable() && desc.setterObject() == nullptr) {
1102 0 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_CANT_SET_WO_SETTER);
1103 0 : return false;
1104 : }
1105 : }
1106 :
1107 : // Step 12.
1108 0 : return result.succeed();
1109 : }
1110 :
1111 : // ES7 0c1bd3004329336774cbc90de727cd0cf5f11e93 9.5.13 Proxy.[[Call]]
1112 : bool
1113 0 : ScriptedProxyHandler::call(JSContext* cx, HandleObject proxy, const CallArgs& args) const
1114 : {
1115 : // Steps 1-3.
1116 0 : RootedObject handler(cx, ScriptedProxyHandler::handlerObject(proxy));
1117 0 : if (!handler) {
1118 0 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_PROXY_REVOKED);
1119 0 : return false;
1120 : }
1121 :
1122 : // Step 4.
1123 0 : RootedObject target(cx, proxy->as<ProxyObject>().target());
1124 0 : MOZ_ASSERT(target);
1125 0 : MOZ_ASSERT(target->isCallable());
1126 :
1127 : // Step 5.
1128 0 : RootedValue trap(cx);
1129 0 : if (!GetProxyTrap(cx, handler, cx->names().apply, &trap))
1130 0 : return false;
1131 :
1132 : // Step 6.
1133 0 : if (trap.isUndefined()) {
1134 0 : InvokeArgs iargs(cx);
1135 0 : if (!FillArgumentsFromArraylike(cx, iargs, args))
1136 0 : return false;
1137 :
1138 0 : RootedValue fval(cx, ObjectValue(*target));
1139 0 : return js::Call(cx, fval, args.thisv(), iargs, args.rval());
1140 : }
1141 :
1142 : // Step 7.
1143 0 : RootedObject argArray(cx, NewDenseCopiedArray(cx, args.length(), args.array()));
1144 0 : if (!argArray)
1145 0 : return false;
1146 :
1147 : // Step 8.
1148 0 : FixedInvokeArgs<3> iargs(cx);
1149 :
1150 0 : iargs[0].setObject(*target);
1151 0 : iargs[1].set(args.thisv());
1152 0 : iargs[2].setObject(*argArray);
1153 :
1154 0 : RootedValue thisv(cx, ObjectValue(*handler));
1155 0 : return js::Call(cx, trap, thisv, iargs, args.rval());
1156 : }
1157 :
1158 : // ES7 0c1bd3004329336774cbc90de727cd0cf5f11e93 9.5.14 Proxy.[[Construct]]
1159 : bool
1160 0 : ScriptedProxyHandler::construct(JSContext* cx, HandleObject proxy, const CallArgs& args) const
1161 : {
1162 : // Steps 1-3.
1163 0 : RootedObject handler(cx, ScriptedProxyHandler::handlerObject(proxy));
1164 0 : if (!handler) {
1165 0 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_PROXY_REVOKED);
1166 0 : return false;
1167 : }
1168 :
1169 : // Step 4.
1170 0 : RootedObject target(cx, proxy->as<ProxyObject>().target());
1171 0 : MOZ_ASSERT(target);
1172 0 : MOZ_ASSERT(target->isConstructor());
1173 :
1174 : // Step 5.
1175 0 : RootedValue trap(cx);
1176 0 : if (!GetProxyTrap(cx, handler, cx->names().construct, &trap))
1177 0 : return false;
1178 :
1179 : // Step 6.
1180 0 : if (trap.isUndefined()) {
1181 0 : ConstructArgs cargs(cx);
1182 0 : if (!FillArgumentsFromArraylike(cx, cargs, args))
1183 0 : return false;
1184 :
1185 0 : RootedValue targetv(cx, ObjectValue(*target));
1186 0 : RootedObject obj(cx);
1187 0 : if (!Construct(cx, targetv, cargs, args.newTarget(), &obj))
1188 0 : return false;
1189 :
1190 0 : args.rval().setObject(*obj);
1191 0 : return true;
1192 : }
1193 :
1194 : // Step 7.
1195 0 : RootedObject argArray(cx, NewDenseCopiedArray(cx, args.length(), args.array()));
1196 0 : if (!argArray)
1197 0 : return false;
1198 :
1199 : // Steps 8, 10.
1200 : {
1201 0 : FixedInvokeArgs<3> iargs(cx);
1202 :
1203 0 : iargs[0].setObject(*target);
1204 0 : iargs[1].setObject(*argArray);
1205 0 : iargs[2].set(args.newTarget());
1206 :
1207 0 : RootedValue thisv(cx, ObjectValue(*handler));
1208 0 : if (!Call(cx, trap, thisv, iargs, args.rval()))
1209 0 : return false;
1210 : }
1211 :
1212 : // Step 9.
1213 0 : if (!args.rval().isObject()) {
1214 0 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_PROXY_CONSTRUCT_OBJECT);
1215 0 : return false;
1216 : }
1217 :
1218 0 : return true;
1219 : }
1220 :
1221 : bool
1222 0 : ScriptedProxyHandler::nativeCall(JSContext* cx, IsAcceptableThis test, NativeImpl impl,
1223 : const CallArgs& args) const
1224 : {
1225 0 : ReportIncompatible(cx, args);
1226 0 : return false;
1227 : }
1228 :
1229 : bool
1230 0 : ScriptedProxyHandler::hasInstance(JSContext* cx, HandleObject proxy, MutableHandleValue v,
1231 : bool* bp) const
1232 : {
1233 0 : return InstanceOfOperator(cx, proxy, v, bp);
1234 : }
1235 :
1236 : bool
1237 0 : ScriptedProxyHandler::getBuiltinClass(JSContext* cx, HandleObject proxy,
1238 : ESClass* cls) const
1239 : {
1240 0 : *cls = ESClass::Other;
1241 0 : return true;
1242 : }
1243 :
1244 : bool
1245 0 : ScriptedProxyHandler::isArray(JSContext* cx, HandleObject proxy, IsArrayAnswer* answer) const
1246 : {
1247 0 : RootedObject target(cx, proxy->as<ProxyObject>().target());
1248 0 : if (target)
1249 0 : return JS::IsArray(cx, target, answer);
1250 :
1251 0 : *answer = IsArrayAnswer::RevokedProxy;
1252 0 : return true;
1253 : }
1254 :
1255 : const char*
1256 0 : ScriptedProxyHandler::className(JSContext* cx, HandleObject proxy) const
1257 : {
1258 : // Right now the caller is not prepared to handle failures.
1259 0 : return BaseProxyHandler::className(cx, proxy);
1260 : }
1261 :
1262 : JSString*
1263 0 : ScriptedProxyHandler::fun_toString(JSContext* cx, HandleObject proxy, unsigned indent) const
1264 : {
1265 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_INCOMPATIBLE_PROTO,
1266 0 : js_Function_str, js_toString_str, "object");
1267 0 : return nullptr;
1268 : }
1269 :
1270 : RegExpShared*
1271 0 : ScriptedProxyHandler::regexp_toShared(JSContext* cx, HandleObject proxy) const
1272 : {
1273 0 : MOZ_CRASH("Should not end up in ScriptedProxyHandler::regexp_toShared");
1274 : }
1275 :
1276 : bool
1277 0 : ScriptedProxyHandler::boxedValue_unbox(JSContext* cx, HandleObject proxy,
1278 : MutableHandleValue vp) const
1279 : {
1280 0 : MOZ_CRASH("Should not end up in ScriptedProxyHandler::boxedValue_unbox");
1281 : return false;
1282 : }
1283 :
1284 : bool
1285 0 : ScriptedProxyHandler::isCallable(JSObject* obj) const
1286 : {
1287 0 : MOZ_ASSERT(obj->as<ProxyObject>().handler() == &ScriptedProxyHandler::singleton);
1288 0 : uint32_t callConstruct = obj->as<ProxyObject>().reservedSlot(IS_CALLCONSTRUCT_EXTRA).toPrivateUint32();
1289 0 : return !!(callConstruct & IS_CALLABLE);
1290 : }
1291 :
1292 : bool
1293 0 : ScriptedProxyHandler::isConstructor(JSObject* obj) const
1294 : {
1295 0 : MOZ_ASSERT(obj->as<ProxyObject>().handler() == &ScriptedProxyHandler::singleton);
1296 0 : uint32_t callConstruct = obj->as<ProxyObject>().reservedSlot(IS_CALLCONSTRUCT_EXTRA).toPrivateUint32();
1297 0 : return !!(callConstruct & IS_CONSTRUCTOR);
1298 : }
1299 :
1300 : const char ScriptedProxyHandler::family = 0;
1301 : const ScriptedProxyHandler ScriptedProxyHandler::singleton;
1302 :
1303 : bool
1304 8 : IsRevokedScriptedProxy(JSObject* obj)
1305 : {
1306 8 : obj = CheckedUnwrap(obj);
1307 8 : return obj && IsScriptedProxy(obj) && !obj->as<ProxyObject>().target();
1308 : }
1309 :
1310 : // ES8 rev 0c1bd3004329336774cbc90de727cd0cf5f11e93 9.5.14 ProxyCreate.
1311 : static bool
1312 4 : ProxyCreate(JSContext* cx, CallArgs& args, const char* callerName)
1313 : {
1314 4 : if (args.length() < 2) {
1315 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_MORE_ARGS_NEEDED,
1316 0 : callerName, "1", "s");
1317 0 : return false;
1318 : }
1319 :
1320 : // Step 1.
1321 8 : RootedObject target(cx, NonNullObjectArg(cx, "`target`", callerName, args[0]));
1322 4 : if (!target)
1323 0 : return false;
1324 :
1325 : // Step 2.
1326 4 : if (IsRevokedScriptedProxy(target)) {
1327 0 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_PROXY_ARG_REVOKED, "1");
1328 0 : return false;
1329 : }
1330 :
1331 : // Step 3.
1332 8 : RootedObject handler(cx, NonNullObjectArg(cx, "`handler`", callerName, args[1]));
1333 4 : if (!handler)
1334 0 : return false;
1335 :
1336 : // Step 4.
1337 4 : if (IsRevokedScriptedProxy(handler)) {
1338 0 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_PROXY_ARG_REVOKED, "2");
1339 0 : return false;
1340 : }
1341 :
1342 : // Steps 5-6, 8.
1343 8 : RootedValue priv(cx, ObjectValue(*target));
1344 : JSObject* proxy_ =
1345 4 : NewProxyObject(cx, &ScriptedProxyHandler::singleton, priv, TaggedProto::LazyProto);
1346 4 : if (!proxy_)
1347 0 : return false;
1348 :
1349 : // Step 9 (reordered).
1350 8 : Rooted<ProxyObject*> proxy(cx, &proxy_->as<ProxyObject>());
1351 4 : proxy->setReservedSlot(ScriptedProxyHandler::HANDLER_EXTRA, ObjectValue(*handler));
1352 :
1353 : // Step 7.
1354 4 : uint32_t callable = target->isCallable() ? ScriptedProxyHandler::IS_CALLABLE : 0;
1355 4 : uint32_t constructor = target->isConstructor() ? ScriptedProxyHandler::IS_CONSTRUCTOR : 0;
1356 4 : proxy->setReservedSlot(ScriptedProxyHandler::IS_CALLCONSTRUCT_EXTRA,
1357 8 : PrivateUint32Value(callable | constructor));
1358 :
1359 : // Step 10.
1360 4 : args.rval().setObject(*proxy);
1361 4 : return true;
1362 : }
1363 :
1364 : bool
1365 4 : js::proxy(JSContext* cx, unsigned argc, Value* vp)
1366 : {
1367 4 : CallArgs args = CallArgsFromVp(argc, vp);
1368 :
1369 4 : if (!ThrowIfNotConstructing(cx, args, "Proxy"))
1370 0 : return false;
1371 :
1372 4 : return ProxyCreate(cx, args, "Proxy");
1373 : }
1374 :
1375 : static bool
1376 0 : RevokeProxy(JSContext* cx, unsigned argc, Value* vp)
1377 : {
1378 0 : CallArgs args = CallArgsFromVp(argc, vp);
1379 :
1380 0 : RootedFunction func(cx, &args.callee().as<JSFunction>());
1381 0 : RootedObject p(cx, func->getExtendedSlot(ScriptedProxyHandler::REVOKE_SLOT).toObjectOrNull());
1382 :
1383 0 : if (p) {
1384 0 : func->setExtendedSlot(ScriptedProxyHandler::REVOKE_SLOT, NullValue());
1385 :
1386 0 : MOZ_ASSERT(p->is<ProxyObject>());
1387 :
1388 0 : p->as<ProxyObject>().setSameCompartmentPrivate(NullValue());
1389 0 : p->as<ProxyObject>().setReservedSlot(ScriptedProxyHandler::HANDLER_EXTRA, NullValue());
1390 : }
1391 :
1392 0 : args.rval().setUndefined();
1393 0 : return true;
1394 : }
1395 :
1396 : bool
1397 0 : js::proxy_revocable(JSContext* cx, unsigned argc, Value* vp)
1398 : {
1399 0 : CallArgs args = CallArgsFromVp(argc, vp);
1400 :
1401 0 : if (!ProxyCreate(cx, args, "Proxy.revocable"))
1402 0 : return false;
1403 :
1404 0 : RootedValue proxyVal(cx, args.rval());
1405 0 : MOZ_ASSERT(proxyVal.toObject().is<ProxyObject>());
1406 :
1407 0 : RootedObject revoker(cx, NewFunctionByIdWithReserved(cx, RevokeProxy, 0, 0,
1408 0 : AtomToId(cx->names().revoke)));
1409 0 : if (!revoker)
1410 0 : return false;
1411 :
1412 0 : revoker->as<JSFunction>().initExtendedSlot(ScriptedProxyHandler::REVOKE_SLOT, proxyVal);
1413 :
1414 0 : RootedPlainObject result(cx, NewBuiltinClassInstance<PlainObject>(cx));
1415 0 : if (!result)
1416 0 : return false;
1417 :
1418 0 : RootedValue revokeVal(cx, ObjectValue(*revoker));
1419 0 : if (!DefineProperty(cx, result, cx->names().proxy, proxyVal) ||
1420 0 : !DefineProperty(cx, result, cx->names().revoke, revokeVal))
1421 : {
1422 0 : return false;
1423 : }
1424 :
1425 0 : args.rval().setObject(*result);
1426 0 : return true;
1427 : }
|