summaryrefslogtreecommitdiffstats
path: root/tests/auto/qscriptcontext/tst_qscriptcontext.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'tests/auto/qscriptcontext/tst_qscriptcontext.cpp')
-rw-r--r--tests/auto/qscriptcontext/tst_qscriptcontext.cpp576
1 files changed, 395 insertions, 181 deletions
diff --git a/tests/auto/qscriptcontext/tst_qscriptcontext.cpp b/tests/auto/qscriptcontext/tst_qscriptcontext.cpp
index 7915eb0..457188c 100644
--- a/tests/auto/qscriptcontext/tst_qscriptcontext.cpp
+++ b/tests/auto/qscriptcontext/tst_qscriptcontext.cpp
@@ -44,11 +44,13 @@
#include <QtScript/qscriptcontext.h>
#include <QtScript/qscriptengine.h>
+#include <QtScript/qscriptvalueiterator.h>
//TESTED_CLASS=
//TESTED_FILES=
Q_DECLARE_METATYPE(QScriptValueList)
+Q_DECLARE_METATYPE(QScriptContext::Error)
QT_BEGIN_NAMESPACE
extern bool qt_script_isJITEnabled();
@@ -64,27 +66,51 @@ public:
private slots:
void callee();
+ void callee_implicitCall();
void arguments();
+ void argumentsInJS();
void thisObject();
void returnValue();
- void throwError();
+ void throwError_data();
+ void throwError_fromEvaluate_data();
+ void throwError_fromEvaluate();
+ void throwError_fromCpp_data();
+ void throwError_fromCpp();
void throwValue();
void evaluateInFunction();
void pushAndPopContext();
+ void pushAndPopContext_variablesInActivation();
+ void pushAndPopContext_setThisObject();
+ void pushAndPopContext_throwException();
void lineNumber();
void backtrace_data();
void backtrace();
- void scopeChain();
- void pushAndPopScope();
- void getSetActivationObject();
+ void scopeChain_globalContext();
+ void scopeChain_closure();
+ void scopeChain_withStatement();
+ void pushAndPopScope_globalContext();
+ void pushAndPopScope_globalContext2();
+ void getSetActivationObject_globalContext();
+ void pushScopeEvaluate();
+ void pushScopeCall();
+ void popScopeSimple();
+ void pushAndPopGlobalObjectSimple();
+ void pushAndPopIterative();
+ void getSetActivationObject_customContext();
void inheritActivationAndThisObject();
void toString();
- void calledAsConstructor();
+ void calledAsConstructor_fromCpp();
+ void calledAsConstructor_fromJS();
+ void calledAsConstructor_parentContext();
void argumentsObjectInNative();
void jsActivationObject();
void qobjectAsActivationObject();
void parentContextCallee_QT2270();
void popNativeContextScope();
+ void throwErrorInGlobalContext();
+ void throwErrorWithTypeInGlobalContext_data();
+ void throwErrorWithTypeInGlobalContext();
+ void throwValueInGlobalContext();
};
tst_QScriptContext::tst_QScriptContext()
@@ -110,33 +136,33 @@ void tst_QScriptContext::callee()
{
QScriptEngine eng;
- {
- QScriptValue fun = eng.newFunction(get_callee);
- fun.setProperty("foo", QScriptValue(&eng, "bar"));
- eng.globalObject().setProperty("get_callee", fun);
+ QScriptValue fun = eng.newFunction(get_callee);
+ fun.setProperty("foo", QScriptValue(&eng, "bar"));
+ eng.globalObject().setProperty("get_callee", fun);
- QScriptValue result = eng.evaluate("get_callee()");
- QCOMPARE(result.isFunction(), true);
- QCOMPARE(result.property("foo").toString(), QString("bar"));
- }
+ QScriptValue result = eng.evaluate("get_callee()");
+ QCOMPARE(result.isFunction(), true);
+ QCOMPARE(result.property("foo").toString(), QString("bar"));
+}
+void tst_QScriptContext::callee_implicitCall()
+{
+ QScriptEngine eng;
// callee when toPrimitive() is called internally
- {
- QScriptValue fun = eng.newFunction(store_callee_and_return_primitive);
- QScriptValue obj = eng.newObject();
- obj.setProperty("toString", fun);
- QVERIFY(!obj.property("callee").isValid());
- (void)obj.toString();
- QVERIFY(obj.property("callee").isFunction());
- QVERIFY(obj.property("callee").strictlyEquals(fun));
-
- obj.setProperty("callee", QScriptValue());
- QVERIFY(!obj.property("callee").isValid());
- obj.setProperty("valueOf", fun);
- (void)obj.toNumber();
- QVERIFY(obj.property("callee").isFunction());
- QVERIFY(obj.property("callee").strictlyEquals(fun));
- }
+ QScriptValue fun = eng.newFunction(store_callee_and_return_primitive);
+ QScriptValue obj = eng.newObject();
+ obj.setProperty("toString", fun);
+ QVERIFY(!obj.property("callee").isValid());
+ (void)obj.toString();
+ QVERIFY(obj.property("callee").isFunction());
+ QVERIFY(obj.property("callee").strictlyEquals(fun));
+
+ obj.setProperty("callee", QScriptValue());
+ QVERIFY(!obj.property("callee").isValid());
+ obj.setProperty("valueOf", fun);
+ (void)obj.toNumber();
+ QVERIFY(obj.property("callee").isFunction());
+ QVERIFY(obj.property("callee").strictlyEquals(fun));
}
static QScriptValue get_arguments(QScriptContext *ctx, QScriptEngine *eng)
@@ -156,6 +182,8 @@ void tst_QScriptContext::arguments()
{
QScriptEngine eng;
+ // See section 10.6 ("Arguments Object") of ECMA-262.
+
{
QScriptValue args = eng.currentContext()->argumentsObject();
QVERIFY(args.isObject());
@@ -167,6 +195,8 @@ void tst_QScriptContext::arguments()
}
for (int x = 0; x < 2; ++x) {
+ // The expected arguments array should be the same regardless of
+ // whether get_arguments() is called as a constructor.
QString prefix;
if (x == 0)
prefix = "";
@@ -213,11 +243,15 @@ void tst_QScriptContext::arguments()
QCOMPARE(result.propertyFlags("length"), QScriptValue::SkipInEnumeration);
QCOMPARE(result.property("callee").strictlyEquals(fun), true);
QCOMPARE(result.propertyFlags("callee"), QScriptValue::SkipInEnumeration);
+
+ // callee and length properties should be writable.
QScriptValue replacedCallee(&eng, 123);
result.setProperty("callee", replacedCallee);
QVERIFY(result.property("callee").equals(replacedCallee));
QScriptValue replacedLength(&eng, 456);
result.setProperty("length", replacedLength);
+
+ // callee and length properties should be deletable.
QVERIFY(result.property("length").equals(replacedLength));
result.setProperty("callee", QScriptValue());
QVERIFY(!result.property("callee").isValid());
@@ -251,28 +285,31 @@ void tst_QScriptContext::arguments()
QCOMPARE(result.property("2").toBoolean(), true);
QCOMPARE(result.property("3").isUndefined(), true);
}
+ }
+}
- // arguments object returned from script
- {
- QScriptValue result = eng.evaluate("(function() { return arguments; })(123)");
- QCOMPARE(result.isArray(), false);
- QVERIFY(result.isObject());
- QCOMPARE(result.property("length").toUInt32(), quint32(1));
- QCOMPARE(result.property("0").isNumber(), true);
- QCOMPARE(result.property("0").toNumber(), 123.0);
- }
+void tst_QScriptContext::argumentsInJS()
+{
+ QScriptEngine eng;
+ {
+ QScriptValue result = eng.evaluate("(function() { return arguments; })(123)");
+ QCOMPARE(result.isArray(), false);
+ QVERIFY(result.isObject());
+ QCOMPARE(result.property("length").toUInt32(), quint32(1));
+ QCOMPARE(result.property("0").isNumber(), true);
+ QCOMPARE(result.property("0").toNumber(), 123.0);
+ }
- {
- QScriptValue result = eng.evaluate("(function() { return arguments; })('ciao', null, true, undefined)");
- QCOMPARE(result.isArray(), false);
- QCOMPARE(result.property("length").toUInt32(), quint32(4));
- QCOMPARE(result.property("0").isString(), true);
- QCOMPARE(result.property("0").toString(), QString("ciao"));
- QCOMPARE(result.property("1").isNull(), true);
- QCOMPARE(result.property("2").isBoolean(), true);
- QCOMPARE(result.property("2").toBoolean(), true);
- QCOMPARE(result.property("3").isUndefined(), true);
- }
+ {
+ QScriptValue result = eng.evaluate("(function() { return arguments; })('ciao', null, true, undefined)");
+ QCOMPARE(result.isArray(), false);
+ QCOMPARE(result.property("length").toUInt32(), quint32(4));
+ QCOMPARE(result.property("0").isString(), true);
+ QCOMPARE(result.property("0").toString(), QString("ciao"));
+ QCOMPARE(result.property("1").isNull(), true);
+ QCOMPARE(result.property("2").isBoolean(), true);
+ QCOMPARE(result.property("2").toBoolean(), true);
+ QCOMPARE(result.property("3").isUndefined(), true);
}
}
@@ -360,73 +397,71 @@ static QScriptValue throw_ErrorAndReturnUndefined(QScriptContext *ctx, QScriptEn
return eng->undefinedValue();
}
-void tst_QScriptContext::throwError()
+static QScriptValue throw_ErrorAndReturnString(QScriptContext *ctx, QScriptEngine *)
{
- QScriptEngine eng;
+ return ctx->throwError(QScriptContext::UnknownError, "foo").toString();
+}
- {
- QScriptValue fun = eng.newFunction(throw_Error);
- eng.globalObject().setProperty("throw_Error", fun);
- QScriptValue result = eng.evaluate("throw_Error()");
- QCOMPARE(eng.hasUncaughtException(), true);
- QCOMPARE(result.isError(), true);
- QCOMPARE(result.toString(), QString("Error: foo"));
- }
+static QScriptValue throw_ErrorAndReturnObject(QScriptContext *ctx, QScriptEngine *eng)
+{
+ ctx->throwError(QScriptContext::UnknownError, "foo");
+ return eng->newObject();
+}
- {
- QScriptValue fun = eng.newFunction(throw_TypeError);
- eng.globalObject().setProperty("throw_TypeError", fun);
- QScriptValue result = eng.evaluate("throw_TypeError()");
- QCOMPARE(eng.hasUncaughtException(), true);
- QCOMPARE(result.isError(), true);
- QCOMPARE(result.toString(), QString("TypeError: foo"));
- }
+void tst_QScriptContext::throwError_data()
+{
+ QTest::addColumn<void*>("nativeFunctionPtr");
+ QTest::addColumn<QString>("stringRepresentation");
+
+ QTest::newRow("Error") << reinterpret_cast<void*>(throw_Error) << QString("Error: foo");
+ QTest::newRow("TypeError") << reinterpret_cast<void*>(throw_TypeError) << QString("TypeError: foo");
+ QTest::newRow("ReferenceError") << reinterpret_cast<void*>(throw_ReferenceError) << QString("ReferenceError: foo");
+ QTest::newRow("SyntaxError") << reinterpret_cast<void*>(throw_SyntaxError) << QString("SyntaxError: foo");
+ QTest::newRow("RangeError") << reinterpret_cast<void*>(throw_RangeError) << QString("RangeError: foo");
+ QTest::newRow("URIError") << reinterpret_cast<void*>(throw_URIError) << QString("URIError: foo");
+ QTest::newRow("ErrorAndReturnUndefined") << reinterpret_cast<void*>(throw_ErrorAndReturnUndefined) << QString("Error: foo");
+ QTest::newRow("ErrorAndReturnString") << reinterpret_cast<void*>(throw_ErrorAndReturnString) << QString("Error: foo");
+ QTest::newRow("ErrorAndReturnObject") << reinterpret_cast<void*>(throw_ErrorAndReturnObject) << QString("Error: foo");
+}
- {
- QScriptValue fun = eng.newFunction(throw_ReferenceError);
- eng.globalObject().setProperty("throw_ReferenceError", fun);
- QScriptValue result = eng.evaluate("throw_ReferenceError()");
- QCOMPARE(eng.hasUncaughtException(), true);
- QCOMPARE(result.isError(), true);
- QCOMPARE(result.toString(), QString("ReferenceError: foo"));
- }
+void tst_QScriptContext::throwError_fromEvaluate_data()
+{
+ throwError_data();
+}
- {
- QScriptValue fun = eng.newFunction(throw_SyntaxError);
- eng.globalObject().setProperty("throw_SyntaxError", fun);
- QScriptValue result = eng.evaluate("throw_SyntaxError()");
- QCOMPARE(eng.hasUncaughtException(), true);
- QCOMPARE(result.isError(), true);
- QCOMPARE(result.toString(), QString("SyntaxError: foo"));
- }
+void tst_QScriptContext::throwError_fromEvaluate()
+{
+ QFETCH(void*, nativeFunctionPtr);
+ QScriptEngine::FunctionSignature nativeFunction = reinterpret_cast<QScriptEngine::FunctionSignature>(nativeFunctionPtr);
+ QFETCH(QString, stringRepresentation);
+ QScriptEngine engine;
- {
- QScriptValue fun = eng.newFunction(throw_RangeError);
- eng.globalObject().setProperty("throw_RangeError", fun);
- QScriptValue result = eng.evaluate("throw_RangeError()");
- QCOMPARE(eng.hasUncaughtException(), true);
- QCOMPARE(result.isError(), true);
- QCOMPARE(result.toString(), QString("RangeError: foo"));
- }
+ QScriptValue fun = engine.newFunction(nativeFunction);
+ engine.globalObject().setProperty("throw_Error", fun);
+ QScriptValue result = engine.evaluate("throw_Error()");
+ QCOMPARE(engine.hasUncaughtException(), true);
+ QCOMPARE(result.isError(), true);
+ QCOMPARE(result.toString(), stringRepresentation);
+}
- {
- QScriptValue fun = eng.newFunction(throw_URIError);
- eng.globalObject().setProperty("throw_URIError", fun);
- QScriptValue result = eng.evaluate("throw_URIError()");
- QCOMPARE(eng.hasUncaughtException(), true);
- QCOMPARE(result.isError(), true);
- QCOMPARE(result.toString(), QString("URIError: foo"));
- }
+void tst_QScriptContext::throwError_fromCpp_data()
+{
+ throwError_data();
+}
- {
- QScriptValue fun = eng.newFunction(throw_ErrorAndReturnUndefined);
- eng.globalObject().setProperty("throw_ErrorAndReturnUndefined", fun);
- QScriptValue result = eng.evaluate("throw_ErrorAndReturnUndefined()");
- QVERIFY(eng.hasUncaughtException());
- QVERIFY(result.isError());
- QCOMPARE(result.toString(), QString("Error: foo"));
- }
+void tst_QScriptContext::throwError_fromCpp()
+{
+ QFETCH(void*, nativeFunctionPtr);
+ QScriptEngine::FunctionSignature nativeFunction = reinterpret_cast<QScriptEngine::FunctionSignature>(nativeFunctionPtr);
+ QFETCH(QString, stringRepresentation);
+ QScriptEngine engine;
+ QScriptValue fun = engine.newFunction(nativeFunction);
+ engine.globalObject().setProperty("throw_Error", fun);
+ QScriptValue result = fun.call();
+ QCOMPARE(engine.hasUncaughtException(), true);
+ QCOMPARE(result.isError(), true);
+ QCOMPARE(result.toString(), stringRepresentation);
}
static QScriptValue throw_value(QScriptContext *ctx, QScriptEngine *)
@@ -508,36 +543,65 @@ void tst_QScriptContext::pushAndPopContext()
QTest::ignoreMessage(QtWarningMsg, "QScriptEngine::popContext() doesn't match with pushContext()");
eng.popContext();
QCOMPARE(eng.currentContext(), topLevel);
+}
- {
- QScriptContext *ctx3 = eng.pushContext();
- ctx3->activationObject().setProperty("foo", QScriptValue(&eng, 123));
- QVERIFY(eng.evaluate("foo").strictlyEquals(QScriptValue(&eng, 123)));
- eng.evaluate("var bar = 'ciao'");
- QVERIFY(ctx3->activationObject().property("bar", QScriptValue::ResolveLocal).strictlyEquals(QScriptValue(&eng, "ciao")));
- eng.popContext();
+void tst_QScriptContext::pushAndPopContext_variablesInActivation()
+{
+ QScriptEngine eng;
+ QScriptContext *ctx = eng.pushContext();
+ ctx->activationObject().setProperty("foo", QScriptValue(&eng, 123));
+ // evaluate() should use the current context.
+ QVERIFY(eng.evaluate("foo").strictlyEquals(QScriptValue(&eng, 123)));
+ QCOMPARE(ctx->activationObject().propertyFlags("foo"), QScriptValue::PropertyFlags(0));
+
+ ctx->activationObject().setProperty(4, 456);
+ QVERIFY(ctx->activationObject().property(4, QScriptValue::ResolveLocal).equals(456));
+
+ // New JS variables should become properties of the current context's activation.
+ eng.evaluate("var bar = 'ciao'");
+ QVERIFY(ctx->activationObject().property("bar", QScriptValue::ResolveLocal).strictlyEquals(QScriptValue(&eng, "ciao")));
+
+ ctx->activationObject().setProperty("baz", 789, QScriptValue::ReadOnly);
+ QVERIFY(eng.evaluate("baz").equals(789));
+ QCOMPARE(ctx->activationObject().propertyFlags("baz"), QScriptValue::ReadOnly);
+
+ QSet<QString> activationPropertyNames;
+ QScriptValueIterator it(ctx->activationObject());
+ while (it.hasNext()) {
+ it.next();
+ activationPropertyNames.insert(it.name());
}
+ QCOMPARE(activationPropertyNames.size(), 4);
+ QVERIFY(activationPropertyNames.contains("foo"));
+ QVERIFY(activationPropertyNames.contains("4"));
+ QVERIFY(activationPropertyNames.contains("bar"));
+ QVERIFY(activationPropertyNames.contains("baz"));
- {
- QScriptContext *ctx4 = eng.pushContext();
- QScriptValue obj = eng.newObject();
- obj.setProperty("prop", QScriptValue(&eng, 456));
- ctx4->setThisObject(obj);
- QScriptValue ret = eng.evaluate("var tmp = this.prop; tmp + 1");
- QCOMPARE(eng.currentContext(), ctx4);
- QVERIFY(ret.strictlyEquals(QScriptValue(&eng, 457)));
- eng.popContext();
- }
+ eng.popContext();
+}
- // throwing an exception
- {
- QScriptContext *ctx5 = eng.pushContext();
- QScriptValue ret = eng.evaluate("throw new Error('oops')");
- QVERIFY(ret.isError());
- QVERIFY(eng.hasUncaughtException());
- QCOMPARE(eng.currentContext(), ctx5);
- eng.popContext();
- }
+void tst_QScriptContext::pushAndPopContext_setThisObject()
+{
+ QScriptEngine eng;
+ QScriptContext *ctx = eng.pushContext();
+ QScriptValue obj = eng.newObject();
+ obj.setProperty("prop", QScriptValue(&eng, 456));
+ ctx->setThisObject(obj);
+ QScriptValue ret = eng.evaluate("var tmp = this.prop; tmp + 1");
+ QCOMPARE(eng.currentContext(), ctx);
+ QVERIFY(ret.strictlyEquals(QScriptValue(&eng, 457)));
+ eng.popContext();
+}
+
+void tst_QScriptContext::pushAndPopContext_throwException()
+{
+ QScriptEngine eng;
+ QScriptContext *ctx = eng.pushContext();
+ QScriptValue ret = eng.evaluate("throw new Error('oops')");
+ QVERIFY(ret.isError());
+ QVERIFY(eng.hasUncaughtException());
+ QCOMPARE(eng.currentContext(), ctx);
+ eng.popContext();
}
void tst_QScriptContext::popNativeContextScope()
@@ -565,7 +629,7 @@ void tst_QScriptContext::popNativeContextScope()
QVERIFY(ctx->activationObject().strictlyEquals(customScope));
QCOMPARE(ctx->scopeChain().size(), 2);
QVERIFY(ctx->scopeChain().at(0).strictlyEquals(customScope));
- QEXPECT_FAIL("", "QTBUG-11012", Continue);
+ QEXPECT_FAIL("", "QTBUG-11012: Global object is replaced in scope chain", Continue);
QVERIFY(ctx->scopeChain().at(1).strictlyEquals(eng.globalObject()));
QVERIFY(!eng.evaluate("baz = 456; var foo = 789; function barbar() {}").isError());
@@ -661,10 +725,7 @@ void tst_QScriptContext::backtrace_data()
expected << "<native>('hey') at -1"
<< "<eval>() at 3"
- << QString::fromLatin1("foo(arg1 = 'hello', arg2 = 456) at testfile:%0")
- // interpreter unfortunately doesn't provide line number for eval()
- .arg(qt_script_isJITEnabled() ? 2 : -1);
- expected
+ << "foo(arg1 = 'hello', arg2 = 456) at testfile:2"
<< "<global>() at testfile:4";
QTest::newRow("eval") << source << expected;
@@ -723,10 +784,7 @@ void tst_QScriptContext::backtrace_data()
expected << "<native>('hey') at -1"
<< "<eval>() at 3"
- << QString::fromLatin1("plop('hello', 456) at testfile:%0")
- // interpreter unfortunately doesn't provide line number for eval()
- .arg(qt_script_isJITEnabled() ? 3 : -1);
- expected
+ << "plop('hello', 456) at testfile:3"
<< "<global>() at testfile:5";
QTest::newRow("eval in member") << source << expected;
@@ -923,6 +981,8 @@ void tst_QScriptContext::backtrace()
QVERIFY(!eng.hasUncaughtException());
QVERIFY(ret.isArray());
QStringList slist = qscriptvalue_cast<QStringList>(ret);
+ QEXPECT_FAIL("eval", "QTBUG-17842: Missing line number in backtrace when function calls eval()", Continue);
+ QEXPECT_FAIL("eval in member", "QTBUG-17842: Missing line number in backtrace when function calls eval()", Continue);
QCOMPARE(slist, expectedbacktrace);
}
@@ -931,7 +991,7 @@ static QScriptValue getScopeChain(QScriptContext *ctx, QScriptEngine *eng)
return qScriptValueFromValue(eng, ctx->parentContext()->scopeChain());
}
-void tst_QScriptContext::scopeChain()
+void tst_QScriptContext::scopeChain_globalContext()
{
QScriptEngine eng;
{
@@ -945,19 +1005,35 @@ void tst_QScriptContext::scopeChain()
QCOMPARE(ret.size(), 1);
QVERIFY(ret.at(0).strictlyEquals(eng.globalObject()));
}
- {
- eng.evaluate("function foo() { function bar() { return getScopeChain(); } return bar() }");
- QScriptValueList ret = qscriptvalue_cast<QScriptValueList>(eng.evaluate("foo()"));
- QEXPECT_FAIL("", "Number of items in returned scope chain is incorrect", Abort);
- QCOMPARE(ret.size(), 3);
- QVERIFY(ret.at(2).strictlyEquals(eng.globalObject()));
- QCOMPARE(ret.at(1).toString(), QString::fromLatin1("activation"));
- QVERIFY(ret.at(1).property("arguments").isObject());
- QCOMPARE(ret.at(0).toString(), QString::fromLatin1("activation"));
- QVERIFY(ret.at(0).property("arguments").isObject());
- }
+}
+
+void tst_QScriptContext::scopeChain_closure()
+{
+ QScriptEngine eng;
+ eng.globalObject().setProperty("getScopeChain", eng.newFunction(getScopeChain));
+
+ eng.evaluate("function foo() { function bar() { return getScopeChain(); } return bar() }");
+ QScriptValueList ret = qscriptvalue_cast<QScriptValueList>(eng.evaluate("foo()"));
+ // JSC will not create an activation for bar() unless we insert a call
+ // to eval() in the function body; JSC has no way of knowing that the
+ // native function will be asking for the activation, and we don't want
+ // to needlessly create it.
+ QEXPECT_FAIL("", "QTBUG-10313: JSC optimizes away the activation object", Abort);
+ QCOMPARE(ret.size(), 3);
+ QVERIFY(ret.at(2).strictlyEquals(eng.globalObject()));
+ QCOMPARE(ret.at(1).toString(), QString::fromLatin1("activation"));
+ QVERIFY(ret.at(1).property("arguments").isObject());
+ QCOMPARE(ret.at(0).toString(), QString::fromLatin1("activation"));
+ QVERIFY(ret.at(0).property("arguments").isObject());
+}
+
+void tst_QScriptContext::scopeChain_withStatement()
+{
+ QScriptEngine eng;
+ eng.globalObject().setProperty("getScopeChain", eng.newFunction(getScopeChain));
{
QScriptValueList ret = qscriptvalue_cast<QScriptValueList>(eng.evaluate("o = { x: 123 }; with(o) getScopeChain();"));
+ QEXPECT_FAIL("", "QTBUG-17131: with-scope isn't reflected by QScriptContext", Abort);
QCOMPARE(ret.size(), 2);
QVERIFY(ret.at(1).strictlyEquals(eng.globalObject()));
QVERIFY(ret.at(0).isObject());
@@ -975,7 +1051,73 @@ void tst_QScriptContext::scopeChain()
}
}
-void tst_QScriptContext::pushAndPopScope()
+void tst_QScriptContext::pushScopeEvaluate()
+{
+ QScriptEngine engine;
+ QScriptValue object = engine.newObject();
+ object.setProperty("foo", 1234);
+ object.setProperty(1, 1234);
+ engine.currentContext()->pushScope(object);
+ object.setProperty("bar", 4321);
+ object.setProperty(2, 4321);
+ QVERIFY(engine.evaluate("foo").equals(1234));
+ QVERIFY(engine.evaluate("bar").equals(4321));
+}
+
+void tst_QScriptContext::pushScopeCall()
+{
+ QScriptEngine engine;
+ QScriptValue object = engine.globalObject();
+ QScriptValue thisObject = engine.newObject();
+ QScriptValue function = engine.evaluate("(function(property){return this[property]; })");
+ QVERIFY(function.isFunction());
+ object.setProperty("foo", 1234);
+ thisObject.setProperty("foo", "foo");
+ engine.currentContext()->pushScope(object);
+ object.setProperty("bar", 4321);
+ thisObject.setProperty("bar", "bar");
+ QVERIFY(function.call(QScriptValue(), QScriptValueList() << "foo").equals(1234));
+ QVERIFY(function.call(QScriptValue(), QScriptValueList() << "bar").equals(4321));
+ QVERIFY(function.call(thisObject, QScriptValueList() << "foo").equals("foo"));
+ QVERIFY(function.call(thisObject, QScriptValueList() << "bar").equals("bar"));
+}
+
+void tst_QScriptContext::popScopeSimple()
+{
+ QScriptEngine engine;
+ QScriptValue object = engine.newObject();
+ QScriptValue globalObject = engine.globalObject();
+ engine.currentContext()->pushScope(object);
+ QVERIFY(engine.currentContext()->popScope().strictlyEquals(object));
+ QVERIFY(engine.globalObject().strictlyEquals(globalObject));
+}
+
+void tst_QScriptContext::pushAndPopGlobalObjectSimple()
+{
+ QScriptEngine engine;
+ QScriptValue globalObject = engine.globalObject();
+ engine.currentContext()->pushScope(globalObject);
+ QVERIFY(engine.currentContext()->popScope().strictlyEquals(globalObject));
+ QVERIFY(engine.globalObject().strictlyEquals(globalObject));
+}
+
+void tst_QScriptContext::pushAndPopIterative()
+{
+ QScriptEngine engine;
+ for (uint repeat = 0; repeat < 2; ++repeat) {
+ for (uint i = 1; i < 11; ++i) {
+ QScriptValue object = engine.newObject();
+ object.setProperty("x", i + 10 * repeat);
+ engine.currentContext()->pushScope(object);
+ }
+ for (uint i = 10; i > 0; --i) {
+ QScriptValue object = engine.currentContext()->popScope();
+ QVERIFY(object.property("x").equals(i + 10 * repeat));
+ }
+ }
+}
+
+void tst_QScriptContext::pushAndPopScope_globalContext()
{
QScriptEngine eng;
QScriptContext *ctx = eng.currentContext();
@@ -1032,14 +1174,19 @@ void tst_QScriptContext::pushAndPopScope()
ctx->pushScope(QScriptValue());
QCOMPARE(ctx->scopeChain().size(), 1);
+}
+void tst_QScriptContext::pushAndPopScope_globalContext2()
+{
+ QScriptEngine eng;
+ QScriptContext *ctx = eng.currentContext();
QVERIFY(ctx->popScope().strictlyEquals(eng.globalObject()));
QVERIFY(ctx->scopeChain().isEmpty());
// Used to work with old back-end, doesn't with new one because JSC requires that the last object in
// a scope chain is the Global Object.
QTest::ignoreMessage(QtWarningMsg, "QScriptContext::pushScope() failed: initial object in scope chain has to be the Global Object");
- ctx->pushScope(obj);
+ ctx->pushScope(eng.newObject());
QCOMPARE(ctx->scopeChain().size(), 0);
QScriptEngine eng2;
@@ -1056,7 +1203,7 @@ static QScriptValue get_activationObject(QScriptContext *ctx, QScriptEngine *)
return ctx->activationObject();
}
-void tst_QScriptContext::getSetActivationObject()
+void tst_QScriptContext::getSetActivationObject_globalContext()
{
QScriptEngine eng;
QScriptContext *ctx = eng.currentContext();
@@ -1089,7 +1236,7 @@ void tst_QScriptContext::getSetActivationObject()
QScriptValue ret = eng.evaluate("get_activationObject(1, 2, 3)");
QVERIFY(ret.isObject());
QScriptValue arguments = ret.property("arguments");
- QEXPECT_FAIL("", "Getting arguments property of activation object doesn't work", Abort);
+ QEXPECT_FAIL("", "QTBUG-17136: arguments property of activation object is missing", Abort);
QVERIFY(arguments.isObject());
QCOMPARE(arguments.property("length").toInt32(), 3);
QCOMPARE(arguments.property("0").toInt32(), 1);
@@ -1098,6 +1245,22 @@ void tst_QScriptContext::getSetActivationObject()
}
}
+void tst_QScriptContext::getSetActivationObject_customContext()
+{
+ QScriptEngine eng;
+ QScriptContext *ctx = eng.pushContext();
+ QVERIFY(ctx->activationObject().isObject());
+ QScriptValue act = eng.newObject();
+ ctx->setActivationObject(act);
+ QVERIFY(ctx->activationObject().equals(act));
+ eng.evaluate("var foo = 123");
+ QCOMPARE(act.property("foo").toInt32(), 123);
+ eng.popContext();
+ QCOMPARE(act.property("foo").toInt32(), 123);
+}
+
+// Helper function that's intended to have the same behavior
+// as the built-in eval() function.
static QScriptValue myEval(QScriptContext *ctx, QScriptEngine *eng)
{
QString code = ctx->argument(0).toString();
@@ -1126,13 +1289,14 @@ void tst_QScriptContext::inheritActivationAndThisObject()
QCOMPARE(ret.toInt32(), 123);
}
- // QT-2219
{
eng.globalObject().setProperty("a", 123);
QScriptValue ret = eng.evaluate("(function() { myEval('var a = 456'); return a; })()");
QVERIFY(ret.isNumber());
QCOMPARE(ret.toInt32(), 456);
- QEXPECT_FAIL("", "QT-2219: Wrong activation object is returned from native function's parent context", Continue);
+ // Since JSC doesn't create an activation object for the anonymous function call,
+ // myEval() will use the global object as the activation, which is wrong.
+ QEXPECT_FAIL("", "QTBUG-10313: Wrong activation object is returned from native function's parent context", Continue);
QVERIFY(eng.globalObject().property("a").strictlyEquals(123));
}
}
@@ -1171,11 +1335,11 @@ static QScriptValue storeCalledAsConstructorV3(QScriptContext *ctx, QScriptEngin
return eng->undefinedValue();
}
-void tst_QScriptContext::calledAsConstructor()
+void tst_QScriptContext::calledAsConstructor_fromCpp()
{
QScriptEngine eng;
- QScriptValue fun1 = eng.newFunction(storeCalledAsConstructor);
{
+ QScriptValue fun1 = eng.newFunction(storeCalledAsConstructor);
fun1.call();
QVERIFY(!fun1.property("calledAsConstructor").toBool());
fun1.construct();
@@ -1188,25 +1352,31 @@ void tst_QScriptContext::calledAsConstructor()
fun.construct();
QVERIFY(fun.property("calledAsConstructor").toBool());
}
- {
- eng.globalObject().setProperty("fun1", fun1);
- eng.evaluate("fun1();");
- QVERIFY(!fun1.property("calledAsConstructor").toBool());
- eng.evaluate("new fun1();");
- QVERIFY(fun1.property("calledAsConstructor").toBool());
- }
- {
- QScriptValue fun3 = eng.newFunction(storeCalledAsConstructorV3);
- eng.globalObject().setProperty("fun3", fun3);
- eng.evaluate("function test() { fun3() }");
- eng.evaluate("test();");
- QVERIFY(!fun3.property("calledAsConstructor").toBool());
- eng.evaluate("new test();");
- if (qt_script_isJITEnabled())
- QEXPECT_FAIL("", "QTBUG-6132: calledAsConstructor is not correctly set for JS functions when JIT is enabled", Continue);
- QVERIFY(fun3.property("calledAsConstructor").toBool());
- }
+}
+
+void tst_QScriptContext::calledAsConstructor_fromJS()
+{
+ QScriptEngine eng;
+ QScriptValue fun1 = eng.newFunction(storeCalledAsConstructor);
+ eng.globalObject().setProperty("fun1", fun1);
+ eng.evaluate("fun1();");
+ QVERIFY(!fun1.property("calledAsConstructor").toBool());
+ eng.evaluate("new fun1();");
+ QVERIFY(fun1.property("calledAsConstructor").toBool());
+}
+void tst_QScriptContext::calledAsConstructor_parentContext()
+{
+ QScriptEngine eng;
+ QScriptValue fun3 = eng.newFunction(storeCalledAsConstructorV3);
+ eng.globalObject().setProperty("fun3", fun3);
+ eng.evaluate("function test() { fun3() }");
+ eng.evaluate("test();");
+ QVERIFY(!fun3.property("calledAsConstructor").toBool());
+ eng.evaluate("new test();");
+ if (qt_script_isJITEnabled())
+ QEXPECT_FAIL("", "QTBUG-6132: calledAsConstructor is not correctly set for JS functions when JIT is enabled", Continue);
+ QVERIFY(fun3.property("calledAsConstructor").toBool());
}
static QScriptValue argumentsObjectInNative_test1(QScriptContext *ctx, QScriptEngine *eng)
@@ -1268,7 +1438,7 @@ void tst_QScriptContext::jsActivationObject()
QScriptValue result1 = eng.evaluate("f2('hello', 'useless', 'world')");
QScriptValue result2 = eng.evaluate("f3()");
QVERIFY(result1.isObject());
- QEXPECT_FAIL("", "JSC optimize away the activation object", Abort);
+ QEXPECT_FAIL("", "QTBUG-10313: JSC optimizes away the activation object", Abort);
QCOMPARE(result1.property("v1").toInt32() , 42);
QCOMPARE(result1.property("arguments").property(1).toString() , QString::fromLatin1("useless"));
QVERIFY(result2.isObject());
@@ -1327,5 +1497,49 @@ void tst_QScriptContext::parentContextCallee_QT2270()
QVERIFY(callee.equals(fun));
}
+void tst_QScriptContext::throwErrorInGlobalContext()
+{
+ QScriptEngine eng;
+ QScriptValue ret = eng.currentContext()->throwError("foo");
+ QVERIFY(ret.isError());
+ QVERIFY(eng.hasUncaughtException());
+ QVERIFY(eng.uncaughtException().strictlyEquals(ret));
+ QCOMPARE(ret.toString(), QString::fromLatin1("Error: foo"));
+}
+
+void tst_QScriptContext::throwErrorWithTypeInGlobalContext_data()
+{
+ QTest::addColumn<QScriptContext::Error>("error");
+ QTest::addColumn<QString>("stringRepresentation");
+ QTest::newRow("ReferenceError") << QScriptContext::ReferenceError << QString::fromLatin1("ReferenceError: foo");
+ QTest::newRow("SyntaxError") << QScriptContext::SyntaxError << QString::fromLatin1("SyntaxError: foo");
+ QTest::newRow("TypeError") << QScriptContext::TypeError << QString::fromLatin1("TypeError: foo");
+ QTest::newRow("RangeError") << QScriptContext::RangeError << QString::fromLatin1("RangeError: foo");
+ QTest::newRow("URIError") << QScriptContext::URIError << QString::fromLatin1("URIError: foo");
+ QTest::newRow("UnknownError") << QScriptContext::UnknownError << QString::fromLatin1("Error: foo");
+}
+
+void tst_QScriptContext::throwErrorWithTypeInGlobalContext()
+{
+ QFETCH(QScriptContext::Error, error);
+ QFETCH(QString, stringRepresentation);
+ QScriptEngine eng;
+ QScriptValue ret = eng.currentContext()->throwError(error, "foo");
+ QVERIFY(ret.isError());
+ QVERIFY(eng.hasUncaughtException());
+ QVERIFY(eng.uncaughtException().strictlyEquals(ret));
+ QCOMPARE(ret.toString(), stringRepresentation);
+}
+
+void tst_QScriptContext::throwValueInGlobalContext()
+{
+ QScriptEngine eng;
+ QScriptValue val(&eng, 123);
+ QScriptValue ret = eng.currentContext()->throwValue(val);
+ QVERIFY(ret.strictlyEquals(val));
+ QVERIFY(eng.hasUncaughtException());
+ QVERIFY(eng.uncaughtException().strictlyEquals(val));
+}
+
QTEST_MAIN(tst_QScriptContext)
#include "tst_qscriptcontext.moc"