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

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