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 test suite of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:GPL-EXCEPT$
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 General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU
19** General Public License version 3 as published by the Free Software
20** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
21** included in the packaging of this file. Please review the following
22** information to ensure the GNU General Public License requirements will
23** be met: https://www.gnu.org/licenses/gpl-3.0.html.
24**
25** $QT_END_LICENSE$
26**
27****************************************************************************/
28
29
30#include <QtTest/QtTest>
31
32#include <QtScript/qscriptengine.h>
33#include <QtScript/qscriptclass.h>
34#include <QtScript/qscriptclasspropertyiterator.h>
35#include <QtScript/qscriptstring.h>
36#include <QtScript/qscriptvalueiterator.h>
37
38Q_DECLARE_METATYPE(QScriptContext*)
39Q_DECLARE_METATYPE(QScriptValueList)
40Q_DECLARE_METATYPE(QScriptValue)
41
42class tst_QScriptClass : public QObject
43{
44 Q_OBJECT
45
46public:
47 tst_QScriptClass();
48 virtual ~tst_QScriptClass();
49
50private slots:
51 void newInstance();
52 void setScriptClassOfExistingObject();
53 void setScriptClassOfNonQtScriptObject();
54 void getAndSetPropertyFromCpp();
55 void getAndSetPropertyFromJS();
56 void deleteUndeletableProperty();
57 void writeReadOnlyProperty();
58 void writePropertyWithoutWriteAccess();
59 void getProperty_invalidValue();
60 void enumerate();
61 void extension_None();
62 void extension_Callable();
63 void extension_Callable_construct();
64 void extension_HasInstance();
65 void originalProperties1();
66 void originalProperties2();
67 void originalProperties3();
68 void originalProperties4();
69 void defaultImplementations();
70 void scriptClassObjectInPrototype();
71 void scriptClassWithNullEngine();
72 void scriptClassInOtherEngine();
73};
74
75tst_QScriptClass::tst_QScriptClass()
76{
77}
78
79tst_QScriptClass::~tst_QScriptClass()
80{
81}
82
83
84
85class TestClass : public QScriptClass
86{
87public:
88 struct CustomProperty {
89 QueryFlags qflags;
90 uint id;
91 QScriptValue::PropertyFlags pflags;
92 QScriptValue value;
93
94 CustomProperty(QueryFlags qf, uint i, QScriptValue::PropertyFlags pf,
95 const QScriptValue &val)
96 : qflags(qf), id(i), pflags(pf), value(val) { }
97 };
98
99 enum CallableMode {
100 NotCallable,
101 CallableReturnsSum,
102 CallableReturnsArgument,
103 CallableReturnsInvalidVariant,
104 CallableReturnsGlobalObject,
105 CallableReturnsThisObject,
106 CallableReturnsCallee,
107 CallableReturnsArgumentsObject,
108 CallableInitializesThisObject
109 };
110
111 TestClass(QScriptEngine *engine);
112 ~TestClass();
113
114 void addCustomProperty(const QScriptString &name, QueryFlags qflags,
115 uint id, QScriptValue::PropertyFlags pflags,
116 const QScriptValue &value);
117 void removeCustomProperty(const QScriptString &name);
118
119 QueryFlags queryProperty(const QScriptValue &object,
120 const QScriptString &name,
121 QueryFlags flags, uint *id);
122
123 QScriptValue property(const QScriptValue &object,
124 const QScriptString &name, uint id);
125
126 void setProperty(QScriptValue &object, const QScriptString &name,
127 uint id, const QScriptValue &value);
128
129 QScriptValue::PropertyFlags propertyFlags(
130 const QScriptValue &object, const QScriptString &name, uint id);
131
132 QScriptClassPropertyIterator *newIterator(const QScriptValue &object);
133
134 QScriptValue prototype() const;
135
136 QString name() const;
137
138 bool supportsExtension(Extension extension) const;
139 QVariant extension(Extension extension,
140 const QVariant &argument = QVariant());
141
142 QScriptValue lastQueryPropertyObject() const;
143 QScriptString lastQueryPropertyName() const;
144 QueryFlags lastQueryPropertyFlags() const;
145
146 QScriptValue lastPropertyObject() const;
147 QScriptString lastPropertyName() const;
148 uint lastPropertyId() const;
149
150 QScriptValue lastSetPropertyObject() const;
151 QScriptString lastSetPropertyName() const;
152 uint lastSetPropertyId() const;
153 QScriptValue lastSetPropertyValue() const;
154
155 QScriptValue lastPropertyFlagsObject() const;
156 QScriptString lastPropertyFlagsName() const;
157 uint lastPropertyFlagsId() const;
158
159 QScriptClass::Extension lastExtensionType() const;
160 QVariant lastExtensionArgument() const;
161
162 void clearReceivedArgs();
163
164 void setIterationEnabled(bool enable);
165 bool isIterationEnabled() const;
166
167 void setCallableMode(CallableMode mode);
168 CallableMode callableMode() const;
169
170 void setHasInstance(bool hasInstance);
171 bool hasInstance() const;
172
173private:
174 CustomProperty *findCustomProperty(const QScriptString &name);
175
176 QHash<QScriptString, CustomProperty*> customProperties;
177
178 QScriptValue m_lastQueryPropertyObject;
179 QScriptString m_lastQueryPropertyName;
180 QScriptClass::QueryFlags m_lastQueryPropertyFlags;
181
182 QScriptValue m_lastPropertyObject;
183 QScriptString m_lastPropertyName;
184 uint m_lastPropertyId;
185
186 QScriptValue m_lastSetPropertyObject;
187 QScriptString m_lastSetPropertyName;
188 uint m_lastSetPropertyId;
189 QScriptValue m_lastSetPropertyValue;
190
191 QScriptValue m_lastPropertyFlagsObject;
192 QScriptString m_lastPropertyFlagsName;
193 uint m_lastPropertyFlagsId;
194
195 QScriptClass::Extension m_lastExtensionType;
196 QVariant m_lastExtensionArgument;
197
198 QScriptValue m_prototype;
199 bool m_iterationEnabled;
200 CallableMode m_callableMode;
201 bool m_hasInstance;
202};
203
204class TestClassPropertyIterator : public QScriptClassPropertyIterator
205{
206public:
207 TestClassPropertyIterator(const QHash<QScriptString, TestClass::CustomProperty*> &props,
208 const QScriptValue &object);
209 ~TestClassPropertyIterator();
210
211 bool hasNext() const;
212 void next();
213
214 bool hasPrevious() const;
215 void previous();
216
217 void toFront();
218 void toBack();
219
220 QScriptString name() const;
221 uint id() const;
222 QScriptValue::PropertyFlags flags() const;
223
224private:
225 int m_index;
226 int m_last;
227 QHash<QScriptString, TestClass::CustomProperty*> m_props;
228};
229
230
231
232TestClass::TestClass(QScriptEngine *engine)
233 : QScriptClass(engine), m_iterationEnabled(true),
234 m_callableMode(NotCallable), m_hasInstance(false)
235{
236 m_prototype = engine->newObject();
237 clearReceivedArgs();
238}
239
240TestClass::~TestClass()
241{
242 qDeleteAll(c: customProperties);
243}
244
245TestClass::CustomProperty* TestClass::findCustomProperty(const QScriptString &name)
246{
247 QHash<QScriptString, CustomProperty*>::const_iterator it;
248 it = customProperties.constFind(akey: name);
249 if (it == customProperties.constEnd())
250 return 0;
251 return it.value();
252
253}
254
255void TestClass::addCustomProperty(const QScriptString &name, QueryFlags qflags,
256 uint id, QScriptValue::PropertyFlags pflags,
257 const QScriptValue &value)
258{
259 customProperties.insert(akey: name, avalue: new CustomProperty(qflags, id, pflags, value));
260}
261
262void TestClass::removeCustomProperty(const QScriptString &name)
263{
264 CustomProperty *prop = customProperties.take(akey: name);
265 if (prop)
266 delete prop;
267}
268
269QScriptClass::QueryFlags TestClass::queryProperty(const QScriptValue &object,
270 const QScriptString &name,
271 QueryFlags flags, uint *id)
272{
273 m_lastQueryPropertyObject = object;
274 m_lastQueryPropertyName = name;
275 m_lastQueryPropertyFlags = flags;
276 CustomProperty *prop = findCustomProperty(name);
277 if (!prop)
278 return {};
279 *id = prop->id;
280 return prop->qflags & flags;
281}
282
283QScriptValue TestClass::property(const QScriptValue &object,
284 const QScriptString &name, uint id)
285{
286 m_lastPropertyObject = object;
287 m_lastPropertyName = name;
288 m_lastPropertyId = id;
289 CustomProperty *prop = findCustomProperty(name);
290 if (!prop)
291 return QScriptValue();
292 return prop->value;
293}
294
295void TestClass::setProperty(QScriptValue &object, const QScriptString &name,
296 uint id, const QScriptValue &value)
297{
298 m_lastSetPropertyObject = object;
299 m_lastSetPropertyName = name;
300 m_lastSetPropertyId = id;
301 m_lastSetPropertyValue = value;
302 CustomProperty *prop = findCustomProperty(name);
303 if (!prop)
304 return;
305 if (prop->pflags & QScriptValue::ReadOnly)
306 return;
307 if (!value.isValid()) // deleteProperty() requested
308 removeCustomProperty(name);
309 else
310 prop->value = value;
311}
312
313QScriptValue::PropertyFlags TestClass::propertyFlags(
314 const QScriptValue &object, const QScriptString &name, uint id)
315{
316 m_lastPropertyFlagsObject = object;
317 m_lastPropertyFlagsName = name;
318 m_lastPropertyFlagsId = id;
319 CustomProperty *prop = findCustomProperty(name);
320 if (!prop)
321 return {};
322 return prop->pflags;
323}
324
325QScriptClassPropertyIterator *TestClass::newIterator(const QScriptValue &object)
326{
327 if (!m_iterationEnabled)
328 return {};
329 return new TestClassPropertyIterator(customProperties, object);
330}
331
332QScriptValue TestClass::prototype() const
333{
334 return m_prototype;
335}
336
337QString TestClass::name() const
338{
339 return QLatin1String("TestClass");
340}
341
342bool TestClass::supportsExtension(Extension extension) const
343{
344 if (extension == Callable)
345 return (m_callableMode != NotCallable);
346 if (extension == HasInstance)
347 return m_hasInstance;
348 return false;
349}
350
351QVariant TestClass::extension(Extension extension,
352 const QVariant &argument)
353{
354 m_lastExtensionType = extension;
355 m_lastExtensionArgument = argument;
356 if (extension == Callable && m_callableMode != NotCallable) {
357 QScriptContext *ctx = qvariant_cast<QScriptContext*>(v: argument);
358 if (m_callableMode == CallableReturnsSum) {
359 qsreal sum = 0;
360 for (int i = 0; i < ctx->argumentCount(); ++i)
361 sum += ctx->argument(index: i).toNumber();
362 QScriptValueIterator it(ctx->callee());
363 while (it.hasNext()) {
364 it.next();
365 sum += it.value().toNumber();
366 }
367 return sum;
368 } else if (m_callableMode == CallableReturnsArgument) {
369 return QVariant::fromValue(value: ctx->argument(index: 0));
370 } else if (m_callableMode == CallableReturnsInvalidVariant) {
371 return QVariant();
372 } else if (m_callableMode == CallableReturnsGlobalObject) {
373 return QVariant::fromValue(value: engine()->globalObject());
374 } else if (m_callableMode == CallableReturnsThisObject) {
375 return QVariant::fromValue(value: ctx->thisObject());
376 } else if (m_callableMode == CallableReturnsCallee) {
377 return QVariant::fromValue(value: ctx->callee());
378 } else if (m_callableMode == CallableReturnsArgumentsObject) {
379 return QVariant::fromValue(value: ctx->argumentsObject());
380 } else if (m_callableMode == CallableInitializesThisObject) {
381 engine()->newQObject(scriptObject: ctx->thisObject(), qtObject: engine());
382 return QVariant();
383 }
384 } else if (extension == HasInstance && m_hasInstance) {
385 QScriptValueList args = qvariant_cast<QScriptValueList>(v: argument);
386 QScriptValue obj = args.at(i: 0);
387 QScriptValue value = args.at(i: 1);
388 return value.property(name: "foo").equals(other: obj.property(name: "foo"));
389 }
390 return QVariant();
391}
392
393QScriptValue TestClass::lastQueryPropertyObject() const
394{
395 return m_lastQueryPropertyObject;
396}
397
398QScriptString TestClass::lastQueryPropertyName() const
399{
400 return m_lastQueryPropertyName;
401}
402
403QScriptClass::QueryFlags TestClass::lastQueryPropertyFlags() const
404{
405 return m_lastQueryPropertyFlags;
406}
407
408QScriptValue TestClass::lastPropertyObject() const
409{
410 return m_lastPropertyObject;
411}
412
413QScriptString TestClass::lastPropertyName() const
414{
415 return m_lastPropertyName;
416}
417
418uint TestClass::lastPropertyId() const
419{
420 return m_lastPropertyId;
421}
422
423QScriptValue TestClass::lastSetPropertyObject() const
424{
425 return m_lastSetPropertyObject;
426}
427
428QScriptString TestClass::lastSetPropertyName() const
429{
430 return m_lastSetPropertyName;
431}
432
433uint TestClass::lastSetPropertyId() const
434{
435 return m_lastSetPropertyId;
436}
437
438QScriptValue TestClass::lastSetPropertyValue() const
439{
440 return m_lastSetPropertyValue;
441}
442
443QScriptValue TestClass::lastPropertyFlagsObject() const
444{
445 return m_lastPropertyFlagsObject;
446}
447
448QScriptString TestClass::lastPropertyFlagsName() const
449{
450 return m_lastPropertyFlagsName;
451}
452
453uint TestClass::lastPropertyFlagsId() const
454{
455 return m_lastPropertyFlagsId;
456}
457
458QScriptClass::Extension TestClass::lastExtensionType() const
459{
460 return m_lastExtensionType;
461}
462
463QVariant TestClass::lastExtensionArgument() const
464{
465 return m_lastExtensionArgument;
466}
467
468void TestClass::clearReceivedArgs()
469{
470 m_lastQueryPropertyObject = QScriptValue();
471 m_lastQueryPropertyName = QScriptString();
472 m_lastQueryPropertyFlags = {};
473
474 m_lastPropertyObject = QScriptValue();
475 m_lastPropertyName = QScriptString();
476 m_lastPropertyId = uint(-1);
477
478 m_lastSetPropertyObject = QScriptValue();
479 m_lastSetPropertyName = QScriptString();
480 m_lastSetPropertyId = uint(-1);
481 m_lastSetPropertyValue = QScriptValue();
482
483 m_lastPropertyFlagsObject = QScriptValue();
484 m_lastPropertyFlagsName = QScriptString();
485 m_lastPropertyFlagsId = uint(-1);
486
487 m_lastExtensionType = static_cast<QScriptClass::Extension>(-1);
488 m_lastExtensionArgument = QVariant();
489}
490
491void TestClass::setIterationEnabled(bool enable)
492{
493 m_iterationEnabled = enable;
494}
495
496bool TestClass::isIterationEnabled() const
497{
498 return m_iterationEnabled;
499}
500
501void TestClass::setCallableMode(CallableMode mode)
502{
503 m_callableMode = mode;
504}
505
506TestClass::CallableMode TestClass::callableMode() const
507{
508 return m_callableMode;
509}
510
511void TestClass::setHasInstance(bool hasInstance)
512{
513 m_hasInstance = hasInstance;
514}
515
516bool TestClass::hasInstance() const
517{
518 return m_hasInstance;
519}
520
521
522TestClassPropertyIterator::TestClassPropertyIterator(const QHash<QScriptString, TestClass::CustomProperty*> &props,
523 const QScriptValue &object)
524 : QScriptClassPropertyIterator(object)
525{
526 m_props = props;
527 toFront();
528}
529
530TestClassPropertyIterator::~TestClassPropertyIterator()
531{
532}
533
534bool TestClassPropertyIterator::hasNext() const
535{
536 return m_index < m_props.size();
537}
538
539void TestClassPropertyIterator::next()
540{
541 m_last = m_index;
542 ++m_index;
543}
544
545bool TestClassPropertyIterator::hasPrevious() const
546{
547 return m_index > 0;
548}
549
550void TestClassPropertyIterator::previous()
551{
552 --m_index;
553 m_last = m_index;
554}
555
556void TestClassPropertyIterator::toFront()
557{
558 m_index = 0;
559 m_last = -1;
560}
561
562void TestClassPropertyIterator::toBack()
563{
564 m_index = m_props.size();
565 m_last = -1;
566}
567
568QScriptString TestClassPropertyIterator::name() const
569{
570 return m_props.keys().value(i: m_last);
571}
572
573uint TestClassPropertyIterator::id() const
574{
575 QScriptString key = m_props.keys().value(i: m_last);
576 if (!key.isValid())
577 return 0;
578 TestClass::CustomProperty *prop = m_props.value(akey: key);
579 return prop->id;
580}
581
582QScriptValue::PropertyFlags TestClassPropertyIterator::flags() const
583{
584 QScriptString key = m_props.keys().value(i: m_last);
585 if (!key.isValid())
586 return {};
587 TestClass::CustomProperty *prop = m_props.value(akey: key);
588 return prop->pflags;
589}
590
591
592
593void tst_QScriptClass::newInstance()
594{
595 QScriptEngine eng;
596
597 TestClass cls(&eng);
598
599 QScriptValue obj1 = eng.newObject(scriptClass: &cls);
600 QVERIFY(!obj1.data().isValid());
601 QVERIFY(obj1.prototype().strictlyEquals(cls.prototype()));
602 QEXPECT_FAIL("", "QTBUG-17599: classname is not implemented", Continue);
603 QCOMPARE(obj1.toString(), QString::fromLatin1("[object TestClass]"));
604 QCOMPARE(obj1.scriptClass(), (QScriptClass*)&cls);
605
606 QScriptValue num(&eng, 456);
607 QScriptValue obj2 = eng.newObject(scriptClass: &cls, data: num);
608 QVERIFY(obj2.data().strictlyEquals(num));
609 QVERIFY(obj2.prototype().strictlyEquals(cls.prototype()));
610 QCOMPARE(obj2.scriptClass(), (QScriptClass*)&cls);
611 QVERIFY(!obj2.equals(obj1));
612 QVERIFY(!obj2.strictlyEquals(obj1));
613}
614
615void tst_QScriptClass::setScriptClassOfExistingObject()
616{
617 QScriptEngine eng;
618 TestClass cls(&eng);
619 QScriptValue obj3 = eng.newObject();
620 QCOMPARE(obj3.scriptClass(), (QScriptClass*)0);
621 obj3.setScriptClass(&cls);
622 QCOMPARE(obj3.scriptClass(), (QScriptClass*)&cls);
623
624 obj3.setScriptClass(0);
625 QCOMPARE(obj3.scriptClass(), (QScriptClass*)0);
626 obj3.setScriptClass(&cls);
627 QCOMPARE(obj3.scriptClass(), (QScriptClass*)&cls);
628
629 TestClass cls2(&eng);
630 obj3.setScriptClass(&cls2);
631 QCOMPARE(obj3.scriptClass(), (QScriptClass*)&cls2);
632}
633
634void tst_QScriptClass::setScriptClassOfNonQtScriptObject()
635{
636 QScriptEngine eng;
637 TestClass cls(&eng);
638 // undefined behavior really, but shouldn't crash
639 QScriptValue arr = eng.newArray();
640 QVERIFY(arr.isArray());
641 QCOMPARE(arr.scriptClass(), (QScriptClass*)0);
642 QTest::ignoreMessage(type: QtWarningMsg, message: "QScriptValue::setScriptClass() failed: cannot change class of non-QScriptObject");
643 arr.setScriptClass(&cls);
644 QEXPECT_FAIL("", "Changing class of arbitrary script object is not allowed (it's OK)", Continue);
645 QCOMPARE(arr.scriptClass(), (QScriptClass*)&cls);
646 QEXPECT_FAIL("", "Changing class of arbitrary script object is not allowed (it's OK)", Continue);
647 QVERIFY(!arr.isArray());
648 QVERIFY(arr.isObject());
649}
650
651void tst_QScriptClass::getAndSetPropertyFromCpp()
652{
653 QScriptEngine eng;
654
655 TestClass cls(&eng);
656
657 QScriptValue obj1 = eng.newObject(scriptClass: &cls);
658 QScriptValue obj2 = eng.newObject(scriptClass: &cls);
659 QScriptString foo = eng.toStringHandle(str: "foo");
660 QScriptString bar = eng.toStringHandle(str: "bar");
661 QScriptValue num(&eng, 123);
662
663 // Initially our TestClass instances have no custom properties,
664 // and queryProperty() will always return false.
665 // Hence, the properties will be created as normal JS properties.
666 for (int x = 0; x < 2; ++x) {
667 QScriptValue &o = (x == 0) ? obj1 : obj2;
668 for (int y = 0; y < 2; ++y) {
669 QScriptString &s = (y == 0) ? foo : bar;
670
671 // read property
672 cls.clearReceivedArgs();
673 QScriptValue ret = o.property(name: s);
674 QVERIFY(!ret.isValid());
675 QVERIFY(cls.lastQueryPropertyObject().strictlyEquals(o));
676 QVERIFY(cls.lastQueryPropertyName() == s);
677 QVERIFY(!cls.lastPropertyObject().isValid());
678 QVERIFY(!cls.lastSetPropertyObject().isValid());
679 QVERIFY(cls.lastQueryPropertyFlags() == QScriptClass::HandlesReadAccess);
680
681 // write property
682 cls.clearReceivedArgs();
683 o.setProperty(name: s, value: num);
684 QVERIFY(cls.lastQueryPropertyObject().strictlyEquals(o));
685 QVERIFY(cls.lastQueryPropertyName() == s);
686 QVERIFY(!cls.lastPropertyObject().isValid());
687 QVERIFY(!cls.lastSetPropertyObject().isValid());
688 QVERIFY(cls.lastQueryPropertyFlags() == QScriptClass::HandlesWriteAccess);
689
690 // re-read property
691 // When a QScriptClass doesn't want to handle a property write,
692 // that property becomes a normal property and the QScriptClass
693 // shall not be queried about it again.
694 cls.clearReceivedArgs();
695 QVERIFY(o.property(s).strictlyEquals(num));
696 QVERIFY(!cls.lastQueryPropertyObject().isValid());
697 }
698 }
699
700 // add a custom property
701 QScriptString foo2 = eng.toStringHandle(str: "foo2");
702 const uint foo2Id = 123;
703 const QScriptValue::PropertyFlags foo2Pflags = QScriptValue::Undeletable;
704 QScriptValue foo2Value(&eng, 456);
705 cls.addCustomProperty(name: foo2, qflags: QScriptClass::HandlesReadAccess | QScriptClass::HandlesWriteAccess,
706 id: foo2Id, pflags: foo2Pflags, value: foo2Value);
707
708 {
709 // read property
710 cls.clearReceivedArgs();
711 {
712 QScriptValue ret = obj1.property(name: foo2);
713 QVERIFY(ret.strictlyEquals(foo2Value));
714 }
715 QVERIFY(cls.lastQueryPropertyObject().strictlyEquals(obj1));
716 QVERIFY(cls.lastQueryPropertyName() == foo2);
717 QVERIFY(cls.lastPropertyObject().strictlyEquals(obj1));
718 QVERIFY(cls.lastPropertyName() == foo2);
719 QCOMPARE(cls.lastPropertyId(), foo2Id);
720
721 // read flags
722 cls.clearReceivedArgs();
723 QCOMPARE(obj1.propertyFlags(foo2), foo2Pflags);
724 QVERIFY(cls.lastQueryPropertyObject().strictlyEquals(obj1));
725 QVERIFY(cls.lastQueryPropertyName() == foo2);
726 QEXPECT_FAIL("", "QTBUG-17601: classObject.getOwnPropertyDescriptor() reads the property value", Continue);
727 QVERIFY(!cls.lastPropertyObject().isValid());
728 QVERIFY(cls.lastPropertyFlagsObject().strictlyEquals(obj1));
729 QVERIFY(cls.lastPropertyFlagsName() == foo2);
730 QCOMPARE(cls.lastPropertyFlagsId(), foo2Id);
731
732 // write property
733 cls.clearReceivedArgs();
734 QScriptValue newFoo2Value(&eng, 789);
735 obj1.setProperty(name: foo2, value: newFoo2Value);
736 QVERIFY(cls.lastQueryPropertyObject().strictlyEquals(obj1));
737 QVERIFY(cls.lastQueryPropertyName() == foo2);
738
739 // read property again
740 cls.clearReceivedArgs();
741 {
742 QScriptValue ret = obj1.property(name: foo2);
743 QVERIFY(ret.strictlyEquals(newFoo2Value));
744 }
745 QVERIFY(cls.lastQueryPropertyObject().strictlyEquals(obj1));
746 QVERIFY(cls.lastQueryPropertyName() == foo2);
747 QVERIFY(cls.lastPropertyObject().strictlyEquals(obj1));
748 QVERIFY(cls.lastPropertyName() == foo2);
749 QCOMPARE(cls.lastPropertyId(), foo2Id);
750 }
751
752 // attempt to delete custom property
753 obj1.setProperty(name: foo2, value: QScriptValue());
754 // delete real property
755 obj1.setProperty(name: foo, value: QScriptValue());
756 QVERIFY(!obj1.property(foo).isValid());
757 obj1.setProperty(name: foo, value: num);
758 QVERIFY(obj1.property(foo).equals(num));
759
760 // remove script class; normal properties should remain
761 obj1.setScriptClass(0);
762 QCOMPARE(obj1.scriptClass(), (QScriptClass*)0);
763 QVERIFY(obj1.property(foo).equals(num));
764 QVERIFY(obj1.property(bar).equals(num));
765 obj1.setProperty(name: foo, value: QScriptValue());
766 QVERIFY(!obj1.property(foo).isValid());
767 obj1.setProperty(name: bar, value: QScriptValue());
768 QVERIFY(!obj1.property(bar).isValid());
769}
770
771void tst_QScriptClass::getAndSetPropertyFromJS()
772{
773 QScriptEngine eng;
774 TestClass cls(&eng);
775 cls.addCustomProperty(name: eng.toStringHandle(str: "x"),
776 qflags: QScriptClass::HandlesReadAccess
777 | QScriptClass::HandlesWriteAccess,
778 /*id=*/ 1, pflags: QScriptValue::PropertyFlags{}, /*value=*/ 123);
779 eng.globalObject().setProperty(name: "o", value: eng.newObject(scriptClass: &cls));
780
781 // Accessing a custom property
782 QCOMPARE(eng.evaluate("o.x").toInt32(), 123);
783 QCOMPARE(eng.evaluate("o.x = 456; o.x").toInt32(), 456);
784
785 // Accessing a new JS property
786 QVERIFY(eng.evaluate("o.y").isUndefined());
787 QCOMPARE(eng.evaluate("o.y = 789; o.y").toInt32(), 789);
788
789 // Deleting custom property
790 QVERIFY(eng.evaluate("delete o.x").toBool());
791 QVERIFY(eng.evaluate("o.x").isUndefined());
792
793 // Deleting JS property
794 QVERIFY(eng.evaluate("delete o.y").toBool());
795 QVERIFY(eng.evaluate("o.y").isUndefined());
796}
797
798void tst_QScriptClass::deleteUndeletableProperty()
799{
800 QScriptEngine eng;
801 TestClass cls(&eng);
802 cls.addCustomProperty(name: eng.toStringHandle(str: "x"), qflags: QScriptClass::HandlesWriteAccess,
803 /*id=*/0, pflags: QScriptValue::Undeletable, value: QScriptValue());
804 eng.globalObject().setProperty(name: "o", value: eng.newObject(scriptClass: &cls));
805 QVERIFY(!eng.evaluate("delete o.x").toBool());
806}
807
808void tst_QScriptClass::writeReadOnlyProperty()
809{
810 QScriptEngine eng;
811 TestClass cls(&eng);
812 cls.addCustomProperty(name: eng.toStringHandle(str: "x"),
813 qflags: QScriptClass::HandlesReadAccess
814 | QScriptClass::HandlesWriteAccess,
815 /*id=*/0, pflags: QScriptValue::ReadOnly, value: 123);
816 eng.globalObject().setProperty(name: "o", value: eng.newObject(scriptClass: &cls));
817 // Note that if a property is read-only, the setProperty()
818 // reimplementation will still get called; it's up to that
819 // function to respect the ReadOnly flag.
820 QCOMPARE(eng.evaluate("o.x = 456; o.x").toInt32(), 123);
821}
822
823void tst_QScriptClass::writePropertyWithoutWriteAccess()
824{
825 QScriptEngine eng;
826 TestClass cls(&eng);
827 cls.addCustomProperty(name: eng.toStringHandle(str: "x"),
828 qflags: QScriptClass::HandlesReadAccess,
829 /*id=*/ 0, pflags: QScriptValue::PropertyFlags{}, value: 123);
830 eng.globalObject().setProperty(name: "o", value: eng.newObject(scriptClass: &cls));
831 QCOMPARE(eng.evaluate("o.x").toInt32(), 123);
832
833 // This will create a JS property on the instance that
834 // shadows the custom property.
835 // This behavior is not documented. It might be more
836 // intuitive to treat a property that only handles read
837 // access as a read-only, non-shadowable property.
838 QCOMPARE(eng.evaluate("o.x = 456; o.x").toInt32(), 456);
839
840 QVERIFY(eng.evaluate("delete o.x").toBool());
841 // Now the custom property is seen again.
842 QCOMPARE(eng.evaluate("o.x").toInt32(), 123);
843}
844
845void tst_QScriptClass::getProperty_invalidValue()
846{
847 QScriptEngine eng;
848 TestClass cls(&eng);
849 cls.addCustomProperty(name: eng.toStringHandle(str: "foo"), qflags: QScriptClass::HandlesReadAccess,
850 /*id=*/0, pflags: QScriptValue::ReadOnly, value: QScriptValue());
851 QScriptValue obj = eng.newObject(scriptClass: &cls);
852
853 QVERIFY(obj.property("foo").isUndefined());
854
855 eng.globalObject().setProperty(name: "obj", value: obj);
856 QVERIFY(eng.evaluate("obj.hasOwnProperty('foo'))").toBool());
857 // The JS environment expects that a valid value is returned,
858 // otherwise we could crash.
859 QVERIFY(eng.evaluate("obj.foo").isUndefined());
860 QVERIFY(eng.evaluate("obj.foo + ''").isString());
861 QVERIFY(eng.evaluate("Object.getOwnPropertyDescriptor(obj, 'foo').value").isUndefined());
862 QVERIFY(eng.evaluate("Object.getOwnPropertyDescriptor(obj, 'foo').value +''").isString());
863}
864
865void tst_QScriptClass::enumerate()
866{
867 QScriptEngine eng;
868
869 TestClass cls(&eng);
870
871 QScriptValue obj = eng.newObject(scriptClass: &cls);
872 QScriptString foo = eng.toStringHandle(str: "foo");
873 obj.setProperty(name: foo, value: QScriptValue(&eng, 123));
874
875 cls.setIterationEnabled(false);
876 {
877 QScriptValueIterator it(obj);
878 QVERIFY(it.hasNext());
879 it.next();
880 QVERIFY(it.scriptName() == foo);
881 QVERIFY(!it.hasNext());
882 }
883
884 // add a custom property
885 QScriptString foo2 = eng.toStringHandle(str: "foo2");
886 const uint foo2Id = 123;
887 const QScriptValue::PropertyFlags foo2Pflags = QScriptValue::Undeletable;
888 QScriptValue foo2Value(&eng, 456);
889 cls.addCustomProperty(name: foo2, qflags: QScriptClass::HandlesReadAccess | QScriptClass::HandlesWriteAccess,
890 id: foo2Id, pflags: foo2Pflags, value: QScriptValue());
891
892 cls.setIterationEnabled(true);
893 QScriptValueIterator it(obj);
894 // This test relies on the order in which properties are enumerated,
895 // which we don't guarantee. However, for compatibility's sake we prefer
896 // that normal JS properties come before QScriptClass properties.
897 for (int x = 0; x < 2; ++x) {
898 QVERIFY(it.hasNext());
899 it.next();
900 QVERIFY(it.scriptName() == foo);
901 QVERIFY(it.hasNext());
902 it.next();
903 QVERIFY(it.scriptName() == foo2);
904 QCOMPARE(it.flags(), foo2Pflags);
905 QVERIFY(!it.hasNext());
906
907 QVERIFY(it.hasPrevious());
908 it.previous();
909 QVERIFY(it.scriptName() == foo2);
910 QCOMPARE(it.flags(), foo2Pflags);
911 QVERIFY(it.hasPrevious());
912 it.previous();
913 QVERIFY(it.scriptName() == foo);
914 QVERIFY(!it.hasPrevious());
915 }
916}
917
918void tst_QScriptClass::extension_None()
919{
920 QScriptEngine eng;
921 TestClass cls(&eng);
922 cls.setCallableMode(TestClass::NotCallable);
923 QVERIFY(!cls.supportsExtension(QScriptClass::Callable));
924 QVERIFY(!cls.supportsExtension(QScriptClass::HasInstance));
925 QScriptValue obj = eng.newObject(scriptClass: &cls);
926 QVERIFY(!obj.call().isValid());
927 QCOMPARE((int)cls.lastExtensionType(), -1);
928 QVERIFY(!obj.instanceOf(obj));
929 QCOMPARE((int)cls.lastExtensionType(), -1);
930 QVERIFY(!obj.construct().isValid());
931}
932
933void tst_QScriptClass::extension_Callable()
934{
935 QScriptEngine eng;
936 TestClass cls(&eng);
937 cls.setCallableMode(TestClass::CallableReturnsSum);
938 QVERIFY(cls.supportsExtension(QScriptClass::Callable));
939
940 QScriptValue obj = eng.newObject(scriptClass: &cls);
941 eng.globalObject().setProperty(name: "obj", value: obj);
942 obj.setProperty(name: "one", value: QScriptValue(&eng, 1));
943 obj.setProperty(name: "two", value: QScriptValue(&eng, 2));
944 obj.setProperty(name: "three", value: QScriptValue(&eng, 3));
945 // From C++
946 cls.clearReceivedArgs();
947 {
948 QScriptValueList args;
949 args << QScriptValue(&eng, 4) << QScriptValue(&eng, 5);
950 QScriptValue ret = obj.call(thisObject: obj, args);
951 QCOMPARE(cls.lastExtensionType(), QScriptClass::Callable);
952 QCOMPARE(cls.lastExtensionArgument().userType(), qMetaTypeId<QScriptContext*>());
953 QVERIFY(ret.isNumber());
954 QCOMPARE(ret.toNumber(), qsreal(1+2+3+4+5));
955 }
956 // From JS
957 cls.clearReceivedArgs();
958 {
959 QScriptValue ret = eng.evaluate(program: "obj(4, 5)");
960 QCOMPARE(cls.lastExtensionType(), QScriptClass::Callable);
961 QCOMPARE(cls.lastExtensionArgument().userType(), qMetaTypeId<QScriptContext*>());
962 QVERIFY(ret.isNumber());
963 QCOMPARE(ret.toNumber(), qsreal(1+2+3+4+5));
964 }
965
966 cls.setCallableMode(TestClass::CallableReturnsArgument);
967 // From C++
968 cls.clearReceivedArgs();
969 {
970 QScriptValue ret = obj.call(thisObject: obj, args: QScriptValueList() << 123);
971 QCOMPARE(cls.lastExtensionType(), QScriptClass::Callable);
972 QCOMPARE(cls.lastExtensionArgument().userType(), qMetaTypeId<QScriptContext*>());
973 QVERIFY(ret.isNumber());
974 QCOMPARE(ret.toInt32(), 123);
975 }
976 cls.clearReceivedArgs();
977 {
978 QScriptValue ret = obj.call(thisObject: obj, args: QScriptValueList() << true);
979 QCOMPARE(cls.lastExtensionType(), QScriptClass::Callable);
980 QCOMPARE(cls.lastExtensionArgument().userType(), qMetaTypeId<QScriptContext*>());
981 QVERIFY(ret.isBoolean());
982 QCOMPARE(ret.toBoolean(), true);
983 }
984 {
985 QScriptValue ret = obj.call(thisObject: obj, args: QScriptValueList() << QString::fromLatin1(str: "ciao"));
986 QVERIFY(ret.isString());
987 QCOMPARE(ret.toString(), QString::fromLatin1("ciao"));
988 }
989 {
990 QScriptValue objobj = eng.newObject();
991 QScriptValue ret = obj.call(thisObject: obj, args: QScriptValueList() << objobj);
992 QVERIFY(ret.isObject());
993 QVERIFY(ret.strictlyEquals(objobj));
994 }
995 {
996 QScriptValue ret = obj.call(thisObject: obj, args: QScriptValueList() << QScriptValue());
997 QVERIFY(ret.isUndefined());
998 }
999 // From JS
1000 cls.clearReceivedArgs();
1001 {
1002 QScriptValue ret = eng.evaluate(program: "obj(123)");
1003 QVERIFY(ret.isNumber());
1004 QCOMPARE(ret.toInt32(), 123);
1005 }
1006
1007 cls.setCallableMode(TestClass::CallableReturnsInvalidVariant);
1008 {
1009 QScriptValue ret = obj.call(thisObject: obj);
1010 QVERIFY(ret.isUndefined());
1011 }
1012
1013 cls.setCallableMode(TestClass::CallableReturnsThisObject);
1014 // From C++
1015 {
1016 QScriptValue ret = obj.call(thisObject: obj);
1017 QVERIFY(ret.isObject());
1018 QVERIFY(ret.strictlyEquals(obj));
1019 }
1020 // From JS
1021 {
1022 QScriptValue ret = eng.evaluate(program: "obj()");
1023 QVERIFY(ret.isObject());
1024 QVERIFY(ret.strictlyEquals(eng.globalObject()));
1025 }
1026
1027 cls.setCallableMode(TestClass::CallableReturnsCallee);
1028 // From C++
1029 {
1030 QScriptValue ret = obj.call();
1031 QVERIFY(ret.isObject());
1032 QVERIFY(ret.strictlyEquals(obj));
1033 }
1034 // From JS
1035 {
1036 QScriptValue ret = eng.evaluate(program: "obj()");
1037 QVERIFY(ret.isObject());
1038 QVERIFY(ret.strictlyEquals(obj));
1039 }
1040
1041 cls.setCallableMode(TestClass::CallableReturnsArgumentsObject);
1042 // From C++
1043 {
1044 QScriptValue ret = obj.call(thisObject: obj, args: QScriptValueList() << 123);
1045 QVERIFY(ret.isObject());
1046 QVERIFY(ret.property("length").isNumber());
1047 QCOMPARE(ret.property("length").toInt32(), 1);
1048 QVERIFY(ret.property(0).isNumber());
1049 QCOMPARE(ret.property(0).toInt32(), 123);
1050 }
1051 // From JS
1052 {
1053 QScriptValue ret = eng.evaluate(program: "obj(123)");
1054 QVERIFY(ret.isObject());
1055 QVERIFY(ret.property("length").isNumber());
1056 QCOMPARE(ret.property("length").toInt32(), 1);
1057 QVERIFY(ret.property(0).isNumber());
1058 QCOMPARE(ret.property(0).toInt32(), 123);
1059 }
1060}
1061
1062void tst_QScriptClass::extension_Callable_construct()
1063{
1064 QScriptEngine eng;
1065 TestClass cls(&eng);
1066 QScriptValue obj = eng.newObject(scriptClass: &cls);
1067 eng.globalObject().setProperty(name: "obj", value: obj);
1068
1069 // From C++
1070 cls.clearReceivedArgs();
1071 cls.setCallableMode(TestClass::CallableReturnsGlobalObject);
1072 {
1073 QScriptValue ret = obj.construct();
1074 QCOMPARE(cls.lastExtensionType(), QScriptClass::Callable);
1075 QCOMPARE(cls.lastExtensionArgument().userType(), qMetaTypeId<QScriptContext*>());
1076 QVERIFY(ret.isObject());
1077 QVERIFY(ret.strictlyEquals(eng.globalObject()));
1078 }
1079 // From JS
1080 cls.clearReceivedArgs();
1081 {
1082 QScriptValue ret = eng.evaluate(program: "new obj()");
1083 QCOMPARE(cls.lastExtensionType(), QScriptClass::Callable);
1084 QCOMPARE(cls.lastExtensionArgument().userType(), qMetaTypeId<QScriptContext*>());
1085 QVERIFY(ret.isObject());
1086 QVERIFY(ret.strictlyEquals(eng.globalObject()));
1087 }
1088 // From C++
1089 cls.clearReceivedArgs();
1090 cls.setCallableMode(TestClass::CallableInitializesThisObject);
1091 {
1092 QScriptValue ret = obj.construct();
1093 QCOMPARE(cls.lastExtensionType(), QScriptClass::Callable);
1094 QCOMPARE(cls.lastExtensionArgument().userType(), qMetaTypeId<QScriptContext*>());
1095 QVERIFY(ret.isQObject());
1096 QCOMPARE(ret.toQObject(), (QObject*)&eng);
1097 }
1098 // From JS
1099 cls.clearReceivedArgs();
1100 {
1101 QScriptValue ret = eng.evaluate(program: "new obj()");
1102 QCOMPARE(cls.lastExtensionType(), QScriptClass::Callable);
1103 QCOMPARE(cls.lastExtensionArgument().userType(), qMetaTypeId<QScriptContext*>());
1104 QVERIFY(ret.isQObject());
1105 QCOMPARE(ret.toQObject(), (QObject*)&eng);
1106 }
1107}
1108
1109void tst_QScriptClass::extension_HasInstance()
1110{
1111 QScriptEngine eng;
1112 TestClass cls(&eng);
1113 cls.setHasInstance(true);
1114 QVERIFY(cls.supportsExtension(QScriptClass::HasInstance));
1115
1116 QScriptValue obj = eng.newObject(scriptClass: &cls);
1117 obj.setProperty(name: "foo", value: QScriptValue(&eng, 123));
1118 QScriptValue plain = eng.newObject();
1119 QVERIFY(!plain.instanceOf(obj));
1120
1121 eng.globalObject().setProperty(name: "HasInstanceTester", value: obj);
1122 eng.globalObject().setProperty(name: "hasInstanceValue", value: plain);
1123 cls.clearReceivedArgs();
1124 {
1125 QScriptValue ret = eng.evaluate(program: "hasInstanceValue instanceof HasInstanceTester");
1126 QCOMPARE(cls.lastExtensionType(), QScriptClass::HasInstance);
1127 QCOMPARE(cls.lastExtensionArgument().userType(), qMetaTypeId<QScriptValueList>());
1128 QScriptValueList lst = qvariant_cast<QScriptValueList>(v: cls.lastExtensionArgument());
1129 QCOMPARE(lst.size(), 2);
1130 QVERIFY(lst.at(0).strictlyEquals(obj));
1131 QVERIFY(lst.at(1).strictlyEquals(plain));
1132 QVERIFY(ret.isBoolean());
1133 QVERIFY(!ret.toBoolean());
1134 }
1135
1136 plain.setProperty(name: "foo", value: QScriptValue(&eng, 456));
1137 QVERIFY(!plain.instanceOf(obj));
1138 {
1139 QScriptValue ret = eng.evaluate(program: "hasInstanceValue instanceof HasInstanceTester");
1140 QVERIFY(ret.isBoolean());
1141 QVERIFY(!ret.toBoolean());
1142 }
1143
1144 plain.setProperty(name: "foo", value: obj.property(name: "foo"));
1145 QVERIFY(plain.instanceOf(obj));
1146 {
1147 QScriptValue ret = eng.evaluate(program: "hasInstanceValue instanceof HasInstanceTester");
1148 QVERIFY(ret.isBoolean());
1149 QVERIFY(ret.toBoolean());
1150 }
1151}
1152
1153// tests made to match Qt 4.7 (JSC) behaviour
1154void tst_QScriptClass::originalProperties1()
1155{
1156 QScriptEngine eng;
1157
1158 QScriptString orig1 = eng.toStringHandle(str: "orig1");
1159 QScriptString orig2 = eng.toStringHandle(str: "orig2");
1160 QScriptString orig3 = eng.toStringHandle(str: "orig3");
1161 QScriptString new1 = eng.toStringHandle(str: "new1");
1162 QScriptString new2 = eng.toStringHandle(str: "new2");
1163
1164 {
1165 TestClass cls1(&eng);
1166 cls1.addCustomProperty(name: orig2, qflags: QScriptClass::HandlesReadAccess | QScriptClass::HandlesWriteAccess, id: 1, pflags: {}, value: 89);
1167 cls1.addCustomProperty(name: new1, qflags: QScriptClass::HandlesReadAccess | QScriptClass::HandlesWriteAccess, id: 1, pflags: {}, value: "hello");
1168
1169 TestClass cls2(&eng);
1170 cls2.addCustomProperty(name: orig2, qflags: QScriptClass::HandlesReadAccess | QScriptClass::HandlesWriteAccess, id: 1, pflags: {}, value: 59);
1171 cls2.addCustomProperty(name: new2, qflags: QScriptClass::HandlesReadAccess | QScriptClass::HandlesWriteAccess, id: 1, pflags: {}, value: "world");
1172
1173 QScriptValue obj1 = eng.newObject();
1174 obj1.setProperty(name: orig1 , value: 42);
1175 obj1.setProperty(name: orig2 , value: "foo");
1176 obj1.prototype().setProperty(name: orig3, value: "bar");
1177
1178 QCOMPARE(obj1.property(orig1).toInt32(), 42);
1179 QCOMPARE(obj1.property(orig2).toString(), QString::fromLatin1("foo"));
1180 QCOMPARE(obj1.property(orig3).toString(), QString::fromLatin1("bar"));
1181 QVERIFY(!obj1.property(new1).isValid());
1182 QVERIFY(!obj1.property(new2).isValid());
1183
1184 eng.globalObject().setProperty(name: "obj" , value: obj1);
1185
1186 obj1.setScriptClass(&cls1);
1187 QCOMPARE(obj1.property(orig1).toInt32(), 42);
1188 QCOMPARE(obj1.property(orig2).toString(), QString::fromLatin1("foo"));
1189 QCOMPARE(obj1.property(orig3).toString(), QString::fromLatin1("bar"));
1190 QCOMPARE(obj1.property(new1).toString(), QString::fromLatin1("hello"));
1191 QVERIFY(!obj1.property(new2).isValid());
1192
1193 QScriptValue obj2 = eng.evaluate(program: "obj");
1194 QCOMPARE(obj2.scriptClass(), &cls1);
1195 QCOMPARE(obj2.property(orig1).toInt32(), 42);
1196 QCOMPARE(obj2.property(orig2).toString(), QString::fromLatin1("foo"));
1197 QCOMPARE(obj2.property(orig3).toString(), QString::fromLatin1("bar"));
1198 QCOMPARE(obj2.property(new1).toString(), QString::fromLatin1("hello"));
1199 QVERIFY(!obj2.property(new2).isValid());
1200
1201 obj1.setScriptClass(&cls2);
1202 QCOMPARE(obj1.property(orig1).toInt32(), 42);
1203 QCOMPARE(obj1.property(orig2).toString(), QString::fromLatin1("foo"));
1204 QCOMPARE(obj1.property(orig3).toString(), QString::fromLatin1("bar"));
1205 QVERIFY(!obj1.property(new1).isValid());
1206 QCOMPARE(obj1.property(new2).toString(), QString::fromLatin1("world"));
1207
1208 QCOMPARE(obj2.scriptClass(), &cls2);
1209 QCOMPARE(obj2.property(orig1).toInt32(), 42);
1210 QCOMPARE(obj2.property(orig2).toString(), QString::fromLatin1("foo"));
1211 QCOMPARE(obj2.property(orig3).toString(), QString::fromLatin1("bar"));
1212 QVERIFY(!obj2.property(new1).isValid());
1213 QCOMPARE(obj2.property(new2).toString(), QString::fromLatin1("world"));
1214
1215 obj1.setScriptClass(0);
1216 QCOMPARE(obj1.property(orig1).toInt32(), 42);
1217 QCOMPARE(obj1.property(orig2).toString(), QString::fromLatin1("foo"));
1218 QCOMPARE(obj1.property(orig3).toString(), QString::fromLatin1("bar"));
1219 QVERIFY(!obj1.property(new1).isValid());
1220 QVERIFY(!obj1.property(new2).isValid());
1221 }
1222}
1223
1224void tst_QScriptClass::originalProperties2()
1225{
1226 QScriptEngine eng;
1227
1228 QScriptString orig1 = eng.toStringHandle(str: "orig1");
1229 QScriptString orig2 = eng.toStringHandle(str: "orig2");
1230 QScriptString orig3 = eng.toStringHandle(str: "orig3");
1231 QScriptString new1 = eng.toStringHandle(str: "new1");
1232 QScriptString new2 = eng.toStringHandle(str: "new2");
1233
1234 {
1235 TestClass cls1(&eng);
1236 cls1.addCustomProperty(name: orig2, qflags: QScriptClass::HandlesReadAccess | QScriptClass::HandlesWriteAccess, id: 1, pflags: {}, value: 89);
1237 cls1.addCustomProperty(name: new1, qflags: QScriptClass::HandlesReadAccess | QScriptClass::HandlesWriteAccess, id: 1, pflags: {}, value: "hello");
1238
1239 TestClass cls2(&eng);
1240 cls2.addCustomProperty(name: orig2, qflags: QScriptClass::HandlesReadAccess | QScriptClass::HandlesWriteAccess, id: 1, pflags: {}, value: 59);
1241 cls2.addCustomProperty(name: new2, qflags: QScriptClass::HandlesReadAccess | QScriptClass::HandlesWriteAccess, id: 1, pflags: {}, value: "world");
1242
1243 QScriptValue obj1 = eng.newObject();
1244 obj1.setProperty(name: orig1 , value: 42);
1245 obj1.setProperty(name: orig2 , value: "foo");
1246 obj1.prototype().setProperty(name: orig3, value: "bar");
1247
1248 QCOMPARE(obj1.property(orig1).toInt32(), 42);
1249 QCOMPARE(obj1.property(orig2).toString(), QString::fromLatin1("foo"));
1250 QCOMPARE(obj1.property(orig3).toString(), QString::fromLatin1("bar"));
1251 QVERIFY(!obj1.property(new1).isValid());
1252 QVERIFY(!obj1.property(new2).isValid());
1253
1254 obj1.setScriptClass(&cls1);
1255 obj1.setProperty(name: orig1 , value: QScriptValue(&eng, 852));
1256 obj1.setProperty(name: orig2 , value: "oli");
1257 obj1.setProperty(name: orig3 , value: "fu*c");
1258 obj1.setProperty(name: new1 , value: "moo");
1259 obj1.setProperty(name: new2 , value: "allo?");
1260 QCOMPARE(obj1.property(orig1).toInt32(), 852);
1261 QCOMPARE(obj1.property(orig2).toString(), QString::fromLatin1("foo"));
1262 QCOMPARE(obj1.property(orig3).toString(), QString::fromLatin1("fu*c"));
1263 QCOMPARE(obj1.property(new1).toString(), QString::fromLatin1("moo"));
1264 QCOMPARE(obj1.property(new2).toString(), QString::fromLatin1("allo?"));
1265
1266 obj1.setScriptClass(&cls2);
1267 QCOMPARE(obj1.property(orig1).toInt32(), 852);
1268 QCOMPARE(obj1.property(orig2).toString(), QString::fromLatin1("foo"));
1269 QCOMPARE(obj1.property(orig3).toString(), QString::fromLatin1("fu*c"));
1270 QVERIFY(!obj1.property(new1).isValid());
1271 QCOMPARE(obj1.property(new2).toString(), QString::fromLatin1("allo?"));
1272
1273 obj1.setScriptClass(0);
1274 QCOMPARE(obj1.property(orig1).toInt32(), 852);
1275 QCOMPARE(obj1.property(orig2).toString(), QString::fromLatin1("foo"));
1276 QCOMPARE(obj1.property(orig3).toString(), QString::fromLatin1("fu*c"));
1277 QVERIFY(!obj1.property(new1).isValid());
1278 QCOMPARE(obj1.property(new2).toString(), QString::fromLatin1("allo?"));
1279 }
1280}
1281
1282void tst_QScriptClass::originalProperties3()
1283{
1284 QScriptEngine eng;
1285
1286 QScriptString orig1 = eng.toStringHandle(str: "orig1");
1287 QScriptString orig2 = eng.toStringHandle(str: "orig2");
1288 QScriptString orig3 = eng.toStringHandle(str: "orig3");
1289 QScriptString new1 = eng.toStringHandle(str: "new1");
1290 QScriptString new2 = eng.toStringHandle(str: "new2");
1291
1292 {
1293 TestClass cls1(&eng);
1294 cls1.addCustomProperty(name: orig2, qflags: QScriptClass::HandlesReadAccess | QScriptClass::HandlesWriteAccess, id: 1, pflags: {}, value: 89);
1295 cls1.addCustomProperty(name: new1, qflags: QScriptClass::HandlesReadAccess | QScriptClass::HandlesWriteAccess, id: 1, pflags: {}, value: "hello");
1296
1297 TestClass cls2(&eng);
1298 cls2.addCustomProperty(name: orig2, qflags: QScriptClass::HandlesReadAccess | QScriptClass::HandlesWriteAccess, id: 1, pflags: {}, value: 59);
1299 cls2.addCustomProperty(name: new2, qflags: QScriptClass::HandlesReadAccess | QScriptClass::HandlesWriteAccess, id: 1, pflags: {}, value: "world");
1300
1301 QScriptValue obj1 = eng.newObject(scriptClass: &cls1);
1302 QVERIFY(!obj1.property(orig1).isValid());
1303 QCOMPARE(obj1.property(orig2).toInt32(), 89);
1304 QCOMPARE(obj1.property(new1).toString(), QString::fromLatin1("hello"));
1305 QVERIFY(!obj1.property(new2).isValid());
1306 obj1.setProperty(name: orig1, value: 42);
1307 QCOMPARE(obj1.property(orig1).toInt32(), 42);
1308
1309 eng.globalObject().setProperty(name: "obj" , value: obj1);
1310 obj1.setScriptClass(&cls2);
1311 QCOMPARE(obj1.property(orig1).toInt32(), 42);
1312 QCOMPARE(obj1.property(orig2).toInt32(), 59);
1313 QVERIFY(!obj1.property(new1).isValid());
1314 QCOMPARE(obj1.property(new2).toString(), QString::fromLatin1("world"));
1315
1316 QScriptValue obj2 = eng.evaluate(program: "obj");
1317 QCOMPARE(obj2.scriptClass(), &cls2);
1318 QCOMPARE(obj2.property(orig1).toInt32(), 42);
1319 QCOMPARE(obj2.property(orig2).toInt32(), 59);
1320 QVERIFY(!obj2.property(new1).isValid());
1321 QCOMPARE(obj2.property(new2).toString(), QString::fromLatin1("world"));
1322
1323 obj1.setScriptClass(0);
1324 QCOMPARE(obj1.property(orig1).toInt32(), 42);
1325 QVERIFY(!obj1.property(orig2).isValid());
1326 QVERIFY(!obj1.property(new1).isValid());
1327 QVERIFY(!obj1.property(new2).isValid());
1328
1329 QCOMPARE(obj2.scriptClass(), (QScriptClass *)0);
1330 QCOMPARE(obj2.property(orig1).toInt32(), 42);
1331 QVERIFY(!obj2.property(orig2).isValid());
1332 QVERIFY(!obj2.property(new1).isValid());
1333 QVERIFY(!obj2.property(new2).isValid());
1334 }
1335}
1336
1337void tst_QScriptClass::originalProperties4()
1338{
1339 QScriptEngine eng;
1340
1341 QScriptString orig1 = eng.toStringHandle(str: "orig1");
1342 QScriptString orig2 = eng.toStringHandle(str: "orig2");
1343 QScriptString orig3 = eng.toStringHandle(str: "orig3");
1344 QScriptString new1 = eng.toStringHandle(str: "new1");
1345 QScriptString new2 = eng.toStringHandle(str: "new2");
1346
1347 {
1348 TestClass cls1(&eng);
1349 cls1.addCustomProperty(name: orig2, qflags: QScriptClass::HandlesReadAccess | QScriptClass::HandlesWriteAccess, id: 1, pflags: {}, value: 89);
1350 cls1.addCustomProperty(name: new1, qflags: QScriptClass::HandlesReadAccess | QScriptClass::HandlesWriteAccess, id: 1, pflags: {}, value: "hello");
1351
1352 TestClass cls2(&eng);
1353 cls2.addCustomProperty(name: orig2, qflags: QScriptClass::HandlesReadAccess | QScriptClass::HandlesWriteAccess, id: 1, pflags: {}, value: 59);
1354 cls2.addCustomProperty(name: new2, qflags: QScriptClass::HandlesReadAccess | QScriptClass::HandlesWriteAccess, id: 1, pflags: {}, value: "world");
1355
1356 QScriptValue obj1 = eng.newObject(scriptClass: &cls1);
1357 QVERIFY(!obj1.property(orig1).isValid());
1358 QCOMPARE(obj1.property(orig2).toInt32(), 89);
1359 QCOMPARE(obj1.property(new1).toString(), QString::fromLatin1("hello"));
1360 QVERIFY(!obj1.property(new2).isValid());
1361
1362 eng.globalObject().setProperty(name: "obj" , value: obj1);
1363
1364 obj1.setScriptClass(0);
1365 QVERIFY(obj1.isObject());
1366 QVERIFY(!obj1.property(orig1).isValid());
1367 QVERIFY(!obj1.property(orig2).isValid());
1368 QVERIFY(!obj1.property(new1).isValid());
1369 QVERIFY(!obj1.property(new2).isValid());
1370 obj1.setProperty(name: orig1, value: 42);
1371 QCOMPARE(obj1.property(orig1).toInt32(), 42);
1372
1373 QScriptValue obj2 = eng.evaluate(program: "obj");
1374 QCOMPARE(obj2.scriptClass(), (QScriptClass *)0);
1375 QVERIFY(obj2.isObject());
1376 QCOMPARE(obj2.property(orig1).toInt32(), 42);
1377 QVERIFY(!obj2.property(orig2).isValid());
1378 QVERIFY(!obj2.property(new1).isValid());
1379 QVERIFY(!obj2.property(new2).isValid());
1380
1381 obj1.setScriptClass(&cls2);
1382 QCOMPARE(obj1.property(orig1).toInt32(), 42);
1383 QCOMPARE(obj1.property(orig2).toInt32(), 59);
1384 QVERIFY(!obj1.property(new1).isValid());
1385 QCOMPARE(obj1.property(new2).toString(), QString::fromLatin1("world"));
1386
1387 QCOMPARE(obj2.scriptClass(), (QScriptClass *)(&cls2));
1388 QCOMPARE(obj2.property(orig1).toInt32(), 42);
1389 QCOMPARE(obj2.property(orig2).toInt32(), 59);
1390 QVERIFY(!obj2.property(new1).isValid());
1391 QCOMPARE(obj2.property(new2).toString(), QString::fromLatin1("world"));
1392 }
1393}
1394
1395void tst_QScriptClass::defaultImplementations()
1396{
1397 QScriptEngine eng;
1398
1399 QScriptClass defaultClass(&eng);
1400 QCOMPARE(defaultClass.engine(), &eng);
1401 QVERIFY(!defaultClass.prototype().isValid());
1402 QCOMPARE(defaultClass.name(), QString());
1403
1404 QScriptValue obj = eng.newObject(scriptClass: &defaultClass);
1405 QCOMPARE(obj.scriptClass(), &defaultClass);
1406
1407 QScriptString name = eng.toStringHandle(str: "foo");
1408 uint id = -1;
1409 QCOMPARE(defaultClass.queryProperty(obj, name, QScriptClass::HandlesReadAccess, &id), QScriptClass::QueryFlags{});
1410 QVERIFY(!defaultClass.property(obj, name, id).isValid());
1411 QCOMPARE(defaultClass.propertyFlags(obj, name, id), QScriptValue::PropertyFlags{});
1412 defaultClass.setProperty(object&: obj, name, id, value: 123);
1413 QVERIFY(!obj.property(name).isValid());
1414
1415 QCOMPARE(defaultClass.newIterator(obj), (QScriptClassPropertyIterator*)0);
1416
1417 QVERIFY(!defaultClass.supportsExtension(QScriptClass::Callable));
1418 QVERIFY(!defaultClass.supportsExtension(QScriptClass::HasInstance));
1419 QVERIFY(!defaultClass.extension(QScriptClass::Callable).isValid());
1420 QVERIFY(!defaultClass.extension(QScriptClass::HasInstance).isValid());
1421}
1422
1423void tst_QScriptClass::scriptClassObjectInPrototype()
1424{
1425 QScriptEngine eng;
1426 TestClass cls(&eng);
1427 QScriptValue plainObject = eng.newObject();
1428 QScriptValue classObject = eng.newObject(scriptClass: &cls);
1429 plainObject.setPrototype(classObject);
1430 QVERIFY(plainObject.prototype().equals(classObject));
1431 eng.globalObject().setProperty(name: "plainObject", value: plainObject);
1432 eng.globalObject().setProperty(name: "classObject", value: classObject);
1433
1434 QScriptString name = eng.toStringHandle(str: "x");
1435 cls.addCustomProperty(name, qflags: QScriptClass::HandlesReadAccess, /*id=*/ 1,
1436 pflags: QScriptValue::PropertyFlags{}, /*value=*/ 123);
1437 QVERIFY(plainObject.property(name).equals(classObject.property(name)));
1438 QVERIFY(eng.evaluate("plainObject.x == classObject.x").toBool());
1439
1440 // Add a property that shadows the one in the script class.
1441 plainObject.setProperty(name, value: 456);
1442 QVERIFY(!plainObject.property(name).equals(classObject.property(name)));
1443 QVERIFY(eng.evaluate("plainObject.x != classObject.x").toBool());
1444
1445 QVERIFY(eng.evaluate("delete plainObject.x").toBool());
1446 QVERIFY(eng.evaluate("plainObject.x == classObject.x").toBool());
1447}
1448
1449void tst_QScriptClass::scriptClassWithNullEngine()
1450{
1451 QScriptClass cls(0);
1452 QCOMPARE(cls.engine(), (QScriptEngine*)0);
1453 QScriptEngine eng;
1454 QScriptValue obj = eng.newObject(scriptClass: &cls);
1455 QVERIFY(obj.isObject());
1456 QCOMPARE(obj.scriptClass(), &cls);
1457 // The class could have been "bound" to the engine at this point,
1458 // but it's currently not.
1459 // This behavior is not documented and is subject to change.
1460 QCOMPARE(cls.engine(), (QScriptEngine*)0);
1461 // The engine pointer stored in the QScriptClass is not actually used
1462 // during property access, so this still works.
1463 obj.setProperty(name: "x", value: 123);
1464 QVERIFY(obj.property("x").isNumber());
1465}
1466
1467void tst_QScriptClass::scriptClassInOtherEngine()
1468{
1469 QScriptEngine eng;
1470 TestClass cls(&eng);
1471 QScriptEngine eng2;
1472 // We don't check that the class is associated with another engine, so
1473 // we only get a warning when trying to set the prototype of the new
1474 // instance.
1475 // This behavior is not documented and is subject to change.
1476 QTest::ignoreMessage(type: QtWarningMsg, message: "QScriptValue::setPrototype() failed: cannot set a prototype created in a different engine");
1477 QScriptValue obj = eng2.newObject(scriptClass: &cls);
1478 QVERIFY(obj.isObject());
1479 QCOMPARE(obj.scriptClass(), &cls);
1480
1481 obj.setProperty(name: "x", value: 123);
1482 QVERIFY(obj.property("x").isNumber());
1483}
1484
1485QTEST_MAIN(tst_QScriptClass)
1486#include "tst_qscriptclass.moc"
1487

source code of qtscript/tests/auto/qscriptclass/tst_qscriptclass.cpp