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