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
48 using namespace QV4;
49
50 DEFINE_OBJECT_VTABLE(ProxyObject);
51 DEFINE_OBJECT_VTABLE(ProxyFunctionObject);
52
init(const QV4::Object * target,const QV4::Object * handler)53 void 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, target->d());
58 this->handler.set(e, handler->d());
59 }
60
init(const QV4::FunctionObject * target,const QV4::Object * handler)61 void Heap::ProxyFunctionObject::init(const QV4::FunctionObject *target, const QV4::Object *handler)
62 {
63 ExecutionEngine *e = internalClass->engine;
64 FunctionObject::init(e->rootContext());
65 this->target.set(e, target->d());
66 this->handler.set(e, handler->d());
67
68 if (!target->isConstructor())
69 jsConstruct = nullptr;
70 }
71
72
virtualGet(const Managed * m,PropertyKey id,const Value * receiver,bool * hasProperty)73 ReturnedValue 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(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(scope.engine);
96 cdata.args[2] = *receiver;
97
98 ScopedValue trapResult(scope, static_cast<const FunctionObject *>(trap.ptr)->call(cdata));
99 if (scope.engine->hasException)
100 return Encode::undefined();
101 ScopedProperty targetDesc(scope);
102 PropertyAttributes attributes = target->getOwnProperty(id, targetDesc);
103 if (attributes != Attr_Invalid && !attributes.isConfigurable()) {
104 if (attributes.isData() && !attributes.isWritable()) {
105 if (!trapResult->sameValue(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
virtualPut(Managed * m,PropertyKey id,const Value & value,Value * receiver)116 bool 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(scope.engine->id_set()));
127 if (scope.hasException())
128 return Encode::undefined();
129 if (trap->isNullOrUndefined())
130 return target->put(id, 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(scope.engine);
137 cdata.args[2] = value;
138 cdata.args[3] = *receiver;
139
140 ScopedValue trapResult(scope, static_cast<const FunctionObject *>(trap.ptr)->call(cdata));
141 if (scope.engine->hasException || !trapResult->toBoolean())
142 return false;
143 ScopedProperty targetDesc(scope);
144 PropertyAttributes attributes = target->getOwnProperty(id, targetDesc);
145 if (attributes != Attr_Invalid && !attributes.isConfigurable()) {
146 if (attributes.isData() && !attributes.isWritable()) {
147 if (!value.sameValue(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
virtualDeleteProperty(Managed * m,PropertyKey id)156 bool 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(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(scope.engine);
178 cdata.args[2] = o->d(); // ### fix receiver handling
179
180 ScopedValue trapResult(scope, static_cast<const FunctionObject *>(trap.ptr)->call(cdata));
181 if (scope.engine->hasException || !trapResult->toBoolean())
182 return false;
183 ScopedProperty targetDesc(scope);
184 PropertyAttributes attributes = target->getOwnProperty(id, targetDesc);
185 if (attributes == Attr_Invalid)
186 return true;
187 if (!attributes.isConfigurable())
188 return scope.engine->throwTypeError();
189 return true;
190 }
191
virtualHasProperty(const Managed * m,PropertyKey id)192 bool 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(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(id.asArrayIndex()).toString(scope.engine) : id.asStringOrSymbol();
214
215 ScopedValue trapResult(scope, static_cast<const FunctionObject *>(trap.ptr)->call(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, targetDesc);
222 if (attributes != Attr_Invalid) {
223 if (!attributes.isConfigurable() || !target->isExtensible())
224 return scope.engine->throwTypeError();
225 }
226 }
227 return result;
228 }
229
virtualGetOwnProperty(const Managed * m,PropertyKey id,Property * p)230 PropertyAttributes 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(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(id.asArrayIndex()).toString(scope.engine) : id.asStringOrSymbol();
256
257 ScopedValue trapResult(scope, static_cast<const FunctionObject *>(trap.ptr)->call(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, 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(scope.engine, trapResult, resultDesc, &resultAttributes);
284 resultDesc->completed(&resultAttributes);
285
286 if (!targetDesc->isCompatible(targetAttributes, resultDesc, 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
virtualDefineOwnProperty(Managed * m,PropertyKey id,const Property * p,PropertyAttributes attrs)305 bool 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(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(id.asArrayIndex()).toString(scope.engine) : id.asStringOrSymbol();
331 cdata.args[2] = ObjectPrototype::fromPropertyDescriptor(scope.engine, p, attrs);
332
333 ScopedValue trapResult(scope, static_cast<const FunctionObject *>(trap.ptr)->call(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, 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(targetAttributes, p, 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
virtualIsExtensible(const Managed * m)361 bool 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(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(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
virtualPreventExtensions(Managed * m)394 bool 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(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(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
virtualGetPrototypeOf(const Managed * m)427 Heap::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(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
virtualSetPrototypeOf(Managed * m,const Object * p)471 bool 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(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
512 struct 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
ProxyObjectOwnPropertyKeyIterator(ArrayObject * keys)524 ProxyObjectOwnPropertyKeyIterator::ProxyObjectOwnPropertyKeyIterator(ArrayObject *keys)
525 {
526 ownKeys = keys;
527 len = keys->getLength();
528 }
529
next(const Object * m,Property * pd,PropertyAttributes * attrs)530 PropertyKey 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(keys->get(PropertyKey::fromArrayIndex(index)));
538 ++index;
539
540 if (pd || attrs) {
541 ScopedProperty p(scope);
542 PropertyAttributes a = const_cast<Object *>(m)->getOwnProperty(key, pd ? pd : p);
543 if (attrs)
544 *attrs = a;
545 }
546
547 return key;
548 }
549
removeAllOccurrences(ArrayObject * target,ReturnedValue val)550 static 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(i);
555 if (v == val) {
556 found = true;
557 target->put(i, Value::undefinedValue());
558 }
559 }
560 return found;
561 }
562
virtualOwnPropertyKeys(const Object * m,Value * iteratorTarget)563 OwnPropertyKeyIterator *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(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(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(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(key->toPropertyKey().id());
609 trapKeys->push_back(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(nullptr, &attrs);
619 if (!k->isValid())
620 break;
621 Value keyAsValue = Value::fromReturnedValue(k->id());
622 if (attrs.isConfigurable())
623 targetConfigurableKeys->push_back(keyAsValue);
624 else
625 targetNonConfigurableKeys->push_back(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(trapKeys);
634
635 len = targetNonConfigurableKeys->getLength();
636 for (uint i = 0; i < len; ++i) {
637 k = PropertyKey::fromId(targetNonConfigurableKeys->get(i));
638 if (!removeAllOccurrences(uncheckedResultKeys, 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(targetConfigurableKeys->get(i));
652 if (!removeAllOccurrences(uncheckedResultKeys, 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(i) != Encode::undefined()) {
661 scope.engine->throwTypeError();
662 return nullptr;
663 }
664 }
665
666 *iteratorTarget = *m;
667 return new ProxyObjectOwnPropertyKeyIterator(trapKeys);
668 }
669
670
virtualCallAsConstructor(const FunctionObject * f,const Value * argv,int argc,const Value * newTarget)671 ReturnedValue 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(3);
695 arguments[0] = target;
696 arguments[1] = scope.engine->newArrayObject(argv, argc);
697 arguments[2] = newTarget ? *newTarget : Value::undefinedValue();
698 ScopedObject result(scope, trapFunction->call(handler, arguments, 3));
699
700 if (!result)
701 return scope.engine->throwTypeError();
702 return result->asReturnedValue();
703 }
704
virtualCall(const FunctionObject * f,const Value * thisObject,const Value * argv,int argc)705 ReturnedValue 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(scope.engine, 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(3);
728 arguments[0] = target;
729 arguments[1] = thisObject ? *thisObject : Value::undefinedValue();
730 arguments[2] = scope.engine->newArrayObject(argv, argc);
731 return trapFunction->call(handler, arguments, 3);
732 }
733
734 DEFINE_OBJECT_VTABLE(Proxy);
735
init(QV4::ExecutionContext * ctx)736 void Heap::Proxy::init(QV4::ExecutionContext *ctx)
737 {
738 Heap::FunctionObject::init(ctx, QStringLiteral("Proxy"));
739
740 Scope scope(ctx);
741 Scoped<QV4::Proxy> ctor(scope, this);
742 ctor->defineDefaultProperty(QStringLiteral("revocable"), QV4::Proxy::method_revocable, 2);
743 ctor->defineReadonlyConfigurableProperty(scope.engine->id_length(), Value::fromInt32(2));
744 }
745
virtualCallAsConstructor(const FunctionObject * f,const Value * argv,int argc,const Value *)746 ReturnedValue 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>(targetFunction, handler)->asReturnedValue();
764 return scope.engine->memoryManager->allocate<ProxyObject>(target, handler)->asReturnedValue();
765 }
766
virtualCall(const FunctionObject * f,const Value *,const Value *,int)767 ReturnedValue Proxy::virtualCall(const FunctionObject *f, const Value *, const Value *, int)
768 {
769 return f->engine()->throwTypeError();
770 }
771
method_revocable(const FunctionObject * f,const Value *,const Value * argv,int argc)772 ReturnedValue 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>(scope.engine->rootContext(), nullptr, method_revoke));
782 revoker->defineReadonlyConfigurableProperty(scope.engine->id_length(), Value::fromInt32(0));
783 revoker->defineDefaultProperty(scope.engine->symbol_revokableProxy(), proxy);
784
785 ScopedObject o(scope, scope.engine->newObject());
786 ScopedString p(scope, scope.engine->newString(QStringLiteral("proxy")));
787 o->defineDefaultProperty(p, proxy);
788 o->defineDefaultProperty(revoke, revoker);
789 return o->asReturnedValue();
790 }
791
method_revoke(const FunctionObject * f,const Value *,const Value *,int)792 ReturnedValue Proxy::method_revoke(const FunctionObject *f, const Value *, const Value *, int)
793 {
794 Scope scope(f);
795 ScopedObject o(scope, f->get(scope.engine->symbol_revokableProxy()));
796 Q_ASSERT(o);
797 ProxyObject *proxy = o->cast<ProxyObject>();
798
799 proxy->d()->target.set(scope.engine, nullptr);
800 proxy->d()->handler.set(scope.engine, nullptr);
801 return Encode::undefined();
802 }
803