diff options
Diffstat (limited to 'tests/auto/qscriptengine')
-rw-r--r-- | tests/auto/qscriptengine/tst_qscriptengine.cpp | 791 |
1 files changed, 676 insertions, 115 deletions
diff --git a/tests/auto/qscriptengine/tst_qscriptengine.cpp b/tests/auto/qscriptengine/tst_qscriptengine.cpp index 37be40c..85cee28 100644 --- a/tests/auto/qscriptengine/tst_qscriptengine.cpp +++ b/tests/auto/qscriptengine/tst_qscriptengine.cpp @@ -83,6 +83,10 @@ private slots: void newQMetaObject(); void newActivationObject(); void getSetGlobalObject(); + void globalObjectProperties(); + void globalObjectGetterSetterProperty(); + void builtinFunctionNames_data(); + void builtinFunctionNames(); void checkSyntax_data(); void checkSyntax(); void canEvaluate_data(); @@ -129,6 +133,9 @@ private slots: void installTranslatorFunctions(); void functionScopes(); void nativeFunctionScopes(); + + void qRegExpInport_data(); + void qRegExpInport(); }; tst_QScriptEngine::tst_QScriptEngine() @@ -192,7 +199,9 @@ void tst_QScriptEngine::pushPopContext() eng.popContext(); eng.popContext(); + QTest::ignoreMessage(QtWarningMsg, "QScriptEngine::popContext() doesn't match with pushContext()"); eng.popContext(); // ignored + QTest::ignoreMessage(QtWarningMsg, "QScriptEngine::popContext() doesn't match with pushContext()"); eng.popContext(); // ignored } @@ -457,10 +466,11 @@ void tst_QScriptEngine::newRegExp() QCOMPARE(rexp.isValid(), true); QCOMPARE(rexp.isRegExp(), true); QCOMPARE(rexp.isObject(), true); - QVERIFY(!rexp.isFunction()); + QVERIFY(rexp.isFunction()); // in JSC, RegExp objects are callable // prototype should be RegExp.prototype QCOMPARE(rexp.prototype().isValid(), true); - QCOMPARE(rexp.prototype().isRegExp(), true); + QCOMPARE(rexp.prototype().isObject(), true); + QCOMPARE(rexp.prototype().isRegExp(), false); QCOMPARE(rexp.prototype().strictlyEquals(eng.evaluate("RegExp.prototype")), true); QCOMPARE(rexp.toRegExp().pattern(), QRegExp("foo").pattern()); @@ -477,7 +487,7 @@ void tst_QScriptEngine::newRegExp() QScriptValue r3 = rxCtor.call(QScriptValue(), QScriptValueList() << r << "gim"); QVERIFY(r3.isError()); - QCOMPARE(r3.toString(), QString::fromLatin1("TypeError: cannot specify flags when creating a copy of a RegExp")); + QCOMPARE(r3.toString(), QString::fromLatin1("TypeError: Cannot supply flags when constructing one RegExp from another.")); QScriptValue r4 = rxCtor.call(QScriptValue(), QScriptValueList() << "foo" << "gim"); QVERIFY(r4.isRegExp()); @@ -485,15 +495,17 @@ void tst_QScriptEngine::newRegExp() QScriptValue r5 = rxCtor.construct(QScriptValueList() << r); QVERIFY(r5.isRegExp()); QCOMPARE(r5.toString(), QString::fromLatin1("/foo/gim")); - QVERIFY(!r5.strictlyEquals(r)); + // In JSC, constructing a RegExp from another produces the same identical object. + // This is different from SpiderMonkey and old back-end. + QVERIFY(r5.strictlyEquals(r)); QScriptValue r6 = rxCtor.construct(QScriptValueList() << "foo" << "bar"); QVERIFY(r6.isError()); - QCOMPARE(r6.toString(), QString::fromLatin1("SyntaxError: invalid regular expression flag 'b'")); + QCOMPARE(r6.toString(), QString::fromLatin1("SyntaxError: Invalid regular expression: invalid regular expression flag")); QScriptValue r7 = eng.evaluate("/foo/gimp"); QVERIFY(r7.isError()); - QCOMPARE(r7.toString(), QString::fromLatin1("SyntaxError: Invalid regular expression flag 'p'")); + QCOMPARE(r7.toString(), QString::fromLatin1("SyntaxError: Invalid regular expression: invalid regular expression flag")); QScriptValue r8 = eng.evaluate("/foo/migmigmig"); QVERIFY(r8.isRegExp()); @@ -599,6 +611,8 @@ void tst_QScriptEngine::newQObject() QScriptValue v = eng.newQObject(ptr, QScriptEngine::ScriptOwnership); } eng.evaluate("gc()"); + if (ptr) + QEXPECT_FAIL("", "In the JSC-based back-end, script-owned QObjects are not always deleted immediately during GC", Continue); QVERIFY(ptr == 0); } { @@ -628,6 +642,8 @@ void tst_QScriptEngine::newQObject() } eng.evaluate("gc()"); // no parent, so it should be like ScriptOwnership + if (ptr) + QEXPECT_FAIL("", "In the JSC-based back-end, script-owned QObjects are not always deleted immediately during GC", Continue); QVERIFY(ptr == 0); } { @@ -798,9 +814,22 @@ void tst_QScriptEngine::newQMetaObject() QVERIFY(instance3.instanceOf(qclass)); args.clear(); + QPointer<QObject> qpointer1 = instance.toQObject(); + QPointer<QObject> qpointer2 = instance2.toQObject(); + QPointer<QObject> qpointer3 = instance3.toQObject(); + + QVERIFY(qpointer1); + QVERIFY(qpointer2); + QVERIFY(qpointer3); + // verify that AutoOwnership is in effect instance = QScriptValue(); eng.collectGarbage(); + + QVERIFY(!qpointer1); + QVERIFY(qpointer2); + QVERIFY(!qpointer3); // was child of instance + QVERIFY(instance.toQObject() == 0); QVERIFY(instance3.toQObject() == 0); // was child of instance QVERIFY(instance2.toQObject() != 0); @@ -851,15 +880,20 @@ void tst_QScriptEngine::newQMetaObject() void tst_QScriptEngine::newActivationObject() { + QSKIP("internal function not implemented in JSC-based back-end", SkipAll); QScriptEngine eng; QScriptValue act = eng.newActivationObject(); + QEXPECT_FAIL("", "", Continue); QCOMPARE(act.isValid(), true); + QEXPECT_FAIL("", "", Continue); QCOMPARE(act.isObject(), true); QVERIFY(!act.isFunction()); QScriptValue v(&eng, 123); act.setProperty("prop", v); + QEXPECT_FAIL("", "", Continue); QCOMPARE(act.property("prop").strictlyEquals(v), true); QCOMPARE(act.scope().isValid(), false); + QEXPECT_FAIL("", "", Continue); QVERIFY(act.prototype().isNull()); } @@ -929,6 +963,342 @@ void tst_QScriptEngine::getSetGlobalObject() } } +static QScriptValue getSetFoo(QScriptContext *ctx, QScriptEngine *) +{ + if (ctx->argumentCount() > 0) + ctx->thisObject().setProperty("foo", ctx->argument(0)); + return ctx->thisObject().property("foo"); +} + +void tst_QScriptEngine::globalObjectProperties() +{ + QScriptEngine eng; + QScriptValue global = eng.globalObject(); + + QVERIFY(global.property("NaN").isNumber()); + QVERIFY(qIsNaN(global.property("NaN").toNumber())); + QCOMPARE(global.propertyFlags("NaN"), QScriptValue::SkipInEnumeration | QScriptValue::Undeletable); + + QVERIFY(global.property("Infinity").isNumber()); + QVERIFY(qIsInf(global.property("Infinity").toNumber())); + QCOMPARE(global.propertyFlags("NaN"), QScriptValue::SkipInEnumeration | QScriptValue::Undeletable); + + QVERIFY(global.property("undefined").isUndefined()); + QCOMPARE(global.propertyFlags("undefined"), QScriptValue::SkipInEnumeration | QScriptValue::Undeletable); + + QVERIFY(global.property("eval").isFunction()); + QCOMPARE(global.propertyFlags("eval"), QScriptValue::SkipInEnumeration); + + QVERIFY(global.property("parseInt").isFunction()); + QCOMPARE(global.propertyFlags("parseInt"), QScriptValue::SkipInEnumeration); + + QVERIFY(global.property("parseFloat").isFunction()); + QCOMPARE(global.propertyFlags("parseFloat"), QScriptValue::SkipInEnumeration); + + QVERIFY(global.property("isNaN").isFunction()); + QCOMPARE(global.propertyFlags("isNaN"), QScriptValue::SkipInEnumeration); + + QVERIFY(global.property("isFinite").isFunction()); + QCOMPARE(global.propertyFlags("isFinite"), QScriptValue::SkipInEnumeration); + + QVERIFY(global.property("decodeURI").isFunction()); + QCOMPARE(global.propertyFlags("decodeURI"), QScriptValue::SkipInEnumeration); + + QVERIFY(global.property("decodeURIComponent").isFunction()); + QCOMPARE(global.propertyFlags("decodeURIComponent"), QScriptValue::SkipInEnumeration); + + QVERIFY(global.property("encodeURI").isFunction()); + QCOMPARE(global.propertyFlags("encodeURI"), QScriptValue::SkipInEnumeration); + + QVERIFY(global.property("encodeURIComponent").isFunction()); + QCOMPARE(global.propertyFlags("encodeURIComponent"), QScriptValue::SkipInEnumeration); + + QVERIFY(global.property("Object").isFunction()); + QCOMPARE(global.propertyFlags("Object"), QScriptValue::SkipInEnumeration); + QVERIFY(global.property("Function").isFunction()); + QCOMPARE(global.propertyFlags("Function"), QScriptValue::SkipInEnumeration); + QVERIFY(global.property("Array").isFunction()); + QCOMPARE(global.propertyFlags("Array"), QScriptValue::SkipInEnumeration); + QVERIFY(global.property("String").isFunction()); + QCOMPARE(global.propertyFlags("String"), QScriptValue::SkipInEnumeration); + QVERIFY(global.property("Boolean").isFunction()); + QCOMPARE(global.propertyFlags("Boolean"), QScriptValue::SkipInEnumeration); + QVERIFY(global.property("Number").isFunction()); + QCOMPARE(global.propertyFlags("Number"), QScriptValue::SkipInEnumeration); + QVERIFY(global.property("Date").isFunction()); + QCOMPARE(global.propertyFlags("Date"), QScriptValue::SkipInEnumeration); + QVERIFY(global.property("RegExp").isFunction()); + QCOMPARE(global.propertyFlags("RegExp"), QScriptValue::SkipInEnumeration); + QVERIFY(global.property("Error").isFunction()); + QCOMPARE(global.propertyFlags("Error"), QScriptValue::SkipInEnumeration); + QVERIFY(global.property("EvalError").isFunction()); + QCOMPARE(global.propertyFlags("EvalError"), QScriptValue::SkipInEnumeration); + QVERIFY(global.property("RangeError").isFunction()); + QCOMPARE(global.propertyFlags("RangeError"), QScriptValue::SkipInEnumeration); + QVERIFY(global.property("ReferenceError").isFunction()); + QCOMPARE(global.propertyFlags("ReferenceError"), QScriptValue::SkipInEnumeration); + QVERIFY(global.property("SyntaxError").isFunction()); + QCOMPARE(global.propertyFlags("SyntaxError"), QScriptValue::SkipInEnumeration); + QVERIFY(global.property("TypeError").isFunction()); + QCOMPARE(global.propertyFlags("TypeError"), QScriptValue::SkipInEnumeration); + QVERIFY(global.property("URIError").isFunction()); + QCOMPARE(global.propertyFlags("URIError"), QScriptValue::SkipInEnumeration); + QVERIFY(global.property("Math").isObject()); + QVERIFY(!global.property("Math").isFunction()); + QEXPECT_FAIL("", "[ECMA compliance] JSC sets DontDelete flag for Math object", Continue); + QCOMPARE(global.propertyFlags("Math"), QScriptValue::SkipInEnumeration); + + // enumeration + QSet<QString> expectedNames; + expectedNames + << "isNaN" + << "parseFloat" + << "String" + << "EvalError" + << "URIError" + << "Math" + << "encodeURIComponent" + << "RangeError" + << "eval" + << "isFinite" + << "ReferenceError" + << "Infinity" + << "Function" + << "RegExp" + << "Number" + << "parseInt" + << "Object" + << "decodeURI" + << "TypeError" + << "Boolean" + << "encodeURI" + << "NaN" + << "Error" + << "decodeURIComponent" + << "Date" + << "Array" + << "escape" + << "unescape" + << "SyntaxError" + << "undefined" + // non-standard + << "gc" + << "version" + << "print" + // JavaScriptCore + << "JSON" + ; + QSet<QString> actualNames; + { + QScriptValueIterator it(global); + while (it.hasNext()) { + it.next(); + actualNames.insert(it.name()); + } + } + + QSet<QString> remainingNames = actualNames; + { + QSet<QString>::const_iterator it; + for (it = expectedNames.constBegin(); it != expectedNames.constEnd(); ++it) { + QString name = *it; + QVERIFY(actualNames.contains(name)); + remainingNames.remove(name); + } + } + QVERIFY(remainingNames.isEmpty()); +} + +void tst_QScriptEngine::globalObjectGetterSetterProperty() +{ + QScriptEngine engine; + QScriptValue global = engine.globalObject(); + global.setProperty("bar", engine.newFunction(getSetFoo), + QScriptValue::PropertySetter | QScriptValue::PropertyGetter); + global.setProperty("foo", 123); + QVERIFY(global.property("bar").equals(global.property("foo"))); + QVERIFY(engine.evaluate("bar").equals(global.property("foo"))); + global.setProperty("bar", 456); + QVERIFY(global.property("bar").equals(global.property("foo"))); + + engine.evaluate("__defineGetter__('baz', function() { return 789; })"); + QVERIFY(engine.evaluate("baz").equals(789)); + QVERIFY(global.property("baz").equals(789)); +} + +void tst_QScriptEngine::builtinFunctionNames_data() +{ + QTest::addColumn<QString>("expression"); + QTest::addColumn<QString>("expectedName"); + + QTest::newRow("print") << QString("print") << QString("print"); + QTest::newRow("parseInt") << QString("parseInt") << QString("parseInt"); + QTest::newRow("parseFloat") << QString("parseFloat") << QString("parseFloat"); + QTest::newRow("isNaN") << QString("isNaN") << QString("isNaN"); + QTest::newRow("isFinite") << QString("isFinite") << QString("isFinite"); + QTest::newRow("decodeURI") << QString("decodeURI") << QString("decodeURI"); + QTest::newRow("decodeURIComponent") << QString("decodeURIComponent") << QString("decodeURIComponent"); + QTest::newRow("encodeURI") << QString("encodeURI") << QString("encodeURI"); + QTest::newRow("encodeURIComponent") << QString("encodeURIComponent") << QString("encodeURIComponent"); + QTest::newRow("escape") << QString("escape") << QString("escape"); + QTest::newRow("unescape") << QString("unescape") << QString("unescape"); + QTest::newRow("version") << QString("version") << QString("version"); + QTest::newRow("gc") << QString("gc") << QString("gc"); + + QTest::newRow("Array") << QString("Array") << QString("Array"); + QTest::newRow("Array.prototype.toString") << QString("Array.prototype.toString") << QString("toString"); + QTest::newRow("Array.prototype.toLocaleString") << QString("Array.prototype.toLocaleString") << QString("toLocaleString"); + QTest::newRow("Array.prototype.concat") << QString("Array.prototype.concat") << QString("concat"); + QTest::newRow("Array.prototype.join") << QString("Array.prototype.join") << QString("join"); + QTest::newRow("Array.prototype.pop") << QString("Array.prototype.pop") << QString("pop"); + QTest::newRow("Array.prototype.push") << QString("Array.prototype.push") << QString("push"); + QTest::newRow("Array.prototype.reverse") << QString("Array.prototype.reverse") << QString("reverse"); + QTest::newRow("Array.prototype.shift") << QString("Array.prototype.shift") << QString("shift"); + QTest::newRow("Array.prototype.slice") << QString("Array.prototype.slice") << QString("slice"); + QTest::newRow("Array.prototype.sort") << QString("Array.prototype.sort") << QString("sort"); + QTest::newRow("Array.prototype.splice") << QString("Array.prototype.splice") << QString("splice"); + QTest::newRow("Array.prototype.unshift") << QString("Array.prototype.unshift") << QString("unshift"); + + QTest::newRow("Boolean") << QString("Boolean") << QString("Boolean"); + QTest::newRow("Boolean.prototype.toString") << QString("Boolean.prototype.toString") << QString("toString"); + + QTest::newRow("Date") << QString("Date") << QString("Date"); + QTest::newRow("Date.prototype.toString") << QString("Date.prototype.toString") << QString("toString"); + QTest::newRow("Date.prototype.toDateString") << QString("Date.prototype.toDateString") << QString("toDateString"); + QTest::newRow("Date.prototype.toTimeString") << QString("Date.prototype.toTimeString") << QString("toTimeString"); + QTest::newRow("Date.prototype.toLocaleString") << QString("Date.prototype.toLocaleString") << QString("toLocaleString"); + QTest::newRow("Date.prototype.toLocaleDateString") << QString("Date.prototype.toLocaleDateString") << QString("toLocaleDateString"); + QTest::newRow("Date.prototype.toLocaleTimeString") << QString("Date.prototype.toLocaleTimeString") << QString("toLocaleTimeString"); + QTest::newRow("Date.prototype.valueOf") << QString("Date.prototype.valueOf") << QString("valueOf"); + QTest::newRow("Date.prototype.getTime") << QString("Date.prototype.getTime") << QString("getTime"); + QTest::newRow("Date.prototype.getYear") << QString("Date.prototype.getYear") << QString("getYear"); + QTest::newRow("Date.prototype.getFullYear") << QString("Date.prototype.getFullYear") << QString("getFullYear"); + QTest::newRow("Date.prototype.getUTCFullYear") << QString("Date.prototype.getUTCFullYear") << QString("getUTCFullYear"); + QTest::newRow("Date.prototype.getMonth") << QString("Date.prototype.getMonth") << QString("getMonth"); + QTest::newRow("Date.prototype.getUTCMonth") << QString("Date.prototype.getUTCMonth") << QString("getUTCMonth"); + QTest::newRow("Date.prototype.getDate") << QString("Date.prototype.getDate") << QString("getDate"); + QTest::newRow("Date.prototype.getUTCDate") << QString("Date.prototype.getUTCDate") << QString("getUTCDate"); + QTest::newRow("Date.prototype.getDay") << QString("Date.prototype.getDay") << QString("getDay"); + QTest::newRow("Date.prototype.getUTCDay") << QString("Date.prototype.getUTCDay") << QString("getUTCDay"); + QTest::newRow("Date.prototype.getHours") << QString("Date.prototype.getHours") << QString("getHours"); + QTest::newRow("Date.prototype.getUTCHours") << QString("Date.prototype.getUTCHours") << QString("getUTCHours"); + QTest::newRow("Date.prototype.getMinutes") << QString("Date.prototype.getMinutes") << QString("getMinutes"); + QTest::newRow("Date.prototype.getUTCMinutes") << QString("Date.prototype.getUTCMinutes") << QString("getUTCMinutes"); + QTest::newRow("Date.prototype.getSeconds") << QString("Date.prototype.getSeconds") << QString("getSeconds"); + QTest::newRow("Date.prototype.getUTCSeconds") << QString("Date.prototype.getUTCSeconds") << QString("getUTCSeconds"); + QTest::newRow("Date.prototype.getMilliseconds") << QString("Date.prototype.getMilliseconds") << QString("getMilliseconds"); + QTest::newRow("Date.prototype.getUTCMilliseconds") << QString("Date.prototype.getUTCMilliseconds") << QString("getUTCMilliseconds"); + QTest::newRow("Date.prototype.getTimezoneOffset") << QString("Date.prototype.getTimezoneOffset") << QString("getTimezoneOffset"); + QTest::newRow("Date.prototype.setTime") << QString("Date.prototype.setTime") << QString("setTime"); + QTest::newRow("Date.prototype.setMilliseconds") << QString("Date.prototype.setMilliseconds") << QString("setMilliseconds"); + QTest::newRow("Date.prototype.setUTCMilliseconds") << QString("Date.prototype.setUTCMilliseconds") << QString("setUTCMilliseconds"); + QTest::newRow("Date.prototype.setSeconds") << QString("Date.prototype.setSeconds") << QString("setSeconds"); + QTest::newRow("Date.prototype.setUTCSeconds") << QString("Date.prototype.setUTCSeconds") << QString("setUTCSeconds"); + QTest::newRow("Date.prototype.setMinutes") << QString("Date.prototype.setMinutes") << QString("setMinutes"); + QTest::newRow("Date.prototype.setUTCMinutes") << QString("Date.prototype.setUTCMinutes") << QString("setUTCMinutes"); + QTest::newRow("Date.prototype.setHours") << QString("Date.prototype.setHours") << QString("setHours"); + QTest::newRow("Date.prototype.setUTCHours") << QString("Date.prototype.setUTCHours") << QString("setUTCHours"); + QTest::newRow("Date.prototype.setDate") << QString("Date.prototype.setDate") << QString("setDate"); + QTest::newRow("Date.prototype.setUTCDate") << QString("Date.prototype.setUTCDate") << QString("setUTCDate"); + QTest::newRow("Date.prototype.setMonth") << QString("Date.prototype.setMonth") << QString("setMonth"); + QTest::newRow("Date.prototype.setUTCMonth") << QString("Date.prototype.setUTCMonth") << QString("setUTCMonth"); + QTest::newRow("Date.prototype.setYear") << QString("Date.prototype.setYear") << QString("setYear"); + QTest::newRow("Date.prototype.setFullYear") << QString("Date.prototype.setFullYear") << QString("setFullYear"); + QTest::newRow("Date.prototype.setUTCFullYear") << QString("Date.prototype.setUTCFullYear") << QString("setUTCFullYear"); + QTest::newRow("Date.prototype.toUTCString") << QString("Date.prototype.toUTCString") << QString("toUTCString"); + QTest::newRow("Date.prototype.toGMTString") << QString("Date.prototype.toGMTString") << QString("toGMTString"); + + QTest::newRow("Error") << QString("Error") << QString("Error"); +// QTest::newRow("Error.prototype.backtrace") << QString("Error.prototype.backtrace") << QString("backtrace"); + QTest::newRow("Error.prototype.toString") << QString("Error.prototype.toString") << QString("toString"); + + QTest::newRow("EvalError") << QString("EvalError") << QString("EvalError"); + QTest::newRow("RangeError") << QString("RangeError") << QString("RangeError"); + QTest::newRow("ReferenceError") << QString("ReferenceError") << QString("ReferenceError"); + QTest::newRow("SyntaxError") << QString("SyntaxError") << QString("SyntaxError"); + QTest::newRow("TypeError") << QString("TypeError") << QString("TypeError"); + QTest::newRow("URIError") << QString("URIError") << QString("URIError"); + + QTest::newRow("Function") << QString("Function") << QString("Function"); + QTest::newRow("Function.prototype.toString") << QString("Function.prototype.toString") << QString("toString"); + QTest::newRow("Function.prototype.apply") << QString("Function.prototype.apply") << QString("apply"); + QTest::newRow("Function.prototype.call") << QString("Function.prototype.call") << QString("call"); + QTest::newRow("Function.prototype.connect") << QString("Function.prototype.connect") << QString("connect"); + QTest::newRow("Function.prototype.disconnect") << QString("Function.prototype.disconnect") << QString("disconnect"); + + QTest::newRow("Math.abs") << QString("Math.abs") << QString("abs"); + QTest::newRow("Math.acos") << QString("Math.acos") << QString("acos"); + QTest::newRow("Math.asin") << QString("Math.asin") << QString("asin"); + QTest::newRow("Math.atan") << QString("Math.atan") << QString("atan"); + QTest::newRow("Math.atan2") << QString("Math.atan2") << QString("atan2"); + QTest::newRow("Math.ceil") << QString("Math.ceil") << QString("ceil"); + QTest::newRow("Math.cos") << QString("Math.cos") << QString("cos"); + QTest::newRow("Math.exp") << QString("Math.exp") << QString("exp"); + QTest::newRow("Math.floor") << QString("Math.floor") << QString("floor"); + QTest::newRow("Math.log") << QString("Math.log") << QString("log"); + QTest::newRow("Math.max") << QString("Math.max") << QString("max"); + QTest::newRow("Math.min") << QString("Math.min") << QString("min"); + QTest::newRow("Math.pow") << QString("Math.pow") << QString("pow"); + QTest::newRow("Math.random") << QString("Math.random") << QString("random"); + QTest::newRow("Math.round") << QString("Math.round") << QString("round"); + QTest::newRow("Math.sin") << QString("Math.sin") << QString("sin"); + QTest::newRow("Math.sqrt") << QString("Math.sqrt") << QString("sqrt"); + QTest::newRow("Math.tan") << QString("Math.tan") << QString("tan"); + + QTest::newRow("Number") << QString("Number") << QString("Number"); + QTest::newRow("Number.prototype.toString") << QString("Number.prototype.toString") << QString("toString"); + QTest::newRow("Number.prototype.toLocaleString") << QString("Number.prototype.toLocaleString") << QString("toLocaleString"); + QTest::newRow("Number.prototype.valueOf") << QString("Number.prototype.valueOf") << QString("valueOf"); + QTest::newRow("Number.prototype.toFixed") << QString("Number.prototype.toFixed") << QString("toFixed"); + QTest::newRow("Number.prototype.toExponential") << QString("Number.prototype.toExponential") << QString("toExponential"); + QTest::newRow("Number.prototype.toPrecision") << QString("Number.prototype.toPrecision") << QString("toPrecision"); + + QTest::newRow("Object") << QString("Object") << QString("Object"); + QTest::newRow("Object.prototype.toString") << QString("Object.prototype.toString") << QString("toString"); + QTest::newRow("Object.prototype.toLocaleString") << QString("Object.prototype.toLocaleString") << QString("toLocaleString"); + QTest::newRow("Object.prototype.valueOf") << QString("Object.prototype.valueOf") << QString("valueOf"); + QTest::newRow("Object.prototype.hasOwnProperty") << QString("Object.prototype.hasOwnProperty") << QString("hasOwnProperty"); + QTest::newRow("Object.prototype.isPrototypeOf") << QString("Object.prototype.isPrototypeOf") << QString("isPrototypeOf"); + QTest::newRow("Object.prototype.propertyIsEnumerable") << QString("Object.prototype.propertyIsEnumerable") << QString("propertyIsEnumerable"); + QTest::newRow("Object.prototype.__defineGetter__") << QString("Object.prototype.__defineGetter__") << QString("__defineGetter__"); + QTest::newRow("Object.prototype.__defineSetter__") << QString("Object.prototype.__defineSetter__") << QString("__defineSetter__"); + + QTest::newRow("RegExp") << QString("RegExp") << QString("RegExp"); + QTest::newRow("RegExp.prototype.exec") << QString("RegExp.prototype.exec") << QString("exec"); + QTest::newRow("RegExp.prototype.test") << QString("RegExp.prototype.test") << QString("test"); + QTest::newRow("RegExp.prototype.toString") << QString("RegExp.prototype.toString") << QString("toString"); + + QTest::newRow("String") << QString("String") << QString("String"); + QTest::newRow("String.prototype.toString") << QString("String.prototype.toString") << QString("toString"); + QTest::newRow("String.prototype.valueOf") << QString("String.prototype.valueOf") << QString("valueOf"); + QTest::newRow("String.prototype.charAt") << QString("String.prototype.charAt") << QString("charAt"); + QTest::newRow("String.prototype.charCodeAt") << QString("String.prototype.charCodeAt") << QString("charCodeAt"); + QTest::newRow("String.prototype.concat") << QString("String.prototype.concat") << QString("concat"); + QTest::newRow("String.prototype.indexOf") << QString("String.prototype.indexOf") << QString("indexOf"); + QTest::newRow("String.prototype.lastIndexOf") << QString("String.prototype.lastIndexOf") << QString("lastIndexOf"); + QTest::newRow("String.prototype.localeCompare") << QString("String.prototype.localeCompare") << QString("localeCompare"); + QTest::newRow("String.prototype.match") << QString("String.prototype.match") << QString("match"); + QTest::newRow("String.prototype.replace") << QString("String.prototype.replace") << QString("replace"); + QTest::newRow("String.prototype.search") << QString("String.prototype.search") << QString("search"); + QTest::newRow("String.prototype.slice") << QString("String.prototype.slice") << QString("slice"); + QTest::newRow("String.prototype.split") << QString("String.prototype.split") << QString("split"); + QTest::newRow("String.prototype.substring") << QString("String.prototype.substring") << QString("substring"); + QTest::newRow("String.prototype.toLowerCase") << QString("String.prototype.toLowerCase") << QString("toLowerCase"); + QTest::newRow("String.prototype.toLocaleLowerCase") << QString("String.prototype.toLocaleLowerCase") << QString("toLocaleLowerCase"); + QTest::newRow("String.prototype.toUpperCase") << QString("String.prototype.toUpperCase") << QString("toUpperCase"); + QTest::newRow("String.prototype.toLocaleUpperCase") << QString("String.prototype.toLocaleUpperCase") << QString("toLocaleUpperCase"); +} + +void tst_QScriptEngine::builtinFunctionNames() +{ + QFETCH(QString, expression); + QFETCH(QString, expectedName); + QScriptEngine eng; + QScriptValue ret = eng.evaluate(QString::fromLatin1("%0.name").arg(expression)); + QVERIFY(ret.isString()); + QCOMPARE(ret.toString(), expectedName); +} + void tst_QScriptEngine::checkSyntax_data() { QTest::addColumn<QString>("code"); @@ -1068,7 +1438,7 @@ void tst_QScriptEngine::evaluate_data() QTest::newRow("(spaces)") << QString(" ") << -1 << false << -1; QTest::newRow("(empty)") << QString("") << -1 << false << -1; QTest::newRow("0") << QString("0") << -1 << false << -1; - QTest::newRow("0=1") << QString("\n0=1\n") << -1 << true << 2; + QTest::newRow("0=1") << QString("\n0=1;\n") << -1 << true << 2; QTest::newRow("a=1") << QString("a=1\n") << -1 << false << -1; QTest::newRow("a=1;K") << QString("a=1;\nK") << -1 << true << 2; @@ -1081,7 +1451,7 @@ void tst_QScriptEngine::evaluate_data() << -1 << true << 4; QTest::newRow("0") << QString("0") << 10 << false << -1; - QTest::newRow("0=1") << QString("\n\n0=1\n") << 10 << true << 12; + QTest::newRow("0=1") << QString("\n\n0=1\n") << 10 << true << 13; QTest::newRow("a=1") << QString("a=1\n") << 10 << false << -1; QTest::newRow("a=1;K") << QString("a=1;\n\nK") << 10 << true << 12; @@ -1135,10 +1505,13 @@ void tst_QScriptEngine::evaluate() static QScriptValue eval_nested(QScriptContext *ctx, QScriptEngine *eng) { QScriptValue result = eng->newObject(); + eng->evaluate("var bar = 'local';"); result.setProperty("thisObjectIdBefore", ctx->thisObject().property("id")); QScriptValue evaluatedThisObject = eng->evaluate("this"); result.setProperty("thisObjectIdAfter", ctx->thisObject().property("id")); result.setProperty("evaluatedThisObjectId", evaluatedThisObject.property("id")); + result.setProperty("local_bar", eng->evaluate("bar")); + return result; } @@ -1147,9 +1520,13 @@ void tst_QScriptEngine::nestedEvaluate() QScriptEngine eng; eng.globalObject().setProperty("fun", eng.newFunction(eval_nested)); QScriptValue result = eng.evaluate("o = { id:'foo'}; o.fun = fun; o.fun()"); + QCOMPARE(result.property("local_bar").toString(), QString("local")); QCOMPARE(result.property("thisObjectIdBefore").toString(), QString("foo")); QCOMPARE(result.property("thisObjectIdAfter").toString(), QString("foo")); QCOMPARE(result.property("evaluatedThisObjectId").toString(), QString("foo")); + QScriptValue bar = eng.evaluate("bar"); + QVERIFY(bar.isError()); + QCOMPARE(bar.toString(), QString::fromLatin1("ReferenceError: Can't find variable: bar")); } void tst_QScriptEngine::uncaughtException() @@ -1157,7 +1534,7 @@ void tst_QScriptEngine::uncaughtException() QScriptEngine eng; QScriptValue fun = eng.newFunction(myFunction); QScriptValue throwFun = eng.newFunction(myThrowingFunction); - for (int x = 0; x < 2; ++x) { + for (int x = -1; x < 2; ++x) { { QScriptValue ret = eng.evaluate("a = 10;\nb = 20;\n0 = 0;\n", /*fileName=*/QString(), /*lineNumber=*/x); QVERIFY(eng.hasUncaughtException()); @@ -1172,7 +1549,7 @@ void tst_QScriptEngine::uncaughtException() QVERIFY(eng.uncaughtException().strictlyEquals(ret)); eng.clearExceptions(); QVERIFY(!eng.hasUncaughtException()); - QCOMPARE(eng.uncaughtExceptionLineNumber(), x+2); + QCOMPARE(eng.uncaughtExceptionLineNumber(), -1); QVERIFY(!eng.uncaughtException().isValid()); eng.evaluate("2 = 3"); @@ -1181,7 +1558,7 @@ void tst_QScriptEngine::uncaughtException() QVERIFY(ret2.isError()); QVERIFY(eng.hasUncaughtException()); QVERIFY(eng.uncaughtException().strictlyEquals(ret2)); - QCOMPARE(eng.uncaughtExceptionLineNumber(), -1); + QCOMPARE(eng.uncaughtExceptionLineNumber(), 0); eng.clearExceptions(); QVERIFY(!eng.hasUncaughtException()); eng.evaluate("1 + 2"); @@ -1586,8 +1963,27 @@ void tst_QScriptEngine::valueConversion() QRegExp in = QRegExp("foo"); QScriptValue val = qScriptValueFromValue(&eng, in); QVERIFY(val.isRegExp()); + QRegExp out = val.toRegExp(); + QEXPECT_FAIL("", "JSC-based back-end doesn't preserve QRegExp::patternSyntax (always uses RegExp2)", Continue); + QCOMPARE(out.patternSyntax(), in.patternSyntax()); + QCOMPARE(out.pattern(), in.pattern()); + QCOMPARE(out.caseSensitivity(), in.caseSensitivity()); + QCOMPARE(out.isMinimal(), in.isMinimal()); + } + { + QRegExp in = QRegExp("foo", Qt::CaseSensitive, QRegExp::RegExp2); + QScriptValue val = qScriptValueFromValue(&eng, in); + QVERIFY(val.isRegExp()); QCOMPARE(val.toRegExp(), in); } + { + QRegExp in = QRegExp("foo"); + in.setMinimal(true); + QScriptValue val = qScriptValueFromValue(&eng, in); + QVERIFY(val.isRegExp()); + QEXPECT_FAIL("", "JSC-based back-end doesn't preserve QRegExp::minimal (always false)", Continue); + QCOMPARE(val.toRegExp().isMinimal(), in.isMinimal()); + } } static QScriptValue __import__(QScriptContext *ctx, QScriptEngine *eng) @@ -1683,9 +2079,10 @@ void tst_QScriptEngine::importExtension() QVERIFY(eng.importedExtensions().isEmpty()); QScriptValue ret = eng.importExtension("com.trolltech.syntaxerror"); QVERIFY(eng.hasUncaughtException()); + QEXPECT_FAIL("", "JSC throws syntax error eagerly", Continue); QCOMPARE(eng.uncaughtExceptionLineNumber(), 4); QVERIFY(ret.isError()); - QCOMPARE(ret.property("message").toString(), QLatin1String("invalid assignment lvalue")); + QCOMPARE(ret.property("message").toString(), QLatin1String("Parse error")); } QStringList imp = eng.importedExtensions(); QCOMPARE(imp.size(), 2); @@ -1711,26 +2108,27 @@ static QScriptValue recurse2(QScriptContext *ctx, QScriptEngine *eng) void tst_QScriptEngine::infiniteRecursion() { - QSKIP("Can cause C stack overflow (task 241294)", SkipAll); - + const QString stackOverflowError = QString::fromLatin1("RangeError: Maximum call stack size exceeded."); QScriptEngine eng; { QScriptValue ret = eng.evaluate("function foo() { foo(); }; foo();"); QCOMPARE(ret.isError(), true); - QCOMPARE(ret.toString(), QLatin1String("Error: call stack overflow")); + QCOMPARE(ret.toString(), stackOverflowError); } +#if 0 //The native C++ stack overflow before the JS stack { QScriptValue fun = eng.newFunction(recurse); QScriptValue ret = fun.call(); QCOMPARE(ret.isError(), true); - QCOMPARE(ret.toString(), QLatin1String("Error: call stack overflow")); + QCOMPARE(ret.toString(), stackOverflowError); } { QScriptValue fun = eng.newFunction(recurse2); QScriptValue ret = fun.construct(); QCOMPARE(ret.isError(), true); - QCOMPARE(ret.toString(), QLatin1String("Error: call stack overflow")); + QCOMPARE(ret.toString(), stackOverflowError); } +#endif } struct Bar { @@ -1898,6 +2296,7 @@ void tst_QScriptEngine::collectGarbage() QScriptValue v = eng.newQObject(ptr, QScriptEngine::ScriptOwnership); } eng.collectGarbage(); + QEXPECT_FAIL("","collectGarbage not working", Continue); QVERIFY(ptr == 0); } @@ -1954,7 +2353,7 @@ void tst_QScriptEngine::processEventsWhileRunning() eng.pushContext(); QString script = QString::fromLatin1( - "var end = Number(new Date()) + 1000;" + "var end = Number(new Date()) + 2000;" "var x = 0;" "while (Number(new Date()) < end) {" " ++x;" @@ -1997,6 +2396,7 @@ public: void tst_QScriptEngine::throwErrorFromProcessEvents() { + QSKIP("Not implemented", SkipAll); QScriptEngine eng; EventReceiver2 receiver(&eng); @@ -2040,6 +2440,7 @@ void tst_QScriptEngine::stacktrace() QVERIFY(eng.hasUncaughtException()); QVERIFY(result.isError()); + QEXPECT_FAIL("", "", Abort); QCOMPARE(eng.uncaughtExceptionBacktrace(), backtrace); QVERIFY(eng.hasUncaughtException()); QVERIFY(result.strictlyEquals(eng.uncaughtException())); @@ -2150,7 +2551,7 @@ void tst_QScriptEngine::automaticSemicolonInsertion() { QScriptValue ret = eng.evaluate("{ 1 2 } 3"); QVERIFY(ret.isError()); - QCOMPARE(ret.toString(), QString::fromLatin1("SyntaxError: Expected `;', `;'")); + QCOMPARE(ret.toString(), QString::fromLatin1("SyntaxError: Parse error")); } { QScriptValue ret = eng.evaluate("{ 1\n2 } 3"); @@ -2160,7 +2561,7 @@ void tst_QScriptEngine::automaticSemicolonInsertion() { QScriptValue ret = eng.evaluate("for (a; b\n)"); QVERIFY(ret.isError()); - QCOMPARE(ret.toString(), QString::fromLatin1("SyntaxError: Expected `;'")); + QCOMPARE(ret.toString(), QString::fromLatin1("SyntaxError: Parse error")); } { QScriptValue ret = eng.evaluate("(function() { return\n1 + 2 })()"); @@ -2175,7 +2576,7 @@ void tst_QScriptEngine::automaticSemicolonInsertion() { QScriptValue ret = eng.evaluate("if (a > b)\nelse c = d"); QVERIFY(ret.isError()); - QCOMPARE(ret.toString(), QString::fromLatin1("SyntaxError")); + QCOMPARE(ret.toString(), QString::fromLatin1("SyntaxError: Parse error")); } { eng.evaluate("function c() { return { foo: function() { return 5; } } }"); @@ -2187,7 +2588,7 @@ void tst_QScriptEngine::automaticSemicolonInsertion() { QScriptValue ret = eng.evaluate("throw\n1"); QVERIFY(ret.isError()); - QCOMPARE(ret.toString(), QString::fromLatin1("SyntaxError")); + QCOMPARE(ret.toString(), QString::fromLatin1("SyntaxError: Parse error")); } { QScriptValue ret = eng.evaluate("a = Number(1)\n++a"); @@ -2393,6 +2794,13 @@ void tst_QScriptEngine::abortEvaluation() eng.abortEvaluation(); QVERIFY(!eng.hasUncaughtException()); + eng.abortEvaluation(123); + { + QScriptValue ret = eng.evaluate("'ciao'"); + QVERIFY(ret.isString()); + QCOMPARE(ret.toString(), QString::fromLatin1("ciao")); + } + EventReceiver3 receiver(&eng); eng.setProcessEventsInterval(100); @@ -2557,31 +2965,46 @@ void tst_QScriptEngine::errorConstructors() QScriptEngine eng; QStringList prefixes; prefixes << "" << "Eval" << "Range" << "Reference" << "Syntax" << "Type" << "URI"; - for (int x = 0; x < 2; ++x) { + for (int x = 0; x < 3; ++x) { for (int i = 0; i < prefixes.size(); ++i) { QString name = prefixes.at(i) + QLatin1String("Error"); QString code = QString(i+1, QLatin1Char('\n')); if (x == 0) + code += QLatin1String("throw "); + else if (x == 1) code += QLatin1String("new "); code += name + QLatin1String("()"); QScriptValue ret = eng.evaluate(code); QVERIFY(ret.isError()); - QVERIFY(!eng.hasUncaughtException()); - QCOMPARE(ret.toString(), name); + QCOMPARE(eng.hasUncaughtException(), x == 0); + eng.clearExceptions(); + QVERIFY(ret.toString().startsWith(name)); + if (x != 0) + QEXPECT_FAIL("", "JSC doesn't assign lineNumber when errors are not thrown", Continue); QCOMPARE(ret.property("lineNumber").toInt32(), i+2); } } } +static QScriptValue argumentsProperty_fun(QScriptContext *, QScriptEngine *eng) +{ + eng->evaluate("var a = arguments[0];"); + eng->evaluate("arguments[0] = 200;"); + return eng->evaluate("a + arguments[0]"); +} + + void tst_QScriptEngine::argumentsProperty() { { QScriptEngine eng; + QEXPECT_FAIL("", "", Continue); QVERIFY(eng.evaluate("arguments").isUndefined()); eng.evaluate("arguments = 10"); QScriptValue ret = eng.evaluate("arguments"); QVERIFY(ret.isNumber()); QCOMPARE(ret.toInt32(), 10); + QEXPECT_FAIL("", "", Continue); QVERIFY(!eng.evaluate("delete arguments").toBoolean()); } { @@ -2596,8 +3019,18 @@ void tst_QScriptEngine::argumentsProperty() QScriptValue ret = eng.evaluate("(function() { arguments = 456; return arguments; })()"); QVERIFY(ret.isNumber()); QCOMPARE(ret.toInt32(), 456); + QEXPECT_FAIL("", "", Continue); QVERIFY(eng.evaluate("arguments").isUndefined()); } + + { + QScriptEngine eng; + QScriptValue fun = eng.newFunction(argumentsProperty_fun); + eng.globalObject().setProperty("fun", eng.newFunction(argumentsProperty_fun)); + QScriptValue result = eng.evaluate("fun(18)"); + QVERIFY(result.isNumber()); + QCOMPARE(result.toInt32(), 218); + } } void tst_QScriptEngine::numberClass() @@ -2695,7 +3128,7 @@ void tst_QScriptEngine::numberClass() { QScriptValue ret = eng.evaluate("new Number(123).toExponential()"); QVERIFY(ret.isString()); - QCOMPARE(ret.toString(), QString::fromLatin1("1e+02")); + QCOMPARE(ret.toString(), QString::fromLatin1("1.23e+2")); } QVERIFY(proto.property("toFixed").isFunction()); { @@ -2707,7 +3140,7 @@ void tst_QScriptEngine::numberClass() { QScriptValue ret = eng.evaluate("new Number(123).toPrecision()"); QVERIFY(ret.isString()); - QCOMPARE(ret.toString(), QString::fromLatin1("1e+02")); + QCOMPARE(ret.toString(), QString::fromLatin1("123")); } } @@ -2781,9 +3214,8 @@ void tst_QScriptEngine::forInStatement() QScriptValue ret = eng.evaluate("o = { p: 123 }; r = [];" "for (var p in o) { r[r.length] = p; o.q = 456; } r"); QStringList lst = qscriptvalue_cast<QStringList>(ret); - QCOMPARE(lst.size(), 2); + QCOMPARE(lst.size(), 1); QCOMPARE(lst.at(0), QString::fromLatin1("p")); - QCOMPARE(lst.at(1), QString::fromLatin1("q")); } // arrays @@ -2800,9 +3232,9 @@ void tst_QScriptEngine::forInStatement() "for (var p in a) r[r.length] = p; r"); QStringList lst = qscriptvalue_cast<QStringList>(ret); QCOMPARE(lst.size(), 3); - QCOMPARE(lst.at(0), QString::fromLatin1("foo")); - QCOMPARE(lst.at(1), QString::fromLatin1("0")); - QCOMPARE(lst.at(2), QString::fromLatin1("1")); + QCOMPARE(lst.at(0), QString::fromLatin1("0")); + QCOMPARE(lst.at(1), QString::fromLatin1("1")); + QCOMPARE(lst.at(2), QString::fromLatin1("foo")); } { QScriptValue ret = eng.evaluate("a = [123, 456]; a.foo = 'bar';" @@ -2811,10 +3243,11 @@ void tst_QScriptEngine::forInStatement() "for (var p in a) r[r.length] = p; r"); QStringList lst = qscriptvalue_cast<QStringList>(ret); QCOMPARE(lst.size(), 5); - QCOMPARE(lst.at(0), QString::fromLatin1("foo")); - QCOMPARE(lst.at(1), QString::fromLatin1("0")); - QCOMPARE(lst.at(2), QString::fromLatin1("1")); - QCOMPARE(lst.at(3), QString::fromLatin1("bar")); + QCOMPARE(lst.at(0), QString::fromLatin1("0")); + QCOMPARE(lst.at(1), QString::fromLatin1("1")); + QCOMPARE(lst.at(2), QString::fromLatin1("foo")); + QCOMPARE(lst.at(3), QString::fromLatin1("2")); + QCOMPARE(lst.at(4), QString::fromLatin1("bar")); } // null and undefined @@ -2984,23 +3417,33 @@ void tst_QScriptEngine::getterSetterThisObject() eng.evaluate("__defineSetter__('x', function() { return this; });"); { QScriptValue ret = eng.evaluate("x = 'foo'"); - QVERIFY(ret.equals(eng.globalObject())); + // SpiderMonkey says setter return value, JSC says RHS. + QVERIFY(ret.isString()); + QCOMPARE(ret.toString(), QString::fromLatin1("foo")); } { QScriptValue ret = eng.evaluate("(function() { return x = 'foo'; })()"); - QVERIFY(ret.equals(eng.globalObject())); + // SpiderMonkey says setter return value, JSC says RHS. + QVERIFY(ret.isString()); + QCOMPARE(ret.toString(), QString::fromLatin1("foo")); } { QScriptValue ret = eng.evaluate("with (this) x = 'foo'"); - QVERIFY(ret.equals(eng.globalObject())); + // SpiderMonkey says setter return value, JSC says RHS. + QVERIFY(ret.isString()); + QCOMPARE(ret.toString(), QString::fromLatin1("foo")); } { QScriptValue ret = eng.evaluate("with ({}) x = 'foo'"); - QVERIFY(ret.equals(eng.globalObject())); + // SpiderMonkey says setter return value, JSC says RHS. + QVERIFY(ret.isString()); + QCOMPARE(ret.toString(), QString::fromLatin1("foo")); } { QScriptValue ret = eng.evaluate("(function() { with ({}) return x = 'foo'; })()"); - QVERIFY(ret.equals(eng.globalObject())); + // SpiderMonkey says setter return value, JSC says RHS. + QVERIFY(ret.isString()); + QCOMPARE(ret.toString(), QString::fromLatin1("foo")); } } @@ -3016,9 +3459,10 @@ void tst_QScriptEngine::getterSetterThisObject() eng.evaluate("q = {}; with (o) with (q) x").equals(eng.evaluate("o")); // write eng.evaluate("o.__defineSetter__('x', function() { return this; });"); - QVERIFY(eng.evaluate("(o.x = 'foo') === o").toBoolean()); - QVERIFY(eng.evaluate("with (o) x = 'foo'").equals(eng.evaluate("o"))); - QVERIFY(eng.evaluate("with (o) with (q) x = 'foo'").equals(eng.evaluate("o"))); + // SpiderMonkey says setter return value, JSC says RHS. + QVERIFY(eng.evaluate("(o.x = 'foo') === 'foo'").toBoolean()); + QVERIFY(eng.evaluate("with (o) x = 'foo'").equals("foo")); + QVERIFY(eng.evaluate("with (o) with (q) x = 'foo'").equals("foo")); } // getter+setter in prototype chain @@ -3034,29 +3478,32 @@ void tst_QScriptEngine::getterSetterThisObject() eng.evaluate("with (q) with (o) x").equals(eng.evaluate("o")); // write eng.evaluate("o.__defineSetter__('x', function() { return this; });"); - QVERIFY(eng.evaluate("(o.x = 'foo') === o").toBoolean()); - QVERIFY(eng.evaluate("with (o) x = 'foo'").equals(eng.evaluate("o"))); - QVERIFY(eng.evaluate("with (o) with (q) x = 'foo'").equals(eng.evaluate("o"))); + // SpiderMonkey says setter return value, JSC says RHS. + QVERIFY(eng.evaluate("(o.x = 'foo') === 'foo'").toBoolean()); + QVERIFY(eng.evaluate("with (o) x = 'foo'").equals("foo")); + QVERIFY(eng.evaluate("with (o) with (q) x = 'foo'").equals("foo")); } // getter+setter in activation { QScriptEngine eng; QScriptContext *ctx = eng.pushContext(); + QVERIFY(ctx != 0); QScriptValue act = ctx->activationObject(); act.setProperty("act", act); // read eng.evaluate("act.__defineGetter__('x', function() { return this; })"); QVERIFY(eng.evaluate("x === act").toBoolean()); - QVERIFY(eng.evaluate("with (act) x").equals(eng.evaluate("act"))); + QEXPECT_FAIL("", "Exotic overload (don't care for now)", Continue); + QVERIFY(eng.evaluate("with (act) x").equals("foo")); QVERIFY(eng.evaluate("(function() { with (act) return x; })() === act").toBoolean()); eng.evaluate("q = {}; with (act) with (q) x").equals(eng.evaluate("act")); eng.evaluate("with (q) with (act) x").equals(eng.evaluate("act")); // write eng.evaluate("act.__defineSetter__('x', function() { return this; });"); - QVERIFY(eng.evaluate("(x = 'foo') === act").toBoolean()); - QVERIFY(eng.evaluate("with (act) x = 'foo'").equals(eng.evaluate("act"))); - QVERIFY(eng.evaluate("with (act) with (q) x = 'foo'").equals(eng.evaluate("act"))); + QVERIFY(eng.evaluate("(x = 'foo') === 'foo'").toBoolean()); + QVERIFY(eng.evaluate("with (act) x = 'foo'").equals("foo")); + QVERIFY(eng.evaluate("with (act) with (q) x = 'foo'").equals("foo")); eng.popContext(); } } @@ -3141,6 +3588,7 @@ void tst_QScriptEngine::continueInSwitch() void tst_QScriptEngine::readOnlyPrototypeProperty() { + QSKIP("JSC semantics differ from old back-end and SpiderMonkey", SkipAll); QScriptEngine eng; QCOMPARE(eng.evaluate("o = {}; o.__proto__ = parseInt; o.length").toInt32(), 2); QCOMPARE(eng.evaluate("o.length = 4; o.length").toInt32(), 2); @@ -3258,7 +3706,8 @@ void tst_QScriptEngine::reservedWords() QScriptEngine eng; QScriptValue ret = eng.evaluate(word + " = 123"); QVERIFY(ret.isError()); - QVERIFY(ret.toString().startsWith("SyntaxError")); + QString str = ret.toString(); + QVERIFY(str.startsWith("SyntaxError") || str.startsWith("ReferenceError")); } { QScriptEngine eng; @@ -3269,14 +3718,16 @@ void tst_QScriptEngine::reservedWords() { QScriptEngine eng; QScriptValue ret = eng.evaluate("o = {}; o." + word + " = 123"); - QVERIFY(!ret.isError()); - QVERIFY(ret.strictlyEquals(eng.evaluate("o." + word))); + // in the old back-end and in SpiderMonkey this is allowed, but not in JSC + QVERIFY(ret.isError()); + QVERIFY(ret.toString().startsWith("SyntaxError")); } { QScriptEngine eng; QScriptValue ret = eng.evaluate("o = { " + word + ": 123 }"); - QVERIFY(!ret.isError()); - QVERIFY(ret.property(word).isNumber()); + // in the old back-end and in SpiderMonkey this is allowed, but not in JSC + QVERIFY(ret.isError()); + QVERIFY(ret.toString().startsWith("SyntaxError")); } { // SpiderMonkey allows this, but we don't @@ -3290,66 +3741,66 @@ void tst_QScriptEngine::reservedWords() void tst_QScriptEngine::futureReservedWords_data() { QTest::addColumn<QString>("word"); - QTest::newRow("abstract") << QString("abstract"); - QTest::newRow("boolean") << QString("boolean"); - QTest::newRow("byte") << QString("byte"); - QTest::newRow("char") << QString("char"); - QTest::newRow("class") << QString("class"); - QTest::newRow("const") << QString("const"); - QTest::newRow("debugger") << QString("debugger"); - QTest::newRow("double") << QString("double"); - QTest::newRow("enum") << QString("enum"); - QTest::newRow("export") << QString("export"); - QTest::newRow("extends") << QString("extends"); - QTest::newRow("final") << QString("final"); - QTest::newRow("float") << QString("float"); - QTest::newRow("goto") << QString("goto"); - QTest::newRow("implements") << QString("implements"); - QTest::newRow("import") << QString("import"); - QTest::newRow("int") << QString("int"); - QTest::newRow("interface") << QString("interface"); - QTest::newRow("long") << QString("long"); - QTest::newRow("native") << QString("native"); - QTest::newRow("package") << QString("package"); - QTest::newRow("private") << QString("private"); - QTest::newRow("protected") << QString("protected"); - QTest::newRow("public") << QString("public"); - QTest::newRow("short") << QString("short"); - QTest::newRow("static") << QString("static"); - QTest::newRow("super") << QString("super"); - QTest::newRow("synchronized") << QString("synchronized"); - QTest::newRow("throws") << QString("throws"); - QTest::newRow("transient") << QString("transient"); - QTest::newRow("volatile") << QString("volatile"); + QTest::addColumn<bool>("allowed"); + QTest::newRow("abstract") << QString("abstract") << true; + QTest::newRow("boolean") << QString("boolean") << true; + QTest::newRow("byte") << QString("byte") << true; + QTest::newRow("char") << QString("char") << true; + QTest::newRow("class") << QString("class") << false; + QTest::newRow("const") << QString("const") << false; + QTest::newRow("debugger") << QString("debugger") << false; + QTest::newRow("double") << QString("double") << true; + QTest::newRow("enum") << QString("enum") << false; + QTest::newRow("export") << QString("export") << false; + QTest::newRow("extends") << QString("extends") << false; + QTest::newRow("final") << QString("final") << true; + QTest::newRow("float") << QString("float") << true; + QTest::newRow("goto") << QString("goto") << true; + QTest::newRow("implements") << QString("implements") << true; + QTest::newRow("import") << QString("import") << false; + QTest::newRow("int") << QString("int") << true; + QTest::newRow("interface") << QString("interface") << true; + QTest::newRow("long") << QString("long") << true; + QTest::newRow("native") << QString("native") << true; + QTest::newRow("package") << QString("package") << true; + QTest::newRow("private") << QString("private") << true; + QTest::newRow("protected") << QString("protected") << true; + QTest::newRow("public") << QString("public") << true; + QTest::newRow("short") << QString("short") << true; + QTest::newRow("static") << QString("static") << true; + QTest::newRow("super") << QString("super") << false; + QTest::newRow("synchronized") << QString("synchronized") << true; + QTest::newRow("throws") << QString("throws") << true; + QTest::newRow("transient") << QString("transient") << true; + QTest::newRow("volatile") << QString("volatile") << true; } void tst_QScriptEngine::futureReservedWords() { QFETCH(QString, word); + QFETCH(bool, allowed); { QScriptEngine eng; QScriptValue ret = eng.evaluate(word + " = 123"); - QVERIFY(ret.isError()); - QVERIFY(ret.toString().startsWith("SyntaxError")); + QCOMPARE(!ret.isError(), allowed); } { QScriptEngine eng; QScriptValue ret = eng.evaluate("var " + word + " = 123"); - QVERIFY(ret.isError()); - QVERIFY(ret.toString().startsWith("SyntaxError")); + QCOMPARE(!ret.isError(), allowed); } { // this should probably be allowed (see task 162567) QScriptEngine eng; QScriptValue ret = eng.evaluate("o = {}; o." + word + " = 123"); - QVERIFY(ret.isNumber()); + QCOMPARE(ret.isNumber(), allowed); + QCOMPARE(!ret.isError(), allowed); } { // this should probably be allowed (see task 162567) QScriptEngine eng; QScriptValue ret = eng.evaluate("o = { " + word + ": 123 }"); - QVERIFY(!ret.isError()); - QVERIFY(ret.isObject()); + QCOMPARE(!ret.isError(), allowed); } } @@ -3368,7 +3819,7 @@ void tst_QScriptEngine::throwInsideWithStatement() " bad;" "}"); QVERIFY(ret.isError()); - QCOMPARE(ret.toString(), QString::fromLatin1("ReferenceError: bad is not defined")); + QCOMPARE(ret.toString(), QString::fromLatin1("ReferenceError: Can't find variable: bad")); } { QScriptValue ret = eng.evaluate( @@ -3381,9 +3832,10 @@ void tst_QScriptEngine::throwInsideWithStatement() " bad;" "}"); QVERIFY(ret.isError()); - QCOMPARE(ret.toString(), QString::fromLatin1("ReferenceError: bad is not defined")); + QCOMPARE(ret.toString(), QString::fromLatin1("ReferenceError: Can't find variable: bad")); } { + eng.clearExceptions(); QScriptValue ret = eng.evaluate( "o = { bug : \"no bug\" };" "with (o) {" @@ -3393,10 +3845,12 @@ void tst_QScriptEngine::throwInsideWithStatement() " bug;" " }" "}"); - QVERIFY(ret.isString()); - QCOMPARE(ret.toString(), QString::fromLatin1("no bug")); + QVERIFY(ret.isNumber()); + QCOMPARE(ret.toInt32(), 123); + QVERIFY(eng.hasUncaughtException()); } { + eng.clearExceptions(); QScriptValue ret = eng.evaluate( "o = { bug : \"no bug\" };" "with (o) {" @@ -3405,7 +3859,7 @@ void tst_QScriptEngine::throwInsideWithStatement() QVERIFY(ret.isNumber()); QScriptValue ret2 = eng.evaluate("bug"); QVERIFY(ret2.isError()); - QCOMPARE(ret2.toString(), QString::fromLatin1("ReferenceError: bug is not defined")); + QCOMPARE(ret2.toString(), QString::fromLatin1("ReferenceError: Can't find variable: bug")); } } @@ -3417,13 +3871,35 @@ public: void tst_QScriptEngine::getSetAgent() { - QScriptEngine eng; - QCOMPARE(eng.agent(), (QScriptEngineAgent*)0); - TestAgent agent(&eng); - eng.setAgent(&agent); - QCOMPARE(eng.agent(), (QScriptEngineAgent*)&agent); - eng.setAgent(0); - QCOMPARE(eng.agent(), (QScriptEngineAgent*)0); + // case 1: engine deleted before agent --> agent deleted too + { + QScriptEngine *eng = new QScriptEngine; + QCOMPARE(eng->agent(), (QScriptEngineAgent*)0); + TestAgent *agent = new TestAgent(eng); + eng->setAgent(agent); + QCOMPARE(eng->agent(), (QScriptEngineAgent*)agent); + eng->setAgent(0); // the engine maintains ownership of the old agent + QCOMPARE(eng->agent(), (QScriptEngineAgent*)0); + delete eng; + } + // case 2: agent deleted before engine --> engine's agent should become 0 + { + QScriptEngine *eng = new QScriptEngine; + TestAgent *agent = new TestAgent(eng); + eng->setAgent(agent); + QCOMPARE(eng->agent(), (QScriptEngineAgent*)agent); + delete agent; + QCOMPARE(eng->agent(), (QScriptEngineAgent*)0); + eng->evaluate("(function(){ return 123; })()"); + delete eng; + } + { + QScriptEngine eng; + QScriptEngine eng2; + TestAgent *agent = new TestAgent(&eng); + QTest::ignoreMessage(QtWarningMsg, "QScriptEngine::setAgent(): cannot set agent belonging to different engine"); + eng2.setAgent(agent); + } } void tst_QScriptEngine::reentrancy() @@ -3511,42 +3987,42 @@ void tst_QScriptEngine:: incDecNonObjectProperty() { QScriptValue ret = eng.evaluate("var a; a.n++"); QVERIFY(ret.isError()); - QCOMPARE(ret.toString(), QString::fromLatin1("TypeError: not an object")); + QCOMPARE(ret.toString(), QString::fromLatin1("TypeError: Result of expression 'a' [undefined] is not an object.")); } { QScriptValue ret = eng.evaluate("var a; a.n--"); QVERIFY(ret.isError()); - QCOMPARE(ret.toString(), QString::fromLatin1("TypeError: not an object")); + QCOMPARE(ret.toString(), QString::fromLatin1("TypeError: Result of expression 'a' [undefined] is not an object.")); } { QScriptValue ret = eng.evaluate("var a = null; a.n++"); QVERIFY(ret.isError()); - QCOMPARE(ret.toString(), QString::fromLatin1("TypeError: not an object")); + QCOMPARE(ret.toString(), QString::fromLatin1("TypeError: Result of expression 'a' [null] is not an object.")); } { QScriptValue ret = eng.evaluate("var a = null; a.n--"); QVERIFY(ret.isError()); - QCOMPARE(ret.toString(), QString::fromLatin1("TypeError: not an object")); + QCOMPARE(ret.toString(), QString::fromLatin1("TypeError: Result of expression 'a' [null] is not an object.")); } { QScriptValue ret = eng.evaluate("var a; ++a.n"); QVERIFY(ret.isError()); - QCOMPARE(ret.toString(), QString::fromLatin1("TypeError: not an object")); + QCOMPARE(ret.toString(), QString::fromLatin1("TypeError: Result of expression 'a' [null] is not an object.")); } { QScriptValue ret = eng.evaluate("var a; --a.n"); QVERIFY(ret.isError()); - QCOMPARE(ret.toString(), QString::fromLatin1("TypeError: not an object")); + QCOMPARE(ret.toString(), QString::fromLatin1("TypeError: Result of expression 'a' [null] is not an object.")); } { QScriptValue ret = eng.evaluate("var a; a.n += 1"); QVERIFY(ret.isError()); - QCOMPARE(ret.toString(), QString::fromLatin1("TypeError: not an object")); + QCOMPARE(ret.toString(), QString::fromLatin1("TypeError: Result of expression 'a' [null] is not an object.")); } { QScriptValue ret = eng.evaluate("var a; a.n -= 1"); QVERIFY(ret.isError()); - QCOMPARE(ret.toString(), QString::fromLatin1("TypeError: not an object")); + QCOMPARE(ret.toString(), QString::fromLatin1("TypeError: Result of expression 'a' [null] is not an object.")); } { QScriptValue ret = eng.evaluate("var a = 'ciao'; a.length++"); @@ -3621,6 +4097,7 @@ void tst_QScriptEngine::functionScopes() // top-level functions have only the global object in their scope QScriptValue fun = eng.evaluate("(function() {})"); QVERIFY(fun.isFunction()); + QEXPECT_FAIL("", "Function scope proxying is not implemented", Abort); QVERIFY(fun.scope().isObject()); QVERIFY(fun.scope().strictlyEquals(eng.globalObject())); QVERIFY(!eng.globalObject().scope().isValid()); @@ -3689,7 +4166,7 @@ static QScriptValue counter_hybrid(QScriptContext *ctx, QScriptEngine *eng) { QScriptValue act = ctx->activationObject(); act.setProperty("count", ctx->argument(0).toInt32()); - return eng->evaluate("function() { return count++; }"); + return eng->evaluate("(function() { return count++; })"); } void tst_QScriptEngine::nativeFunctionScopes() @@ -3715,6 +4192,90 @@ void tst_QScriptEngine::nativeFunctionScopes() QCOMPARE(ret.toInt32(), 123); } } + + //from http://doc.trolltech.com/latest/qtscript.html#nested-functions-and-the-scope-chain + { + QScriptEngine eng; + eng.evaluate("function counter() { var count = 0; return function() { return count++; } }\n" + "var c1 = counter(); var c2 = counter(); "); + QCOMPARE(eng.evaluate("c1()").toString(), QString::fromLatin1("0")); + QCOMPARE(eng.evaluate("c1()").toString(), QString::fromLatin1("1")); + QCOMPARE(eng.evaluate("c2()").toString(), QString::fromLatin1("0")); + QCOMPARE(eng.evaluate("c2()").toString(), QString::fromLatin1("1")); + QVERIFY(!eng.hasUncaughtException()); + } + { + QScriptEngine eng; + eng.globalObject().setProperty("counter", eng.newFunction(counter)); + eng.evaluate("var c1 = counter(); var c2 = counter(); "); + QCOMPARE(eng.evaluate("c1()").toString(), QString::fromLatin1("0")); + QCOMPARE(eng.evaluate("c1()").toString(), QString::fromLatin1("1")); + QCOMPARE(eng.evaluate("c2()").toString(), QString::fromLatin1("0")); + QCOMPARE(eng.evaluate("c2()").toString(), QString::fromLatin1("1")); + QVERIFY(!eng.hasUncaughtException()); + } + { + QScriptEngine eng; + eng.globalObject().setProperty("counter", eng.newFunction(counter_hybrid)); + eng.evaluate("var c1 = counter(); var c2 = counter(); "); + QCOMPARE(eng.evaluate("c1()").toString(), QString::fromLatin1("0")); + QCOMPARE(eng.evaluate("c1()").toString(), QString::fromLatin1("1")); + QCOMPARE(eng.evaluate("c2()").toString(), QString::fromLatin1("0")); + QCOMPARE(eng.evaluate("c2()").toString(), QString::fromLatin1("1")); + QVERIFY(!eng.hasUncaughtException()); + } +} + +static QRegExp minimal(QRegExp r) { r.setMinimal(true); return r; } + +void tst_QScriptEngine::qRegExpInport_data() +{ + QTest::addColumn<QRegExp>("rx"); + QTest::addColumn<QString>("string"); + QTest::addColumn<QString>("matched"); + + QTest::newRow("normal") << QRegExp("(test|foo)") << "test _ foo _ test _ Foo"; + QTest::newRow("normal2") << QRegExp("(Test|Foo)") << "test _ foo _ test _ Foo"; + QTest::newRow("case insensitive)") << QRegExp("(test|foo)", Qt::CaseInsensitive) << "test _ foo _ test _ Foo"; + QTest::newRow("case insensitive2)") << QRegExp("(Test|Foo)", Qt::CaseInsensitive) << "test _ foo _ test _ Foo"; + QTest::newRow("b(a*)(b*)") << QRegExp("b(a*)(b*)", Qt::CaseInsensitive) << "aaabbBbaAabaAaababaaabbaaab"; + QTest::newRow("greedy") << QRegExp("a*(a*)", Qt::CaseInsensitive, QRegExp::RegExp2) << "aaaabaaba"; + // this one will fail because we do not support the QRegExp::RegExp in JSC + //QTest::newRow("not_greedy") << QRegExp("a*(a*)", Qt::CaseInsensitive, QRegExp::RegExp) << "aaaabaaba"; + QTest::newRow("willcard") << QRegExp("*.txt", Qt::CaseSensitive, QRegExp::Wildcard) << "file.txt"; + QTest::newRow("willcard 2") << QRegExp("a?b.txt", Qt::CaseSensitive, QRegExp::Wildcard) << "ab.txt abb.rtc acb.txt"; + QTest::newRow("slash") << QRegExp("g/.*/s", Qt::CaseInsensitive, QRegExp::RegExp2) << "string/string/string"; + QTest::newRow("slash2") << QRegExp("g / .* / s", Qt::CaseInsensitive, QRegExp::RegExp2) << "string / string / string"; + QTest::newRow("fixed") << QRegExp("a*aa.a(ba)*a\\ba", Qt::CaseInsensitive, QRegExp::FixedString) << "aa*aa.a(ba)*a\\ba"; + QTest::newRow("fixed insensitive") << QRegExp("A*A", Qt::CaseInsensitive, QRegExp::FixedString) << "a*A A*a A*A a*a"; + QTest::newRow("fixed sensitive") << QRegExp("A*A", Qt::CaseSensitive, QRegExp::FixedString) << "a*A A*a A*A a*a"; + QTest::newRow("html") << QRegExp("<b>(.*)</b>", Qt::CaseSensitive, QRegExp::RegExp2) << "<b>bold</b><i>italic</i><b>bold</b>"; + QTest::newRow("html minimal") << minimal(QRegExp("<b>(.*)</b>", Qt::CaseSensitive, QRegExp::RegExp2)) << "<b>bold</b><i>italic</i><b>bold</b>"; + QTest::newRow("aaa") << QRegExp("a{2,5}") << "aAaAaaaaaAa"; + QTest::newRow("aaa minimal") << minimal(QRegExp("a{2,5}")) << "aAaAaaaaaAa"; + QTest::newRow("minimal") << minimal(QRegExp(".*\\} [*8]")) << "}?} ?} *"; +} + +void tst_QScriptEngine::qRegExpInport() +{ + QFETCH(QRegExp, rx); + QFETCH(QString, string); + + QScriptEngine eng; + QScriptValue rexp; + rexp = eng.newRegExp(rx); + + QCOMPARE(rexp.isValid(), true); + QCOMPARE(rexp.isRegExp(), true); + QVERIFY(rexp.isFunction()); + + QScriptValue func = eng.evaluate("(function(string, regexp) { return string.match(regexp); })"); + QScriptValue result = func.call(QScriptValue(), QScriptValueList() << string << rexp); + + rx.indexIn(string); + for (int i = 0; i <= rx.numCaptures(); i++) { + QCOMPARE(result.property(i).toString(), rx.cap(i)); + } } QTEST_MAIN(tst_QScriptEngine) |