Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qv4proxy.cpp
Go to the documentation of this file.
1// Copyright (C) 2018 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3// Qt-Security score:significant
4
5
6#include "qv4proxy_p.h"
7#include "qv4symbol_p.h"
8#include "qv4jscall_p.h"
12
13using namespace QV4;
14
18
19void Heap::ProxyObject::init(const QV4::Object *target, const QV4::Object *handler)
20{
21 Object::init();
22 ExecutionEngine *e = internalClass->engine;
23 this->target.set(e, target->d());
24 this->handler.set(e, handler->d());
25}
26
27void Heap::ProxyFunctionObject::init(const QV4::FunctionObject *target, const QV4::Object *handler)
28{
29 ExecutionEngine *e = internalClass->engine;
30 FunctionObject::init(e);
31 this->target.set(e, target->d());
32 this->handler.set(e, handler->d());
33}
34
35
36ReturnedValue ProxyObject::virtualGet(const Managed *m, PropertyKey id, const Value *receiver, bool *hasProperty)
37{
38 Scope scope(m);
39 const ProxyObject *o = static_cast<const ProxyObject *>(m);
40 if (!o->d()->handler)
41 return scope.engine->throwTypeError();
42
43 ScopedObject target(scope, o->d()->target);
44 Q_ASSERT(target);
45 ScopedObject handler(scope, o->d()->handler);
46 ScopedValue trap(scope, handler->get(scope.engine->id_get()));
47 if (scope.hasException())
48 return Encode::undefined();
49 if (trap->isNullOrUndefined())
50 return target->get(id, receiver, hasProperty);
51 if (!trap->isFunctionObject())
52 return scope.engine->throwTypeError();
53 if (hasProperty)
54 *hasProperty = true;
55
56 Value *args = scope.constructUndefined(3);
57 args[0] = target;
58 args[1] = id.toStringOrSymbol(scope.engine);
59 args[2] = *receiver;
60 JSCallData cdata(handler, args, 3);
61
62 ScopedValue trapResult(scope, static_cast<const FunctionObject *>(trap.ptr)->call(cdata));
63 if (scope.hasException())
64 return Encode::undefined();
65 ScopedProperty targetDesc(scope);
66 PropertyAttributes attributes = target->getOwnProperty(id, targetDesc);
67 if (attributes != Attr_Invalid && !attributes.isConfigurable()) {
68 if (attributes.isData() && !attributes.isWritable()) {
69 if (!trapResult->sameValue(targetDesc->value))
70 return scope.engine->throwTypeError();
71 }
72 if (attributes.isAccessor() && targetDesc->value.isUndefined()) {
73 if (!trapResult->isUndefined())
74 return scope.engine->throwTypeError();
75 }
76 }
77 return trapResult->asReturnedValue();
78}
79
80bool ProxyObject::virtualPut(Managed *m, PropertyKey id, const Value &value, Value *receiver)
81{
82 Scope scope(m);
83 const ProxyObject *o = static_cast<const ProxyObject *>(m);
84 if (!o->d()->handler)
85 return scope.engine->throwTypeError();
86
87 ScopedObject target(scope, o->d()->target);
88 Q_ASSERT(target);
89 ScopedObject handler(scope, o->d()->handler);
90 ScopedValue trap(scope, handler->get(scope.engine->id_set()));
91 if (scope.hasException())
92 return Encode::undefined();
93 if (trap->isNullOrUndefined())
94 return target->put(id, value, receiver);
95 if (!trap->isFunctionObject())
96 return scope.engine->throwTypeError();
97
98 Value *args = scope.constructUndefined(4);
99 args[0] = target;
100 args[1] = id.toStringOrSymbol(scope.engine);
101 args[2] = value;
102 args[3] = *receiver;
103 JSCallData cdata(handler, args, 4);
104
105 ScopedValue trapResult(scope, static_cast<const FunctionObject *>(trap.ptr)->call(cdata));
106 if (scope.hasException() || !trapResult->toBoolean())
107 return false;
108 ScopedProperty targetDesc(scope);
109 PropertyAttributes attributes = target->getOwnProperty(id, targetDesc);
110 if (attributes != Attr_Invalid && !attributes.isConfigurable()) {
111 if (attributes.isData() && !attributes.isWritable()) {
112 if (!value.sameValue(targetDesc->value))
113 return scope.engine->throwTypeError();
114 }
115 if (attributes.isAccessor() && targetDesc->set.isUndefined())
116 return scope.engine->throwTypeError();
117 }
118 return true;
119}
120
121bool ProxyObject::virtualDeleteProperty(Managed *m, PropertyKey id)
122{
123 Scope scope(m);
124 const ProxyObject *o = static_cast<const ProxyObject *>(m);
125 if (!o->d()->handler)
126 return scope.engine->throwTypeError();
127
128 ScopedObject target(scope, o->d()->target);
129 Q_ASSERT(target);
130 ScopedObject handler(scope, o->d()->handler);
131 ScopedString deleteProp(scope, scope.engine->newString(QStringLiteral("deleteProperty")));
132 ScopedValue trap(scope, handler->get(deleteProp));
133 if (scope.hasException())
134 return Encode::undefined();
135 if (trap->isNullOrUndefined())
136 return target->deleteProperty(id);
137 if (!trap->isFunctionObject())
138 return scope.engine->throwTypeError();
139
140 Value *args = scope.constructUndefined(3);
141 args[0] = target;
142 args[1] = id.toStringOrSymbol(scope.engine);
143 args[2] = o->d(); // ### fix receiver handling
144 JSCallData cdata(handler, args, 3);
145
146 ScopedValue trapResult(scope, static_cast<const FunctionObject *>(trap.ptr)->call(cdata));
147 if (scope.hasException() || !trapResult->toBoolean())
148 return false;
149 ScopedProperty targetDesc(scope);
150 PropertyAttributes attributes = target->getOwnProperty(id, targetDesc);
151 if (attributes == Attr_Invalid)
152 return true;
153 if (!attributes.isConfigurable())
154 return scope.engine->throwTypeError();
155 return true;
156}
157
158bool ProxyObject::virtualHasProperty(const Managed *m, PropertyKey id)
159{
160 Scope scope(m);
161 const ProxyObject *o = static_cast<const ProxyObject *>(m);
162 if (!o->d()->handler)
163 return scope.engine->throwTypeError();
164
165 ScopedObject target(scope, o->d()->target);
166 Q_ASSERT(target);
167 ScopedObject handler(scope, o->d()->handler);
168 ScopedString hasProp(scope, scope.engine->newString(QStringLiteral("has")));
169 ScopedValue trap(scope, handler->get(hasProp));
170 if (scope.hasException())
171 return Encode::undefined();
172 if (trap->isNullOrUndefined())
173 return target->hasProperty(id);
174 if (!trap->isFunctionObject())
175 return scope.engine->throwTypeError();
176
177 Value *args = scope.constructUndefined(2);
178 args[0] = target;
179 args[1] = id.isArrayIndex() ? Value::fromUInt32(id.asArrayIndex()).toString(scope.engine) : id.asStringOrSymbol();
180 JSCallData cdata(handler, args, 2);
181
182 ScopedValue trapResult(scope, static_cast<const FunctionObject *>(trap.ptr)->call(cdata));
183 if (scope.hasException())
184 return false;
185 bool result = trapResult->toBoolean();
186 if (!result) {
187 ScopedProperty targetDesc(scope);
188 PropertyAttributes attributes = target->getOwnProperty(id, targetDesc);
189 if (attributes != Attr_Invalid) {
190 if (!attributes.isConfigurable() || !target->isExtensible())
191 return scope.engine->throwTypeError();
192 }
193 }
194 return result;
195}
196
197PropertyAttributes ProxyObject::virtualGetOwnProperty(const Managed *m, PropertyKey id, Property *p)
198{
199 Scope scope(m);
200 const ProxyObject *o = static_cast<const ProxyObject *>(m);
201 if (!o->d()->handler) {
202 scope.engine->throwTypeError();
203 return Attr_Invalid;
204 }
205
206 ScopedObject target(scope, o->d()->target);
207 Q_ASSERT(target);
208 ScopedObject handler(scope, o->d()->handler);
209 ScopedString deleteProp(scope, scope.engine->newString(QStringLiteral("getOwnPropertyDescriptor")));
210 ScopedValue trap(scope, handler->get(deleteProp));
211 if (scope.hasException())
212 return Attr_Invalid;
213 if (trap->isNullOrUndefined())
214 return target->getOwnProperty(id, p);
215 if (!trap->isFunctionObject()) {
216 scope.engine->throwTypeError();
217 return Attr_Invalid;
218 }
219
220 Value *args = scope.constructUndefined(2);
221 args[0] = target;
222 args[1] = id.isArrayIndex() ? Value::fromUInt32(id.asArrayIndex()).toString(scope.engine) : id.asStringOrSymbol();
223 JSCallData cdata(handler, args, 2);
224
225 ScopedValue trapResult(scope, static_cast<const FunctionObject *>(trap.ptr)->call(cdata));
226 if (scope.hasException())
227 return Attr_Invalid;
228 if (!trapResult->isObject() && !trapResult->isUndefined()) {
229 scope.engine->throwTypeError();
230 return Attr_Invalid;
231 }
232
233 ScopedProperty targetDesc(scope);
234 PropertyAttributes targetAttributes = target->getOwnProperty(id, targetDesc);
235 if (trapResult->isUndefined()) {
236 if (p)
237 p->value = Encode::undefined();
238 if (targetAttributes == Attr_Invalid) {
239 return Attr_Invalid;
240 }
241 if (!targetAttributes.isConfigurable() || !target->isExtensible()) {
242 scope.engine->throwTypeError();
243 return Attr_Invalid;
244 }
245 return Attr_Invalid;
246 }
247
248 //bool extensibleTarget = target->isExtensible();
249 ScopedProperty resultDesc(scope);
250 PropertyAttributes resultAttributes;
251 ObjectPrototype::toPropertyDescriptor(scope.engine, trapResult, resultDesc, &resultAttributes);
252 resultDesc->completed(&resultAttributes);
253
254 if (!targetDesc->isCompatible(targetAttributes, resultDesc, resultAttributes)) {
255 scope.engine->throwTypeError();
256 return Attr_Invalid;
257 }
258
259 if (!resultAttributes.isConfigurable()) {
260 if (targetAttributes == Attr_Invalid || targetAttributes.isConfigurable()) {
261 scope.engine->throwTypeError();
262 return Attr_Invalid;
263 }
264 }
265
266 if (p) {
267 p->value = resultDesc->value;
268 p->set = resultDesc->set;
269 }
270 return resultAttributes;
271}
272
273bool ProxyObject::virtualDefineOwnProperty(Managed *m, PropertyKey id, const Property *p, PropertyAttributes attrs)
274{
275 Scope scope(m);
276 const ProxyObject *o = static_cast<const ProxyObject *>(m);
277 if (!o->d()->handler) {
278 scope.engine->throwTypeError();
279 return false;
280 }
281
282 ScopedObject target(scope, o->d()->target);
283 Q_ASSERT(target);
284 ScopedObject handler(scope, o->d()->handler);
285 ScopedString prop(scope, scope.engine->newString(QStringLiteral("defineProperty")));
286 ScopedValue trap(scope, handler->get(prop));
287 if (scope.hasException())
288 return false;
289 if (trap->isNullOrUndefined())
290 return target->defineOwnProperty(id, p, attrs);
291 if (!trap->isFunctionObject()) {
292 scope.engine->throwTypeError();
293 return false;
294 }
295
296 Value *args = scope.constructUndefined(3);
297 args[0] = target;
298 args[1] = id.isArrayIndex() ? Value::fromUInt32(id.asArrayIndex()).toString(scope.engine) : id.asStringOrSymbol();
299 args[2] = ObjectPrototype::fromPropertyDescriptor(scope.engine, p, attrs);
300 JSCallData cdata(handler, args, 3);
301
302 ScopedValue trapResult(scope, static_cast<const FunctionObject *>(trap.ptr)->call(cdata));
303 bool result = !scope.hasException() && trapResult->toBoolean();
304 if (!result)
305 return false;
306
307 ScopedProperty targetDesc(scope);
308 PropertyAttributes targetAttributes = target->getOwnProperty(id, targetDesc);
309 bool extensibleTarget = target->isExtensible();
310 bool settingConfigFalse = attrs.hasConfigurable() && !attrs.isConfigurable();
311 if (targetAttributes == Attr_Invalid) {
312 if (!extensibleTarget || settingConfigFalse) {
313 scope.engine->throwTypeError();
314 return false;
315 }
316 } else {
317 if (!targetDesc->isCompatible(targetAttributes, p, attrs)) {
318 scope.engine->throwTypeError();
319 return false;
320 }
321 if (settingConfigFalse && targetAttributes.isConfigurable()) {
322 scope.engine->throwTypeError();
323 return false;
324 }
325 }
326
327 return true;
328}
329
330bool ProxyObject::virtualIsExtensible(const Managed *m)
331{
332 Scope scope(m);
333 const ProxyObject *o = static_cast<const ProxyObject *>(m);
334 if (!o->d()->handler)
335 return scope.engine->throwTypeError();
336
337 ScopedObject target(scope, o->d()->target);
338 Q_ASSERT(target);
339 ScopedObject handler(scope, o->d()->handler);
340 ScopedString hasProp(scope, scope.engine->newString(QStringLiteral("isExtensible")));
341 ScopedValue trap(scope, handler->get(hasProp));
342 if (scope.hasException())
343 return Encode::undefined();
344 if (trap->isNullOrUndefined())
345 return target->isExtensible();
346 if (!trap->isFunctionObject())
347 return scope.engine->throwTypeError();
348
349 Value *args = scope.constructUndefined(1);
350 args[0] = target;
351 JSCallData cdata(handler, args, 1);
352
353 ScopedValue trapResult(scope, static_cast<const FunctionObject *>(trap.ptr)->call(cdata));
354 if (scope.hasException())
355 return false;
356 bool result = trapResult->toBoolean();
357 if (result != target->isExtensible()) {
358 scope.engine->throwTypeError();
359 return false;
360 }
361 return result;
362}
363
364bool ProxyObject::virtualPreventExtensions(Managed *m)
365{
366 Scope scope(m);
367 const ProxyObject *o = static_cast<const ProxyObject *>(m);
368 if (!o->d()->handler)
369 return scope.engine->throwTypeError();
370
371 ScopedObject target(scope, o->d()->target);
372 Q_ASSERT(target);
373 ScopedObject handler(scope, o->d()->handler);
374 ScopedString hasProp(scope, scope.engine->newString(QStringLiteral("preventExtensions")));
375 ScopedValue trap(scope, handler->get(hasProp));
376 if (scope.hasException())
377 return Encode::undefined();
378 if (trap->isNullOrUndefined())
379 return target->preventExtensions();
380 if (!trap->isFunctionObject())
381 return scope.engine->throwTypeError();
382
383 Value *args = scope.constructUndefined(1);
384 args[0] = target;
385 JSCallData cdata(handler, args, 1);
386
387 ScopedValue trapResult(scope, static_cast<const FunctionObject *>(trap.ptr)->call(cdata));
388 if (scope.hasException())
389 return false;
390 bool result = trapResult->toBoolean();
391 if (result && target->isExtensible()) {
392 scope.engine->throwTypeError();
393 return false;
394 }
395 return result;
396}
397
398Heap::Object *ProxyObject::virtualGetPrototypeOf(const Managed *m)
399{
400 Scope scope(m);
401 const ProxyObject *o = static_cast<const ProxyObject *>(m);
402 if (!o->d()->handler) {
403 scope.engine->throwTypeError();
404 return nullptr;
405 }
406
407 ScopedObject target(scope, o->d()->target);
408 Q_ASSERT(target);
409 ScopedObject handler(scope, o->d()->handler);
410 ScopedString name(scope, scope.engine->newString(QStringLiteral("getPrototypeOf")));
411 ScopedValue trap(scope, handler->get(name));
412 if (scope.hasException())
413 return nullptr;
414 if (trap->isNullOrUndefined())
415 return target->getPrototypeOf();
416 if (!trap->isFunctionObject()) {
417 scope.engine->throwTypeError();
418 return nullptr;
419 }
420
421 Value *args = scope.constructUndefined(1);
422 args[0] = target;
423 JSCallData cdata(handler, args, 1);
424
425 ScopedValue trapResult(scope, static_cast<const FunctionObject *>(trap.ptr)->call(cdata));
426 if (scope.hasException())
427 return nullptr;
428 if (!trapResult->isNull() && !trapResult->isObject()) {
429 scope.engine->throwTypeError();
430 return nullptr;
431 }
432 Heap::Object *proto = trapResult->isNull() ? nullptr : static_cast<Heap::Object *>(trapResult->heapObject());
433 if (!target->isExtensible()) {
434 Heap::Object *targetProto = target->getPrototypeOf();
435 if (proto != targetProto) {
436 scope.engine->throwTypeError();
437 return nullptr;
438 }
439 }
440 return proto;
441}
442
443bool ProxyObject::virtualSetPrototypeOf(Managed *m, const Object *p)
444{
445 Scope scope(m);
446 const ProxyObject *o = static_cast<const ProxyObject *>(m);
447 if (!o->d()->handler) {
448 scope.engine->throwTypeError();
449 return false;
450 }
451
452 ScopedObject target(scope, o->d()->target);
453 Q_ASSERT(target);
454 ScopedObject handler(scope, o->d()->handler);
455 ScopedString name(scope, scope.engine->newString(QStringLiteral("setPrototypeOf")));
456 ScopedValue trap(scope, handler->get(name));
457 if (scope.hasException())
458 return false;
459 if (trap->isNullOrUndefined())
460 return target->setPrototypeOf(p);
461 if (!trap->isFunctionObject()) {
462 scope.engine->throwTypeError();
463 return false;
464 }
465
466 Value *args = scope.constructUndefined(2);
467 args[0] = target;
468 args[1] = p ? p->asReturnedValue() : Encode::null();
469 JSCallData cdata(handler, args, 2);
470
471 ScopedValue trapResult(scope, static_cast<const FunctionObject *>(trap.ptr)->call(cdata));
472 bool result = !scope.hasException() && trapResult->toBoolean();
473 if (!result)
474 return false;
475 if (!target->isExtensible()) {
476 Heap::Object *targetProto = target->getPrototypeOf();
477 if (p->d() != targetProto) {
478 scope.engine->throwTypeError();
479 return false;
480 }
481 }
482 return true;
483}
484
485namespace {
486
487struct ProxyObjectOwnPropertyKeyIterator : OwnPropertyKeyIterator
488{
489 PersistentValue ownKeys;
490 uint index = 0;
491 uint len = 0;
492
493 ProxyObjectOwnPropertyKeyIterator(ArrayObject *keys);
494 ~ProxyObjectOwnPropertyKeyIterator() override = default;
495 PropertyKey next(const Object *o, Property *pd = nullptr, PropertyAttributes *attrs = nullptr) override;
496};
497
498ProxyObjectOwnPropertyKeyIterator::ProxyObjectOwnPropertyKeyIterator(ArrayObject *keys)
499{
500 ownKeys = keys;
501 len = keys->getLength();
502}
503
504PropertyKey ProxyObjectOwnPropertyKeyIterator::next(const Object *m, Property *pd, PropertyAttributes *attrs)
505{
506 if (index >= len || m == nullptr)
507 return PropertyKey::invalid();
508
509 Scope scope(m);
510 ScopedObject keys(scope, ownKeys.asManaged());
511 PropertyKey key = PropertyKey::fromId(keys->get(PropertyKey::fromArrayIndex(index)));
512 ++index;
513
514 if (pd || attrs) {
515 ScopedProperty p(scope);
516 PropertyAttributes a = const_cast<Object *>(m)->getOwnProperty(key, pd ? pd : p);
517 if (attrs)
518 *attrs = a;
519 }
520
521 return key;
522}
523
524bool removeAllOccurrences(ArrayObject *target, ReturnedValue val)
525{
526 uint len = target->getLength();
527 bool found = false;
528 for (uint i = 0; i < len; ++i) {
529 ReturnedValue v = target->get(i);
530 if (v == val) {
531 found = true;
532 target->put(i, Value::undefinedValue());
533 }
534 }
535 return found;
536}
537
538} // namespace
539
540OwnPropertyKeyIterator *ProxyObject::virtualOwnPropertyKeys(const Object *m, Value *iteratorTarget)
541{
542 Scope scope(m);
543 const ProxyObject *o = static_cast<const ProxyObject *>(m);
544 if (!o->d()->handler) {
545 scope.engine->throwTypeError();
546 return nullptr;
547 }
548
549 ScopedObject target(scope, o->d()->target);
550 Q_ASSERT(target);
551 ScopedObject handler(scope, o->d()->handler);
552 ScopedString name(scope, scope.engine->newString(QStringLiteral("ownKeys")));
553 ScopedValue trap(scope, handler->get(name));
554
555 if (scope.hasException())
556 return nullptr;
557 if (trap->isUndefined())
558 return target->ownPropertyKeys(iteratorTarget);
559 if (!trap->isFunctionObject()) {
560 scope.engine->throwTypeError();
561 return nullptr;
562 }
563
564 Value *args = scope.constructUndefined(1);
565 args[0] = target;
566 JSCallData cdata(handler, args, 1);
567 ScopedObject trapResult(scope, static_cast<const FunctionObject *>(trap.ptr)->call(cdata));
568 if (scope.hasException())
569 return nullptr;
570 if (!trapResult) {
571 scope.engine->throwTypeError();
572 return nullptr;
573 }
574
575 uint len = trapResult->getLength();
576 ScopedArrayObject trapKeys(scope, scope.engine->newArrayObject());
577 ScopedStringOrSymbol key(scope);
578 for (uint i = 0; i < len; ++i) {
579 key = trapResult->get(i);
580 if (scope.hasException())
581 return nullptr;
582 if (!key) {
583 scope.engine->throwTypeError();
584 return nullptr;
585 }
586 Value keyAsValue = Value::fromReturnedValue(key->toPropertyKey().id());
587 trapKeys->push_back(keyAsValue);
588 }
589
590 ScopedArrayObject targetConfigurableKeys(scope, scope.engine->newArrayObject());
591 ScopedArrayObject targetNonConfigurableKeys(scope, scope.engine->newArrayObject());
592 ObjectIterator it(scope, target, ObjectIterator::EnumerableOnly);
593 ScopedPropertyKey k(scope);
594 while (1) {
595 PropertyAttributes attrs;
596 k = it.next(nullptr, &attrs);
597 if (!k->isValid())
598 break;
599 Value keyAsValue = Value::fromReturnedValue(k->id());
600 if (attrs.isConfigurable())
601 targetConfigurableKeys->push_back(keyAsValue);
602 else
603 targetNonConfigurableKeys->push_back(keyAsValue);
604 }
605 if (target->isExtensible() && targetNonConfigurableKeys->getLength() == 0) {
606 *iteratorTarget = *m;
607 return new ProxyObjectOwnPropertyKeyIterator(trapKeys);
608 }
609
610 ScopedArrayObject uncheckedResultKeys(scope, scope.engine->newArrayObject());
611 uncheckedResultKeys->copyArrayData(trapKeys);
612
613 len = targetNonConfigurableKeys->getLength();
614 for (uint i = 0; i < len; ++i) {
615 k = PropertyKey::fromId(targetNonConfigurableKeys->get(i));
616 if (!removeAllOccurrences(uncheckedResultKeys, k->id())) {
617 scope.engine->throwTypeError();
618 return nullptr;
619 }
620 }
621
622 if (target->isExtensible()) {
623 *iteratorTarget = *m;
624 return new ProxyObjectOwnPropertyKeyIterator(trapKeys);
625 }
626
627 len = targetConfigurableKeys->getLength();
628 for (uint i = 0; i < len; ++i) {
629 k = PropertyKey::fromId(targetConfigurableKeys->get(i));
630 if (!removeAllOccurrences(uncheckedResultKeys, k->id())) {
631 scope.engine->throwTypeError();
632 return nullptr;
633 }
634 }
635
636 len = uncheckedResultKeys->getLength();
637 for (uint i = 0; i < len; ++i) {
638 if (uncheckedResultKeys->get(i) != Encode::undefined()) {
639 scope.engine->throwTypeError();
640 return nullptr;
641 }
642 }
643
644 *iteratorTarget = *m;
645 return new ProxyObjectOwnPropertyKeyIterator(trapKeys);
646}
647
648
649ReturnedValue ProxyConstructorObject::virtualCallAsConstructor(
650 const FunctionObject *f, const Value *argv, int argc, const Value *newTarget)
651{
652 Scope scope(f);
653 const ProxyObject *o = static_cast<const ProxyObject *>(f);
654 if (!o->d()->handler)
655 return scope.engine->throwTypeError();
656
657 ScopedFunctionObject target(scope, o->d()->target);
658 Q_ASSERT(target);
659 ScopedObject handler(scope, o->d()->handler);
660 ScopedString name(scope, scope.engine->newString(QStringLiteral("construct")));
661 ScopedValue trap(scope, handler->get(name));
662
663 if (scope.hasException())
664 return Encode::undefined();
665 if (trap->isNullOrUndefined())
666 return target->callAsConstructor(argv, argc, newTarget);
667 if (!trap->isFunctionObject())
668 return scope.engine->throwTypeError();
669
670 ScopedFunctionObject trapFunction(scope, trap);
671 Value *arguments = scope.constructUndefined(3);
672 arguments[0] = target;
673 arguments[1] = scope.engine->newArrayObject(argv, argc);
674 arguments[2] = newTarget ? *newTarget : Value::undefinedValue();
675 ScopedObject result(scope, trapFunction->call(handler, arguments, 3));
676
677 if (!result)
678 return scope.engine->throwTypeError();
679 return result->asReturnedValue();
680}
681
682ReturnedValue ProxyFunctionObject::virtualCall(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc)
683{
684 Scope scope(f);
685
686 const ProxyObject *o = static_cast<const ProxyObject *>(f);
687 if (!o->d()->handler)
688 return scope.engine->throwTypeError();
689
690 ScopedFunctionObject target(scope, o->d()->target);
691 Q_ASSERT(target);
692 ScopedObject handler(scope, o->d()->handler);
693 ScopedString name(scope, scope.engine->newString(QStringLiteral("apply")));
694 ScopedValue trap(scope, handler->get(name));
695
696 if (scope.hasException())
697 return Encode::undefined();
698 if (trap->isNullOrUndefined())
699 return checkedResult(scope.engine, target->call(thisObject, argv, argc));
700 if (!trap->isFunctionObject())
701 return scope.engine->throwTypeError();
702
703 ScopedFunctionObject trapFunction(scope, trap);
704 Value *arguments = scope.constructUndefined(3);
705 arguments[0] = target;
706 arguments[1] = thisObject ? *thisObject : Value::undefinedValue();
707 arguments[2] = scope.engine->newArrayObject(argv, argc);
708 return trapFunction->call(handler, arguments, 3);
709}
710
712
713void Heap::Proxy::init(QV4::ExecutionEngine *engine)
714{
715 Heap::FunctionObject::init(engine, QStringLiteral("Proxy"));
716
717 Scope scope(engine);
718 Scoped<QV4::Proxy> ctor(scope, this);
719 ctor->defineDefaultProperty(QStringLiteral("revocable"), QV4::Proxy::method_revocable, 2);
720 ctor->defineReadonlyConfigurableProperty(scope.engine->id_length(), Value::fromInt32(2));
721}
722
723ReturnedValue Proxy::virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *)
724{
725 Scope scope(f);
726 if (argc < 2 || !argv[0].isObject() || !argv[1].isObject())
727 return scope.engine->throwTypeError();
728
729 const Object *target = static_cast<const Object *>(argv);
730 const Object *handler = static_cast<const Object *>(argv + 1);
731 if (const ProxyObject *ptarget = target->as<ProxyObject>())
732 if (!ptarget->d()->handler)
733 return scope.engine->throwTypeError();
734 if (const ProxyObject *phandler = handler->as<ProxyObject>())
735 if (!phandler->d()->handler)
736 return scope.engine->throwTypeError();
737
738 const FunctionObject *targetFunction = target->as<FunctionObject>();
739 if (!targetFunction) {
740 return scope.engine->memoryManager->allocate<ProxyObject>(target, handler)
741 ->asReturnedValue();
742 }
743
744 if (targetFunction->isConstructor()) {
745 return scope.engine->memoryManager->allocate<ProxyConstructorObject>(
746 targetFunction, handler)->asReturnedValue();
747 }
748
749 return scope.engine->memoryManager->allocate<ProxyFunctionObject>(targetFunction, handler)
750 ->asReturnedValue();
751}
752
753ReturnedValue Proxy::virtualCall(const FunctionObject *f, const Value *, const Value *, int)
754{
755 return f->engine()->throwTypeError();
756}
757
758ReturnedValue Proxy::method_revocable(const FunctionObject *f, const Value *, const Value *argv, int argc)
759{
760 Scope scope(f);
761 ScopedObject proxy(scope, Proxy::virtualCallAsConstructor(f, argv, argc, f));
762 if (scope.hasException())
763 return Encode::undefined();
764 Q_ASSERT(proxy);
765
766 ScopedString revoke(scope, scope.engine->newString(QStringLiteral("revoke")));
767 ScopedFunctionObject revoker(
768 scope,
769 scope.engine->memoryManager->allocate<DynamicFunctionObject>(
770 scope.engine, nullptr, method_revoke));
771 revoker->defineReadonlyConfigurableProperty(scope.engine->id_length(), Value::fromInt32(0));
772 revoker->defineDefaultProperty(scope.engine->symbol_revokableProxy(), proxy);
773
774 ScopedObject o(scope, scope.engine->newObject());
775 ScopedString p(scope, scope.engine->newString(QStringLiteral("proxy")));
776 o->defineDefaultProperty(p, proxy);
777 o->defineDefaultProperty(revoke, revoker);
778 return o->asReturnedValue();
779}
780
781ReturnedValue Proxy::method_revoke(const FunctionObject *f, const Value *, const Value *, int)
782{
783 Scope scope(f);
784 ScopedObject o(scope, f->get(scope.engine->symbol_revokableProxy()));
785 Q_ASSERT(o);
786 ProxyObject *proxy = o->cast<ProxyObject>();
787
788 proxy->d()->target.set(scope.engine, nullptr);
789 proxy->d()->handler.set(scope.engine, nullptr);
790 return Encode::undefined();
791}
DEFINE_OBJECT_VTABLE(Proxy)
DEFINE_OBJECT_VTABLE(ProxyObject)
DEFINE_OBJECT_VTABLE(ProxyConstructorObject)
DEFINE_OBJECT_VTABLE(ProxyFunctionObject)