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 27class tst_QScriptEngine : public QObject { 28 Q_OBJECT 29 30public: 31 tst_QScriptEngine() {} 32 virtual ~tst_QScriptEngine() {} 33 34public slots: 35 void init() {} 36 void cleanup() {} 37 38private 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. */ 58void 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 65static QScriptValue myFunction(QScriptContext*, QScriptEngine* eng) 66{ 67 return eng->nullValue(); 68} 69 70static QScriptValue myFunctionWithArg(QScriptContext*, QScriptEngine* eng, void* arg) 71{ 72 int* result = reinterpret_cast<int*>(arg); 73 return QScriptValue(eng, *result); 74} 75 76static QScriptValue myFunctionThatReturns(QScriptContext*, QScriptEngine* eng) 77{ 78 return QScriptValue(eng, 42); 79} 80 81static QScriptValue myFunctionThatReturnsWithoutEngine(QScriptContext*, QScriptEngine*) 82{ 83 return QScriptValue(1024); 84} 85 86static QScriptValue myFunctionThatReturnsWrongEngine(QScriptContext*, QScriptEngine*, void* arg) 87{ 88 QScriptEngine* wrongEngine = reinterpret_cast<QScriptEngine*>(arg); 89 return QScriptValue(wrongEngine, 42); 90} 91 92void 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 205void 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 217void 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. */ 228void 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 237void 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 257void tst_QScriptEngine::nullValue() 258{ 259 QScriptEngine engine; 260 QScriptValue value = engine.nullValue(); 261 QVERIFY(value.isValid()); 262 QVERIFY(value.isNull()); 263} 264 265void tst_QScriptEngine::undefinedValue() 266{ 267 QScriptEngine engine; 268 QScriptValue value = engine.undefinedValue(); 269 QVERIFY(value.isValid()); 270 QVERIFY(value.isUndefined()); 271} 272 273void 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 373void 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 429void 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 465void 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 516void 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 557void 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 606void 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 686void 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 733QTEST_MAIN(tst_QScriptEngine) 734#include "tst_qscriptengine.moc" 735