Update to 5.0.0-beta1
[profile/ivi/qtdeclarative.git] / tests / auto / qml / qjsengine / tst_qjsengine.cpp
1 /****************************************************************************
2 **
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/
5 **
6 ** This file is part of the test suite of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** GNU Lesser General Public License Usage
10 ** This file may be used under the terms of the GNU Lesser General Public
11 ** License version 2.1 as published by the Free Software Foundation and
12 ** appearing in the file LICENSE.LGPL included in the packaging of this
13 ** file. Please review the following information to ensure the GNU Lesser
14 ** General Public License version 2.1 requirements will be met:
15 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
16 **
17 ** In addition, as a special exception, Nokia gives you certain additional
18 ** rights. These rights are described in the Nokia Qt LGPL Exception
19 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
20 **
21 ** GNU General Public License Usage
22 ** Alternatively, this file may be used under the terms of the GNU General
23 ** Public License version 3.0 as published by the Free Software Foundation
24 ** and appearing in the file LICENSE.GPL included in the packaging of this
25 ** file. Please review the following information to ensure the GNU General
26 ** Public License version 3.0 requirements will be met:
27 ** http://www.gnu.org/copyleft/gpl.html.
28 **
29 ** Other Usage
30 ** Alternatively, this file may be used in accordance with the terms and
31 ** conditions contained in a signed written agreement between you and Nokia.
32 **
33 **
34 **
35 **
36 **
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41
42
43 #include <QtTest/QtTest>
44
45 #include <qjsengine.h>
46 #include <qjsvalueiterator.h>
47 #include <qgraphicsitem.h>
48 #include <qstandarditemmodel.h>
49 #include <QtCore/qnumeric.h>
50 #include <stdlib.h>
51
52 #include <private/v8.h>
53
54 Q_DECLARE_METATYPE(QList<int>)
55 Q_DECLARE_METATYPE(QObjectList)
56
57 // The JavaScriptCore GC marks the C stack. To try to ensure that there is
58 // no JSObject* left in stack memory by the compiler, we call this function
59 // to zap some bytes of memory before calling collectGarbage().
60 static void zapSomeStack()
61 {
62     char buf[4096];
63     memset(buf, 0, sizeof(buf));
64 }
65
66 static void collectGarbage_helper(QJSEngine &eng)
67 {
68     zapSomeStack();
69     eng.collectGarbage();
70 }
71
72 QT_BEGIN_NAMESPACE
73 extern Q_QML_EXPORT v8::Local<v8::Context> qt_QJSEngineV8Context(QJSEngine *);
74 extern Q_QML_EXPORT v8::Local<v8::Value> qt_QJSValueV8Value(const QJSValue &);
75 QT_END_NAMESPACE
76
77 class tst_QJSEngine : public QObject
78 {
79     Q_OBJECT
80
81 public:
82     tst_QJSEngine();
83     virtual ~tst_QJSEngine();
84
85 private slots:
86     void constructWithParent();
87     void newObject();
88     void newArray();
89     void newArray_HooliganTask218092();
90     void newArray_HooliganTask233836();
91     void newVariant();
92     void newVariant_valueOfToString();
93     void newRegExp();
94     void jsRegExp();
95     void newDate();
96     void jsParseDate();
97     void newQObject();
98     void newQObject_ownership();
99     void newQObject_deletedEngine();
100     void globalObjectProperties();
101     void globalObjectEquals();
102     void globalObjectProperties_enumerate();
103     void createGlobalObjectProperty();
104     void globalObjectWithCustomPrototype();
105     void builtinFunctionNames_data();
106     void builtinFunctionNames();
107     void evaluate_data();
108     void evaluate();
109     void errorMessage_QT679();
110     void valueConversion_basic();
111     void valueConversion_QVariant();
112     void valueConversion_basic2();
113     void valueConversion_dateTime();
114     void valueConversion_regExp();
115     void castWithMultipleInheritance();
116     void collectGarbage();
117     void gcWithNestedDataStructure();
118     void stacktrace();
119     void numberParsing_data();
120     void numberParsing();
121     void automaticSemicolonInsertion();
122     void errorConstructors();
123     void argumentsProperty_globalContext();
124     void argumentsProperty_JS();
125     void jsNumberClass();
126     void jsForInStatement_simple();
127     void jsForInStatement_prototypeProperties();
128     void jsForInStatement_mutateWhileIterating();
129     void jsForInStatement_arrays();
130     void jsForInStatement_nullAndUndefined();
131     void jsFunctionDeclarationAsStatement();
132     void stringObjects();
133     void jsStringPrototypeReplaceBugs();
134     void getterSetterThisObject_global();
135     void getterSetterThisObject_plain();
136     void getterSetterThisObject_prototypeChain();
137     void jsContinueInSwitch();
138     void jsShadowReadOnlyPrototypeProperty();
139     void jsReservedWords_data();
140     void jsReservedWords();
141     void jsFutureReservedWords_data();
142     void jsFutureReservedWords();
143     void jsThrowInsideWithStatement();
144     void reentrancy_globalObjectProperties();
145     void reentrancy_Array();
146     void reentrancy_objectCreation();
147     void jsIncDecNonObjectProperty();
148
149     void qRegExpInport_data();
150     void qRegExpInport();
151     void dateRoundtripJSQtJS();
152     void dateRoundtripQtJSQt();
153     void dateConversionJSQt();
154     void dateConversionQtJS();
155     void functionPrototypeExtensions();
156     void threadedEngine();
157
158     void v8Context_simple();
159     void v8Context_exception();
160     void v8Context_mixAPIs();
161 };
162
163 tst_QJSEngine::tst_QJSEngine()
164 {
165 }
166
167 tst_QJSEngine::~tst_QJSEngine()
168 {
169 }
170
171 void tst_QJSEngine::constructWithParent()
172 {
173     QPointer<QJSEngine> ptr;
174     {
175         QObject obj;
176         QJSEngine *engine = new QJSEngine(&obj);
177         ptr = engine;
178     }
179     QVERIFY(ptr == 0);
180 }
181
182 void tst_QJSEngine::newObject()
183 {
184     QJSEngine eng;
185     QJSValue object = eng.newObject();
186     QVERIFY(!object.isUndefined());
187     QCOMPARE(object.isObject(), true);
188     QCOMPARE(object.isCallable(), false);
189     // prototype should be Object.prototype
190     QVERIFY(!object.prototype().isUndefined());
191     QCOMPARE(object.prototype().isObject(), true);
192     QCOMPARE(object.prototype().strictlyEquals(eng.evaluate("Object.prototype")), true);
193 }
194
195 void tst_QJSEngine::newArray()
196 {
197     QJSEngine eng;
198     QJSValue array = eng.newArray();
199     QVERIFY(!array.isUndefined());
200     QCOMPARE(array.isArray(), true);
201     QCOMPARE(array.isObject(), true);
202     QVERIFY(!array.isCallable());
203     // prototype should be Array.prototype
204     QVERIFY(!array.prototype().isUndefined());
205     QCOMPARE(array.prototype().isArray(), true);
206     QCOMPARE(array.prototype().strictlyEquals(eng.evaluate("Array.prototype")), true);
207 }
208
209 void tst_QJSEngine::newArray_HooliganTask218092()
210 {
211     QJSEngine eng;
212     {
213         QJSValue ret = eng.evaluate("[].splice(0, 0, 'a')");
214         QVERIFY(ret.isArray());
215         QCOMPARE(ret.property("length").toInt(), 0);
216     }
217     {
218         QJSValue ret = eng.evaluate("['a'].splice(0, 1, 'b')");
219         QVERIFY(ret.isArray());
220         QCOMPARE(ret.property("length").toInt(), 1);
221     }
222     {
223         QJSValue ret = eng.evaluate("['a', 'b'].splice(0, 1, 'c')");
224         QVERIFY(ret.isArray());
225         QCOMPARE(ret.property("length").toInt(), 1);
226     }
227     {
228         QJSValue ret = eng.evaluate("['a', 'b', 'c'].splice(0, 2, 'd')");
229         QVERIFY(ret.isArray());
230         QCOMPARE(ret.property("length").toInt(), 2);
231     }
232     {
233         QJSValue ret = eng.evaluate("['a', 'b', 'c'].splice(1, 2, 'd', 'e', 'f')");
234         QVERIFY(ret.isArray());
235         QCOMPARE(ret.property("length").toInt(), 2);
236     }
237 }
238
239 void tst_QJSEngine::newArray_HooliganTask233836()
240 {
241     QJSEngine eng;
242     {
243         // According to ECMA-262, this should cause a RangeError.
244         QJSValue ret = eng.evaluate("a = new Array(4294967295); a.push('foo')");
245         QVERIFY(ret.isError() && ret.toString().contains(QLatin1String("RangeError")));
246     }
247     {
248         QJSValue ret = eng.newArray(0xFFFFFFFF);
249         QEXPECT_FAIL("", "The maximum length of arrays is defined by v8 currently and differs from QtScript", Abort);
250         QCOMPARE(ret.property("length").toUInt(), uint(0xFFFFFFFF));
251         ret.setProperty(0xFFFFFFFF, 123);
252         QCOMPARE(ret.property("length").toUInt(), uint(0xFFFFFFFF));
253         QVERIFY(ret.property(0xFFFFFFFF).isNumber());
254         QCOMPARE(ret.property(0xFFFFFFFF).toInt(), 123);
255         ret.setProperty(123, 456);
256         QCOMPARE(ret.property("length").toUInt(), uint(0xFFFFFFFF));
257         QVERIFY(ret.property(123).isNumber());
258         QCOMPARE(ret.property(123).toInt(), 456);
259     }
260 }
261
262 void tst_QJSEngine::newVariant()
263 {
264     QJSEngine eng;
265     {
266         QJSValue opaque = eng.toScriptValue(QVariant(QPoint(1, 2)));
267         QVERIFY(!opaque.isUndefined());
268         QCOMPARE(opaque.isVariant(), true);
269         QVERIFY(!opaque.isCallable());
270         QCOMPARE(opaque.isObject(), true);
271         QVERIFY(!opaque.prototype().isUndefined());
272         QEXPECT_FAIL("", "FIXME: newly created QObject's prototype is an JS Object", Continue);
273         QCOMPARE(opaque.prototype().isVariant(), true);
274         QVERIFY(opaque.property("valueOf").callWithInstance(opaque).equals(opaque));
275     }
276 }
277
278 void tst_QJSEngine::newVariant_valueOfToString()
279 {
280     // valueOf() and toString()
281     QJSEngine eng;
282     {
283         QJSValue object = eng.toScriptValue(QVariant(QPoint(10, 20)));
284         QJSValue value = object.property("valueOf").callWithInstance(object);
285         QVERIFY(value.isObject());
286         QVERIFY(value.strictlyEquals(object));
287         QCOMPARE(object.toString(), QString::fromLatin1("QVariant(QPoint)"));
288     }
289 }
290
291 void tst_QJSEngine::newRegExp()
292 {
293     QSKIP("Test failing - QTBUG-22238");
294     QJSEngine eng;
295     QJSValue rexp = eng.toScriptValue(QRegExp("foo"));
296     QVERIFY(!rexp.isUndefined());
297     QCOMPARE(rexp.isRegExp(), true);
298     QCOMPARE(rexp.isObject(), true);
299     QVERIFY(rexp.isCallable()); // in JSC, RegExp objects are callable
300     // prototype should be RegExp.prototype
301     QVERIFY(!rexp.prototype().isUndefined());
302     QCOMPARE(rexp.prototype().isObject(), true);
303     QCOMPARE(rexp.prototype().isRegExp(), false);
304     QCOMPARE(rexp.prototype().strictlyEquals(eng.evaluate("RegExp.prototype")), true);
305
306     QCOMPARE(qjsvalue_cast<QRegExp>(rexp).pattern(), QRegExp("foo").pattern());
307 }
308
309 void tst_QJSEngine::jsRegExp()
310 {
311     QSKIP("Test failing - QTBUG-22238");
312
313     // See ECMA-262 Section 15.10, "RegExp Objects".
314     // These should really be JS-only tests, as they test the implementation's
315     // ECMA-compliance, not the C++ API. Compliance should already be covered
316     // by the Mozilla tests (qscriptjstestsuite).
317     // We can consider updating the expected results of this test if the
318     // RegExp implementation changes.
319
320     QJSEngine eng;
321     QJSValue r = eng.evaluate("/foo/gim");
322     QVERIFY(r.isRegExp());
323     QCOMPARE(r.toString(), QString::fromLatin1("/foo/gim"));
324
325     QJSValue rxCtor = eng.globalObject().property("RegExp");
326     QJSValue r2 = rxCtor.call(QJSValueList() << r);
327     QVERIFY(r2.isRegExp());
328     QVERIFY(r2.strictlyEquals(r));
329
330     QJSValue r3 = rxCtor.call(QJSValueList() << r << "gim");
331     QVERIFY(r3.isError());
332     QVERIFY(r3.toString().contains(QString::fromLatin1("TypeError"))); // Cannot supply flags when constructing one RegExp from another
333
334     QJSValue r4 = rxCtor.call(QJSValueList() << "foo" << "gim");
335     QVERIFY(r4.isRegExp());
336
337     QJSValue r5 = rxCtor.callAsConstructor(QJSValueList() << r);
338     QVERIFY(r5.isRegExp());
339     QCOMPARE(r5.toString(), QString::fromLatin1("/foo/gim"));
340     // In JSC, constructing a RegExp from another produces the same identical object.
341     // This is different from SpiderMonkey and old back-end.
342     QVERIFY(!r5.strictlyEquals(r));
343
344     QEXPECT_FAIL("", "V8 and jsc ignores invalid flags", Continue); //https://bugs.webkit.org/show_bug.cgi?id=41614
345     QJSValue r6 = rxCtor.callAsConstructor(QJSValueList() << "foo" << "bar");
346     QVERIFY(r6.isError());
347     // QVERIFY(r6.toString().contains(QString::fromLatin1("SyntaxError"))); // Invalid regular expression flag
348
349
350     QJSValue r7 = eng.evaluate("/foo/gimp");
351     /*  v8 and jsc ignores invalid flags
352     QVERIFY(r7.isError());
353     QVERIFY(r7.toString().contains(QString::fromLatin1("SyntaxError"))); // Invalid regular expression flag
354     */
355
356     // JSC doesn't complain about duplicate flags.
357     QJSValue r8 = eng.evaluate("/foo/migmigmig");
358     QVERIFY(r8.isRegExp());
359     QCOMPARE(r8.toString(), QString::fromLatin1("/foo/gim"));
360
361     QJSValue r9 = rxCtor.callAsConstructor();
362     QVERIFY(r9.isRegExp());
363     QCOMPARE(r9.toString(), QString::fromLatin1("/(?:)/"));
364
365     QJSValue r10 = rxCtor.callAsConstructor(QJSValueList() << "" << "gim");
366     QVERIFY(r10.isRegExp());
367     QCOMPARE(r10.toString(), QString::fromLatin1("/(?:)/gim"));
368
369     QJSValue r11 = rxCtor.callAsConstructor(QJSValueList() << "{1.*}" << "g");
370     QVERIFY(r11.isRegExp());
371     QCOMPARE(r11.toString(), QString::fromLatin1("/{1.*}/g"));
372 }
373
374 void tst_QJSEngine::newDate()
375 {
376     QJSEngine eng;
377
378     {
379         QJSValue date = eng.evaluate("new Date(0)");
380         QVERIFY(!date.isUndefined());
381         QCOMPARE(date.isDate(), true);
382         QCOMPARE(date.isObject(), true);
383         QVERIFY(!date.isCallable());
384         // prototype should be Date.prototype
385         QVERIFY(!date.prototype().isUndefined());
386         QCOMPARE(date.prototype().isDate(), true);
387         QCOMPARE(date.prototype().strictlyEquals(eng.evaluate("Date.prototype")), true);
388     }
389
390     {
391         QDateTime dt = QDateTime(QDate(1, 2, 3), QTime(4, 5, 6, 7), Qt::LocalTime);
392         QJSValue date = eng.toScriptValue(dt);
393         QVERIFY(!date.isUndefined());
394         QCOMPARE(date.isDate(), true);
395         QCOMPARE(date.isObject(), true);
396         // prototype should be Date.prototype
397         QVERIFY(!date.prototype().isUndefined());
398         QCOMPARE(date.prototype().isDate(), true);
399         QCOMPARE(date.prototype().strictlyEquals(eng.evaluate("Date.prototype")), true);
400
401         QCOMPARE(date.toDateTime(), dt);
402     }
403
404     {
405         QDateTime dt = QDateTime(QDate(1, 2, 3), QTime(4, 5, 6, 7), Qt::UTC);
406         QJSValue date = eng.toScriptValue(dt);
407         // toDateTime() result should be in local time
408         QCOMPARE(date.toDateTime(), dt.toLocalTime());
409     }
410 }
411
412 void tst_QJSEngine::jsParseDate()
413 {
414     QJSEngine eng;
415     // Date.parse() should return NaN when it fails
416     {
417         QJSValue ret = eng.evaluate("Date.parse()");
418         QVERIFY(ret.isNumber());
419         QVERIFY(qIsNaN(ret.toNumber()));
420     }
421
422     // Date.parse() should be able to parse the output of Date().toString()
423     {
424         QJSValue ret = eng.evaluate("var x = new Date(); var s = x.toString(); s == new Date(Date.parse(s)).toString()");
425         QVERIFY(ret.isBool());
426         QCOMPARE(ret.toBool(), true);
427     }
428 }
429
430 void tst_QJSEngine::newQObject()
431 {
432     QJSEngine eng;
433     QObject temp;
434
435     {
436         QJSValue qobject = eng.newQObject(0);
437         QCOMPARE(qobject.isNull(), true);
438         QCOMPARE(qobject.isObject(), false);
439         QCOMPARE(qobject.toQObject(), (QObject *)0);
440     }
441     {
442         QJSValue qobject = eng.newQObject(&temp);
443         QVERIFY(!qobject.isUndefined());
444         QCOMPARE(qobject.isQObject(), true);
445         QCOMPARE(qobject.isObject(), true);
446         QCOMPARE(qobject.toQObject(), (QObject *)&temp);
447         QVERIFY(!qobject.isCallable());
448         // prototype should be QObject.prototype
449         QCOMPARE(qobject.prototype().isObject(), true);
450         QEXPECT_FAIL("", "FIXME: newly created QObject's prototype is an JS Object", Continue);
451         QCOMPARE(qobject.prototype().isQObject(), true);
452     }
453 }
454
455 void tst_QJSEngine::newQObject_ownership()
456 {
457     QJSEngine eng;
458     {
459         QPointer<QObject> ptr = new QObject();
460         QVERIFY(ptr != 0);
461         {
462             QJSValue v = eng.newQObject(ptr);
463         }
464         collectGarbage_helper(eng);
465         if (ptr)
466             QGuiApplication::sendPostedEvents(ptr, QEvent::DeferredDelete);
467         QVERIFY(ptr == 0);
468     }
469     {
470         QPointer<QObject> ptr = new QObject(this);
471         QVERIFY(ptr != 0);
472         {
473             QJSValue v = eng.newQObject(ptr);
474         }
475         QObject *before = ptr;
476         collectGarbage_helper(eng);
477         QVERIFY(ptr == before);
478         delete ptr;
479     }
480     {
481         QObject *parent = new QObject();
482         QObject *child = new QObject(parent);
483         QJSValue v = eng.newQObject(child);
484         QCOMPARE(v.toQObject(), child);
485         delete parent;
486         QCOMPARE(v.toQObject(), (QObject *)0);
487     }
488     {
489         QPointer<QObject> ptr = new QObject();
490         QVERIFY(ptr != 0);
491         {
492             QJSValue v = eng.newQObject(ptr);
493         }
494         collectGarbage_helper(eng);
495         // no parent, so it should be like ScriptOwnership
496         if (ptr)
497             QGuiApplication::sendPostedEvents(ptr, QEvent::DeferredDelete);
498         QVERIFY(ptr == 0);
499     }
500     {
501         QObject *parent = new QObject();
502         QPointer<QObject> child = new QObject(parent);
503         QVERIFY(child != 0);
504         {
505             QJSValue v = eng.newQObject(child);
506         }
507         collectGarbage_helper(eng);
508         // has parent, so it should be like QtOwnership
509         QVERIFY(child != 0);
510         delete parent;
511     }
512 }
513
514 void tst_QJSEngine::newQObject_deletedEngine()
515 {
516     QJSValue object;
517     QObject *ptr = new QObject();
518     QSignalSpy spy(ptr, SIGNAL(destroyed()));
519     {
520         QJSEngine engine;
521         object = engine.newQObject(ptr);
522         engine.globalObject().setProperty("obj", object);
523     }
524     QTRY_VERIFY(spy.count());
525 }
526
527 void tst_QJSEngine::globalObjectProperties()
528 {
529     QSKIP("Test failing - QTBUG-22238");
530     // See ECMA-262 Section 15.1, "The Global Object".
531
532     QJSEngine eng;
533     QJSValue global = eng.globalObject();
534
535     QVERIFY(global.property("NaN").isNumber());
536     QVERIFY(qIsNaN(global.property("NaN").toNumber()));
537
538     QVERIFY(global.property("Infinity").isNumber());
539     QVERIFY(qIsInf(global.property("Infinity").toNumber()));
540
541     QVERIFY(global.property("undefined").isUndefined());
542
543     QVERIFY(global.property("eval").isCallable());
544
545     QVERIFY(global.property("parseInt").isCallable());
546
547     QVERIFY(global.property("parseFloat").isCallable());
548
549     QVERIFY(global.property("isNaN").isCallable());
550
551     QVERIFY(global.property("isFinite").isCallable());
552
553     QVERIFY(global.property("decodeURI").isCallable());
554
555     QVERIFY(global.property("decodeURIComponent").isCallable());
556
557     QVERIFY(global.property("encodeURI").isCallable());
558
559     QVERIFY(global.property("encodeURIComponent").isCallable());
560
561     QVERIFY(global.property("Object").isCallable());
562     QVERIFY(global.property("Function").isCallable());
563     QVERIFY(global.property("Array").isCallable());
564     QVERIFY(global.property("String").isCallable());
565     QVERIFY(global.property("Boolean").isCallable());
566     QVERIFY(global.property("Number").isCallable());
567     QVERIFY(global.property("Date").isCallable());
568     QVERIFY(global.property("RegExp").isCallable());
569     QVERIFY(global.property("Error").isCallable());
570     QVERIFY(global.property("EvalError").isCallable());
571     QVERIFY(global.property("RangeError").isCallable());
572     QVERIFY(global.property("ReferenceError").isCallable());
573     QVERIFY(global.property("SyntaxError").isCallable());
574     QVERIFY(global.property("TypeError").isCallable());
575     QVERIFY(global.property("URIError").isCallable());
576     QVERIFY(global.property("Math").isObject());
577     QVERIFY(!global.property("Math").isCallable());
578 }
579
580 void tst_QJSEngine::globalObjectEquals()
581 {
582     QJSEngine eng;
583     QJSValue o = eng.globalObject();
584     QVERIFY(o.strictlyEquals(eng.globalObject()));
585     QVERIFY(o.equals(eng.globalObject()));
586 }
587
588 void tst_QJSEngine::globalObjectProperties_enumerate()
589 {
590     QSKIP("Test failing - QTBUG-22238");
591     QJSEngine eng;
592     QJSValue global = eng.globalObject();
593
594     QSet<QString> expectedNames;
595     expectedNames
596         << "isNaN"
597         << "parseFloat"
598         << "String"
599         << "EvalError"
600         << "URIError"
601         << "Math"
602         << "encodeURIComponent"
603         << "RangeError"
604         << "eval"
605         << "isFinite"
606         << "ReferenceError"
607         << "Infinity"
608         << "Function"
609         << "RegExp"
610         << "Number"
611         << "parseInt"
612         << "Object"
613         << "decodeURI"
614         << "TypeError"
615         << "Boolean"
616         << "encodeURI"
617         << "NaN"
618         << "Error"
619         << "decodeURIComponent"
620         << "Date"
621         << "Array"
622         << "escape"
623         << "unescape"
624         << "SyntaxError"
625         << "undefined"
626         // JavaScriptCore
627         << "JSON"
628         // V8
629         << "execScript" //execScript for IE compatibility.
630         ;
631     QSet<QString> actualNames;
632     {
633         QJSValueIterator it(global);
634         while (it.hasNext()) {
635             it.next();
636             actualNames.insert(it.name());
637         }
638     }
639
640     QSet<QString> remainingNames = actualNames;
641     {
642         QSet<QString>::const_iterator it;
643         for (it = expectedNames.constBegin(); it != expectedNames.constEnd(); ++it) {
644             QString name = *it;
645             QVERIFY(actualNames.contains(name));
646             remainingNames.remove(name);
647         }
648     }
649     QVERIFY(remainingNames.isEmpty());
650 }
651
652 void tst_QJSEngine::createGlobalObjectProperty()
653 {
654     QJSEngine eng;
655     QJSValue global = eng.globalObject();
656     // create property with no attributes
657     {
658         QString name = QString::fromLatin1("foo");
659         QVERIFY(global.property(name).isUndefined());
660         QJSValue val(123);
661         global.setProperty(name, val);
662         QVERIFY(global.property(name).equals(val));
663         global.deleteProperty(name);
664         QVERIFY(global.property(name).isUndefined());
665     }
666 }
667
668 void tst_QJSEngine::globalObjectWithCustomPrototype()
669 {
670     QJSEngine engine;
671     QJSValue proto = engine.newObject();
672     proto.setProperty("protoProperty", 123);
673     QJSValue global = engine.globalObject();
674     QJSValue originalProto = global.prototype();
675     global.setPrototype(proto);
676     {
677         QJSValue ret = engine.evaluate("protoProperty");
678         QEXPECT_FAIL("", "Replacing the prototype of the global object is currently unsupported (see also v8 issue 1078)", Abort);
679         QVERIFY(ret.isNumber());
680         QVERIFY(ret.strictlyEquals(global.property("protoProperty")));
681     }
682     {
683         QJSValue ret = engine.evaluate("this.protoProperty");
684         QVERIFY(ret.isNumber());
685         QVERIFY(ret.strictlyEquals(global.property("protoProperty")));
686     }
687     {
688         QJSValue ret = engine.evaluate("hasOwnProperty('protoProperty')");
689         QVERIFY(ret.isBool());
690         QVERIFY(!ret.toBool());
691     }
692     {
693         QJSValue ret = engine.evaluate("this.hasOwnProperty('protoProperty')");
694         QVERIFY(ret.isBool());
695         QVERIFY(!ret.toBool());
696     }
697
698     // Custom prototype set from JS
699     {
700         QJSValue ret = engine.evaluate("this.__proto__ = { 'a': 123 }; a");
701         QVERIFY(ret.isNumber());
702         QVERIFY(ret.strictlyEquals(global.property("a")));
703     }
704 }
705
706 void tst_QJSEngine::builtinFunctionNames_data()
707 {
708     QTest::addColumn<QString>("expression");
709     QTest::addColumn<QString>("expectedName");
710
711     // See ECMA-262 Chapter 15, "Standard Built-in ECMAScript Objects".
712
713     QTest::newRow("parseInt") << QString("parseInt") << QString("parseInt");
714     QTest::newRow("parseFloat") << QString("parseFloat") << QString("parseFloat");
715     QTest::newRow("isNaN") << QString("isNaN") << QString("isNaN");
716     QTest::newRow("isFinite") << QString("isFinite") << QString("isFinite");
717     QTest::newRow("decodeURI") << QString("decodeURI") << QString("decodeURI");
718     QTest::newRow("decodeURIComponent") << QString("decodeURIComponent") << QString("decodeURIComponent");
719     QTest::newRow("encodeURI") << QString("encodeURI") << QString("encodeURI");
720     QTest::newRow("encodeURIComponent") << QString("encodeURIComponent") << QString("encodeURIComponent");
721     QTest::newRow("escape") << QString("escape") << QString("escape");
722     QTest::newRow("unescape") << QString("unescape") << QString("unescape");
723
724     QTest::newRow("Array") << QString("Array") << QString("Array");
725     QTest::newRow("Array.prototype.toString") << QString("Array.prototype.toString") << QString("toString");
726     QTest::newRow("Array.prototype.toLocaleString") << QString("Array.prototype.toLocaleString") << QString("toLocaleString");
727     QTest::newRow("Array.prototype.concat") << QString("Array.prototype.concat") << QString("concat");
728     QTest::newRow("Array.prototype.join") << QString("Array.prototype.join") << QString("join");
729     QTest::newRow("Array.prototype.pop") << QString("Array.prototype.pop") << QString("pop");
730     QTest::newRow("Array.prototype.push") << QString("Array.prototype.push") << QString("push");
731     QTest::newRow("Array.prototype.reverse") << QString("Array.prototype.reverse") << QString("reverse");
732     QTest::newRow("Array.prototype.shift") << QString("Array.prototype.shift") << QString("shift");
733     QTest::newRow("Array.prototype.slice") << QString("Array.prototype.slice") << QString("slice");
734     QTest::newRow("Array.prototype.sort") << QString("Array.prototype.sort") << QString("sort");
735     QTest::newRow("Array.prototype.splice") << QString("Array.prototype.splice") << QString("splice");
736     QTest::newRow("Array.prototype.unshift") << QString("Array.prototype.unshift") << QString("unshift");
737
738     QTest::newRow("Boolean") << QString("Boolean") << QString("Boolean");
739     QTest::newRow("Boolean.prototype.toString") << QString("Boolean.prototype.toString") << QString("toString");
740
741     QTest::newRow("Date") << QString("Date") << QString("Date");
742     QTest::newRow("Date.prototype.toString") << QString("Date.prototype.toString") << QString("toString");
743     QTest::newRow("Date.prototype.toDateString") << QString("Date.prototype.toDateString") << QString("toDateString");
744     QTest::newRow("Date.prototype.toTimeString") << QString("Date.prototype.toTimeString") << QString("toTimeString");
745     QTest::newRow("Date.prototype.toLocaleString") << QString("Date.prototype.toLocaleString") << QString("toLocaleString");
746     QTest::newRow("Date.prototype.toLocaleDateString") << QString("Date.prototype.toLocaleDateString") << QString("toLocaleDateString");
747     QTest::newRow("Date.prototype.toLocaleTimeString") << QString("Date.prototype.toLocaleTimeString") << QString("toLocaleTimeString");
748     QTest::newRow("Date.prototype.valueOf") << QString("Date.prototype.valueOf") << QString("valueOf");
749     QTest::newRow("Date.prototype.getTime") << QString("Date.prototype.getTime") << QString("getTime");
750     QTest::newRow("Date.prototype.getYear") << QString("Date.prototype.getYear") << QString("getYear");
751     QTest::newRow("Date.prototype.getFullYear") << QString("Date.prototype.getFullYear") << QString("getFullYear");
752     QTest::newRow("Date.prototype.getUTCFullYear") << QString("Date.prototype.getUTCFullYear") << QString("getUTCFullYear");
753     QTest::newRow("Date.prototype.getMonth") << QString("Date.prototype.getMonth") << QString("getMonth");
754     QTest::newRow("Date.prototype.getUTCMonth") << QString("Date.prototype.getUTCMonth") << QString("getUTCMonth");
755     QTest::newRow("Date.prototype.getDate") << QString("Date.prototype.getDate") << QString("getDate");
756     QTest::newRow("Date.prototype.getUTCDate") << QString("Date.prototype.getUTCDate") << QString("getUTCDate");
757     QTest::newRow("Date.prototype.getDay") << QString("Date.prototype.getDay") << QString("getDay");
758     QTest::newRow("Date.prototype.getUTCDay") << QString("Date.prototype.getUTCDay") << QString("getUTCDay");
759     QTest::newRow("Date.prototype.getHours") << QString("Date.prototype.getHours") << QString("getHours");
760     QTest::newRow("Date.prototype.getUTCHours") << QString("Date.prototype.getUTCHours") << QString("getUTCHours");
761     QTest::newRow("Date.prototype.getMinutes") << QString("Date.prototype.getMinutes") << QString("getMinutes");
762     QTest::newRow("Date.prototype.getUTCMinutes") << QString("Date.prototype.getUTCMinutes") << QString("getUTCMinutes");
763     QTest::newRow("Date.prototype.getSeconds") << QString("Date.prototype.getSeconds") << QString("getSeconds");
764     QTest::newRow("Date.prototype.getUTCSeconds") << QString("Date.prototype.getUTCSeconds") << QString("getUTCSeconds");
765     QTest::newRow("Date.prototype.getMilliseconds") << QString("Date.prototype.getMilliseconds") << QString("getMilliseconds");
766     QTest::newRow("Date.prototype.getUTCMilliseconds") << QString("Date.prototype.getUTCMilliseconds") << QString("getUTCMilliseconds");
767     QTest::newRow("Date.prototype.getTimezoneOffset") << QString("Date.prototype.getTimezoneOffset") << QString("getTimezoneOffset");
768     QTest::newRow("Date.prototype.setTime") << QString("Date.prototype.setTime") << QString("setTime");
769     QTest::newRow("Date.prototype.setMilliseconds") << QString("Date.prototype.setMilliseconds") << QString("setMilliseconds");
770     QTest::newRow("Date.prototype.setUTCMilliseconds") << QString("Date.prototype.setUTCMilliseconds") << QString("setUTCMilliseconds");
771     QTest::newRow("Date.prototype.setSeconds") << QString("Date.prototype.setSeconds") << QString("setSeconds");
772     QTest::newRow("Date.prototype.setUTCSeconds") << QString("Date.prototype.setUTCSeconds") << QString("setUTCSeconds");
773     QTest::newRow("Date.prototype.setMinutes") << QString("Date.prototype.setMinutes") << QString("setMinutes");
774     QTest::newRow("Date.prototype.setUTCMinutes") << QString("Date.prototype.setUTCMinutes") << QString("setUTCMinutes");
775     QTest::newRow("Date.prototype.setHours") << QString("Date.prototype.setHours") << QString("setHours");
776     QTest::newRow("Date.prototype.setUTCHours") << QString("Date.prototype.setUTCHours") << QString("setUTCHours");
777     QTest::newRow("Date.prototype.setDate") << QString("Date.prototype.setDate") << QString("setDate");
778     QTest::newRow("Date.prototype.setUTCDate") << QString("Date.prototype.setUTCDate") << QString("setUTCDate");
779     QTest::newRow("Date.prototype.setMonth") << QString("Date.prototype.setMonth") << QString("setMonth");
780     QTest::newRow("Date.prototype.setUTCMonth") << QString("Date.prototype.setUTCMonth") << QString("setUTCMonth");
781     QTest::newRow("Date.prototype.setYear") << QString("Date.prototype.setYear") << QString("setYear");
782     QTest::newRow("Date.prototype.setFullYear") << QString("Date.prototype.setFullYear") << QString("setFullYear");
783     QTest::newRow("Date.prototype.setUTCFullYear") << QString("Date.prototype.setUTCFullYear") << QString("setUTCFullYear");
784     QTest::newRow("Date.prototype.toUTCString") << QString("Date.prototype.toUTCString") << QString("toUTCString");
785     QTest::newRow("Date.prototype.toGMTString") << QString("Date.prototype.toGMTString") << QString("toGMTString");
786
787     QTest::newRow("Error") << QString("Error") << QString("Error");
788 //    QTest::newRow("Error.prototype.backtrace") << QString("Error.prototype.backtrace") << QString("backtrace");
789     QTest::newRow("Error.prototype.toString") << QString("Error.prototype.toString") << QString("toString");
790
791     QTest::newRow("EvalError") << QString("EvalError") << QString("EvalError");
792     QTest::newRow("RangeError") << QString("RangeError") << QString("RangeError");
793     QTest::newRow("ReferenceError") << QString("ReferenceError") << QString("ReferenceError");
794     QTest::newRow("SyntaxError") << QString("SyntaxError") << QString("SyntaxError");
795     QTest::newRow("TypeError") << QString("TypeError") << QString("TypeError");
796     QTest::newRow("URIError") << QString("URIError") << QString("URIError");
797
798     QTest::newRow("Function") << QString("Function") << QString("Function");
799     QTest::newRow("Function.prototype.toString") << QString("Function.prototype.toString") << QString("toString");
800     QTest::newRow("Function.prototype.apply") << QString("Function.prototype.apply") << QString("apply");
801     QTest::newRow("Function.prototype.call") << QString("Function.prototype.call") << QString("call");
802 /*  In V8, those function are only there for signals
803     QTest::newRow("Function.prototype.connect") << QString("Function.prototype.connect") << QString("connect");
804     QTest::newRow("Function.prototype.disconnect") << QString("Function.prototype.disconnect") << QString("disconnect");*/
805
806     QTest::newRow("Math.abs") << QString("Math.abs") << QString("abs");
807     QTest::newRow("Math.acos") << QString("Math.acos") << QString("acos");
808     QTest::newRow("Math.asin") << QString("Math.asin") << QString("asin");
809     QTest::newRow("Math.atan") << QString("Math.atan") << QString("atan");
810     QTest::newRow("Math.atan2") << QString("Math.atan2") << QString("atan2");
811     QTest::newRow("Math.ceil") << QString("Math.ceil") << QString("ceil");
812     QTest::newRow("Math.cos") << QString("Math.cos") << QString("cos");
813     QTest::newRow("Math.exp") << QString("Math.exp") << QString("exp");
814     QTest::newRow("Math.floor") << QString("Math.floor") << QString("floor");
815     QTest::newRow("Math.log") << QString("Math.log") << QString("log");
816     QTest::newRow("Math.max") << QString("Math.max") << QString("max");
817     QTest::newRow("Math.min") << QString("Math.min") << QString("min");
818     QTest::newRow("Math.pow") << QString("Math.pow") << QString("pow");
819     QTest::newRow("Math.random") << QString("Math.random") << QString("random");
820     QTest::newRow("Math.round") << QString("Math.round") << QString("round");
821     QTest::newRow("Math.sin") << QString("Math.sin") << QString("sin");
822     QTest::newRow("Math.sqrt") << QString("Math.sqrt") << QString("sqrt");
823     QTest::newRow("Math.tan") << QString("Math.tan") << QString("tan");
824
825     QTest::newRow("Number") << QString("Number") << QString("Number");
826     QTest::newRow("Number.prototype.toString") << QString("Number.prototype.toString") << QString("toString");
827     QTest::newRow("Number.prototype.toLocaleString") << QString("Number.prototype.toLocaleString") << QString("toLocaleString");
828     QTest::newRow("Number.prototype.valueOf") << QString("Number.prototype.valueOf") << QString("valueOf");
829     QTest::newRow("Number.prototype.toFixed") << QString("Number.prototype.toFixed") << QString("toFixed");
830     QTest::newRow("Number.prototype.toExponential") << QString("Number.prototype.toExponential") << QString("toExponential");
831     QTest::newRow("Number.prototype.toPrecision") << QString("Number.prototype.toPrecision") << QString("toPrecision");
832
833     QTest::newRow("Object") << QString("Object") << QString("Object");
834     QTest::newRow("Object.prototype.toString") << QString("Object.prototype.toString") << QString("toString");
835     QTest::newRow("Object.prototype.toLocaleString") << QString("Object.prototype.toLocaleString") << QString("toLocaleString");
836     QTest::newRow("Object.prototype.valueOf") << QString("Object.prototype.valueOf") << QString("valueOf");
837     QTest::newRow("Object.prototype.hasOwnProperty") << QString("Object.prototype.hasOwnProperty") << QString("hasOwnProperty");
838     QTest::newRow("Object.prototype.isPrototypeOf") << QString("Object.prototype.isPrototypeOf") << QString("isPrototypeOf");
839     QTest::newRow("Object.prototype.propertyIsEnumerable") << QString("Object.prototype.propertyIsEnumerable") << QString("propertyIsEnumerable");
840     QTest::newRow("Object.prototype.__defineGetter__") << QString("Object.prototype.__defineGetter__") << QString("__defineGetter__");
841     QTest::newRow("Object.prototype.__defineSetter__") << QString("Object.prototype.__defineSetter__") << QString("__defineSetter__");
842
843     QTest::newRow("RegExp") << QString("RegExp") << QString("RegExp");
844     QTest::newRow("RegExp.prototype.exec") << QString("RegExp.prototype.exec") << QString("exec");
845     QTest::newRow("RegExp.prototype.test") << QString("RegExp.prototype.test") << QString("test");
846     QTest::newRow("RegExp.prototype.toString") << QString("RegExp.prototype.toString") << QString("toString");
847
848     QTest::newRow("String") << QString("String") << QString("String");
849     QTest::newRow("String.prototype.toString") << QString("String.prototype.toString") << QString("toString");
850     QTest::newRow("String.prototype.valueOf") << QString("String.prototype.valueOf") << QString("valueOf");
851     QTest::newRow("String.prototype.charAt") << QString("String.prototype.charAt") << QString("charAt");
852     QTest::newRow("String.prototype.charCodeAt") << QString("String.prototype.charCodeAt") << QString("charCodeAt");
853     QTest::newRow("String.prototype.concat") << QString("String.prototype.concat") << QString("concat");
854     QTest::newRow("String.prototype.indexOf") << QString("String.prototype.indexOf") << QString("indexOf");
855     QTest::newRow("String.prototype.lastIndexOf") << QString("String.prototype.lastIndexOf") << QString("lastIndexOf");
856     QTest::newRow("String.prototype.localeCompare") << QString("String.prototype.localeCompare") << QString("localeCompare");
857     QTest::newRow("String.prototype.match") << QString("String.prototype.match") << QString("match");
858     QTest::newRow("String.prototype.replace") << QString("String.prototype.replace") << QString("replace");
859     QTest::newRow("String.prototype.search") << QString("String.prototype.search") << QString("search");
860     QTest::newRow("String.prototype.slice") << QString("String.prototype.slice") << QString("slice");
861     QTest::newRow("String.prototype.split") << QString("String.prototype.split") << QString("split");
862     QTest::newRow("String.prototype.substring") << QString("String.prototype.substring") << QString("substring");
863     QTest::newRow("String.prototype.toLowerCase") << QString("String.prototype.toLowerCase") << QString("toLowerCase");
864     QTest::newRow("String.prototype.toLocaleLowerCase") << QString("String.prototype.toLocaleLowerCase") << QString("toLocaleLowerCase");
865     QTest::newRow("String.prototype.toUpperCase") << QString("String.prototype.toUpperCase") << QString("toUpperCase");
866     QTest::newRow("String.prototype.toLocaleUpperCase") << QString("String.prototype.toLocaleUpperCase") << QString("toLocaleUpperCase");
867 }
868
869 void tst_QJSEngine::builtinFunctionNames()
870 {
871     QFETCH(QString, expression);
872     QFETCH(QString, expectedName);
873     QJSEngine eng;
874     // The "name" property is actually non-standard, but JSC supports it.
875     QJSValue ret = eng.evaluate(QString::fromLatin1("%0.name").arg(expression));
876     QVERIFY(ret.isString());
877     QCOMPARE(ret.toString(), expectedName);
878 }
879
880 void tst_QJSEngine::evaluate_data()
881 {
882     QTest::addColumn<QString>("code");
883     QTest::addColumn<int>("lineNumber");
884     QTest::addColumn<bool>("expectHadError");
885     QTest::addColumn<int>("expectErrorLineNumber");
886
887     QTest::newRow("(newline)") << QString("\n") << -1 << false << -1;
888     QTest::newRow("0 //")   << QString("0 //") << -1 << false << -1;
889     QTest::newRow("/* */")   << QString("/* */") << -1 << false << -1;
890     QTest::newRow("//") << QString("//") << -1 << false << -1;
891     QTest::newRow("(spaces)")  << QString("  ") << -1 << false << -1;
892     QTest::newRow("(empty)")   << QString("") << -1 << false << -1;
893     QTest::newRow("0")     << QString("0")       << -1 << false << -1;
894     QTest::newRow("0=1")   << QString("\n0=1;\n") << -1 << true  << 2;
895     QTest::newRow("a=1")   << QString("a=1\n")   << -1 << false << -1;
896     QTest::newRow("a=1;K") << QString("a=1;\nK") << -1 << true  << 2;
897
898     QTest::newRow("f()") << QString("function f()\n"
899                                     "{\n"
900                                     "  var a;\n"
901                                     "  var b=\";\n" // here's the error
902                                     "}\n"
903                                     "f();\n")
904                          << -1 << true << 4;
905
906     QTest::newRow("0")     << QString("0")       << 10 << false << -1;
907     QTest::newRow("0=1")   << QString("\n\n0=1\n") << 10 << true  << 12;
908     QTest::newRow("a=1")   << QString("a=1\n")   << 10 << false << -1;
909     QTest::newRow("a=1;K") << QString("a=1;\n\nK") << 10 << true  << 12;
910
911     QTest::newRow("f()") << QString("function f()\n"
912                                     "{\n"
913                                     "  var a;\n"
914                                     "\n\n"
915                                     "  var b=\";\n" // here's the error
916                                     "}\n"
917                                     "f();\n")
918                          << 10 << true << 15;
919     QTest::newRow("functionThatDoesntExist()")
920         << QString(";\n;\n;\nfunctionThatDoesntExist()")
921         << -1 << true << 4;
922     QTest::newRow("for (var p in this) { continue labelThatDoesntExist; }")
923         << QString("for (var p in this) {\ncontinue labelThatDoesntExist; }")
924         << 4 << true << 5;
925     QTest::newRow("duplicateLabel: { duplicateLabel: ; }")
926         << QString("duplicateLabel: { duplicateLabel: ; }")
927         << 12 << true << 12;
928
929     QTest::newRow("/=/") << QString("/=/") << -1 << false << -1;
930     QTest::newRow("/=/g") << QString("/=/g") << -1 << false << -1;
931     QTest::newRow("/a/") << QString("/a/") << -1 << false << -1;
932     QTest::newRow("/a/g") << QString("/a/g") << -1 << false << -1;
933     QTest::newRow("/a/gim") << QString("/a/gim") << -1 << false << -1;
934     QTest::newRow("/a/gimp") << QString("/a/gimp") << 1 << true << 1;
935 }
936
937 void tst_QJSEngine::evaluate()
938 {
939     QFETCH(QString, code);
940     QFETCH(int, lineNumber);
941     QFETCH(bool, expectHadError);
942     QFETCH(int, expectErrorLineNumber);
943
944     QJSEngine eng;
945     QJSValue ret;
946     if (lineNumber != -1)
947         ret = eng.evaluate(code, /*fileName =*/QString(), lineNumber);
948     else
949         ret = eng.evaluate(code);
950     QCOMPARE(ret.isError(), expectHadError);
951     if (ret.isError()) {
952         QEXPECT_FAIL("", "we have no more lineNumber property ", Continue);
953         QVERIFY(ret.property("lineNumber").strictlyEquals(eng.toScriptValue(expectErrorLineNumber)));
954     }
955 }
956
957 void tst_QJSEngine::errorMessage_QT679()
958 {
959     QJSEngine engine;
960     engine.globalObject().setProperty("foo", 15);
961     QJSValue error = engine.evaluate("'hello world';\nfoo.bar.blah");
962     QVERIFY(error.isError());
963     QVERIFY(error.toString().startsWith(QString::fromLatin1("TypeError: ")));
964 }
965
966 struct Foo {
967 public:
968     int x, y;
969     Foo() : x(-1), y(-1) { }
970 };
971
972 Q_DECLARE_METATYPE(Foo)
973 Q_DECLARE_METATYPE(Foo*)
974
975 Q_DECLARE_METATYPE(QLinkedList<QString>)
976 Q_DECLARE_METATYPE(QList<Foo>)
977 Q_DECLARE_METATYPE(QVector<QChar>)
978 Q_DECLARE_METATYPE(QStack<int>)
979 Q_DECLARE_METATYPE(QQueue<char>)
980 Q_DECLARE_METATYPE(QLinkedList<QStack<int> >)
981
982 void tst_QJSEngine::valueConversion_basic()
983 {
984     QJSEngine eng;
985     {
986         QJSValue num = eng.toScriptValue(123);
987         QCOMPARE(num.isNumber(), true);
988         QCOMPARE(num.strictlyEquals(eng.toScriptValue(123)), true);
989
990         int inum = eng.fromScriptValue<int>(num);
991         QCOMPARE(inum, 123);
992
993         QString snum = eng.fromScriptValue<QString>(num);
994         QCOMPARE(snum, QLatin1String("123"));
995     }
996     {
997         QJSValue num = eng.toScriptValue(123);
998         QCOMPARE(num.isNumber(), true);
999         QCOMPARE(num.strictlyEquals(eng.toScriptValue(123)), true);
1000
1001         int inum = eng.fromScriptValue<int>(num);
1002         QCOMPARE(inum, 123);
1003
1004         QString snum = eng.fromScriptValue<QString>(num);
1005         QCOMPARE(snum, QLatin1String("123"));
1006     }
1007     {
1008         QJSValue num = eng.toScriptValue(123);
1009         QCOMPARE(eng.fromScriptValue<char>(num), char(123));
1010         QCOMPARE(eng.fromScriptValue<unsigned char>(num), (unsigned char)(123));
1011         QCOMPARE(eng.fromScriptValue<short>(num), short(123));
1012         QCOMPARE(eng.fromScriptValue<unsigned short>(num), (unsigned short)(123));
1013         QCOMPARE(eng.fromScriptValue<float>(num), float(123));
1014         QCOMPARE(eng.fromScriptValue<double>(num), double(123));
1015         QCOMPARE(eng.fromScriptValue<qlonglong>(num), qlonglong(123));
1016         QCOMPARE(eng.fromScriptValue<qulonglong>(num), qulonglong(123));
1017     }
1018     {
1019         QJSValue num(123);
1020         QCOMPARE(eng.fromScriptValue<char>(num), char(123));
1021         QCOMPARE(eng.fromScriptValue<unsigned char>(num), (unsigned char)(123));
1022         QCOMPARE(eng.fromScriptValue<short>(num), short(123));
1023         QCOMPARE(eng.fromScriptValue<unsigned short>(num), (unsigned short)(123));
1024         QCOMPARE(eng.fromScriptValue<float>(num), float(123));
1025         QCOMPARE(eng.fromScriptValue<double>(num), double(123));
1026         QCOMPARE(eng.fromScriptValue<qlonglong>(num), qlonglong(123));
1027         QCOMPARE(eng.fromScriptValue<qulonglong>(num), qulonglong(123));
1028     }
1029
1030     {
1031         QJSValue num = eng.toScriptValue(Q_INT64_C(0x100000000));
1032         QCOMPARE(eng.fromScriptValue<qlonglong>(num), Q_INT64_C(0x100000000));
1033         QCOMPARE(eng.fromScriptValue<qulonglong>(num), Q_UINT64_C(0x100000000));
1034     }
1035
1036     {
1037         QChar c = QLatin1Char('c');
1038         QJSValue str = eng.toScriptValue(QString::fromLatin1("ciao"));
1039         QCOMPARE(eng.fromScriptValue<QChar>(str), c);
1040         QJSValue code = eng.toScriptValue(c.unicode());
1041         QCOMPARE(eng.fromScriptValue<QChar>(code), c);
1042         QCOMPARE(eng.fromScriptValue<QChar>(eng.toScriptValue(c)), c);
1043     }
1044
1045     QVERIFY(eng.toScriptValue(static_cast<void *>(0)).isNull());
1046 }
1047
1048 void tst_QJSEngine::valueConversion_QVariant()
1049 {
1050     QJSEngine eng;
1051     // qScriptValueFromValue() should be "smart" when the argument is a QVariant
1052     {
1053         QJSValue val = eng.toScriptValue(QVariant());
1054         QVERIFY(!val.isVariant());
1055         QVERIFY(val.isUndefined());
1056     }
1057     // Checking nested QVariants
1058     {
1059         QVariant tmp1;
1060         QVariant tmp2(QMetaType::QVariant, &tmp1);
1061         QVERIFY(QMetaType::Type(tmp2.type()) == QMetaType::QVariant);
1062
1063         QJSValue val1 = eng.toScriptValue(tmp1);
1064         QJSValue val2 = eng.toScriptValue(tmp2);
1065         QVERIFY(val1.isUndefined());
1066         QEXPECT_FAIL("", "Variant are unrwapped, maybe we should not...", Continue);
1067         QVERIFY(!val2.isUndefined());
1068         QVERIFY(!val1.isVariant());
1069         QEXPECT_FAIL("", "Variant are unrwapped, maybe we should not...", Continue);
1070         QVERIFY(val2.isVariant());
1071     }
1072     {
1073         QVariant tmp1(123);
1074         QVariant tmp2(QMetaType::QVariant, &tmp1);
1075         QVariant tmp3(QMetaType::QVariant, &tmp2);
1076         QVERIFY(QMetaType::Type(tmp1.type()) == QMetaType::Int);
1077         QVERIFY(QMetaType::Type(tmp2.type()) == QMetaType::QVariant);
1078         QVERIFY(QMetaType::Type(tmp3.type()) == QMetaType::QVariant);
1079
1080         QJSValue val1 = eng.toScriptValue(tmp2);
1081         QJSValue val2 = eng.toScriptValue(tmp3);
1082         QVERIFY(!val1.isUndefined());
1083         QVERIFY(!val2.isUndefined());
1084         QEXPECT_FAIL("", "Variant are unrwapped, maybe we should not...", Continue);
1085         QVERIFY(val1.isVariant());
1086         QEXPECT_FAIL("", "Variant are unrwapped, maybe we should not...", Continue);
1087         QVERIFY(val2.isVariant());
1088         QVERIFY(val1.toVariant().toInt() == 123);
1089         QVERIFY(eng.toScriptValue(val2.toVariant()).toVariant().toInt() == 123);
1090     }
1091     {
1092         QJSValue val = eng.toScriptValue(QVariant(true));
1093         QVERIFY(!val.isVariant());
1094         QVERIFY(val.isBool());
1095         QCOMPARE(val.toBool(), true);
1096     }
1097     {
1098         QJSValue val = eng.toScriptValue(QVariant(int(123)));
1099         QVERIFY(!val.isVariant());
1100         QVERIFY(val.isNumber());
1101         QCOMPARE(val.toNumber(), qreal(123));
1102     }
1103     {
1104         QJSValue val = eng.toScriptValue(QVariant(qreal(1.25)));
1105         QVERIFY(!val.isVariant());
1106         QVERIFY(val.isNumber());
1107         QCOMPARE(val.toNumber(), qreal(1.25));
1108     }
1109     {
1110         QString str = QString::fromLatin1("ciao");
1111         QJSValue val = eng.toScriptValue(QVariant(str));
1112         QVERIFY(!val.isVariant());
1113         QVERIFY(val.isString());
1114         QCOMPARE(val.toString(), str);
1115     }
1116     {
1117         QJSValue val = eng.toScriptValue(qVariantFromValue((QObject*)this));
1118         QVERIFY(!val.isVariant());
1119         QVERIFY(val.isQObject());
1120         QCOMPARE(val.toQObject(), (QObject*)this);
1121     }
1122     {
1123         QVariant var = qVariantFromValue(QPoint(123, 456));
1124         QJSValue val = eng.toScriptValue(var);
1125         QVERIFY(val.isVariant());
1126         QCOMPARE(val.toVariant(), var);
1127     }
1128
1129     QCOMPARE(qjsvalue_cast<QVariant>(QJSValue(123)), QVariant(123));
1130
1131     QVERIFY(eng.toScriptValue(QVariant(QMetaType::VoidStar, 0)).isNull());
1132 }
1133
1134 void tst_QJSEngine::valueConversion_basic2()
1135 {
1136     QJSEngine eng;
1137     // more built-in types
1138     {
1139         QJSValue val = eng.toScriptValue(uint(123));
1140         QVERIFY(val.isNumber());
1141         QCOMPARE(val.toInt(), 123);
1142     }
1143     {
1144         QJSValue val = eng.toScriptValue(qulonglong(123));
1145         QVERIFY(val.isNumber());
1146         QCOMPARE(val.toInt(), 123);
1147     }
1148     {
1149         QJSValue val = eng.toScriptValue(float(123));
1150         QVERIFY(val.isNumber());
1151         QCOMPARE(val.toInt(), 123);
1152     }
1153     {
1154         QJSValue val = eng.toScriptValue(short(123));
1155         QVERIFY(val.isNumber());
1156         QCOMPARE(val.toInt(), 123);
1157     }
1158     {
1159         QJSValue val = eng.toScriptValue(ushort(123));
1160         QVERIFY(val.isNumber());
1161         QCOMPARE(val.toInt(), 123);
1162     }
1163     {
1164         QJSValue val = eng.toScriptValue(char(123));
1165         QVERIFY(val.isNumber());
1166         QCOMPARE(val.toInt(), 123);
1167     }
1168     {
1169         QJSValue val = eng.toScriptValue(uchar(123));
1170         QVERIFY(val.isNumber());
1171         QCOMPARE(val.toInt(), 123);
1172     }
1173 }
1174
1175 void tst_QJSEngine::valueConversion_dateTime()
1176 {
1177     QJSEngine eng;
1178     {
1179         QDateTime in = QDateTime::currentDateTime();
1180         QJSValue val = eng.toScriptValue(in);
1181         QVERIFY(val.isDate());
1182         QCOMPARE(val.toDateTime(), in);
1183     }
1184     {
1185         QDate in = QDate::currentDate();
1186         QJSValue val = eng.toScriptValue(in);
1187         QVERIFY(val.isDate());
1188         QCOMPARE(val.toDateTime().date(), in);
1189     }
1190 }
1191
1192 void tst_QJSEngine::valueConversion_regExp()
1193 {
1194     QJSEngine eng;
1195     {
1196         QRegExp in = QRegExp("foo");
1197         QJSValue val = eng.toScriptValue(in);
1198         QVERIFY(val.isRegExp());
1199         QRegExp out = qjsvalue_cast<QRegExp>(val);
1200         QEXPECT_FAIL("", "QTBUG-6136: JSC-based back-end doesn't preserve QRegExp::patternSyntax (always uses RegExp2)", Continue);
1201         QCOMPARE(out.patternSyntax(), in.patternSyntax());
1202         QCOMPARE(out.pattern(), in.pattern());
1203         QCOMPARE(out.caseSensitivity(), in.caseSensitivity());
1204         QCOMPARE(out.isMinimal(), in.isMinimal());
1205     }
1206     {
1207         QRegExp in = QRegExp("foo", Qt::CaseSensitive, QRegExp::RegExp2);
1208         QJSValue val = eng.toScriptValue(in);
1209         QVERIFY(val.isRegExp());
1210         QCOMPARE(qjsvalue_cast<QRegExp>(val), in);
1211     }
1212     {
1213         QRegExp in = QRegExp("foo");
1214         in.setMinimal(true);
1215         QJSValue val = eng.toScriptValue(in);
1216         QVERIFY(val.isRegExp());
1217         QEXPECT_FAIL("", "QTBUG-6136: JSC-based back-end doesn't preserve QRegExp::minimal (always false)", Continue);
1218         QCOMPARE(qjsvalue_cast<QRegExp>(val).isMinimal(), in.isMinimal());
1219     }
1220 }
1221
1222 Q_DECLARE_METATYPE(QGradient)
1223 Q_DECLARE_METATYPE(QGradient*)
1224 Q_DECLARE_METATYPE(QLinearGradient)
1225
1226 class Klazz : public QWidget,
1227               public QStandardItem,
1228               public QGraphicsItem
1229 {
1230     Q_INTERFACES(QGraphicsItem)
1231     Q_OBJECT
1232 public:
1233     Klazz(QWidget *parent = 0) : QWidget(parent) { }
1234     virtual QRectF boundingRect() const { return QRectF(); }
1235     virtual void paint(QPainter*, const QStyleOptionGraphicsItem*, QWidget*) { }
1236 };
1237
1238 Q_DECLARE_METATYPE(Klazz*)
1239 Q_DECLARE_METATYPE(QStandardItem*)
1240
1241 void tst_QJSEngine::castWithMultipleInheritance()
1242 {
1243     QJSEngine eng;
1244     Klazz klz;
1245     QJSValue v = eng.newQObject(&klz);
1246
1247     QCOMPARE(qjsvalue_cast<Klazz*>(v), &klz);
1248     QCOMPARE(qjsvalue_cast<QWidget*>(v), (QWidget *)&klz);
1249     QCOMPARE(qjsvalue_cast<QObject*>(v), (QObject *)&klz);
1250     QCOMPARE(qjsvalue_cast<QStandardItem*>(v), (QStandardItem *)&klz);
1251     QCOMPARE(qjsvalue_cast<QGraphicsItem*>(v), (QGraphicsItem *)&klz);
1252 }
1253
1254 void tst_QJSEngine::collectGarbage()
1255 {
1256     QJSEngine eng;
1257     eng.evaluate("a = new Object(); a = new Object(); a = new Object()");
1258     QJSValue a = eng.newObject();
1259     a = eng.newObject();
1260     a = eng.newObject();
1261     QPointer<QObject> ptr = new QObject();
1262     QVERIFY(ptr != 0);
1263     (void)eng.newQObject(ptr);
1264     collectGarbage_helper(eng);
1265     if (ptr)
1266         QGuiApplication::sendPostedEvents(ptr, QEvent::DeferredDelete);
1267     QVERIFY(ptr == 0);
1268 }
1269
1270 void tst_QJSEngine::gcWithNestedDataStructure()
1271 {
1272     // The GC must be able to traverse deeply nested objects, otherwise this
1273     // test would crash.
1274     QJSEngine eng;
1275     QJSValue ret = eng.evaluate(
1276         "function makeList(size)"
1277         "{"
1278         "  var head = { };"
1279         "  var l = head;"
1280         "  for (var i = 0; i < size; ++i) {"
1281         "    l.data = i + \"\";"
1282         "    l.next = { }; l = l.next;"
1283         "  }"
1284         "  l.next = null;"
1285         "  return head;"
1286         "}");
1287     QVERIFY(!ret.isError());
1288     const int size = 200;
1289     QJSValue head = eng.evaluate(QString::fromLatin1("makeList(%0)").arg(size));
1290     QVERIFY(!head.isError());
1291     for (int x = 0; x < 2; ++x) {
1292         if (x == 1)
1293             eng.evaluate("gc()");
1294         QJSValue l = head;
1295         // Make sure all the nodes are still alive.
1296         for (int i = 0; i < 200; ++i) {
1297             QCOMPARE(l.property("data").toString(), QString::number(i));
1298             l = l.property("next");
1299         }
1300     }
1301 }
1302
1303 void tst_QJSEngine::stacktrace()
1304 {
1305     QString script = QString::fromLatin1(
1306         "function foo(counter) {\n"
1307         "    switch (counter) {\n"
1308         "        case 0: foo(counter+1); break;\n"
1309         "        case 1: foo(counter+1); break;\n"
1310         "        case 2: foo(counter+1); break;\n"
1311         "        case 3: foo(counter+1); break;\n"
1312         "        case 4: foo(counter+1); break;\n"
1313         "        default:\n"
1314         "        throw new Error('blah');\n"
1315         "    }\n"
1316         "}\n"
1317         "foo(0);");
1318
1319     const QString fileName("testfile");
1320
1321     QStringList backtrace;
1322     backtrace << "foo(5)@testfile:9"
1323               << "foo(4)@testfile:7"
1324               << "foo(3)@testfile:6"
1325               << "foo(2)@testfile:5"
1326               << "foo(1)@testfile:4"
1327               << "foo(0)@testfile:3"
1328               << "<global>()@testfile:12";
1329
1330     QJSEngine eng;
1331     QJSValue result = eng.evaluate(script, fileName);
1332     QVERIFY(result.isError());
1333
1334     QJSValue stack = result.property("stack");
1335
1336     QJSValueIterator it(stack);
1337     int counter = 5;
1338     while (it.hasNext()) {
1339         it.next();
1340         QJSValue obj = it.value();
1341         QJSValue frame = obj.property("frame");
1342
1343         QCOMPARE(obj.property("fileName").toString(), fileName);
1344         if (counter >= 0) {
1345             QJSValue callee = frame.property("arguments").property("callee");
1346             QVERIFY(callee.strictlyEquals(eng.globalObject().property("foo")));
1347             QCOMPARE(obj.property("functionName").toString(), QString("foo"));
1348             int line = obj.property("lineNumber").toInt();
1349             if (counter == 5)
1350                 QCOMPARE(line, 9);
1351             else
1352                 QCOMPARE(line, 3 + counter);
1353         } else {
1354             QVERIFY(frame.strictlyEquals(eng.globalObject()));
1355             QVERIFY(obj.property("functionName").toString().isEmpty());
1356         }
1357
1358         --counter;
1359     }
1360
1361     // throw something that isn't an Error object
1362     // ###FIXME: No uncaughtExceptionBacktrace: QVERIFY(eng.uncaughtExceptionBacktrace().isEmpty());
1363     QString script2 = QString::fromLatin1(
1364         "function foo(counter) {\n"
1365         "    switch (counter) {\n"
1366         "        case 0: foo(counter+1); break;\n"
1367         "        case 1: foo(counter+1); break;\n"
1368         "        case 2: foo(counter+1); break;\n"
1369         "        case 3: foo(counter+1); break;\n"
1370         "        case 4: foo(counter+1); break;\n"
1371         "        default:\n"
1372         "        throw 'just a string';\n"
1373         "    }\n"
1374         "}\n"
1375         "foo(0);");
1376
1377     QJSValue result2 = eng.evaluate(script2, fileName);
1378     QVERIFY(!result2.isError());
1379     QVERIFY(result2.isString());
1380 }
1381
1382 void tst_QJSEngine::numberParsing_data()
1383 {
1384     QTest::addColumn<QString>("string");
1385     QTest::addColumn<qreal>("expect");
1386
1387     QTest::newRow("decimal 0") << QString("0") << qreal(0);
1388     QTest::newRow("octal 0") << QString("00") << qreal(00);
1389     QTest::newRow("hex 0") << QString("0x0") << qreal(0x0);
1390     QTest::newRow("decimal 100") << QString("100") << qreal(100);
1391     QTest::newRow("hex 100") << QString("0x100") << qreal(0x100);
1392     QTest::newRow("octal 100") << QString("0100") << qreal(0100);
1393     QTest::newRow("decimal 4G") << QString("4294967296") << qreal(Q_UINT64_C(4294967296));
1394     QTest::newRow("hex 4G") << QString("0x100000000") << qreal(Q_UINT64_C(0x100000000));
1395     QTest::newRow("octal 4G") << QString("040000000000") << qreal(Q_UINT64_C(040000000000));
1396     QTest::newRow("0.5") << QString("0.5") << qreal(0.5);
1397     QTest::newRow("1.5") << QString("1.5") << qreal(1.5);
1398     QTest::newRow("1e2") << QString("1e2") << qreal(100);
1399 }
1400
1401 void tst_QJSEngine::numberParsing()
1402 {
1403     QFETCH(QString, string);
1404     QFETCH(qreal, expect);
1405
1406     QJSEngine eng;
1407     QJSValue ret = eng.evaluate(string);
1408     QVERIFY(ret.isNumber());
1409     qreal actual = ret.toNumber();
1410     QCOMPARE(actual, expect);
1411 }
1412
1413 // see ECMA-262, section 7.9
1414 // This is testing ECMA compliance, not our C++ API, but it's important that
1415 // the back-end is conformant in this regard.
1416 void tst_QJSEngine::automaticSemicolonInsertion()
1417 {
1418     QJSEngine eng;
1419     {
1420         QJSValue ret = eng.evaluate("{ 1 2 } 3");
1421         QVERIFY(ret.isError());
1422         QVERIFY(ret.toString().contains("SyntaxError"));
1423     }
1424     {
1425         QJSValue ret = eng.evaluate("{ 1\n2 } 3");
1426         QVERIFY(ret.isNumber());
1427         QCOMPARE(ret.toInt(), 3);
1428     }
1429     {
1430         QJSValue ret = eng.evaluate("for (a; b\n)");
1431         QVERIFY(ret.isError());
1432         QVERIFY(ret.toString().contains("SyntaxError"));
1433     }
1434     {
1435         QJSValue ret = eng.evaluate("(function() { return\n1 + 2 })()");
1436         QVERIFY(ret.isUndefined());
1437     }
1438     {
1439         eng.evaluate("c = 2; b = 1");
1440         QJSValue ret = eng.evaluate("a = b\n++c");
1441         QVERIFY(ret.isNumber());
1442         QCOMPARE(ret.toInt(), 3);
1443     }
1444     {
1445         QJSValue ret = eng.evaluate("if (a > b)\nelse c = d");
1446         QVERIFY(ret.isError());
1447         QVERIFY(ret.toString().contains("SyntaxError"));
1448     }
1449     {
1450         eng.evaluate("function c() { return { foo: function() { return 5; } } }");
1451         eng.evaluate("b = 1; d = 2; e = 3");
1452         QJSValue ret = eng.evaluate("a = b + c\n(d + e).foo()");
1453         QVERIFY(ret.isNumber());
1454         QCOMPARE(ret.toInt(), 6);
1455     }
1456     {
1457         QJSValue ret = eng.evaluate("throw\n1");
1458         QVERIFY(ret.isError());
1459         QVERIFY(ret.toString().contains("SyntaxError"));
1460     }
1461     {
1462         QJSValue ret = eng.evaluate("a = Number(1)\n++a");
1463         QVERIFY(ret.isNumber());
1464         QCOMPARE(ret.toInt(), 2);
1465     }
1466
1467     // "a semicolon is never inserted automatically if the semicolon
1468     // would then be parsed as an empty statement"
1469     {
1470         eng.evaluate("a = 123");
1471         QJSValue ret = eng.evaluate("if (0)\n ++a; a");
1472         QVERIFY(ret.isNumber());
1473         QCOMPARE(ret.toInt(), 123);
1474     }
1475     {
1476         eng.evaluate("a = 123");
1477         QJSValue ret = eng.evaluate("if (0)\n --a; a");
1478         QVERIFY(ret.isNumber());
1479         QCOMPARE(ret.toInt(), 123);
1480     }
1481     {
1482         eng.evaluate("a = 123");
1483         QJSValue ret = eng.evaluate("if ((0))\n ++a; a");
1484         QVERIFY(ret.isNumber());
1485         QCOMPARE(ret.toInt(), 123);
1486     }
1487     {
1488         eng.evaluate("a = 123");
1489         QJSValue ret = eng.evaluate("if ((0))\n --a; a");
1490         QVERIFY(ret.isNumber());
1491         QCOMPARE(ret.toInt(), 123);
1492     }
1493     {
1494         eng.evaluate("a = 123");
1495         QJSValue ret = eng.evaluate("if (0\n)\n ++a; a");
1496         QVERIFY(ret.isNumber());
1497         QCOMPARE(ret.toInt(), 123);
1498     }
1499     {
1500         eng.evaluate("a = 123");
1501         QJSValue ret = eng.evaluate("if (0\n ++a; a");
1502         QVERIFY(ret.isError());
1503     }
1504     {
1505         eng.evaluate("a = 123");
1506         QJSValue ret = eng.evaluate("if (0))\n ++a; a");
1507         QVERIFY(ret.isError());
1508     }
1509     {
1510         QJSValue ret = eng.evaluate("n = 0; for (i = 0; i < 10; ++i)\n ++n; n");
1511         QVERIFY(ret.isNumber());
1512         QCOMPARE(ret.toInt(), 10);
1513     }
1514     {
1515         QJSValue ret = eng.evaluate("n = 30; for (i = 0; i < 10; ++i)\n --n; n");
1516         QVERIFY(ret.isNumber());
1517         QCOMPARE(ret.toInt(), 20);
1518     }
1519     {
1520         QJSValue ret = eng.evaluate("n = 0; for (var i = 0; i < 10; ++i)\n ++n; n");
1521         QVERIFY(ret.isNumber());
1522         QCOMPARE(ret.toInt(), 10);
1523     }
1524     {
1525         QJSValue ret = eng.evaluate("n = 30; for (var i = 0; i < 10; ++i)\n --n; n");
1526         QVERIFY(ret.isNumber());
1527         QCOMPARE(ret.toInt(), 20);
1528     }
1529     {
1530         QJSValue ret = eng.evaluate("n = 0; i = 0; while (i++ < 10)\n ++n; n");
1531         QVERIFY(ret.isNumber());
1532         QCOMPARE(ret.toInt(), 10);
1533     }
1534     {
1535         QJSValue ret = eng.evaluate("n = 30; i = 0; while (i++ < 10)\n --n; n");
1536         QVERIFY(ret.isNumber());
1537         QCOMPARE(ret.toInt(), 20);
1538     }
1539     {
1540         QJSValue ret = eng.evaluate("o = { a: 0, b: 1, c: 2 }; n = 0; for (i in o)\n ++n; n");
1541         QVERIFY(ret.isNumber());
1542         QCOMPARE(ret.toInt(), 3);
1543     }
1544     {
1545         QJSValue ret = eng.evaluate("o = { a: 0, b: 1, c: 2 }; n = 9; for (i in o)\n --n; n");
1546         QVERIFY(ret.isNumber());
1547         QCOMPARE(ret.toInt(), 6);
1548     }
1549     {
1550         QJSValue ret = eng.evaluate("o = { a: 0, b: 1, c: 2 }; n = 0; for (var i in o)\n ++n; n");
1551         QVERIFY(ret.isNumber());
1552         QCOMPARE(ret.toInt(), 3);
1553     }
1554     {
1555         QJSValue ret = eng.evaluate("o = { a: 0, b: 1, c: 2 }; n = 9; for (var i in o)\n --n; n");
1556         QVERIFY(ret.isNumber());
1557         QCOMPARE(ret.toInt(), 6);
1558     }
1559     {
1560         QJSValue ret = eng.evaluate("o = { n: 3 }; n = 5; with (o)\n ++n; n");
1561         QVERIFY(ret.isNumber());
1562         QCOMPARE(ret.toInt(), 5);
1563     }
1564     {
1565         QJSValue ret = eng.evaluate("o = { n: 3 }; n = 10; with (o)\n --n; n");
1566         QVERIFY(ret.isNumber());
1567         QCOMPARE(ret.toInt(), 10);
1568     }
1569     {
1570         QJSValue ret = eng.evaluate("n = 5; i = 0; do\n ++n; while (++i < 10); n");
1571         QVERIFY(ret.isNumber());
1572         QCOMPARE(ret.toInt(), 15);
1573     }
1574     {
1575         QJSValue ret = eng.evaluate("n = 20; i = 0; do\n --n; while (++i < 10); n");
1576         QVERIFY(ret.isNumber());
1577         QCOMPARE(ret.toInt(), 10);
1578     }
1579
1580     {
1581         QJSValue ret = eng.evaluate("n = 1; i = 0; if (n) i\n++n; n");
1582         QVERIFY(ret.isNumber());
1583         QCOMPARE(ret.toInt(), 2);
1584     }
1585     {
1586         QJSValue ret = eng.evaluate("n = 1; i = 0; if (n) i\n--n; n");
1587         QVERIFY(ret.isNumber());
1588         QCOMPARE(ret.toInt(), 0);
1589     }
1590
1591     {
1592         QJSValue ret = eng.evaluate("if (0)");
1593         QVERIFY(ret.isError());
1594     }
1595     {
1596         QJSValue ret = eng.evaluate("while (0)");
1597         QVERIFY(ret.isError());
1598     }
1599     {
1600         QJSValue ret = eng.evaluate("for (;;)");
1601         QVERIFY(ret.isError());
1602     }
1603     {
1604         QJSValue ret = eng.evaluate("for (p in this)");
1605         QVERIFY(ret.isError());
1606     }
1607     {
1608         QJSValue ret = eng.evaluate("with (this)");
1609         QVERIFY(ret.isError());
1610     }
1611     {
1612         QJSValue ret = eng.evaluate("do");
1613         QVERIFY(ret.isError());
1614     }
1615 }
1616
1617 void tst_QJSEngine::errorConstructors()
1618 {
1619     QJSEngine eng;
1620     QStringList prefixes;
1621     prefixes << "" << "Eval" << "Range" << "Reference" << "Syntax" << "Type" << "URI";
1622     for (int x = 0; x < 3; ++x) {
1623         for (int i = 0; i < prefixes.size(); ++i) {
1624             QString name = prefixes.at(i) + QLatin1String("Error");
1625             QString code = QString(i+1, QLatin1Char('\n'));
1626             if (x == 0)
1627                 code += QLatin1String("throw ");
1628             else if (x == 1)
1629                 code += QLatin1String("new ");
1630             code += name + QLatin1String("()");
1631             QJSValue ret = eng.evaluate(code);
1632             QVERIFY(ret.isError());
1633             QVERIFY(ret.toString().startsWith(name));
1634             //QTBUG-6138: JSC doesn't assign lineNumber when errors are not thrown
1635             QEXPECT_FAIL("", "we have no more lineNumber property ", Continue);
1636             QCOMPARE(ret.property("lineNumber").toInt(), i+2);
1637         }
1638     }
1639 }
1640
1641 void tst_QJSEngine::argumentsProperty_globalContext()
1642 {
1643     QJSEngine eng;
1644     {
1645         // Unlike function calls, the global context doesn't have an
1646         // arguments property.
1647         QJSValue ret = eng.evaluate("arguments");
1648         QVERIFY(ret.isError());
1649         QVERIFY(ret.toString().contains(QString::fromLatin1("ReferenceError")));
1650     }
1651     eng.evaluate("arguments = 10");
1652     {
1653         QJSValue ret = eng.evaluate("arguments");
1654         QVERIFY(ret.isNumber());
1655         QCOMPARE(ret.toInt(), 10);
1656     }
1657     QVERIFY(eng.evaluate("delete arguments").toBool());
1658     QVERIFY(eng.globalObject().property("arguments").isUndefined());
1659 }
1660
1661 void tst_QJSEngine::argumentsProperty_JS()
1662 {
1663     {
1664         QJSEngine eng;
1665         eng.evaluate("o = { arguments: 123 }");
1666         QJSValue ret = eng.evaluate("with (o) { arguments; }");
1667         QVERIFY(ret.isNumber());
1668         QCOMPARE(ret.toInt(), 123);
1669     }
1670     {
1671         QJSEngine eng;
1672         QVERIFY(eng.globalObject().property("arguments").isUndefined());
1673         // This is testing ECMA-262 compliance. In function calls, "arguments"
1674         // appears like a local variable, and it can be replaced.
1675         QJSValue ret = eng.evaluate("(function() { arguments = 456; return arguments; })()");
1676         QVERIFY(ret.isNumber());
1677         QCOMPARE(ret.toInt(), 456);
1678         QVERIFY(eng.globalObject().property("arguments").isUndefined());
1679     }
1680 }
1681
1682 void tst_QJSEngine::jsNumberClass()
1683 {
1684     // See ECMA-262 Section 15.7, "Number Objects".
1685
1686     QJSEngine eng;
1687
1688     QJSValue ctor = eng.globalObject().property("Number");
1689     QVERIFY(ctor.property("length").isNumber());
1690     QCOMPARE(ctor.property("length").toNumber(), qreal(1));
1691     QJSValue proto = ctor.property("prototype");
1692     QVERIFY(proto.isObject());
1693     {
1694         QVERIFY(ctor.property("MAX_VALUE").isNumber());
1695         QVERIFY(ctor.property("MIN_VALUE").isNumber());
1696         QVERIFY(ctor.property("NaN").isNumber());
1697         QVERIFY(ctor.property("NEGATIVE_INFINITY").isNumber());
1698         QVERIFY(ctor.property("POSITIVE_INFINITY").isNumber());
1699     }
1700     QCOMPARE(proto.toNumber(), qreal(0));
1701     QVERIFY(proto.property("constructor").strictlyEquals(ctor));
1702
1703     {
1704         QJSValue ret = eng.evaluate("Number()");
1705         QVERIFY(ret.isNumber());
1706         QCOMPARE(ret.toNumber(), qreal(0));
1707     }
1708     {
1709         QJSValue ret = eng.evaluate("Number(123)");
1710         QVERIFY(ret.isNumber());
1711         QCOMPARE(ret.toNumber(), qreal(123));
1712     }
1713     {
1714         QJSValue ret = eng.evaluate("Number('456')");
1715         QVERIFY(ret.isNumber());
1716         QCOMPARE(ret.toNumber(), qreal(456));
1717     }
1718     {
1719         QJSValue ret = eng.evaluate("new Number()");
1720         QVERIFY(!ret.isNumber());
1721         QVERIFY(ret.isObject());
1722         QCOMPARE(ret.toNumber(), qreal(0));
1723     }
1724     {
1725         QJSValue ret = eng.evaluate("new Number(123)");
1726         QVERIFY(!ret.isNumber());
1727         QVERIFY(ret.isObject());
1728         QCOMPARE(ret.toNumber(), qreal(123));
1729     }
1730     {
1731         QJSValue ret = eng.evaluate("new Number('456')");
1732         QVERIFY(!ret.isNumber());
1733         QVERIFY(ret.isObject());
1734         QCOMPARE(ret.toNumber(), qreal(456));
1735     }
1736
1737     QVERIFY(proto.property("toString").isCallable());
1738     {
1739         QJSValue ret = eng.evaluate("new Number(123).toString()");
1740         QVERIFY(ret.isString());
1741         QCOMPARE(ret.toString(), QString::fromLatin1("123"));
1742     }
1743     {
1744         QJSValue ret = eng.evaluate("new Number(123).toString(8)");
1745         QVERIFY(ret.isString());
1746         QCOMPARE(ret.toString(), QString::fromLatin1("173"));
1747     }
1748     {
1749         QJSValue ret = eng.evaluate("new Number(123).toString(16)");
1750         QVERIFY(ret.isString());
1751         QCOMPARE(ret.toString(), QString::fromLatin1("7b"));
1752     }
1753     QVERIFY(proto.property("toLocaleString").isCallable());
1754     {
1755         QJSValue ret = eng.evaluate("new Number(123).toLocaleString()");
1756         QVERIFY(ret.isString());
1757         QCOMPARE(ret.toString(), QString::fromLatin1("123"));
1758     }
1759     QVERIFY(proto.property("valueOf").isCallable());
1760     {
1761         QJSValue ret = eng.evaluate("new Number(123).valueOf()");
1762         QVERIFY(ret.isNumber());
1763         QCOMPARE(ret.toNumber(), qreal(123));
1764     }
1765     QVERIFY(proto.property("toExponential").isCallable());
1766     {
1767         QJSValue ret = eng.evaluate("new Number(123).toExponential()");
1768         QVERIFY(ret.isString());
1769         QCOMPARE(ret.toString(), QString::fromLatin1("1.23e+2"));
1770     }
1771     QVERIFY(proto.property("toFixed").isCallable());
1772     {
1773         QJSValue ret = eng.evaluate("new Number(123).toFixed()");
1774         QVERIFY(ret.isString());
1775         QCOMPARE(ret.toString(), QString::fromLatin1("123"));
1776     }
1777     QVERIFY(proto.property("toPrecision").isCallable());
1778     {
1779         QJSValue ret = eng.evaluate("new Number(123).toPrecision()");
1780         QVERIFY(ret.isString());
1781         QCOMPARE(ret.toString(), QString::fromLatin1("123"));
1782     }
1783 }
1784
1785 void tst_QJSEngine::jsForInStatement_simple()
1786 {
1787     QJSEngine eng;
1788     {
1789         QJSValue ret = eng.evaluate("o = { }; r = []; for (var p in o) r[r.length] = p; r");
1790         QStringList lst = qjsvalue_cast<QStringList>(ret);
1791         QVERIFY(lst.isEmpty());
1792     }
1793     {
1794         QJSValue ret = eng.evaluate("o = { p: 123 }; r = [];"
1795                                         "for (var p in o) r[r.length] = p; r");
1796         QStringList lst = qjsvalue_cast<QStringList>(ret);
1797         QCOMPARE(lst.size(), 1);
1798         QCOMPARE(lst.at(0), QString::fromLatin1("p"));
1799     }
1800     {
1801         QJSValue ret = eng.evaluate("o = { p: 123, q: 456 }; r = [];"
1802                                         "for (var p in o) r[r.length] = p; r");
1803         QStringList lst = qjsvalue_cast<QStringList>(ret);
1804         QCOMPARE(lst.size(), 2);
1805         QCOMPARE(lst.at(0), QString::fromLatin1("p"));
1806         QCOMPARE(lst.at(1), QString::fromLatin1("q"));
1807     }
1808 }
1809
1810 void tst_QJSEngine::jsForInStatement_prototypeProperties()
1811 {
1812     QJSEngine eng;
1813     {
1814         QJSValue ret = eng.evaluate("o = { }; o.__proto__ = { p: 123 }; r = [];"
1815                                         "for (var p in o) r[r.length] = p; r");
1816         QStringList lst = qjsvalue_cast<QStringList>(ret);
1817         QCOMPARE(lst.size(), 1);
1818         QCOMPARE(lst.at(0), QString::fromLatin1("p"));
1819     }
1820     {
1821         QJSValue ret = eng.evaluate("o = { p: 123 }; o.__proto__ = { q: 456 }; r = [];"
1822                                         "for (var p in o) r[r.length] = p; r");
1823         QStringList lst = qjsvalue_cast<QStringList>(ret);
1824         QCOMPARE(lst.size(), 2);
1825         QCOMPARE(lst.at(0), QString::fromLatin1("p"));
1826         QCOMPARE(lst.at(1), QString::fromLatin1("q"));
1827     }
1828     {
1829         // shadowed property
1830         QJSValue ret = eng.evaluate("o = { p: 123 }; o.__proto__ = { p: 456 }; r = [];"
1831                                         "for (var p in o) r[r.length] = p; r");
1832         QStringList lst = qjsvalue_cast<QStringList>(ret);
1833         QCOMPARE(lst.size(), 1);
1834         QCOMPARE(lst.at(0), QString::fromLatin1("p"));
1835     }
1836
1837 }
1838
1839 void tst_QJSEngine::jsForInStatement_mutateWhileIterating()
1840 {
1841     QJSEngine eng;
1842     // deleting property during enumeration
1843     {
1844         QJSValue ret = eng.evaluate("o = { p: 123 }; r = [];"
1845                                         "for (var p in o) { r[r.length] = p; delete r[p]; } r");
1846         QStringList lst = qjsvalue_cast<QStringList>(ret);
1847         QCOMPARE(lst.size(), 1);
1848         QCOMPARE(lst.at(0), QString::fromLatin1("p"));
1849     }
1850     {
1851         QJSValue ret = eng.evaluate("o = { p: 123, q: 456 }; r = [];"
1852                                         "for (var p in o) { r[r.length] = p; delete o.q; } r");
1853         QStringList lst = qjsvalue_cast<QStringList>(ret);
1854         QCOMPARE(lst.size(), 1);
1855         QCOMPARE(lst.at(0), QString::fromLatin1("p"));
1856     }
1857
1858     // adding property during enumeration
1859     {
1860         QJSValue ret = eng.evaluate("o = { p: 123 }; r = [];"
1861                                         "for (var p in o) { r[r.length] = p; o.q = 456; } r");
1862         QStringList lst = qjsvalue_cast<QStringList>(ret);
1863         QCOMPARE(lst.size(), 1);
1864         QCOMPARE(lst.at(0), QString::fromLatin1("p"));
1865     }
1866
1867 }
1868
1869 void tst_QJSEngine::jsForInStatement_arrays()
1870 {
1871     QJSEngine eng;
1872     {
1873         QJSValue ret = eng.evaluate("a = [123, 456]; r = [];"
1874                                         "for (var p in a) r[r.length] = p; r");
1875         QStringList lst = qjsvalue_cast<QStringList>(ret);
1876         QCOMPARE(lst.size(), 2);
1877         QCOMPARE(lst.at(0), QString::fromLatin1("0"));
1878         QCOMPARE(lst.at(1), QString::fromLatin1("1"));
1879     }
1880     {
1881         QJSValue ret = eng.evaluate("a = [123, 456]; a.foo = 'bar'; r = [];"
1882                                         "for (var p in a) r[r.length] = p; r");
1883         QStringList lst = qjsvalue_cast<QStringList>(ret);
1884         QCOMPARE(lst.size(), 3);
1885         QCOMPARE(lst.at(0), QString::fromLatin1("0"));
1886         QCOMPARE(lst.at(1), QString::fromLatin1("1"));
1887         QCOMPARE(lst.at(2), QString::fromLatin1("foo"));
1888     }
1889     {
1890         QJSValue ret = eng.evaluate("a = [123, 456]; a.foo = 'bar';"
1891                                         "b = [111, 222, 333]; b.bar = 'baz';"
1892                                         "a.__proto__ = b; r = [];"
1893                                         "for (var p in a) r[r.length] = p; r");
1894         QStringList lst = qjsvalue_cast<QStringList>(ret);
1895         QCOMPARE(lst.size(), 5);
1896         QCOMPARE(lst.at(0), QString::fromLatin1("0"));
1897         QCOMPARE(lst.at(1), QString::fromLatin1("1"));
1898         QCOMPARE(lst.at(2), QString::fromLatin1("foo"));
1899         QCOMPARE(lst.at(3), QString::fromLatin1("2"));
1900         QCOMPARE(lst.at(4), QString::fromLatin1("bar"));
1901     }
1902 }
1903
1904 void tst_QJSEngine::jsForInStatement_nullAndUndefined()
1905 {
1906     QJSEngine eng;
1907     {
1908         QJSValue ret = eng.evaluate("r = true; for (var p in undefined) r = false; r");
1909         QVERIFY(ret.isBool());
1910         QVERIFY(ret.toBool());
1911     }
1912     {
1913         QJSValue ret = eng.evaluate("r = true; for (var p in null) r = false; r");
1914         QVERIFY(ret.isBool());
1915         QVERIFY(ret.toBool());
1916     }
1917 }
1918
1919 void tst_QJSEngine::jsFunctionDeclarationAsStatement()
1920 {
1921     // ECMA-262 does not allow function declarations to be used as statements,
1922     // but several popular implementations (including JSC) do. See the NOTE
1923     // at the beginning of chapter 12 in ECMA-262 5th edition, where it's
1924     // recommended that implementations either disallow this usage or issue
1925     // a warning.
1926     // Since we had a bug report long ago about QtScript not supporting this
1927     // "feature" (and thus deviating from other implementations), we still
1928     // check this behavior.
1929
1930     QJSEngine eng;
1931     QVERIFY(eng.globalObject().property("bar").isUndefined());
1932     eng.evaluate("function foo(arg) {\n"
1933                  "  if (arg == 'bar')\n"
1934                  "    function bar() { return 'bar'; }\n"
1935                  "  else\n"
1936                  "    function baz() { return 'baz'; }\n"
1937                  "  return (arg == 'bar') ? bar : baz;\n"
1938                  "}");
1939     QVERIFY(eng.globalObject().property("bar").isUndefined());
1940     QVERIFY(eng.globalObject().property("baz").isUndefined());
1941     QVERIFY(eng.evaluate("foo").isCallable());
1942     {
1943         QJSValue ret = eng.evaluate("foo('bar')");
1944         QVERIFY(ret.isCallable());
1945         QJSValue ret2 = ret.call();
1946         QCOMPARE(ret2.toString(), QString::fromLatin1("bar"));
1947         QVERIFY(eng.globalObject().property("bar").isUndefined());
1948         QVERIFY(eng.globalObject().property("baz").isUndefined());
1949     }
1950     {
1951         QJSValue ret = eng.evaluate("foo('baz')");
1952         QVERIFY(ret.isCallable());
1953         QJSValue ret2 = ret.call();
1954         QCOMPARE(ret2.toString(), QString::fromLatin1("baz"));
1955         QVERIFY(eng.globalObject().property("bar").isUndefined());
1956         QVERIFY(eng.globalObject().property("baz").isUndefined());
1957     }
1958 }
1959
1960 void tst_QJSEngine::stringObjects()
1961 {
1962     // See ECMA-262 Section 15.5, "String Objects".
1963
1964     QJSEngine eng;
1965     QString str("ciao");
1966     // in C++
1967     {
1968         QJSValue obj = eng.evaluate(QString::fromLatin1("new String('%0')").arg(str));
1969         QCOMPARE(obj.property("length").toInt(), str.length());
1970         for (int i = 0; i < str.length(); ++i) {
1971             QString pname = QString::number(i);
1972             QVERIFY(obj.property(pname).isString());
1973             QCOMPARE(obj.property(pname).toString(), QString(str.at(i)));
1974             QEXPECT_FAIL("", "FIXME: This is V8 issue 862. ECMA script standard 15.5.5.2 compliance.", Continue);
1975             QVERIFY(!obj.deleteProperty(pname));
1976             obj.setProperty(pname, 123);
1977             QVERIFY(obj.property(pname).isString());
1978             QCOMPARE(obj.property(pname).toString(), QString(str.at(i)));
1979         }
1980         QVERIFY(obj.property("-1").isUndefined());
1981         QVERIFY(obj.property(QString::number(str.length())).isUndefined());
1982
1983         QJSValue val = eng.toScriptValue(123);
1984         obj.setProperty("-1", val);
1985         QVERIFY(obj.property("-1").strictlyEquals(val));
1986         obj.setProperty("100", val);
1987         QVERIFY(obj.property("100").strictlyEquals(val));
1988     }
1989
1990     {
1991         QJSValue ret = eng.evaluate("s = new String('ciao'); r = []; for (var p in s) r.push(p); r");
1992         QVERIFY(ret.isArray());
1993         QStringList lst = qjsvalue_cast<QStringList>(ret);
1994         QCOMPARE(lst.size(), str.length());
1995         for (int i = 0; i < str.length(); ++i)
1996             QCOMPARE(lst.at(i), QString::number(i));
1997
1998         QJSValue ret2 = eng.evaluate("s[0] = 123; s[0]");
1999         QVERIFY(ret2.isString());
2000         QCOMPARE(ret2.toString().length(), 1);
2001         QCOMPARE(ret2.toString().at(0), str.at(0));
2002
2003         QJSValue ret3 = eng.evaluate("s[-1] = 123; s[-1]");
2004         QVERIFY(ret3.isNumber());
2005         QCOMPARE(ret3.toInt(), 123);
2006
2007         QJSValue ret4 = eng.evaluate("s[s.length] = 456; s[s.length]");
2008         QVERIFY(ret4.isNumber());
2009         QCOMPARE(ret4.toInt(), 456);
2010
2011         QJSValue ret5 = eng.evaluate("delete s[0]");
2012         QVERIFY(ret5.isBool());
2013         QEXPECT_FAIL("", "FIXME: This is V8 bug, please report it! ECMA script standard 15.5.5.2", Abort);
2014         QVERIFY(!ret5.toBool());
2015
2016         QJSValue ret6 = eng.evaluate("delete s[-1]");
2017         QVERIFY(ret6.isBool());
2018         QVERIFY(ret6.toBool());
2019
2020         QJSValue ret7 = eng.evaluate("delete s[s.length]");
2021         QVERIFY(ret7.isBool());
2022         QVERIFY(ret7.toBool());
2023     }
2024 }
2025
2026 void tst_QJSEngine::jsStringPrototypeReplaceBugs()
2027 {
2028     QJSEngine eng;
2029     // task 212440
2030     {
2031         QJSValue ret = eng.evaluate("replace_args = []; \"a a a\".replace(/(a)/g, function() { replace_args.push(arguments); }); replace_args");
2032         QVERIFY(ret.isArray());
2033         int len = ret.property("length").toInt();
2034         QCOMPARE(len, 3);
2035         for (int i = 0; i < len; ++i) {
2036             QJSValue args = ret.property(i);
2037             QCOMPARE(args.property("length").toInt(), 4);
2038             QCOMPARE(args.property(0).toString(), QString::fromLatin1("a")); // matched string
2039             QCOMPARE(args.property(1).toString(), QString::fromLatin1("a")); // capture
2040             QVERIFY(args.property(2).isNumber());
2041             QCOMPARE(args.property(2).toInt(), i*2); // index of match
2042             QCOMPARE(args.property(3).toString(), QString::fromLatin1("a a a"));
2043         }
2044     }
2045     // task 212501
2046     {
2047         QJSValue ret = eng.evaluate("\"foo\".replace(/a/g, function() {})");
2048         QVERIFY(ret.isString());
2049         QCOMPARE(ret.toString(), QString::fromLatin1("foo"));
2050     }
2051 }
2052
2053 void tst_QJSEngine::getterSetterThisObject_global()
2054 {
2055     {
2056         QJSEngine eng;
2057         // read
2058         eng.evaluate("__defineGetter__('x', function() { return this; });");
2059         {
2060             QJSValue ret = eng.evaluate("x");
2061             QVERIFY(ret.equals(eng.globalObject()));
2062         }
2063         {
2064             QJSValue ret = eng.evaluate("(function() { return x; })()");
2065             QVERIFY(ret.equals(eng.globalObject()));
2066         }
2067         {
2068             QJSValue ret = eng.evaluate("with (this) x");
2069             QVERIFY(ret.equals(eng.globalObject()));
2070         }
2071         {
2072             QJSValue ret = eng.evaluate("with ({}) x");
2073             QVERIFY(ret.equals(eng.globalObject()));
2074         }
2075         {
2076             QJSValue ret = eng.evaluate("(function() { with ({}) return x; })()");
2077             QVERIFY(ret.equals(eng.globalObject()));
2078         }
2079         // write
2080         eng.evaluate("__defineSetter__('x', function() { return this; });");
2081         {
2082             QJSValue ret = eng.evaluate("x = 'foo'");
2083             // SpiderMonkey says setter return value, JSC says RHS.
2084             QVERIFY(ret.isString());
2085             QCOMPARE(ret.toString(), QString::fromLatin1("foo"));
2086         }
2087         {
2088             QJSValue ret = eng.evaluate("(function() { return x = 'foo'; })()");
2089             // SpiderMonkey says setter return value, JSC says RHS.
2090             QVERIFY(ret.isString());
2091             QCOMPARE(ret.toString(), QString::fromLatin1("foo"));
2092         }
2093         {
2094             QJSValue ret = eng.evaluate("with (this) x = 'foo'");
2095             // SpiderMonkey says setter return value, JSC says RHS.
2096             QVERIFY(ret.isString());
2097             QCOMPARE(ret.toString(), QString::fromLatin1("foo"));
2098         }
2099         {
2100             QJSValue ret = eng.evaluate("with ({}) x = 'foo'");
2101             // SpiderMonkey says setter return value, JSC says RHS.
2102             QVERIFY(ret.isString());
2103             QCOMPARE(ret.toString(), QString::fromLatin1("foo"));
2104         }
2105         {
2106             QJSValue ret = eng.evaluate("(function() { with ({}) return x = 'foo'; })()");
2107             // SpiderMonkey says setter return value, JSC says RHS.
2108             QVERIFY(ret.isString());
2109             QCOMPARE(ret.toString(), QString::fromLatin1("foo"));
2110         }
2111     }
2112 }
2113
2114 void tst_QJSEngine::getterSetterThisObject_plain()
2115 {
2116     {
2117         QJSEngine eng;
2118         eng.evaluate("o = {}");
2119         // read
2120         eng.evaluate("o.__defineGetter__('x', function() { return this; })");
2121         QVERIFY(eng.evaluate("o.x === o").toBool());
2122         QVERIFY(eng.evaluate("with (o) x").equals(eng.evaluate("o")));
2123         QVERIFY(eng.evaluate("(function() { with (o) return x; })() === o").toBool());
2124         eng.evaluate("q = {}; with (o) with (q) x").equals(eng.evaluate("o"));
2125         // write
2126         eng.evaluate("o.__defineSetter__('x', function() { return this; });");
2127         // SpiderMonkey says setter return value, JSC says RHS.
2128         QVERIFY(eng.evaluate("(o.x = 'foo') === 'foo'").toBool());
2129         QVERIFY(eng.evaluate("with (o) x = 'foo'").equals("foo"));
2130         QVERIFY(eng.evaluate("with (o) with (q) x = 'foo'").equals("foo"));
2131     }
2132 }
2133
2134 void tst_QJSEngine::getterSetterThisObject_prototypeChain()
2135 {
2136     {
2137         QJSEngine eng;
2138         eng.evaluate("o = {}; p = {}; o.__proto__ = p");
2139         // read
2140         eng.evaluate("p.__defineGetter__('x', function() { return this; })");
2141         QVERIFY(eng.evaluate("o.x === o").toBool());
2142         QVERIFY(eng.evaluate("with (o) x").equals(eng.evaluate("o")));
2143         QVERIFY(eng.evaluate("(function() { with (o) return x; })() === o").toBool());
2144         eng.evaluate("q = {}; with (o) with (q) x").equals(eng.evaluate("o"));
2145         eng.evaluate("with (q) with (o) x").equals(eng.evaluate("o"));
2146         // write
2147         eng.evaluate("o.__defineSetter__('x', function() { return this; });");
2148         // SpiderMonkey says setter return value, JSC says RHS.
2149         QVERIFY(eng.evaluate("(o.x = 'foo') === 'foo'").toBool());
2150         QVERIFY(eng.evaluate("with (o) x = 'foo'").equals("foo"));
2151         QVERIFY(eng.evaluate("with (o) with (q) x = 'foo'").equals("foo"));
2152     }
2153 }
2154
2155 void tst_QJSEngine::jsContinueInSwitch()
2156 {
2157     // This is testing ECMA-262 compliance, not C++ API.
2158
2159     QJSEngine eng;
2160     // switch - continue
2161     {
2162         QJSValue ret = eng.evaluate("switch (1) { default: continue; }");
2163         QVERIFY(ret.isError());
2164     }
2165     // for - switch - case - continue
2166     {
2167         QJSValue ret = eng.evaluate("j = 0; for (i = 0; i < 100000; ++i) {\n"
2168                                         "  switch (i) {\n"
2169                                         "    case 1: ++j; continue;\n"
2170                                         "    case 100: ++j; continue;\n"
2171                                         "    case 1000: ++j; continue;\n"
2172                                         "  }\n"
2173                                         "}; j");
2174         QVERIFY(ret.isNumber());
2175         QCOMPARE(ret.toInt(), 3);
2176     }
2177     // for - switch - case - default - continue
2178     {
2179         QJSValue ret = eng.evaluate("j = 0; for (i = 0; i < 100000; ++i) {\n"
2180                                         "  switch (i) {\n"
2181                                         "    case 1: ++j; continue;\n"
2182                                         "    case 100: ++j; continue;\n"
2183                                         "    case 1000: ++j; continue;\n"
2184                                         "    default: if (i < 50000) break; else continue;\n"
2185                                         "  }\n"
2186                                         "}; j");
2187         QVERIFY(ret.isNumber());
2188         QCOMPARE(ret.toInt(), 3);
2189     }
2190     // switch - for - continue
2191     {
2192         QJSValue ret = eng.evaluate("j = 123; switch (j) {\n"
2193                                         "  case 123:\n"
2194                                         "  for (i = 0; i < 100000; ++i) {\n"
2195                                         "    continue;\n"
2196                                         "  }\n"
2197                                         "}; i\n");
2198         QVERIFY(ret.isNumber());
2199         QCOMPARE(ret.toInt(), 100000);
2200     }
2201     // switch - switch - continue
2202     {
2203         QJSValue ret = eng.evaluate("i = 1; switch (i) { default: switch (i) { case 1: continue; } }");
2204         QVERIFY(ret.isError());
2205     }
2206     // for - switch - switch - continue
2207     {
2208         QJSValue ret = eng.evaluate("j = 0; for (i = 0; i < 100000; ++i) {\n"
2209                                         "  switch (i) {\n"
2210                                         "    case 1:\n"
2211                                         "    switch (i) {\n"
2212                                         "      case 1: ++j; continue;\n"
2213                                         "    }\n"
2214                                         "  }\n"
2215                                         "}; j");
2216         QVERIFY(ret.isNumber());
2217         QCOMPARE(ret.toInt(), 1);
2218     }
2219     // switch - for - switch - continue
2220     {
2221         QJSValue ret = eng.evaluate("j = 123; switch (j) {\n"
2222                                         "  case 123:\n"
2223                                         "  for (i = 0; i < 100000; ++i) {\n"
2224                                         "    switch (i) {\n"
2225                                         "      case 1:\n"
2226                                         "      ++j; continue;\n"
2227                                         "    }\n"
2228                                         "  }\n"
2229                                         "}; i\n");
2230         QVERIFY(ret.isNumber());
2231         QCOMPARE(ret.toInt(), 100000);
2232     }
2233 }
2234
2235 void tst_QJSEngine::jsShadowReadOnlyPrototypeProperty()
2236 {
2237     // SpiderMonkey has different behavior than JSC and V8; it disallows
2238     // creating a property on the instance if there's a property with the
2239     // same name in the prototype, and that property is read-only. We
2240     // adopted that behavior in the old (4.5) QtScript back-end, but it
2241     // just seems weird -- and non-compliant. Adopt the JSC behavior instead.
2242     QJSEngine eng;
2243     QVERIFY(eng.evaluate("o = {}; o.__proto__ = parseInt; o.length").isNumber());
2244     QCOMPARE(eng.evaluate("o.length = 123; o.length").toInt(), 123);
2245     QVERIFY(eng.evaluate("o.hasOwnProperty('length')").toBool());
2246 }
2247
2248 void tst_QJSEngine::jsReservedWords_data()
2249 {
2250     QTest::addColumn<QString>("word");
2251     QTest::newRow("break") << QString("break");
2252     QTest::newRow("case") << QString("case");
2253     QTest::newRow("catch") << QString("catch");
2254     QTest::newRow("continue") << QString("continue");
2255     QTest::newRow("default") << QString("default");
2256     QTest::newRow("delete") << QString("delete");
2257     QTest::newRow("do") << QString("do");
2258     QTest::newRow("else") << QString("else");
2259     QTest::newRow("false") << QString("false");
2260     QTest::newRow("finally") << QString("finally");
2261     QTest::newRow("for") << QString("for");
2262     QTest::newRow("function") << QString("function");
2263     QTest::newRow("if") << QString("if");
2264     QTest::newRow("in") << QString("in");
2265     QTest::newRow("instanceof") << QString("instanceof");
2266     QTest::newRow("new") << QString("new");
2267     QTest::newRow("null") << QString("null");
2268     QTest::newRow("return") << QString("return");
2269     QTest::newRow("switch") << QString("switch");
2270     QTest::newRow("this") << QString("this");
2271     QTest::newRow("throw") << QString("throw");
2272     QTest::newRow("true") << QString("true");
2273     QTest::newRow("try") << QString("try");
2274     QTest::newRow("typeof") << QString("typeof");
2275     QTest::newRow("var") << QString("var");
2276     QTest::newRow("void") << QString("void");
2277     QTest::newRow("while") << QString("while");
2278     QTest::newRow("with") << QString("with");
2279 }
2280
2281 void tst_QJSEngine::jsReservedWords()
2282 {
2283     // See ECMA-262 Section 7.6.1, "Reserved Words".
2284     // We prefer that the implementation is less strict than the spec; e.g.
2285     // it's good to allow reserved words as identifiers in object literals,
2286     // and when accessing properties using dot notation.
2287
2288     QFETCH(QString, word);
2289     {
2290         QJSEngine eng;
2291         QJSValue ret = eng.evaluate(word + " = 123");
2292         QVERIFY(ret.isError());
2293         QString str = ret.toString();
2294         QVERIFY(str.startsWith("SyntaxError") || str.startsWith("ReferenceError"));
2295     }
2296     {
2297         QJSEngine eng;
2298         QJSValue ret = eng.evaluate("var " + word + " = 123");
2299         QVERIFY(ret.isError());
2300         QVERIFY(ret.toString().startsWith("SyntaxError"));
2301     }
2302     {
2303         QJSEngine eng;
2304         QJSValue ret = eng.evaluate("o = {}; o." + word + " = 123");
2305         // in the old back-end, in SpiderMonkey and in v8, this is allowed, but not in JSC
2306         QVERIFY(!ret.isError());
2307         QVERIFY(ret.strictlyEquals(eng.evaluate("o." + word)));
2308     }
2309     {
2310         QJSEngine eng;
2311         QJSValue ret = eng.evaluate("o = { " + word + ": 123 }");
2312         // in the old back-end, in SpiderMonkey and in v8, this is allowed, but not in JSC
2313         QVERIFY(!ret.isError());
2314         QVERIFY(ret.property(word).isNumber());
2315     }
2316     {
2317         // SpiderMonkey allows this, but we don't
2318         QJSEngine eng;
2319         QJSValue ret = eng.evaluate("function " + word + "() {}");
2320         QVERIFY(ret.isError());
2321         QVERIFY(ret.toString().startsWith("SyntaxError"));
2322     }
2323 }
2324
2325 void tst_QJSEngine::jsFutureReservedWords_data()
2326 {
2327     QTest::addColumn<QString>("word");
2328     QTest::addColumn<bool>("allowed");
2329     QTest::newRow("abstract") << QString("abstract") << true;
2330     QTest::newRow("boolean") << QString("boolean") << true;
2331     QTest::newRow("byte") << QString("byte") << true;
2332     QTest::newRow("char") << QString("char") << true;
2333     QTest::newRow("class") << QString("class") << false;
2334     QTest::newRow("const") << QString("const") << false;
2335     QTest::newRow("debugger") << QString("debugger") << false;
2336     QTest::newRow("double") << QString("double") << true;
2337     QTest::newRow("enum") << QString("enum") << false;
2338     QTest::newRow("export") << QString("export") << false;
2339     QTest::newRow("extends") << QString("extends") << false;
2340     QTest::newRow("final") << QString("final") << true;
2341     QTest::newRow("float") << QString("float") << true;
2342     QTest::newRow("goto") << QString("goto") << true;
2343     QTest::newRow("implements") << QString("implements") << true;
2344     QTest::newRow("import") << QString("import") << false;
2345     QTest::newRow("int") << QString("int") << true;
2346     QTest::newRow("interface") << QString("interface") << true;
2347     QTest::newRow("long") << QString("long") << true;
2348     QTest::newRow("native") << QString("native") << true;
2349     QTest::newRow("package") << QString("package") << true;
2350     QTest::newRow("private") << QString("private") << true;
2351     QTest::newRow("protected") << QString("protected") << true;
2352     QTest::newRow("public") << QString("public") << true;
2353     QTest::newRow("short") << QString("short") << true;
2354     QTest::newRow("static") << QString("static") << true;
2355     QTest::newRow("super") << QString("super") << false;
2356     QTest::newRow("synchronized") << QString("synchronized") << true;
2357     QTest::newRow("throws") << QString("throws") << true;
2358     QTest::newRow("transient") << QString("transient") << true;
2359     QTest::newRow("volatile") << QString("volatile") << true;
2360 }
2361
2362 void tst_QJSEngine::jsFutureReservedWords()
2363 {
2364     QSKIP("Fails");
2365     // See ECMA-262 Section 7.6.1.2, "Future Reserved Words".
2366     // In real-world implementations, most of these words are
2367     // actually allowed as normal identifiers.
2368
2369     QFETCH(QString, word);
2370     QFETCH(bool, allowed);
2371     {
2372         QJSEngine eng;
2373         QJSValue ret = eng.evaluate(word + " = 123");
2374         QCOMPARE(!ret.isError(), allowed);
2375     }
2376     {
2377         QJSEngine eng;
2378         QJSValue ret = eng.evaluate("var " + word + " = 123");
2379         QCOMPARE(!ret.isError(), allowed);
2380     }
2381     {
2382         // this should probably be allowed (see task 162567)
2383         QJSEngine eng;
2384         QJSValue ret = eng.evaluate("o = {}; o." + word + " = 123");
2385         QCOMPARE(ret.isNumber(), allowed);
2386         QCOMPARE(!ret.isError(), allowed);
2387     }
2388     {
2389         // this should probably be allowed (see task 162567)
2390         QJSEngine eng;
2391         QJSValue ret = eng.evaluate("o = { " + word + ": 123 }");
2392         QCOMPARE(!ret.isError(), allowed);
2393     }
2394 }
2395
2396 void tst_QJSEngine::jsThrowInsideWithStatement()
2397 {
2398     // This is testing ECMA-262 compliance, not C++ API.
2399
2400     // task 209988
2401     QJSEngine eng;
2402     {
2403         QJSValue ret = eng.evaluate(
2404             "try {"
2405             "  o = { bad : \"bug\" };"
2406             "  with (o) {"
2407             "    throw 123;"
2408             "  }"
2409             "} catch (e) {"
2410             "  bad;"
2411             "}");
2412         QVERIFY(ret.isError());
2413         QVERIFY(ret.toString().contains(QString::fromLatin1("ReferenceError")));
2414     }
2415     {
2416         QJSValue ret = eng.evaluate(
2417             "try {"
2418             "  o = { bad : \"bug\" };"
2419             "  with (o) {"
2420             "    throw 123;"
2421             "  }"
2422             "} finally {"
2423             "  bad;"
2424             "}");
2425         QVERIFY(ret.isError());
2426         QVERIFY(ret.toString().contains(QString::fromLatin1("ReferenceError")));
2427     }
2428     {
2429         QJSValue ret = eng.evaluate(
2430             "o = { bug : \"no bug\" };"
2431             "with (o) {"
2432             "  try {"
2433             "    throw 123;"
2434             "  } finally {"
2435             "    bug;"
2436             "  }"
2437             "}");
2438         QVERIFY(!ret.isError());
2439         QVERIFY(ret.isNumber());
2440         QCOMPARE(ret.toInt(), 123);
2441     }
2442     {
2443         QJSValue ret = eng.evaluate(
2444             "o = { bug : \"no bug\" };"
2445             "with (o) {"
2446             "    throw 123;"
2447             "}");
2448         QVERIFY(ret.isNumber());
2449         QJSValue ret2 = eng.evaluate("bug");
2450         QVERIFY(ret2.isError());
2451         QVERIFY(ret2.toString().contains(QString::fromLatin1("ReferenceError")));
2452     }
2453 }
2454
2455 void tst_QJSEngine::reentrancy_globalObjectProperties()
2456 {
2457     QJSEngine eng1;
2458     QJSEngine eng2;
2459     QVERIFY(eng2.globalObject().property("a").isUndefined());
2460     eng1.evaluate("a = 10");
2461     QVERIFY(eng1.globalObject().property("a").isNumber());
2462     QVERIFY(eng2.globalObject().property("a").isUndefined());
2463     eng2.evaluate("a = 20");
2464     QVERIFY(eng2.globalObject().property("a").isNumber());
2465     QCOMPARE(eng1.globalObject().property("a").toInt(), 10);
2466 }
2467
2468 void tst_QJSEngine::reentrancy_Array()
2469 {
2470     // weird bug with JSC backend
2471     {
2472         QJSEngine eng;
2473         QCOMPARE(eng.evaluate("Array()").toString(), QString());
2474         eng.evaluate("Array.prototype.toString");
2475         QCOMPARE(eng.evaluate("Array()").toString(), QString());
2476     }
2477     {
2478         QJSEngine eng;
2479         QCOMPARE(eng.evaluate("Array()").toString(), QString());
2480     }
2481 }
2482
2483 void tst_QJSEngine::reentrancy_objectCreation()
2484 {
2485     // Owned by JS engine, as newQObject() sets JS ownership explicitly
2486     QObject *temp = new QObject();
2487
2488     QJSEngine eng1;
2489     QJSEngine eng2;
2490     {
2491         QDateTime dt = QDateTime::currentDateTime();
2492         QJSValue d1 = eng1.toScriptValue(dt);
2493         QJSValue d2 = eng2.toScriptValue(dt);
2494         QCOMPARE(d1.toDateTime(), d2.toDateTime());
2495         QCOMPARE(d2.toDateTime(), d1.toDateTime());
2496     }
2497     {
2498         QJSValue r1 = eng1.evaluate("new RegExp('foo', 'gim')");
2499         QJSValue r2 = eng2.evaluate("new RegExp('foo', 'gim')");
2500         QCOMPARE(qjsvalue_cast<QRegExp>(r1), qjsvalue_cast<QRegExp>(r2));
2501         QCOMPARE(qjsvalue_cast<QRegExp>(r2), qjsvalue_cast<QRegExp>(r1));
2502     }
2503     {
2504         QJSValue o1 = eng1.newQObject(temp);
2505         QJSValue o2 = eng2.newQObject(temp);
2506         QCOMPARE(o1.toQObject(), o2.toQObject());
2507         QCOMPARE(o2.toQObject(), o1.toQObject());
2508     }
2509 }
2510
2511 void tst_QJSEngine::jsIncDecNonObjectProperty()
2512 {
2513     // This is testing ECMA-262 compliance, not C++ API.
2514
2515     QJSEngine eng;
2516     {
2517         QJSValue ret = eng.evaluate("var a; a.n++");
2518         QVERIFY(ret.isError());
2519         QVERIFY(ret.toString().contains(QString::fromLatin1("TypeError")));
2520     }
2521     {
2522         QJSValue ret = eng.evaluate("var a; a.n--");
2523         QVERIFY(ret.isError());
2524         QVERIFY(ret.toString().contains(QString::fromLatin1("TypeError")));
2525     }
2526     {
2527         QJSValue ret = eng.evaluate("var a = null; a.n++");
2528         QVERIFY(ret.isError());
2529         QVERIFY(ret.toString().contains(QString::fromLatin1("TypeError")));
2530     }
2531     {
2532         QJSValue ret = eng.evaluate("var a = null; a.n--");
2533         QVERIFY(ret.isError());
2534         QVERIFY(ret.toString().contains(QString::fromLatin1("TypeError")));
2535     }
2536     {
2537         QJSValue ret = eng.evaluate("var a; ++a.n");
2538         QVERIFY(ret.isError());
2539         QVERIFY(ret.toString().contains(QString::fromLatin1("TypeError")));
2540     }
2541     {
2542         QJSValue ret = eng.evaluate("var a; --a.n");
2543         QVERIFY(ret.isError());
2544         QVERIFY(ret.toString().contains(QString::fromLatin1("TypeError")));
2545     }
2546     {
2547         QJSValue ret = eng.evaluate("var a; a.n += 1");
2548         QVERIFY(ret.isError());
2549         QVERIFY(ret.toString().contains(QString::fromLatin1("TypeError")));
2550     }
2551     {
2552         QJSValue ret = eng.evaluate("var a; a.n -= 1");
2553         QVERIFY(ret.isError());
2554         QVERIFY(ret.toString().contains(QString::fromLatin1("TypeError")));
2555     }
2556     {
2557         QJSValue ret = eng.evaluate("var a = 'ciao'; a.length++");
2558         QVERIFY(ret.isNumber());
2559         QCOMPARE(ret.toInt(), 4);
2560     }
2561     {
2562         QJSValue ret = eng.evaluate("var a = 'ciao'; a.length--");
2563         QVERIFY(ret.isNumber());
2564         QCOMPARE(ret.toInt(), 4);
2565     }
2566     {
2567         QJSValue ret = eng.evaluate("var a = 'ciao'; ++a.length");
2568         QVERIFY(ret.isNumber());
2569         QCOMPARE(ret.toInt(), 5);
2570     }
2571     {
2572         QJSValue ret = eng.evaluate("var a = 'ciao'; --a.length");
2573         QVERIFY(ret.isNumber());
2574         QCOMPARE(ret.toInt(), 3);
2575     }
2576 }
2577
2578 static QRegExp minimal(QRegExp r) { r.setMinimal(true); return r; }
2579
2580 void tst_QJSEngine::qRegExpInport_data()
2581 {
2582     QTest::addColumn<QRegExp>("rx");
2583     QTest::addColumn<QString>("string");
2584     QTest::addColumn<QString>("matched");
2585
2586     QTest::newRow("normal")  << QRegExp("(test|foo)") << "test _ foo _ test _ Foo";
2587     QTest::newRow("normal2")  << QRegExp("(Test|Foo)") << "test _ foo _ test _ Foo";
2588     QTest::newRow("case insensitive)")  << QRegExp("(test|foo)", Qt::CaseInsensitive) << "test _ foo _ test _ Foo";
2589     QTest::newRow("case insensitive2)")  << QRegExp("(Test|Foo)", Qt::CaseInsensitive) << "test _ foo _ test _ Foo";
2590     QTest::newRow("b(a*)(b*)")  << QRegExp("b(a*)(b*)", Qt::CaseInsensitive) << "aaabbBbaAabaAaababaaabbaaab";
2591     QTest::newRow("greedy")  << QRegExp("a*(a*)", Qt::CaseInsensitive, QRegExp::RegExp2) << "aaaabaaba";
2592     QTest::newRow("willcard")  << QRegExp("*.txt", Qt::CaseSensitive, QRegExp::Wildcard) << "file.txt";
2593     QTest::newRow("willcard 2")  << QRegExp("a?b.txt", Qt::CaseSensitive, QRegExp::Wildcard) << "ab.txt abb.rtc acb.txt";
2594     QTest::newRow("slash")  << QRegExp("g/.*/s", Qt::CaseInsensitive, QRegExp::RegExp2) << "string/string/string";
2595     QTest::newRow("slash2")  << QRegExp("g / .* / s", Qt::CaseInsensitive, QRegExp::RegExp2) << "string / string / string";
2596     QTest::newRow("fixed")  << QRegExp("a*aa.a(ba)*a\\ba", Qt::CaseInsensitive, QRegExp::FixedString) << "aa*aa.a(ba)*a\\ba";
2597     QTest::newRow("fixed insensitive")  << QRegExp("A*A", Qt::CaseInsensitive, QRegExp::FixedString) << "a*A A*a A*A a*a";
2598     QTest::newRow("fixed sensitive")  << QRegExp("A*A", Qt::CaseSensitive, QRegExp::FixedString) << "a*A A*a A*A a*a";
2599     QTest::newRow("html")  << QRegExp("<b>(.*)</b>", Qt::CaseSensitive, QRegExp::RegExp2) << "<b>bold</b><i>italic</i><b>bold</b>";
2600     QTest::newRow("html minimal")  << minimal(QRegExp("<b>(.*)</b>", Qt::CaseSensitive, QRegExp::RegExp2)) << "<b>bold</b><i>italic</i><b>bold</b>";
2601     QTest::newRow("aaa")  << QRegExp("a{2,5}") << "aAaAaaaaaAa";
2602     QTest::newRow("aaa minimal")  << minimal(QRegExp("a{2,5}")) << "aAaAaaaaaAa";
2603     QTest::newRow("minimal")  << minimal(QRegExp(".*\\} [*8]")) << "}?} ?} *";
2604     QTest::newRow(".? minimal")  << minimal(QRegExp(".?")) << ".?";
2605     QTest::newRow(".+ minimal")  << minimal(QRegExp(".+")) << ".+";
2606     QTest::newRow("[.?] minimal")  << minimal(QRegExp("[.?]")) << ".?";
2607     QTest::newRow("[.+] minimal")  << minimal(QRegExp("[.+]")) << ".+";
2608 }
2609
2610 void tst_QJSEngine::qRegExpInport()
2611 {
2612     QSKIP("Test failing - QTBUG-22238");
2613     QFETCH(QRegExp, rx);
2614     QFETCH(QString, string);
2615
2616     QJSEngine eng;
2617     QJSValue rexp;
2618     rexp = eng.toScriptValue(rx);
2619
2620     QCOMPARE(rexp.isRegExp(), true);
2621     QVERIFY(rexp.isCallable());
2622
2623     QJSValue func = eng.evaluate("(function(string, regexp) { return string.match(regexp); })");
2624     QJSValue result = func.call(QJSValueList() << string << rexp);
2625
2626     rx.indexIn(string);
2627     for (int i = 0; i <= rx.captureCount(); i++)  {
2628         QCOMPARE(result.property(i).toString(), rx.cap(i));
2629     }
2630 }
2631
2632 // QScriptValue::toDateTime() returns a local time, whereas JS dates
2633 // are always stored as UTC. QtScript must respect the current time
2634 // zone, and correctly adjust for daylight saving time that may be in
2635 // effect at a given date (QTBUG-9770).
2636 void tst_QJSEngine::dateRoundtripJSQtJS()
2637 {
2638     uint secs = QDateTime(QDate(2009, 1, 1)).toUTC().toTime_t();
2639     QJSEngine eng;
2640     for (int i = 0; i < 8000; ++i) {
2641         QJSValue jsDate = eng.evaluate(QString::fromLatin1("new Date(%0)").arg(secs * 1000.0));
2642         QDateTime qtDate = jsDate.toDateTime();
2643         QJSValue jsDate2 = eng.toScriptValue(qtDate);
2644         if (jsDate2.toNumber() != jsDate.toNumber())
2645             QFAIL(qPrintable(jsDate.toString()));
2646         secs += 2*60*60;
2647     }
2648 }
2649
2650 void tst_QJSEngine::dateRoundtripQtJSQt()
2651 {
2652     QDateTime qtDate = QDateTime(QDate(2009, 1, 1));
2653     QJSEngine eng;
2654     for (int i = 0; i < 8000; ++i) {
2655         QJSValue jsDate = eng.toScriptValue(qtDate);
2656         QDateTime qtDate2 = jsDate.toDateTime();
2657         if (qtDate2 != qtDate)
2658             QFAIL(qPrintable(qtDate.toString()));
2659         qtDate = qtDate.addSecs(2*60*60);
2660     }
2661 }
2662
2663 void tst_QJSEngine::dateConversionJSQt()
2664 {
2665     uint secs = QDateTime(QDate(2009, 1, 1)).toUTC().toTime_t();
2666     QJSEngine eng;
2667     for (int i = 0; i < 8000; ++i) {
2668         QJSValue jsDate = eng.evaluate(QString::fromLatin1("new Date(%0)").arg(secs * 1000.0));
2669         QDateTime qtDate = jsDate.toDateTime();
2670         QString qtUTCDateStr = qtDate.toUTC().toString(Qt::ISODate);
2671         QString jsUTCDateStr = jsDate.property("toISOString").callWithInstance(jsDate).toString();
2672         jsUTCDateStr.remove(jsUTCDateStr.length() - 5, 4); // get rid of milliseconds (".000")
2673         if (qtUTCDateStr != jsUTCDateStr)
2674             QFAIL(qPrintable(jsDate.toString()));
2675         secs += 2*60*60;
2676     }
2677 }
2678
2679 void tst_QJSEngine::dateConversionQtJS()
2680 {
2681     QDateTime qtDate = QDateTime(QDate(2009, 1, 1));
2682     QJSEngine eng;
2683     for (int i = 0; i < 8000; ++i) {
2684         QJSValue jsDate = eng.toScriptValue(qtDate);
2685         QString jsUTCDateStr = jsDate.property("toISOString").callWithInstance(jsDate).toString();
2686         jsUTCDateStr.remove(jsUTCDateStr.length() - 5, 4); // get rid of milliseconds (".000")
2687         QString qtUTCDateStr = qtDate.toUTC().toString(Qt::ISODate);
2688         if (jsUTCDateStr != qtUTCDateStr)
2689             QFAIL(qPrintable(qtDate.toString()));
2690         qtDate = qtDate.addSecs(2*60*60);
2691     }
2692 }
2693
2694 void tst_QJSEngine::functionPrototypeExtensions()
2695 {
2696     // QJS adds connect and disconnect properties to Function.prototype.
2697     QJSEngine eng;
2698     QJSValue funProto = eng.globalObject().property("Function").property("prototype");
2699     QVERIFY(funProto.isCallable());
2700     QVERIFY(funProto.property("connect").isCallable());
2701     QVERIFY(funProto.property("disconnect").isCallable());
2702
2703     // No properties should appear in for-in statements.
2704     QJSValue props = eng.evaluate("props = []; for (var p in Function.prototype) props.push(p); props");
2705     QVERIFY(props.isArray());
2706     QCOMPARE(props.property("length").toInt(), 0);
2707 }
2708
2709 class ThreadedTestEngine : public QThread {
2710     Q_OBJECT;
2711
2712 public:
2713     int result;
2714
2715     ThreadedTestEngine()
2716         : result(0) {}
2717
2718     void run() {
2719         QJSEngine firstEngine;
2720         QJSEngine secondEngine;
2721         QJSValue value = firstEngine.evaluate("1");
2722         result = secondEngine.evaluate("1 + " + QString::number(value.toInt())).toInt();
2723     }
2724 };
2725
2726 void tst_QJSEngine::threadedEngine()
2727 {
2728     ThreadedTestEngine thread1;
2729     ThreadedTestEngine thread2;
2730     thread1.start();
2731     thread2.start();
2732     thread1.wait();
2733     thread2.wait();
2734     QCOMPARE(thread1.result, 2);
2735     QCOMPARE(thread2.result, 2);
2736 }
2737
2738 void tst_QJSEngine::v8Context_simple()
2739 {
2740     QJSEngine eng;
2741
2742     v8::HandleScope handleScope;
2743     v8::Local<v8::Context> context = QT_PREPEND_NAMESPACE(qt_QJSEngineV8Context(&eng));
2744     v8::Context::Scope contextScope(context);
2745
2746     v8::Local<v8::Script> script = v8::Script::Compile(
2747                 v8::String::New("({ foo: 123, bar: 'ciao', baz: true })"));
2748
2749     v8::TryCatch tc;
2750     v8::Local<v8::Value> result = script->Run();
2751
2752     QVERIFY(!tc.HasCaught());
2753     QVERIFY(result->IsObject());
2754
2755     v8::Local<v8::Object> object = result.As<v8::Object>();
2756     QVERIFY(object->Get(v8::String::New("foo"))->Equals(v8::Number::New(123)));
2757     QVERIFY(object->Get(v8::String::New("bar"))->Equals(v8::String::New("ciao")));
2758     QVERIFY(object->Get(v8::String::New("baz"))->IsTrue());
2759 }
2760
2761 void tst_QJSEngine::v8Context_exception()
2762 {
2763     QJSEngine eng;
2764
2765     v8::HandleScope handleScope;
2766     v8::Local<v8::Context> context = qt_QJSEngineV8Context(&eng);
2767     v8::Context::Scope contextScope(context);
2768
2769     int startLineNumber = 42;
2770     v8::ScriptOrigin origin(v8::String::New("test.js"), v8::Integer::New(startLineNumber));
2771     v8::Local<v8::Script> script = v8::Script::Compile(
2772                 v8::String::New(
2773                     "function foo(i) {\n"
2774                     "  if (i > 5)\n"
2775                     "    throw Error('Catch me if you can');\n"
2776                     "  foo(i + 1);\n"
2777                     "}\n"
2778                     "foo(0);"),
2779                 &origin);
2780
2781 // QJS does this for us:
2782 //    v8::V8::SetCaptureStackTraceForUncaughtExceptions(true);
2783
2784     v8::TryCatch tc;
2785     v8::Local<v8::Value> result = script->Run();
2786
2787     QVERIFY(tc.HasCaught());
2788     QVERIFY(result.IsEmpty());
2789
2790     v8::Local<v8::Message> message = tc.Message();
2791     QVERIFY(!message.IsEmpty());
2792     QCOMPARE(*v8::String::AsciiValue(message->Get()), "Uncaught Error: Catch me if you can");
2793     QCOMPARE(*v8::String::AsciiValue(message->GetScriptResourceName()), "test.js");
2794     QCOMPARE(message->GetLineNumber(), startLineNumber + 3);
2795 }
2796
2797 void tst_QJSEngine::v8Context_mixAPIs()
2798 {
2799     QJSEngine eng;
2800
2801     v8::HandleScope handleScope;
2802     v8::Local<v8::Context> context = qt_QJSEngineV8Context(&eng);
2803     v8::Context::Scope contextScope(context);
2804
2805     QJSValue globalQJS = eng.globalObject();
2806     v8::Local<v8::Value> globalV8Value = qt_QJSValueV8Value(globalQJS);
2807     QVERIFY(!globalV8Value.IsEmpty());
2808     QVERIFY(globalV8Value->IsObject());
2809     v8::Local<v8::Object> globalV8 = globalV8Value.As<v8::Object>();
2810
2811     QVERIFY(globalQJS.property("foo").isUndefined());
2812     QVERIFY(globalV8->Get(v8::String::New("foo"))->IsUndefined());
2813
2814     globalQJS.setProperty("foo", 123);
2815     QVERIFY(globalV8->Get(v8::String::New("foo"))->Equals(v8::Number::New(123)));
2816
2817     globalV8->Set(v8::String::New("bar"), v8::String::New("ciao"));
2818     QVERIFY(globalQJS.property("bar").equals("ciao"));
2819
2820     QJSValue arrayQJS = eng.newArray(10);
2821     v8::Local<v8::Value> arrayV8Value = qt_QJSValueV8Value(arrayQJS);
2822     QVERIFY(!arrayV8Value.IsEmpty());
2823     QVERIFY(arrayV8Value->IsArray());
2824     v8::Local<v8::Array> arrayV8 = arrayV8Value.As<v8::Array>();
2825
2826     QCOMPARE(int(arrayV8->Length()), 10);
2827     arrayV8->Set(5, v8::Null());
2828     QVERIFY(arrayQJS.property(5).isNull());
2829 }
2830
2831 QTEST_MAIN(tst_QJSEngine)
2832
2833 #include "tst_qjsengine.moc"
2834