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