1 /****************************************************************************
3 ** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
4 ** All rights reserved.
5 ** Contact: Nokia Corporation (qt-info@nokia.com)
7 ** This file is part of the test suite of the Qt Toolkit.
9 ** $QT_BEGIN_LICENSE:LGPL$
10 ** No Commercial Usage
11 ** This file contains pre-release code and may not be distributed.
12 ** You may use this file in accordance with the terms and conditions
13 ** contained in the Technology Preview License Agreement accompanying
16 ** GNU Lesser General Public License Usage
17 ** Alternatively, this file may be used under the terms of the GNU Lesser
18 ** General Public License version 2.1 as published by the Free Software
19 ** Foundation and appearing in the file LICENSE.LGPL included in the
20 ** packaging of this file. Please review the following information to
21 ** ensure the GNU Lesser General Public License version 2.1 requirements
22 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
24 ** In addition, as a special exception, Nokia gives you certain additional
25 ** rights. These rights are described in the Nokia Qt LGPL Exception
26 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
28 ** If you have questions regarding the use of this file, please contact
29 ** Nokia at qt-info@nokia.com.
40 ****************************************************************************/
43 #include <QtTest/QtTest>
45 #include <QtScript/qscriptcontext.h>
46 #include <QtScript/qscriptengine.h>
51 Q_DECLARE_METATYPE(QScriptValueList)
54 extern bool qt_script_isJITEnabled();
57 class tst_QScriptContext : public QObject
63 virtual ~tst_QScriptContext();
72 void evaluateInFunction();
73 void pushAndPopContext();
75 void backtrace_data();
78 void pushAndPopScope();
79 void getSetActivationObject();
80 void inheritActivationAndThisObject();
82 void calledAsConstructor();
83 void argumentsObjectInNative();
84 void jsActivationObject();
85 void qobjectAsActivationObject();
86 void parentContextCallee_QT2270();
87 void popNativeContextScope();
90 tst_QScriptContext::tst_QScriptContext()
94 tst_QScriptContext::~tst_QScriptContext()
98 static QScriptValue get_callee(QScriptContext *ctx, QScriptEngine *)
100 return ctx->callee();
103 static QScriptValue store_callee_and_return_primitive(QScriptContext *ctx, QScriptEngine *eng)
105 ctx->thisObject().setProperty("callee", ctx->callee());
106 return QScriptValue(eng, 123);
109 void tst_QScriptContext::callee()
114 QScriptValue fun = eng.newFunction(get_callee);
115 fun.setProperty("foo", QScriptValue(&eng, "bar"));
116 eng.globalObject().setProperty("get_callee", fun);
118 QScriptValue result = eng.evaluate("get_callee()");
119 QCOMPARE(result.isFunction(), true);
120 QCOMPARE(result.property("foo").toString(), QString("bar"));
123 // callee when toPrimitive() is called internally
125 QScriptValue fun = eng.newFunction(store_callee_and_return_primitive);
126 QScriptValue obj = eng.newObject();
127 obj.setProperty("toString", fun);
128 QVERIFY(!obj.property("callee").isValid());
129 (void)obj.toString();
130 QVERIFY(obj.property("callee").isFunction());
131 QVERIFY(obj.property("callee").strictlyEquals(fun));
133 obj.setProperty("callee", QScriptValue());
134 QVERIFY(!obj.property("callee").isValid());
135 obj.setProperty("valueOf", fun);
136 (void)obj.toNumber();
137 QVERIFY(obj.property("callee").isFunction());
138 QVERIFY(obj.property("callee").strictlyEquals(fun));
142 static QScriptValue get_arguments(QScriptContext *ctx, QScriptEngine *eng)
144 QScriptValue array = eng->newArray();
145 for (int i = 0; i < ctx->argumentCount(); ++i)
146 array.setProperty(QString::number(i), ctx->argument(i));
150 static QScriptValue get_argumentsObject(QScriptContext *ctx, QScriptEngine *)
152 return ctx->argumentsObject();
155 void tst_QScriptContext::arguments()
160 QScriptValue args = eng.currentContext()->argumentsObject();
161 QVERIFY(args.isObject());
162 QCOMPARE(args.property("length").toInt32(), 0);
165 QScriptValue fun = eng.newFunction(get_arguments);
166 eng.globalObject().setProperty("get_arguments", fun);
169 for (int x = 0; x < 2; ++x) {
176 QScriptValue result = eng.evaluate(prefix+"get_arguments()");
177 QCOMPARE(result.isArray(), true);
178 QCOMPARE(result.property("length").toUInt32(), quint32(0));
182 QScriptValue result = eng.evaluate(prefix+"get_arguments(123)");
183 QCOMPARE(result.isArray(), true);
184 QCOMPARE(result.property("length").toUInt32(), quint32(1));
185 QCOMPARE(result.property("0").isNumber(), true);
186 QCOMPARE(result.property("0").toNumber(), 123.0);
190 QScriptValue result = eng.evaluate(prefix+"get_arguments(\"ciao\", null, true, undefined)");
191 QCOMPARE(result.isArray(), true);
192 QCOMPARE(result.property("length").toUInt32(), quint32(4));
193 QCOMPARE(result.property("0").isString(), true);
194 QCOMPARE(result.property("0").toString(), QString("ciao"));
195 QCOMPARE(result.property("1").isNull(), true);
196 QCOMPARE(result.property("2").isBoolean(), true);
197 QCOMPARE(result.property("2").toBoolean(), true);
198 QCOMPARE(result.property("3").isUndefined(), true);
202 QScriptValue fun = eng.newFunction(get_argumentsObject);
203 eng.globalObject().setProperty("get_argumentsObject", fun);
207 QScriptValue fun = eng.evaluate("get_argumentsObject");
208 QCOMPARE(fun.isFunction(), true);
209 QScriptValue result = eng.evaluate(prefix+"get_argumentsObject()");
210 QCOMPARE(result.isArray(), false);
211 QVERIFY(result.isObject());
212 QCOMPARE(result.property("length").toUInt32(), quint32(0));
213 QCOMPARE(result.propertyFlags("length"), QScriptValue::SkipInEnumeration);
214 QCOMPARE(result.property("callee").strictlyEquals(fun), true);
215 QCOMPARE(result.propertyFlags("callee"), QScriptValue::SkipInEnumeration);
216 QScriptValue replacedCallee(&eng, 123);
217 result.setProperty("callee", replacedCallee);
218 QVERIFY(result.property("callee").equals(replacedCallee));
219 QScriptValue replacedLength(&eng, 456);
220 result.setProperty("length", replacedLength);
221 QVERIFY(result.property("length").equals(replacedLength));
222 result.setProperty("callee", QScriptValue());
223 QVERIFY(!result.property("callee").isValid());
224 result.setProperty("length", QScriptValue());
225 QVERIFY(!result.property("length").isValid());
229 QScriptValue result = eng.evaluate(prefix+"get_argumentsObject(123)");
230 eng.evaluate("function nestedArg(x,y,z) { var w = get_argumentsObject('ABC' , x+y+z); return w; }");
231 QScriptValue result2 = eng.evaluate("nestedArg(1, 'a', 2)");
232 QCOMPARE(result.isArray(), false);
233 QVERIFY(result.isObject());
234 QCOMPARE(result.property("length").toUInt32(), quint32(1));
235 QCOMPARE(result.property("0").isNumber(), true);
236 QCOMPARE(result.property("0").toNumber(), 123.0);
237 QVERIFY(result2.isObject());
238 QCOMPARE(result2.property("length").toUInt32(), quint32(2));
239 QCOMPARE(result2.property("0").toString(), QString::fromLatin1("ABC"));
240 QCOMPARE(result2.property("1").toString(), QString::fromLatin1("1a2"));
244 QScriptValue result = eng.evaluate(prefix+"get_argumentsObject(\"ciao\", null, true, undefined)");
245 QCOMPARE(result.isArray(), false);
246 QCOMPARE(result.property("length").toUInt32(), quint32(4));
247 QCOMPARE(result.property("0").isString(), true);
248 QCOMPARE(result.property("0").toString(), QString("ciao"));
249 QCOMPARE(result.property("1").isNull(), true);
250 QCOMPARE(result.property("2").isBoolean(), true);
251 QCOMPARE(result.property("2").toBoolean(), true);
252 QCOMPARE(result.property("3").isUndefined(), true);
255 // arguments object returned from script
257 QScriptValue result = eng.evaluate("(function() { return arguments; })(123)");
258 QCOMPARE(result.isArray(), false);
259 QVERIFY(result.isObject());
260 QCOMPARE(result.property("length").toUInt32(), quint32(1));
261 QCOMPARE(result.property("0").isNumber(), true);
262 QCOMPARE(result.property("0").toNumber(), 123.0);
266 QScriptValue result = eng.evaluate("(function() { return arguments; })('ciao', null, true, undefined)");
267 QCOMPARE(result.isArray(), false);
268 QCOMPARE(result.property("length").toUInt32(), quint32(4));
269 QCOMPARE(result.property("0").isString(), true);
270 QCOMPARE(result.property("0").toString(), QString("ciao"));
271 QCOMPARE(result.property("1").isNull(), true);
272 QCOMPARE(result.property("2").isBoolean(), true);
273 QCOMPARE(result.property("2").toBoolean(), true);
274 QCOMPARE(result.property("3").isUndefined(), true);
279 static QScriptValue get_thisObject(QScriptContext *ctx, QScriptEngine *)
281 return ctx->thisObject();
284 void tst_QScriptContext::thisObject()
288 QScriptValue fun = eng.newFunction(get_thisObject);
289 eng.globalObject().setProperty("get_thisObject", fun);
292 QScriptValue result = eng.evaluate("get_thisObject()");
293 QCOMPARE(result.isObject(), true);
294 QCOMPARE(result.toString(), QString("[object global]"));
298 QScriptValue result = eng.evaluate("get_thisObject.apply(new Number(123))");
299 QCOMPARE(result.isObject(), true);
300 QCOMPARE(result.toNumber(), 123.0);
304 QScriptValue obj = eng.newObject();
305 eng.currentContext()->setThisObject(obj);
306 QVERIFY(eng.currentContext()->thisObject().equals(obj));
307 eng.currentContext()->setThisObject(QScriptValue());
308 QVERIFY(eng.currentContext()->thisObject().equals(obj));
311 QScriptValue obj2 = eng2.newObject();
312 QTest::ignoreMessage(QtWarningMsg, "QScriptContext::setThisObject() failed: cannot set an object created in a different engine");
313 eng.currentContext()->setThisObject(obj2);
317 void tst_QScriptContext::returnValue()
319 QSKIP("Internal function not implemented in JSC-based back-end", SkipAll);
322 QCOMPARE(eng.currentContext()->returnValue().toNumber(), 123.0);
323 eng.evaluate("\"ciao\"");
324 QCOMPARE(eng.currentContext()->returnValue().toString(), QString("ciao"));
327 static QScriptValue throw_Error(QScriptContext *ctx, QScriptEngine *)
329 return ctx->throwError(QScriptContext::UnknownError, "foo");
332 static QScriptValue throw_TypeError(QScriptContext *ctx, QScriptEngine *)
334 return ctx->throwError(QScriptContext::TypeError, "foo");
337 static QScriptValue throw_ReferenceError(QScriptContext *ctx, QScriptEngine *)
339 return ctx->throwError(QScriptContext::ReferenceError, "foo");
342 static QScriptValue throw_SyntaxError(QScriptContext *ctx, QScriptEngine *)
344 return ctx->throwError(QScriptContext::SyntaxError, "foo");
347 static QScriptValue throw_RangeError(QScriptContext *ctx, QScriptEngine *)
349 return ctx->throwError(QScriptContext::RangeError, "foo");
352 static QScriptValue throw_URIError(QScriptContext *ctx, QScriptEngine *)
354 return ctx->throwError(QScriptContext::URIError, "foo");
357 static QScriptValue throw_ErrorAndReturnUndefined(QScriptContext *ctx, QScriptEngine *eng)
359 ctx->throwError(QScriptContext::UnknownError, "foo");
360 return eng->undefinedValue();
363 void tst_QScriptContext::throwError()
368 QScriptValue fun = eng.newFunction(throw_Error);
369 eng.globalObject().setProperty("throw_Error", fun);
370 QScriptValue result = eng.evaluate("throw_Error()");
371 QCOMPARE(eng.hasUncaughtException(), true);
372 QCOMPARE(result.isError(), true);
373 QCOMPARE(result.toString(), QString("Error: foo"));
377 QScriptValue fun = eng.newFunction(throw_TypeError);
378 eng.globalObject().setProperty("throw_TypeError", fun);
379 QScriptValue result = eng.evaluate("throw_TypeError()");
380 QCOMPARE(eng.hasUncaughtException(), true);
381 QCOMPARE(result.isError(), true);
382 QCOMPARE(result.toString(), QString("TypeError: foo"));
386 QScriptValue fun = eng.newFunction(throw_ReferenceError);
387 eng.globalObject().setProperty("throw_ReferenceError", fun);
388 QScriptValue result = eng.evaluate("throw_ReferenceError()");
389 QCOMPARE(eng.hasUncaughtException(), true);
390 QCOMPARE(result.isError(), true);
391 QCOMPARE(result.toString(), QString("ReferenceError: foo"));
395 QScriptValue fun = eng.newFunction(throw_SyntaxError);
396 eng.globalObject().setProperty("throw_SyntaxError", fun);
397 QScriptValue result = eng.evaluate("throw_SyntaxError()");
398 QCOMPARE(eng.hasUncaughtException(), true);
399 QCOMPARE(result.isError(), true);
400 QCOMPARE(result.toString(), QString("SyntaxError: foo"));
404 QScriptValue fun = eng.newFunction(throw_RangeError);
405 eng.globalObject().setProperty("throw_RangeError", fun);
406 QScriptValue result = eng.evaluate("throw_RangeError()");
407 QCOMPARE(eng.hasUncaughtException(), true);
408 QCOMPARE(result.isError(), true);
409 QCOMPARE(result.toString(), QString("RangeError: foo"));
413 QScriptValue fun = eng.newFunction(throw_URIError);
414 eng.globalObject().setProperty("throw_URIError", fun);
415 QScriptValue result = eng.evaluate("throw_URIError()");
416 QCOMPARE(eng.hasUncaughtException(), true);
417 QCOMPARE(result.isError(), true);
418 QCOMPARE(result.toString(), QString("URIError: foo"));
422 QScriptValue fun = eng.newFunction(throw_ErrorAndReturnUndefined);
423 eng.globalObject().setProperty("throw_ErrorAndReturnUndefined", fun);
424 QScriptValue result = eng.evaluate("throw_ErrorAndReturnUndefined()");
425 QVERIFY(eng.hasUncaughtException());
426 QVERIFY(result.isError());
427 QCOMPARE(result.toString(), QString("Error: foo"));
432 static QScriptValue throw_value(QScriptContext *ctx, QScriptEngine *)
434 return ctx->throwValue(ctx->argument(0));
437 void tst_QScriptContext::throwValue()
441 QScriptValue fun = eng.newFunction(throw_value);
442 eng.globalObject().setProperty("throw_value", fun);
445 QScriptValue result = eng.evaluate("throw_value(123)");
446 QCOMPARE(result.isError(), false);
447 QCOMPARE(result.toNumber(), 123.0);
448 QCOMPARE(eng.hasUncaughtException(), true);
452 static QScriptValue evaluate(QScriptContext *, QScriptEngine *eng)
454 return eng->evaluate("a = 123; a");
455 // return eng->evaluate("a");
458 void tst_QScriptContext::evaluateInFunction()
462 QScriptValue fun = eng.newFunction(evaluate);
463 eng.globalObject().setProperty("evaluate", fun);
465 QScriptValue result = eng.evaluate("evaluate()");
466 QCOMPARE(result.isError(), false);
467 QCOMPARE(result.isNumber(), true);
468 QCOMPARE(result.toNumber(), 123.0);
469 QCOMPARE(eng.hasUncaughtException(), false);
471 QCOMPARE(eng.evaluate("a").toNumber(), 123.0);
474 void tst_QScriptContext::pushAndPopContext()
477 QScriptContext *topLevel = eng.currentContext();
478 QCOMPARE(topLevel->engine(), &eng);
480 QScriptContext *ctx = eng.pushContext();
482 QCOMPARE(ctx->parentContext(), topLevel);
483 QCOMPARE(eng.currentContext(), ctx);
484 QCOMPARE(ctx->engine(), &eng);
485 QCOMPARE(ctx->state(), QScriptContext::NormalState);
486 QCOMPARE(ctx->isCalledAsConstructor(), false);
487 QCOMPARE(ctx->argumentCount(), 0);
488 QCOMPARE(ctx->argument(0).isUndefined(), true);
489 QVERIFY(!ctx->argument(-1).isValid());
490 QCOMPARE(ctx->argumentsObject().isObject(), true);
491 QCOMPARE(ctx->activationObject().isObject(), true);
492 QCOMPARE(ctx->callee().isValid(), false);
493 QCOMPARE(ctx->thisObject().strictlyEquals(eng.globalObject()), true);
494 QCOMPARE(ctx->scopeChain().size(), 2);
495 QVERIFY(ctx->scopeChain().at(0).equals(ctx->activationObject()));
496 QVERIFY(ctx->scopeChain().at(1).equals(eng.globalObject()));
498 QScriptContext *ctx2 = eng.pushContext();
499 QCOMPARE(ctx2->parentContext(), ctx);
500 QCOMPARE(eng.currentContext(), ctx2);
503 QCOMPARE(eng.currentContext(), ctx);
505 QCOMPARE(eng.currentContext(), topLevel);
507 // popping the top-level context is not allowed
508 QTest::ignoreMessage(QtWarningMsg, "QScriptEngine::popContext() doesn't match with pushContext()");
510 QCOMPARE(eng.currentContext(), topLevel);
513 QScriptContext *ctx3 = eng.pushContext();
514 ctx3->activationObject().setProperty("foo", QScriptValue(&eng, 123));
515 QVERIFY(eng.evaluate("foo").strictlyEquals(QScriptValue(&eng, 123)));
516 eng.evaluate("var bar = 'ciao'");
517 QVERIFY(ctx3->activationObject().property("bar", QScriptValue::ResolveLocal).strictlyEquals(QScriptValue(&eng, "ciao")));
522 QScriptContext *ctx4 = eng.pushContext();
523 QScriptValue obj = eng.newObject();
524 obj.setProperty("prop", QScriptValue(&eng, 456));
525 ctx4->setThisObject(obj);
526 QScriptValue ret = eng.evaluate("var tmp = this.prop; tmp + 1");
527 QCOMPARE(eng.currentContext(), ctx4);
528 QVERIFY(ret.strictlyEquals(QScriptValue(&eng, 457)));
532 // throwing an exception
534 QScriptContext *ctx5 = eng.pushContext();
535 QScriptValue ret = eng.evaluate("throw new Error('oops')");
536 QVERIFY(ret.isError());
537 QVERIFY(eng.hasUncaughtException());
538 QCOMPARE(eng.currentContext(), ctx5);
543 void tst_QScriptContext::popNativeContextScope()
546 QScriptContext *ctx = eng.pushContext();
547 QVERIFY(ctx->popScope().isObject()); // the activation object
549 QCOMPARE(ctx->scopeChain().size(), 1);
550 QVERIFY(ctx->scopeChain().at(0).strictlyEquals(eng.globalObject()));
551 // This was different in 4.5: scope and activation were decoupled
552 QVERIFY(ctx->activationObject().strictlyEquals(eng.globalObject()));
554 QVERIFY(!eng.evaluate("var foo = 123; function bar() {}").isError());
555 QVERIFY(eng.globalObject().property("foo").isNumber());
556 QVERIFY(eng.globalObject().property("bar").isFunction());
558 QScriptValue customScope = eng.newObject();
559 ctx->pushScope(customScope);
560 QCOMPARE(ctx->scopeChain().size(), 2);
561 QVERIFY(ctx->scopeChain().at(0).strictlyEquals(customScope));
562 QVERIFY(ctx->scopeChain().at(1).strictlyEquals(eng.globalObject()));
563 QVERIFY(ctx->activationObject().strictlyEquals(eng.globalObject()));
564 ctx->setActivationObject(customScope);
565 QVERIFY(ctx->activationObject().strictlyEquals(customScope));
566 QCOMPARE(ctx->scopeChain().size(), 2);
567 QVERIFY(ctx->scopeChain().at(0).strictlyEquals(customScope));
568 QEXPECT_FAIL("", "QTBUG-11012", Continue);
569 QVERIFY(ctx->scopeChain().at(1).strictlyEquals(eng.globalObject()));
571 QVERIFY(!eng.evaluate("baz = 456; var foo = 789; function barbar() {}").isError());
572 QEXPECT_FAIL("", "QTBUG-11012", Continue);
573 QVERIFY(eng.globalObject().property("baz").isNumber());
574 QVERIFY(customScope.property("foo").isNumber());
575 QVERIFY(customScope.property("barbar").isFunction());
577 QVERIFY(ctx->popScope().strictlyEquals(customScope));
578 QCOMPARE(ctx->scopeChain().size(), 1);
579 QEXPECT_FAIL("", "QTBUG-11012", Continue);
580 QVERIFY(ctx->scopeChain().at(0).strictlyEquals(eng.globalObject()));
582 // Need to push another object, otherwise we crash in popContext() (QTBUG-11012)
583 ctx->pushScope(customScope);
587 void tst_QScriptContext::lineNumber()
591 QScriptValue result = eng.evaluate("try { eval(\"foo = 123;\\n this[is{a{syntax|error@#$%@#% \"); } catch (e) { e.lineNumber; }", "foo.qs", 123);
592 QVERIFY(!eng.hasUncaughtException());
593 QVERIFY(result.isNumber());
594 QCOMPARE(result.toInt32(), 2);
596 result = eng.evaluate("foo = 123;\n bar = 42\n0 = 0");
597 QVERIFY(eng.hasUncaughtException());
598 QCOMPARE(eng.uncaughtExceptionLineNumber(), 3);
599 QCOMPARE(result.property("lineNumber").toInt32(), 3);
602 static QScriptValue getBacktrace(QScriptContext *ctx, QScriptEngine *eng)
604 return qScriptValueFromValue(eng, ctx->backtrace());
607 static QScriptValue custom_eval(QScriptContext *ctx, QScriptEngine *eng)
609 return eng->evaluate(ctx->argumentsObject().property(0).toString(), ctx->argumentsObject().property(1).toString());
612 static QScriptValue custom_call(QScriptContext *ctx, QScriptEngine *)
614 return ctx->argumentsObject().property(0).call(QScriptValue(), QScriptValueList() << ctx->argumentsObject().property(1));
617 static QScriptValue native_recurse(QScriptContext *ctx, QScriptEngine *eng)
619 QScriptValue func = ctx->argumentsObject().property(0);
620 QScriptValue n = ctx->argumentsObject().property(1);
622 if(n.toUInt32() <= 1) {
623 return func.call(QScriptValue(), QScriptValueList());
625 return eng->evaluate("native_recurse").call(QScriptValue(),
626 QScriptValueList() << func << QScriptValue(n.toUInt32() - 1));
630 void tst_QScriptContext::backtrace_data()
632 QTest::addColumn<QString>("code");
633 QTest::addColumn<QStringList>("expectedbacktrace");
640 "foo('hello', { })\n"
643 QStringList expected;
644 expected << "<native>(123) at -1"
645 << "foo('hello', [object Object]) at testfile:2"
646 << "<global>() at testfile:4";
649 QTest::newRow("simple") << source << expected;
653 QStringList expected;
654 QString source = QString(
655 "function foo(arg1 , arg2) {\n"
656 " return eval(\"%1\");\n"
658 "foo('hello', 456)\n"
660 ).arg("\\n \\n bt('hey'); \\n");
662 expected << "<native>('hey') at -1"
664 << QString::fromLatin1("foo(arg1 = 'hello', arg2 = 456) at testfile:%0")
665 // interpreter unfortunately doesn't provide line number for eval()
666 .arg(qt_script_isJITEnabled() ? 2 : -1);
668 << "<global>() at testfile:4";
670 QTest::newRow("eval") << source << expected;
675 "function bar(a) {\\n"
676 " return bt('m');\\n"
679 QString source = QString(
681 " return custom_eval(\"%1\", 'eval.js');\n"
686 QStringList expected;
687 expected << "<native>('m') at -1"
688 << "bar(a = 'b') at eval.js:2"
689 << "<eval>() at eval.js:4"
690 << QString("<native>('%1', 'eval.js') at -1").arg(eval_code.replace("\\n", "\n"))
691 << "foo() at testfile:2"
692 << "<global>() at testfile:4";
694 QTest::newRow("custom_eval") << source << expected;
697 QString f("function (a) {\n return bt(a); \n }");
698 QString source = QString(
699 "function foo(f) {\n"
705 QStringList expected;
706 expected << "<native>('b') at -1"
707 << "<anonymous>(a = 'b') at testfile:5"
708 << QString("foo(f = %1) at testfile:2").arg(f)
709 << "<global>() at testfile:6";
711 QTest::newRow("closure") << source << expected;
715 QStringList expected;
716 QString source = QString(
717 "var o = new Object;\n"
718 "o.foo = function plop() {\n"
719 " return eval(\"%1\");\n"
721 "o.foo('hello', 456)\n"
722 ).arg("\\n \\n bt('hey'); \\n");
724 expected << "<native>('hey') at -1"
726 << QString::fromLatin1("plop('hello', 456) at testfile:%0")
727 // interpreter unfortunately doesn't provide line number for eval()
728 .arg(qt_script_isJITEnabled() ? 3 : -1);
730 << "<global>() at testfile:5";
732 QTest::newRow("eval in member") << source << expected;
737 "function foo(a) {\n"
741 " var v = foo('arg', 4);\n"
744 "bar('hello', { });\n");
746 QStringList expected;
747 expected << "<native>(123) at -1"
748 << "foo(a = 'arg', 4) at testfile:2"
749 << "bar('hello', [object Object]) at testfile:5"
750 << "<global>() at testfile:8";
753 QTest::newRow("two function") << source << expected;
757 QString func("function foo(a, b) {\n"
761 QString source = func + QString::fromLatin1("\n"
762 "custom_call(foo, 'hello');\n"
765 QStringList expected;
766 expected << "<native>('hello') at -1"
767 << "foo(a = 'hello') at testfile:2"
768 << QString("<native>(%1, 'hello') at -1").arg(func)
769 << "<global>() at testfile:4";
771 QTest::newRow("call") << source << expected;
775 QString source = QString::fromLatin1("\n"
776 "custom_call(bt, 'hello_world');\n"
779 QStringList expected;
780 expected << "<native>('hello_world') at -1"
781 << "<native>(function () {\n [native code]\n}, 'hello_world') at -1"
782 << "<global>() at testfile:2";
784 QTest::newRow("call native") << source << expected;
788 QLatin1String func( "function f1() {\n"
789 " eval('var q = 4');\n"
790 " return custom_call(bt, 22);\n"
793 QString source = QString::fromLatin1("\n"
796 " return custom_call(func, 12);\n"
798 "f2();\n").arg(func);
800 QStringList expected;
801 expected << "<native>(22) at -1"
802 << "<native>(function () {\n [native code]\n}, 22) at -1"
803 << "f1(12) at testfile:5"
804 << QString::fromLatin1("<native>(%1, 12) at -1").arg(func)
805 << "f2() at testfile:7"
806 << "<global>() at testfile:9";
809 QTest::newRow("calls with closures") << source << expected;
813 QLatin1String func( "function js_bt() {\n"
817 QString source = QString::fromLatin1("\n"
820 " return native_recurse(js_bt, 12);\n"
824 QStringList expected;
825 expected << "<native>() at -1" << "js_bt() at testfile:3";
826 for(int n = 1; n <= 12; n++) {
827 expected << QString::fromLatin1("<native>(%1, %2) at -1")
830 expected << "f() at testfile:6";
831 expected << "<global>() at testfile:8";
833 QTest::newRow("native recursive") << source << expected;
837 QString source = QString::fromLatin1("\n"
838 "function finish() {\n"
841 "function rec(n) {\n"
843 " return finish();\n"
845 " return rec (n - 1);\n"
852 QStringList expected;
853 expected << "<native>() at -1" << "finish() at testfile:3";
854 for(int n = 1; n <= 12; n++) {
855 expected << QString::fromLatin1("rec(n = %1) at testfile:%2")
856 .arg(n).arg((n==1) ? 7 : 9);
858 expected << "f() at testfile:12";
859 expected << "<global>() at testfile:14";
861 QTest::newRow("js recursive") << source << expected;
865 QString source = QString::fromLatin1(
871 QStringList expected;
872 expected << "<native>() at -1"
873 << "<anonymous>(0, 0, 0) at testfile:3"
874 << "forEach(0) at -1"
875 << "<global>() at testfile:4";
876 QTest::newRow("js callback from built-in") << source << expected;
881 void tst_QScriptContext::backtrace()
883 QFETCH(QString, code);
884 QFETCH(QStringList, expectedbacktrace);
887 eng.globalObject().setProperty("bt", eng.newFunction(getBacktrace));
888 eng.globalObject().setProperty("custom_eval", eng.newFunction(custom_eval));
889 eng.globalObject().setProperty("custom_call", eng.newFunction(custom_call));
890 eng.globalObject().setProperty("native_recurse", eng.newFunction(native_recurse));
892 QString fileName = "testfile";
893 QScriptValue ret = eng.evaluate(code, fileName);
894 QVERIFY(!eng.hasUncaughtException());
895 QVERIFY(ret.isArray());
896 QStringList slist = qscriptvalue_cast<QStringList>(ret);
897 QCOMPARE(slist, expectedbacktrace);
900 static QScriptValue getScopeChain(QScriptContext *ctx, QScriptEngine *eng)
902 return qScriptValueFromValue(eng, ctx->parentContext()->scopeChain());
905 void tst_QScriptContext::scopeChain()
909 QScriptValueList ret = eng.currentContext()->scopeChain();
910 QCOMPARE(ret.size(), 1);
911 QVERIFY(ret.at(0).strictlyEquals(eng.globalObject()));
914 eng.globalObject().setProperty("getScopeChain", eng.newFunction(getScopeChain));
915 QScriptValueList ret = qscriptvalue_cast<QScriptValueList>(eng.evaluate("getScopeChain()"));
916 QCOMPARE(ret.size(), 1);
917 QVERIFY(ret.at(0).strictlyEquals(eng.globalObject()));
920 eng.evaluate("function foo() { function bar() { return getScopeChain(); } return bar() }");
921 QScriptValueList ret = qscriptvalue_cast<QScriptValueList>(eng.evaluate("foo()"));
922 QEXPECT_FAIL("", "Number of items in returned scope chain is incorrect", Abort);
923 QCOMPARE(ret.size(), 3);
924 QVERIFY(ret.at(2).strictlyEquals(eng.globalObject()));
925 QCOMPARE(ret.at(1).toString(), QString::fromLatin1("activation"));
926 QVERIFY(ret.at(1).property("arguments").isObject());
927 QCOMPARE(ret.at(0).toString(), QString::fromLatin1("activation"));
928 QVERIFY(ret.at(0).property("arguments").isObject());
931 QScriptValueList ret = qscriptvalue_cast<QScriptValueList>(eng.evaluate("o = { x: 123 }; with(o) getScopeChain();"));
932 QCOMPARE(ret.size(), 2);
933 QVERIFY(ret.at(1).strictlyEquals(eng.globalObject()));
934 QVERIFY(ret.at(0).isObject());
935 QCOMPARE(ret.at(0).property("x").toInt32(), 123);
938 QScriptValueList ret = qscriptvalue_cast<QScriptValueList>(
939 eng.evaluate("o1 = { x: 123}; o2 = { y: 456 }; with(o1) { with(o2) { getScopeChain(); } }"));
940 QCOMPARE(ret.size(), 3);
941 QVERIFY(ret.at(2).strictlyEquals(eng.globalObject()));
942 QVERIFY(ret.at(1).isObject());
943 QCOMPARE(ret.at(1).property("x").toInt32(), 123);
944 QVERIFY(ret.at(0).isObject());
945 QCOMPARE(ret.at(0).property("y").toInt32(), 456);
949 void tst_QScriptContext::pushAndPopScope()
952 QScriptContext *ctx = eng.currentContext();
953 QCOMPARE(ctx->scopeChain().size(), 1);
954 QVERIFY(ctx->scopeChain().at(0).strictlyEquals(eng.globalObject()));
956 QVERIFY(ctx->popScope().strictlyEquals(eng.globalObject()));
957 ctx->pushScope(eng.globalObject());
958 QCOMPARE(ctx->scopeChain().size(), 1);
959 QVERIFY(ctx->scopeChain().at(0).strictlyEquals(eng.globalObject()));
961 QScriptValue obj = eng.newObject();
963 QCOMPARE(ctx->scopeChain().size(), 2);
964 QVERIFY(ctx->scopeChain().at(0).strictlyEquals(obj));
965 QVERIFY(ctx->scopeChain().at(1).strictlyEquals(eng.globalObject()));
967 QVERIFY(ctx->popScope().strictlyEquals(obj));
968 QCOMPARE(ctx->scopeChain().size(), 1);
969 QVERIFY(ctx->scopeChain().at(0).strictlyEquals(eng.globalObject()));
972 QScriptValue ret = eng.evaluate("x");
973 QVERIFY(ret.isError());
974 eng.clearExceptions();
976 QCOMPARE(ctx->scopeChain().size(), 1);
977 QVERIFY(ctx->scopeChain().at(0).strictlyEquals(eng.globalObject()));
980 QScriptValue qobj = eng.newQObject(this, QScriptEngine::QtOwnership, QScriptEngine::AutoCreateDynamicProperties);
981 ctx->pushScope(qobj);
982 QCOMPARE(ctx->scopeChain().size(), 2);
983 QVERIFY(ctx->scopeChain().at(0).strictlyEquals(qobj));
984 QVERIFY(ctx->scopeChain().at(1).strictlyEquals(eng.globalObject()));
986 QScriptValue ret = eng.evaluate("print");
987 QVERIFY(ret.isFunction());
992 QCOMPARE(ctx->scopeChain().size(), 2);
993 QVERIFY(ctx->scopeChain().at(0).strictlyEquals(obj));
994 obj.setProperty("x", 123);
996 QScriptValue ret = eng.evaluate("x");
997 QVERIFY(ret.isNumber());
998 QCOMPARE(ret.toInt32(), 123);
1000 QVERIFY(ctx->popScope().strictlyEquals(obj));
1001 QCOMPARE(ctx->scopeChain().size(), 1);
1002 QVERIFY(ctx->scopeChain().at(0).strictlyEquals(eng.globalObject()));
1004 ctx->pushScope(QScriptValue());
1005 QCOMPARE(ctx->scopeChain().size(), 1);
1007 QVERIFY(ctx->popScope().strictlyEquals(eng.globalObject()));
1008 QVERIFY(ctx->scopeChain().isEmpty());
1010 // Used to work with old back-end, doesn't with new one because JSC requires that the last object in
1011 // a scope chain is the Global Object.
1012 QTest::ignoreMessage(QtWarningMsg, "QScriptContext::pushScope() failed: initial object in scope chain has to be the Global Object");
1013 ctx->pushScope(obj);
1014 QCOMPARE(ctx->scopeChain().size(), 0);
1017 QScriptValue obj2 = eng2.newObject();
1018 QTest::ignoreMessage(QtWarningMsg, "QScriptContext::pushScope() failed: cannot push an object created in a different engine");
1019 ctx->pushScope(obj2);
1020 QVERIFY(ctx->scopeChain().isEmpty());
1022 QVERIFY(!ctx->popScope().isValid());
1025 static QScriptValue get_activationObject(QScriptContext *ctx, QScriptEngine *)
1027 return ctx->activationObject();
1030 void tst_QScriptContext::getSetActivationObject()
1033 QScriptContext *ctx = eng.currentContext();
1034 QVERIFY(ctx->activationObject().equals(eng.globalObject()));
1036 ctx->setActivationObject(QScriptValue());
1037 QVERIFY(ctx->activationObject().equals(eng.globalObject()));
1038 QCOMPARE(ctx->engine(), &eng);
1040 QScriptValue obj = eng.newObject();
1041 ctx->setActivationObject(obj);
1042 QVERIFY(ctx->activationObject().equals(obj));
1043 QCOMPARE(ctx->scopeChain().size(), 1);
1044 QVERIFY(ctx->scopeChain().at(0).equals(obj));
1048 QScriptValue obj2 = eng2.newObject();
1049 QTest::ignoreMessage(QtWarningMsg, "QScriptContext::setActivationObject() failed: cannot set an object created in a different engine");
1050 QScriptValue was = ctx->activationObject();
1051 ctx->setActivationObject(obj2);
1052 QVERIFY(ctx->activationObject().equals(was));
1055 ctx->setActivationObject(eng.globalObject());
1056 QVERIFY(ctx->activationObject().equals(eng.globalObject()));
1057 QScriptValue fun = eng.newFunction(get_activationObject);
1058 eng.globalObject().setProperty("get_activationObject", fun);
1060 QScriptValue ret = eng.evaluate("get_activationObject(1, 2, 3)");
1061 QVERIFY(ret.isObject());
1062 QScriptValue arguments = ret.property("arguments");
1063 QEXPECT_FAIL("", "Getting arguments property of activation object doesn't work", Abort);
1064 QVERIFY(arguments.isObject());
1065 QCOMPARE(arguments.property("length").toInt32(), 3);
1066 QCOMPARE(arguments.property("0").toInt32(), 1);
1067 QCOMPARE(arguments.property("1").toInt32(), 1);
1068 QCOMPARE(arguments.property("2").toInt32(), 1);
1072 static QScriptValue myEval(QScriptContext *ctx, QScriptEngine *eng)
1074 QString code = ctx->argument(0).toString();
1075 ctx->setActivationObject(ctx->parentContext()->activationObject());
1076 ctx->setThisObject(ctx->parentContext()->thisObject());
1077 return eng->evaluate(code);
1080 void tst_QScriptContext::inheritActivationAndThisObject()
1083 eng.globalObject().setProperty("myEval", eng.newFunction(myEval));
1085 QScriptValue ret = eng.evaluate("var a = 123; myEval('a')");
1086 QVERIFY(ret.isNumber());
1087 QCOMPARE(ret.toInt32(), 123);
1090 QScriptValue ret = eng.evaluate("(function() { return myEval('this'); }).call(Number)");
1091 QVERIFY(ret.isFunction());
1092 QVERIFY(ret.equals(eng.globalObject().property("Number")));
1095 QScriptValue ret = eng.evaluate("(function(a) { return myEval('a'); })(123)");
1096 QVERIFY(ret.isNumber());
1097 QCOMPARE(ret.toInt32(), 123);
1102 eng.globalObject().setProperty("a", 123);
1103 QScriptValue ret = eng.evaluate("(function() { myEval('var a = 456'); return a; })()");
1104 QVERIFY(ret.isNumber());
1105 QCOMPARE(ret.toInt32(), 456);
1106 QEXPECT_FAIL("", "QT-2219: Wrong activation object is returned from native function's parent context", Continue);
1107 QVERIFY(eng.globalObject().property("a").strictlyEquals(123));
1111 static QScriptValue parentContextToString(QScriptContext *ctx, QScriptEngine *)
1113 return ctx->parentContext()->toString();
1116 void tst_QScriptContext::toString()
1119 eng.globalObject().setProperty("parentContextToString", eng.newFunction(parentContextToString));
1120 QScriptValue ret = eng.evaluate("function foo(first, second, third) {\n"
1121 " return parentContextToString();\n"
1122 "}; foo(1, 2, 3)", "script.qs");
1123 QVERIFY(ret.isString());
1124 QCOMPARE(ret.toString(), QString::fromLatin1("foo(first = 1, second = 2, third = 3) at script.qs:2"));
1127 static QScriptValue storeCalledAsConstructor(QScriptContext *ctx, QScriptEngine *eng)
1129 ctx->callee().setProperty("calledAsConstructor", ctx->isCalledAsConstructor());
1130 return eng->undefinedValue();
1133 static QScriptValue storeCalledAsConstructorV2(QScriptContext *ctx, QScriptEngine *eng, void *)
1135 ctx->callee().setProperty("calledAsConstructor", ctx->isCalledAsConstructor());
1136 return eng->undefinedValue();
1139 static QScriptValue storeCalledAsConstructorV3(QScriptContext *ctx, QScriptEngine *eng)
1141 ctx->callee().setProperty("calledAsConstructor", ctx->parentContext()->isCalledAsConstructor());
1142 return eng->undefinedValue();
1145 void tst_QScriptContext::calledAsConstructor()
1148 QScriptValue fun1 = eng.newFunction(storeCalledAsConstructor);
1151 QVERIFY(!fun1.property("calledAsConstructor").toBool());
1153 QVERIFY(fun1.property("calledAsConstructor").toBool());
1156 QScriptValue fun = eng.newFunction(storeCalledAsConstructorV2, (void*)0);
1158 QVERIFY(!fun.property("calledAsConstructor").toBool());
1160 QVERIFY(fun.property("calledAsConstructor").toBool());
1163 eng.globalObject().setProperty("fun1", fun1);
1164 eng.evaluate("fun1();");
1165 QVERIFY(!fun1.property("calledAsConstructor").toBool());
1166 eng.evaluate("new fun1();");
1167 QVERIFY(fun1.property("calledAsConstructor").toBool());
1170 QScriptValue fun3 = eng.newFunction(storeCalledAsConstructorV3);
1171 eng.globalObject().setProperty("fun3", fun3);
1172 eng.evaluate("function test() { fun3() }");
1173 eng.evaluate("test();");
1174 QVERIFY(!fun3.property("calledAsConstructor").toBool());
1175 eng.evaluate("new test();");
1176 if (qt_script_isJITEnabled())
1177 QEXPECT_FAIL("", "QTBUG-6132: calledAsConstructor is not correctly set for JS functions when JIT is enabled", Continue);
1178 QVERIFY(fun3.property("calledAsConstructor").toBool());
1183 static QScriptValue argumentsObjectInNative_test1(QScriptContext *ctx, QScriptEngine *eng)
1185 #define VERIFY(statement) \
1187 if (!QTest::qVerify((statement), #statement, "", __FILE__, __LINE__))\
1188 return QString::fromLatin1("Failed " #statement);\
1191 QScriptValue obj = ctx->argumentsObject();
1192 VERIFY(obj.isObject());
1193 VERIFY(obj.property(0).toUInt32() == 123);
1194 VERIFY(obj.property(1).toString() == QString::fromLatin1("456"));
1196 obj.setProperty(0, "abc");
1197 VERIFY(eng->evaluate("arguments[0]").toString() == QString::fromLatin1("abc") );
1199 return QString::fromLatin1("success");
1203 void tst_QScriptContext::argumentsObjectInNative()
1207 QScriptValue fun = eng.newFunction(argumentsObjectInNative_test1);
1208 QScriptValueList args;
1209 args << QScriptValue(&eng, 123.0);
1210 args << QScriptValue(&eng, QString::fromLatin1("456"));
1211 QScriptValue result = fun.call(eng.undefinedValue(), args);
1212 QVERIFY(!eng.hasUncaughtException());
1213 QCOMPARE(result.toString(), QString::fromLatin1("success"));
1217 QScriptValue fun = eng.newFunction(argumentsObjectInNative_test1);
1218 eng.globalObject().setProperty("func", fun);
1219 QScriptValue result = eng.evaluate("func(123.0 , 456);");
1220 QVERIFY(!eng.hasUncaughtException());
1221 QCOMPARE(result.toString(), QString::fromLatin1("success"));
1225 static QScriptValue get_jsActivationObject(QScriptContext *ctx, QScriptEngine *)
1227 return ctx->parentContext()->parentContext()->activationObject();
1230 void tst_QScriptContext::jsActivationObject()
1233 eng.globalObject().setProperty("get_jsActivationObject", eng.newFunction(get_jsActivationObject));
1234 eng.evaluate("function f1() { var w = get_jsActivationObject('arg1'); return w; }");
1235 eng.evaluate("function f2(x,y,z) { var v1 = 42;\n"
1236 // "function foo() {};\n" //this would avoid JSC to optimize
1237 "var v2 = f1(); return v2; }");
1238 eng.evaluate("function f3() { var v1 = 'nothing'; return f2(1,2,3); }");
1239 QScriptValue result1 = eng.evaluate("f2('hello', 'useless', 'world')");
1240 QScriptValue result2 = eng.evaluate("f3()");
1241 QVERIFY(result1.isObject());
1242 QEXPECT_FAIL("", "JSC optimize away the activation object", Abort);
1243 QCOMPARE(result1.property("v1").toInt32() , 42);
1244 QCOMPARE(result1.property("arguments").property(1).toString() , QString::fromLatin1("useless"));
1245 QVERIFY(result2.isObject());
1246 QCOMPARE(result2.property("v1").toInt32() , 42);
1247 QCOMPARE(result2.property("arguments").property(1).toString() , QString::fromLatin1("2"));
1250 void tst_QScriptContext::qobjectAsActivationObject()
1254 QScriptValue scriptObject = eng.newQObject(&object);
1255 QScriptContext *ctx = eng.pushContext();
1256 ctx->setActivationObject(scriptObject);
1257 QVERIFY(ctx->activationObject().equals(scriptObject));
1259 QVERIFY(!scriptObject.property("foo").isValid());
1260 eng.evaluate("function foo() { return 123; }");
1262 QScriptValue val = scriptObject.property("foo");
1263 QVERIFY(val.isValid());
1264 QVERIFY(val.isFunction());
1266 QVERIFY(!eng.globalObject().property("foo").isValid());
1268 QVERIFY(!scriptObject.property("bar").isValid());
1269 eng.evaluate("var bar = 123");
1271 QScriptValue val = scriptObject.property("bar");
1272 QVERIFY(val.isValid());
1273 QVERIFY(val.isNumber());
1274 QCOMPARE(val.toInt32(), 123);
1276 QVERIFY(!eng.globalObject().property("bar").isValid());
1279 QScriptValue val = eng.evaluate("delete foo");
1280 QVERIFY(val.isBool());
1281 QVERIFY(val.toBool());
1282 QVERIFY(!scriptObject.property("foo").isValid());
1286 static QScriptValue getParentContextCallee(QScriptContext *ctx, QScriptEngine *)
1288 return ctx->parentContext()->callee();
1291 void tst_QScriptContext::parentContextCallee_QT2270()
1293 QScriptEngine engine;
1294 engine.globalObject().setProperty("getParentContextCallee", engine.newFunction(getParentContextCallee));
1295 QScriptValue fun = engine.evaluate("(function() { return getParentContextCallee(); })");
1296 QVERIFY(fun.isFunction());
1297 QScriptValue callee = fun.call();
1298 QVERIFY(callee.equals(fun));
1301 QTEST_MAIN(tst_QScriptContext)
1302 #include "tst_qscriptcontext.moc"