Home | History | Annotate | Download | only in qscriptengine
      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"
    735