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

source code of qtdeclarative/src/qml/jsruntime/qv4proxy.cpp