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

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