initial import
[vuplus_webkit] / Source / JavaScriptCore / qt / tests / qscriptengine / tst_qscriptengine.cpp
1 /*
2     Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies)
3
4     This library is free software; you can redistribute it and/or
5     modify it under the terms of the GNU Library General Public
6     License as published by the Free Software Foundation; either
7     version 2 of the License, or (at your option) any later version.
8
9     This library is distributed in the hope that it will be useful,
10     but WITHOUT ANY WARRANTY; without even the implied warranty of
11     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12     Library General Public License for more details.
13
14     You should have received a copy of the GNU Library General Public License
15     along with this library; see the file COPYING.LIB.  If not, write to
16     the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17     Boston, MA 02110-1301, USA.
18 */
19
20 #include "qscriptengine.h"
21 #include "qscriptprogram.h"
22 #include "qscriptsyntaxcheckresult.h"
23 #include "qscriptvalue.h"
24 #include <QtCore/qnumeric.h>
25 #include <QtTest/qtest.h>
26
27 class tst_QScriptEngine : public QObject {
28     Q_OBJECT
29
30 public:
31     tst_QScriptEngine() {}
32     virtual ~tst_QScriptEngine() {}
33
34 public slots:
35     void init() {}
36     void cleanup() {}
37
38 private slots:
39     void newFunction();
40     void newObject();
41     void globalObject();
42     void evaluate();
43     void collectGarbage();
44     void reportAdditionalMemoryCost();
45     void nullValue();
46     void undefinedValue();
47     void evaluateProgram();
48     void checkSyntax_data();
49     void checkSyntax();
50     void toObject();
51     void toObjectTwoEngines();
52     void newArray();
53     void uncaughtException();
54     void newDate();
55 };
56
57 /* Evaluating a script that throw an unhandled exception should return an invalid value. */
58 void tst_QScriptEngine::evaluate()
59 {
60     QScriptEngine engine;
61     QVERIFY2(engine.evaluate("1+1").isValid(), "the expression should be evaluated and an valid result should be returned");
62     QVERIFY2(engine.evaluate("ping").isValid(), "Script throwing an unhandled exception should return an exception value");
63 }
64
65 static QScriptValue myFunction(QScriptContext*, QScriptEngine* eng)
66 {
67     return eng->nullValue();
68 }
69
70 static QScriptValue myFunctionWithArg(QScriptContext*, QScriptEngine* eng, void* arg)
71 {
72     int* result = reinterpret_cast<int*>(arg);
73     return QScriptValue(eng, *result);
74 }
75
76 static QScriptValue myFunctionThatReturns(QScriptContext*, QScriptEngine* eng)
77 {
78     return QScriptValue(eng, 42);
79 }
80
81 static QScriptValue myFunctionThatReturnsWithoutEngine(QScriptContext*, QScriptEngine*)
82 {
83     return QScriptValue(1024);
84 }
85
86 static QScriptValue myFunctionThatReturnsWrongEngine(QScriptContext*, QScriptEngine*, void* arg)
87 {
88     QScriptEngine* wrongEngine = reinterpret_cast<QScriptEngine*>(arg);
89     return QScriptValue(wrongEngine, 42);
90 }
91
92 void tst_QScriptEngine::newFunction()
93 {
94     QScriptEngine eng;
95     {
96         QScriptValue fun = eng.newFunction(myFunction);
97         QCOMPARE(fun.isValid(), true);
98         QCOMPARE(fun.isFunction(), true);
99         QCOMPARE(fun.isObject(), true);
100         // QCOMPARE(fun.scriptClass(), (QScriptClass*)0);
101         // a prototype property is automatically constructed
102         {
103             QScriptValue prot = fun.property("prototype", QScriptValue::ResolveLocal);
104             QVERIFY(prot.isObject());
105             QVERIFY(prot.property("constructor").strictlyEquals(fun));
106             QEXPECT_FAIL("", "JSCallbackObject::getOwnPropertyDescriptor() doesn't return correct information yet", Continue);
107             QCOMPARE(fun.propertyFlags("prototype"), QScriptValue::Undeletable);
108             QEXPECT_FAIL("", "WebKit bug: 40613 (The JSObjectSetProperty doesn't overwrite property flags)", Continue);
109             QCOMPARE(prot.propertyFlags("constructor"), QScriptValue::PropertyFlags(QScriptValue::Undeletable | QScriptValue::SkipInEnumeration));
110         }
111         // prototype should be Function.prototype
112         QCOMPARE(fun.prototype().isValid(), true);
113         QCOMPARE(fun.prototype().isFunction(), true);
114         QCOMPARE(fun.prototype().strictlyEquals(eng.evaluate("Function.prototype")), true);
115
116         QCOMPARE(fun.call().isNull(), true);
117         // QCOMPARE(fun.construct().isObject(), true);
118     }
119     // the overload that takes an extra argument
120     {
121         int expectedResult = 42;
122         QScriptValue fun = eng.newFunction(myFunctionWithArg, reinterpret_cast<void*>(&expectedResult));
123         QVERIFY(fun.isFunction());
124         // QCOMPARE(fun.scriptClass(), (QScriptClass*)0);
125         // a prototype property is automatically constructed
126         {
127             QScriptValue prot = fun.property("prototype", QScriptValue::ResolveLocal);
128             QVERIFY(prot.isObject());
129             QVERIFY(prot.property("constructor").strictlyEquals(fun));
130             QEXPECT_FAIL("", "JSCallbackObject::getOwnPropertyDescriptor() doesn't return correct information yet", Continue);
131             QCOMPARE(fun.propertyFlags("prototype"), QScriptValue::Undeletable);
132             QEXPECT_FAIL("", "WebKit bug: 40613 (The JSObjectSetProperty doesn't overwrite property flags)", Continue);
133             QCOMPARE(prot.propertyFlags("constructor"), QScriptValue::PropertyFlags(QScriptValue::Undeletable | QScriptValue::SkipInEnumeration));
134         }
135         // prototype should be Function.prototype
136         QCOMPARE(fun.prototype().isValid(), true);
137         QCOMPARE(fun.prototype().isFunction(), true);
138         QCOMPARE(fun.prototype().strictlyEquals(eng.evaluate("Function.prototype")), true);
139
140         QScriptValue result = fun.call();
141         QCOMPARE(result.isNumber(), true);
142         QCOMPARE(result.toInt32(), expectedResult);
143     }
144     // the overload that takes a prototype
145     {
146         QScriptValue proto = eng.newObject();
147         QScriptValue fun = eng.newFunction(myFunction, proto);
148         QCOMPARE(fun.isValid(), true);
149         QCOMPARE(fun.isFunction(), true);
150         QCOMPARE(fun.isObject(), true);
151         // internal prototype should be Function.prototype
152         QCOMPARE(fun.prototype().isValid(), true);
153         QCOMPARE(fun.prototype().isFunction(), true);
154         QCOMPARE(fun.prototype().strictlyEquals(eng.evaluate("Function.prototype")), true);
155         // public prototype should be the one we passed
156         QCOMPARE(fun.property("prototype").strictlyEquals(proto), true);
157         QEXPECT_FAIL("", "JSCallbackObject::getOwnPropertyDescriptor() doesn't return correct information yet", Continue);
158         QCOMPARE(fun.propertyFlags("prototype"), QScriptValue::Undeletable);
159         QCOMPARE(proto.property("constructor").strictlyEquals(fun), true);
160         QEXPECT_FAIL("", "WebKit bug: 40613 (The JSObjectSetProperty doesn't overwrite property flags)", Continue);
161         QCOMPARE(proto.propertyFlags("constructor"), QScriptValue::PropertyFlags(QScriptValue::Undeletable | QScriptValue::SkipInEnumeration));
162
163         QCOMPARE(fun.call().isNull(), true);
164         // QCOMPARE(fun.construct().isObject(), true);
165     }
166     // whether the return value is correct
167     {
168         QScriptValue fun = eng.newFunction(myFunctionThatReturns);
169         QCOMPARE(fun.isValid(), true);
170         QCOMPARE(fun.isFunction(), true);
171         QCOMPARE(fun.isObject(), true);
172
173         QScriptValue result = fun.call();
174         QCOMPARE(result.isNumber(), true);
175         QCOMPARE(result.toInt32(), 42);
176     }
177     // whether the return value is assigned to the correct engine
178     {
179         QScriptValue fun = eng.newFunction(myFunctionThatReturnsWithoutEngine);
180         QCOMPARE(fun.isValid(), true);
181         QCOMPARE(fun.isFunction(), true);
182         QCOMPARE(fun.isObject(), true);
183
184         QScriptValue result = fun.call();
185         QCOMPARE(result.engine(), &eng);
186         QCOMPARE(result.isNumber(), true);
187         QCOMPARE(result.toInt32(), 1024);
188     }
189     // whether the return value is undefined when returning a value with wrong engine
190     {
191         QScriptEngine wrongEngine;
192
193         QScriptValue fun = eng.newFunction(myFunctionThatReturnsWrongEngine, reinterpret_cast<void*>(&wrongEngine));
194         QCOMPARE(fun.isValid(), true);
195         QCOMPARE(fun.isFunction(), true);
196         QCOMPARE(fun.isObject(), true);
197
198         QTest::ignoreMessage(QtWarningMsg, "Value from different engine returned from native function, returning undefined value instead.");
199         QScriptValue result = fun.call();
200         QCOMPARE(result.isValid(), true);
201         QCOMPARE(result.isUndefined(), true);
202     }
203 }
204
205 void tst_QScriptEngine::newObject()
206 {
207     QScriptEngine engine;
208     QScriptValue object = engine.newObject();
209     QVERIFY(object.isObject());
210     QVERIFY(object.engine() == &engine);
211     QVERIFY(!object.isError());
212     QVERIFY(!object.equals(engine.newObject()));
213     QVERIFY(!object.strictlyEquals(engine.newObject()));
214     QCOMPARE(object.toString(), QString::fromAscii("[object Object]"));
215 }
216
217 void tst_QScriptEngine::globalObject()
218 {
219     QScriptEngine engine;
220     QScriptValue global = engine.globalObject();
221     QScriptValue self = engine.evaluate("this");
222     QVERIFY(global.isObject());
223     QVERIFY(engine.globalObject().equals(engine.evaluate("this")));
224     QVERIFY(engine.globalObject().strictlyEquals(self));
225 }
226
227 /* Test garbage collection, at least try to not crash. */
228 void tst_QScriptEngine::collectGarbage()
229 {
230     QScriptEngine engine;
231     QScriptValue foo = engine.evaluate("( function foo() {return 'pong';} )");
232     QVERIFY(foo.isFunction());
233     engine.collectGarbage();
234     QCOMPARE(foo.call().toString(), QString::fromAscii("pong"));
235 }
236
237 void tst_QScriptEngine::reportAdditionalMemoryCost()
238 {
239     // There isn't any easy way to test the responsiveness of the GC;
240     // just try to call the function a few times with various sizes.
241     QScriptEngine eng;
242     for (int i = 0; i < 100; ++i) {
243         eng.reportAdditionalMemoryCost(0);
244         eng.reportAdditionalMemoryCost(10);
245         eng.reportAdditionalMemoryCost(1000);
246         eng.reportAdditionalMemoryCost(10000);
247         eng.reportAdditionalMemoryCost(100000);
248         eng.reportAdditionalMemoryCost(1000000);
249         eng.reportAdditionalMemoryCost(10000000);
250         eng.reportAdditionalMemoryCost(-1);
251         eng.reportAdditionalMemoryCost(-1000);
252         QScriptValue obj = eng.evaluate("new Object");
253         eng.collectGarbage();
254     }
255 }
256
257 void tst_QScriptEngine::nullValue()
258 {
259     QScriptEngine engine;
260     QScriptValue value = engine.nullValue();
261     QVERIFY(value.isValid());
262     QVERIFY(value.isNull());
263 }
264
265 void tst_QScriptEngine::undefinedValue()
266 {
267     QScriptEngine engine;
268     QScriptValue value = engine.undefinedValue();
269     QVERIFY(value.isValid());
270     QVERIFY(value.isUndefined());
271 }
272
273 void tst_QScriptEngine::evaluateProgram()
274 {
275     QScriptEngine eng;
276     {
277         QString code("1 + 2");
278         QString fileName("hello.js");
279         int lineNumber = 123;
280         QScriptProgram program(code, fileName, lineNumber);
281         QVERIFY(!program.isNull());
282         QCOMPARE(program.sourceCode(), code);
283         QCOMPARE(program.fileName(), fileName);
284         QCOMPARE(program.firstLineNumber(), lineNumber);
285
286         QScriptValue expected = eng.evaluate(code);
287         for (int x = 0; x < 10; ++x) {
288             QScriptValue ret = eng.evaluate(program);
289             QVERIFY(ret.equals(expected));
290         }
291
292         // operator=
293         QScriptProgram sameProgram = program;
294         QVERIFY(sameProgram == program);
295         QVERIFY(eng.evaluate(sameProgram).equals(expected));
296
297         // copy constructor
298         QScriptProgram sameProgram2(program);
299         QVERIFY(sameProgram2 == program);
300         QVERIFY(eng.evaluate(sameProgram2).equals(expected));
301
302         QScriptProgram differentProgram("2 + 3");
303         QVERIFY(differentProgram != program);
304         QVERIFY(!eng.evaluate(differentProgram).equals(expected));
305     }
306
307     // Program that accesses variable in the scope
308     {
309         QScriptProgram program("a");
310         QVERIFY(!program.isNull());
311         {
312             QScriptValue ret = eng.evaluate(program);
313             QVERIFY(ret.isError());
314             QCOMPARE(ret.toString(), QString::fromLatin1("ReferenceError: Can't find variable: a"));
315         }
316         {
317             QScriptValue ret = eng.evaluate(program);
318             QVERIFY(ret.isError());
319         }
320         eng.evaluate("a = 456");
321         {
322             QScriptValue ret = eng.evaluate(program);
323             QVERIFY(!ret.isError());
324             QCOMPARE(ret.toNumber(), 456.0);
325         }
326     }
327
328     // Program that creates closure
329     {
330         QScriptProgram program("(function() { var count = 0; return function() { return count++; }; })");
331         QVERIFY(!program.isNull());
332         QScriptValue createCounter = eng.evaluate(program);
333         QVERIFY(createCounter.isFunction());
334         QScriptValue counter = createCounter.call();
335         QVERIFY(counter.isFunction());
336         {
337             QScriptValue ret = counter.call();
338             QVERIFY(ret.isNumber());
339         }
340         QScriptValue counter2 = createCounter.call();
341         QVERIFY(counter2.isFunction());
342         QVERIFY(!counter2.equals(counter));
343         {
344             QScriptValue ret = counter2.call();
345             QVERIFY(ret.isNumber());
346         }
347     }
348
349     // Same program run in different engines
350     {
351         QString code("1 + 2");
352         QScriptProgram program(code);
353         QVERIFY(!program.isNull());
354         double expected = eng.evaluate(program).toNumber();
355         for (int x = 0; x < 2; ++x) {
356             QScriptEngine eng2;
357             for (int y = 0; y < 2; ++y) {
358                 double ret = eng2.evaluate(program).toNumber();
359                 QCOMPARE(ret, expected);
360             }
361         }
362     }
363
364     // No program
365     {
366         QScriptProgram program;
367         QVERIFY(program.isNull());
368         QScriptValue ret = eng.evaluate(program);
369         QVERIFY(!ret.isValid());
370     }
371 }
372
373 void tst_QScriptEngine::checkSyntax_data()
374 {
375     QTest::addColumn<QString>("code");
376     QTest::addColumn<int>("expectedState");
377     QTest::addColumn<int>("errorLineNumber");
378     QTest::addColumn<int>("errorColumnNumber");
379     QTest::addColumn<QString>("errorMessage");
380
381     QTest::newRow("0")
382         << QString("0") << int(QScriptSyntaxCheckResult::Valid)
383         << -1 << -1 << "";
384     QTest::newRow("if (")
385         << QString("if (\n") << int(QScriptSyntaxCheckResult::Intermediate)
386         << 1 << 4 << "";
387     QTest::newRow("if else")
388         << QString("\nif else") << int(QScriptSyntaxCheckResult::Error)
389         << 2 << 4 << "SyntaxError: Parse error";
390     QTest::newRow("{if}")
391             << QString("{\n{\nif\n}\n") << int(QScriptSyntaxCheckResult::Error)
392         << 4 << 1 << "SyntaxError: Parse error";
393     QTest::newRow("foo[")
394         << QString("foo[") << int(QScriptSyntaxCheckResult::Error)
395         << 1 << 4 << "SyntaxError: Parse error";
396     QTest::newRow("foo['bar']")
397         << QString("foo['bar']") << int(QScriptSyntaxCheckResult::Valid)
398         << -1 << -1 << "";
399
400     QTest::newRow("/*")
401         << QString("/*") << int(QScriptSyntaxCheckResult::Intermediate)
402         << 1 << 1 << "Unclosed comment at end of file";
403     QTest::newRow("/*\nMy comment")
404         << QString("/*\nMy comment") << int(QScriptSyntaxCheckResult::Intermediate)
405         << 1 << 1 << "Unclosed comment at end of file";
406     QTest::newRow("/*\nMy comment */\nfoo = 10")
407         << QString("/*\nMy comment */\nfoo = 10") << int(QScriptSyntaxCheckResult::Valid)
408         << -1 << -1 << "";
409     QTest::newRow("foo = 10 /*")
410         << QString("foo = 10 /*") << int(QScriptSyntaxCheckResult::Intermediate)
411         << -1 << -1 << "";
412     QTest::newRow("foo = 10; /*")
413         << QString("foo = 10; /*") << int(QScriptSyntaxCheckResult::Intermediate)
414         << 1 << 11 << "Expected `end of file'";
415     QTest::newRow("foo = 10 /* My comment */")
416         << QString("foo = 10 /* My comment */") << int(QScriptSyntaxCheckResult::Valid)
417         << -1 << -1 << "";
418
419     QTest::newRow("/=/")
420         << QString("/=/") << int(QScriptSyntaxCheckResult::Valid) << -1 << -1 << "";
421     QTest::newRow("/=/g")
422         << QString("/=/g") << int(QScriptSyntaxCheckResult::Valid) << -1 << -1 << "";
423     QTest::newRow("/a/")
424         << QString("/a/") << int(QScriptSyntaxCheckResult::Valid) << -1 << -1 << "";
425     QTest::newRow("/a/g")
426         << QString("/a/g") << int(QScriptSyntaxCheckResult::Valid) << -1 << -1 << "";
427 }
428
429 void tst_QScriptEngine::checkSyntax()
430 {
431     QFETCH(QString, code);
432     QFETCH(int, expectedState);
433     QFETCH(int, errorLineNumber);
434     QFETCH(int, errorColumnNumber);
435     QFETCH(QString, errorMessage);
436
437     QScriptSyntaxCheckResult result = QScriptEngine::checkSyntax(code);
438
439     // assignment
440     {
441         QScriptSyntaxCheckResult copy = result;
442         QCOMPARE(copy.state(), result.state());
443         QCOMPARE(copy.errorLineNumber(), result.errorLineNumber());
444         QCOMPARE(copy.errorColumnNumber(), result.errorColumnNumber());
445         QCOMPARE(copy.errorMessage(), result.errorMessage());
446     }
447     {
448         QScriptSyntaxCheckResult copy(result);
449         QCOMPARE(copy.state(), result.state());
450         QCOMPARE(copy.errorLineNumber(), result.errorLineNumber());
451         QCOMPARE(copy.errorColumnNumber(), result.errorColumnNumber());
452         QCOMPARE(copy.errorMessage(), result.errorMessage());
453     }
454
455     if (expectedState == QScriptSyntaxCheckResult::Intermediate)
456         QEXPECT_FAIL("", "QScriptSyntaxCheckResult::state() doesn't return the Intermediate state", Abort);
457     QCOMPARE(result.state(), QScriptSyntaxCheckResult::State(expectedState));
458     QCOMPARE(result.errorLineNumber(), errorLineNumber);
459     if (expectedState != QScriptSyntaxCheckResult::Valid && errorColumnNumber != 1)
460             QEXPECT_FAIL("", "QScriptSyntaxCheckResult::errorColumnNumber() doesn't return correct value", Continue);
461     QCOMPARE(result.errorColumnNumber(), errorColumnNumber);
462     QCOMPARE(result.errorMessage(), errorMessage);
463 }
464
465 void tst_QScriptEngine::toObject()
466 {
467     QScriptEngine eng;
468     QVERIFY(!eng.toObject(eng.undefinedValue()).isValid());
469     QVERIFY(!eng.toObject(eng.nullValue()).isValid());
470     QVERIFY(!eng.toObject(QScriptValue()).isValid());
471
472     QScriptValue falskt(false);
473     {
474         QScriptValue tmp = eng.toObject(falskt);
475         QVERIFY(tmp.isObject());
476         QVERIFY(!falskt.isObject());
477         QVERIFY(!falskt.engine());
478         QCOMPARE(tmp.toNumber(), falskt.toNumber());
479     }
480
481     QScriptValue sant(true);
482     {
483         QScriptValue tmp = eng.toObject(sant);
484         QVERIFY(tmp.isObject());
485         QVERIFY(!sant.isObject());
486         QVERIFY(!sant.engine());
487         QCOMPARE(tmp.toNumber(), sant.toNumber());
488     }
489
490     QScriptValue number(123.0);
491     {
492         QScriptValue tmp = eng.toObject(number);
493         QVERIFY(tmp.isObject());
494         QVERIFY(!number.isObject());
495         QVERIFY(!number.engine());
496         QCOMPARE(tmp.toNumber(), number.toNumber());
497     }
498
499     QScriptValue str = QScriptValue(&eng, QString("ciao"));
500     {
501         QScriptValue tmp = eng.toObject(str);
502         QVERIFY(tmp.isObject());
503         QVERIFY(!str.isObject());
504         QCOMPARE(tmp.toString(), str.toString());
505     }
506
507     QScriptValue object = eng.evaluate("new Object");
508     {
509         QScriptValue tmp = eng.toObject(object);
510         QVERIFY(tmp.isObject());
511         QVERIFY(object.isObject());
512         QVERIFY(tmp.strictlyEquals(object));
513     }
514 }
515
516 void tst_QScriptEngine::toObjectTwoEngines()
517 {
518     QScriptEngine engine1;
519     QScriptEngine engine2;
520
521     {
522         QScriptValue null = engine1.nullValue();
523         QTest::ignoreMessage(QtWarningMsg, "QScriptEngine::toObject: cannot convert value created in a different engine");
524         QVERIFY(!engine2.toObject(null).isValid());
525         QVERIFY(null.isValid());
526         QTest::ignoreMessage(QtWarningMsg, "QScriptEngine::toObject: cannot convert value created in a different engine");
527         QVERIFY(engine2.toObject(null).engine() != &engine2);
528     }
529     {
530         QScriptValue undefined = engine1.undefinedValue();
531         QTest::ignoreMessage(QtWarningMsg, "QScriptEngine::toObject: cannot convert value created in a different engine");
532         QVERIFY(!engine2.toObject(undefined).isValid());
533         QVERIFY(undefined.isValid());
534         QTest::ignoreMessage(QtWarningMsg, "QScriptEngine::toObject: cannot convert value created in a different engine");
535         QVERIFY(engine2.toObject(undefined).engine() != &engine2);
536     }
537     {
538         QScriptValue value = engine1.evaluate("1");
539         QTest::ignoreMessage(QtWarningMsg, "QScriptEngine::toObject: cannot convert value created in a different engine");
540         QVERIFY(engine2.toObject(value).engine() != &engine2);
541         QVERIFY(!value.isObject());
542     }
543     {
544         QScriptValue string = engine1.evaluate("'Qt'");
545         QTest::ignoreMessage(QtWarningMsg, "QScriptEngine::toObject: cannot convert value created in a different engine");
546         QVERIFY(engine2.toObject(string).engine() != &engine2);
547         QVERIFY(!string.isObject());
548     }
549     {
550         QScriptValue object = engine1.evaluate("new Object");
551         QTest::ignoreMessage(QtWarningMsg, "QScriptEngine::toObject: cannot convert value created in a different engine");
552         QVERIFY(engine2.toObject(object).engine() != &engine2);
553         QVERIFY(object.isObject());
554     }
555 }
556
557 void tst_QScriptEngine::newArray()
558 {
559     QScriptEngine eng;
560     QScriptValue array = eng.newArray();
561     QCOMPARE(array.isValid(), true);
562     QCOMPARE(array.isArray(), true);
563     QCOMPARE(array.isObject(), true);
564     QVERIFY(!array.isFunction());
565     // QCOMPARE(array.scriptClass(), (QScriptClass*)0);
566
567     // Prototype should be Array.prototype.
568     QCOMPARE(array.prototype().isValid(), true);
569     QCOMPARE(array.prototype().isArray(), true);
570     QCOMPARE(array.prototype().strictlyEquals(eng.evaluate("Array.prototype")), true);
571
572     QScriptValue arrayWithSize = eng.newArray(42);
573     QCOMPARE(arrayWithSize.isValid(), true);
574     QCOMPARE(arrayWithSize.isArray(), true);
575     QCOMPARE(arrayWithSize.isObject(), true);
576     QCOMPARE(arrayWithSize.property("length").toInt32(), 42);
577
578     // task 218092
579     {
580         QScriptValue ret = eng.evaluate("[].splice(0, 0, 'a')");
581         QVERIFY(ret.isArray());
582         QCOMPARE(ret.property("length").toInt32(), 0);
583     }
584     {
585         QScriptValue ret = eng.evaluate("['a'].splice(0, 1, 'b')");
586         QVERIFY(ret.isArray());
587         QCOMPARE(ret.property("length").toInt32(), 1);
588     }
589     {
590         QScriptValue ret = eng.evaluate("['a', 'b'].splice(0, 1, 'c')");
591         QVERIFY(ret.isArray());
592         QCOMPARE(ret.property("length").toInt32(), 1);
593     }
594     {
595         QScriptValue ret = eng.evaluate("['a', 'b', 'c'].splice(0, 2, 'd')");
596         QVERIFY(ret.isArray());
597         QCOMPARE(ret.property("length").toInt32(), 2);
598     }
599     {
600         QScriptValue ret = eng.evaluate("['a', 'b', 'c'].splice(1, 2, 'd', 'e', 'f')");
601         QVERIFY(ret.isArray());
602         QCOMPARE(ret.property("length").toInt32(), 2);
603     }
604 }
605
606 void tst_QScriptEngine::uncaughtException()
607 {
608     QScriptEngine eng;
609     QScriptValue fun = eng.evaluate("(function foo () { return null; });");
610     QVERIFY(!eng.uncaughtException().isValid());
611     QVERIFY(fun.isFunction());
612     QScriptValue throwFun = eng.evaluate("( function() { throw new Error('Pong'); });");
613     QVERIFY(throwFun.isFunction());
614     {
615         eng.evaluate("a = 10");
616         QVERIFY(!eng.hasUncaughtException());
617         QVERIFY(!eng.uncaughtException().isValid());
618     }
619     {
620         eng.evaluate("1 = 2");
621         QVERIFY(eng.hasUncaughtException());
622         eng.clearExceptions();
623         QVERIFY(!eng.hasUncaughtException());
624     }
625     {
626         // Check if the call or toString functions can remove the last exception.
627         QVERIFY(throwFun.call().isError());
628         QVERIFY(eng.hasUncaughtException());
629         QScriptValue exception = eng.uncaughtException();
630         fun.call();
631         exception.toString();
632         QVERIFY(eng.hasUncaughtException());
633         QVERIFY(eng.uncaughtException().strictlyEquals(exception));
634     }
635     eng.clearExceptions();
636     {
637         // Check if in the call function a new exception can override an existing one.
638         throwFun.call();
639         QVERIFY(eng.hasUncaughtException());
640         QScriptValue exception = eng.uncaughtException();
641         throwFun.call();
642         QVERIFY(eng.hasUncaughtException());
643         QVERIFY(!exception.strictlyEquals(eng.uncaughtException()));
644     }
645     {
646         eng.evaluate("throwFun = (function foo () { throw new Error('bla') });");
647         eng.evaluate("1;\nthrowFun();");
648         QVERIFY(eng.hasUncaughtException());
649         QCOMPARE(eng.uncaughtExceptionLineNumber(), 1);
650         eng.clearExceptions();
651         QVERIFY(!eng.hasUncaughtException());
652     }
653     for (int x = 1; x < 4; ++x) {
654         QScriptValue ret = eng.evaluate("a = 10;\nb = 20;\n0 = 0;\n",
655                                         QString::fromLatin1("FooScript") + QString::number(x),
656                                         /* lineNumber */ x);
657         QVERIFY(eng.hasUncaughtException());
658         QCOMPARE(eng.uncaughtExceptionLineNumber(), x + 2);
659         QVERIFY(eng.uncaughtException().strictlyEquals(ret));
660         QVERIFY(eng.hasUncaughtException());
661         QVERIFY(eng.uncaughtException().strictlyEquals(ret));
662         QString backtrace = QString::fromLatin1("<anonymous>()@FooScript") + QString::number(x) + ":" + QString::number(x + 2);
663         QCOMPARE(eng.uncaughtExceptionBacktrace().join(""), backtrace);
664         QVERIFY(fun.call().isNull());
665         QVERIFY(eng.hasUncaughtException());
666         QCOMPARE(eng.uncaughtExceptionLineNumber(), x + 2);
667         QVERIFY(eng.uncaughtException().strictlyEquals(ret));
668         eng.clearExceptions();
669         QVERIFY(!eng.hasUncaughtException());
670         QCOMPARE(eng.uncaughtExceptionLineNumber(), -1);
671         QVERIFY(!eng.uncaughtException().isValid());
672         eng.evaluate("2 = 3");
673         QVERIFY(eng.hasUncaughtException());
674         QScriptValue ret2 = throwFun.call();
675         QVERIFY(ret2.isError());
676         QVERIFY(eng.hasUncaughtException());
677         QVERIFY(eng.uncaughtException().strictlyEquals(ret2));
678         QCOMPARE(eng.uncaughtExceptionLineNumber(), 1);
679         eng.clearExceptions();
680         QVERIFY(!eng.hasUncaughtException());
681         eng.evaluate("1 + 2");
682         QVERIFY(!eng.hasUncaughtException());
683     }
684 }
685
686 void tst_QScriptEngine::newDate()
687 {
688     QScriptEngine eng;
689     {
690         QScriptValue date = eng.newDate(0);
691         QCOMPARE(date.isValid(), true);
692         QCOMPARE(date.isDate(), true);
693         QCOMPARE(date.isObject(), true);
694         QVERIFY(!date.isFunction());
695         // prototype should be Date.prototype
696         QCOMPARE(date.prototype().isValid(), true);
697         QCOMPARE(date.prototype().isDate(), true);
698         QCOMPARE(date.prototype().strictlyEquals(eng.evaluate("Date.prototype")), true);
699     }
700     {
701         QDateTime dt = QDateTime(QDate(1, 2, 3), QTime(4, 5, 6, 7), Qt::LocalTime);
702         QScriptValue date = eng.newDate(dt);
703         QCOMPARE(date.isValid(), true);
704         QCOMPARE(date.isDate(), true);
705         QCOMPARE(date.isObject(), true);
706         // prototype should be Date.prototype
707         QCOMPARE(date.prototype().isValid(), true);
708         QCOMPARE(date.prototype().isDate(), true);
709         QCOMPARE(date.prototype().strictlyEquals(eng.evaluate("Date.prototype")), true);
710
711         QCOMPARE(date.toDateTime(), dt);
712     }
713     {
714         QDateTime dt = QDateTime(QDate(1, 2, 3), QTime(4, 5, 6, 7), Qt::UTC);
715         QScriptValue date = eng.newDate(dt);
716         // toDateTime() result should be in local time
717         QCOMPARE(date.toDateTime(), dt.toLocalTime());
718     }
719     // Date.parse() should return NaN when it fails
720     {
721         QScriptValue ret = eng.evaluate("Date.parse()");
722         QVERIFY(ret.isNumber());
723         QVERIFY(qIsNaN(ret.toNumber()));
724     }
725     // Date.parse() should be able to parse the output of Date().toString()
726     {
727         QScriptValue ret = eng.evaluate("var x = new Date(); var s = x.toString(); s == new Date(Date.parse(s)).toString()");
728         QVERIFY(ret.isBoolean());
729         QCOMPARE(ret.toBoolean(), true);
730     }
731 }
732
733 QTEST_MAIN(tst_QScriptEngine)
734 #include "tst_qscriptengine.moc"