tests/auto/qscriptcontext/tst_qscriptcontext.cpp
changeset 0 1918ee327afb
child 3 41300fa6a67c
equal deleted inserted replaced
-1:000000000000 0:1918ee327afb
       
     1 /****************************************************************************
       
     2 **
       
     3 ** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
       
     4 ** All rights reserved.
       
     5 ** Contact: Nokia Corporation (qt-info@nokia.com)
       
     6 **
       
     7 ** This file is part of the test suite of the Qt Toolkit.
       
     8 **
       
     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
       
    14 ** this package.
       
    15 **
       
    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.
       
    23 **
       
    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.
       
    27 **
       
    28 ** If you have questions regarding the use of this file, please contact
       
    29 ** Nokia at qt-info@nokia.com.
       
    30 **
       
    31 **
       
    32 **
       
    33 **
       
    34 **
       
    35 **
       
    36 **
       
    37 **
       
    38 ** $QT_END_LICENSE$
       
    39 **
       
    40 ****************************************************************************/
       
    41 
       
    42 
       
    43 #include <QtTest/QtTest>
       
    44 
       
    45 #include <QtScript/qscriptcontext.h>
       
    46 #include <QtScript/qscriptengine.h>
       
    47 
       
    48 //TESTED_CLASS=
       
    49 //TESTED_FILES=
       
    50 
       
    51 Q_DECLARE_METATYPE(QScriptValueList)
       
    52 
       
    53 QT_BEGIN_NAMESPACE
       
    54 extern bool qt_script_isJITEnabled();
       
    55 QT_END_NAMESPACE
       
    56 
       
    57 class tst_QScriptContext : public QObject
       
    58 {
       
    59     Q_OBJECT
       
    60 
       
    61 public:
       
    62     tst_QScriptContext();
       
    63     virtual ~tst_QScriptContext();
       
    64 
       
    65 private slots:
       
    66     void callee();
       
    67     void arguments();
       
    68     void thisObject();
       
    69     void returnValue();
       
    70     void throwError();
       
    71     void throwValue();
       
    72     void evaluateInFunction();
       
    73     void pushAndPopContext();
       
    74     void lineNumber();
       
    75     void backtrace_data();
       
    76     void backtrace();
       
    77     void scopeChain();
       
    78     void pushAndPopScope();
       
    79     void getSetActivationObject();
       
    80     void inheritActivationAndThisObject();
       
    81     void toString();
       
    82     void calledAsConstructor();
       
    83     void argumentsObjectInNative();
       
    84     void jsActivationObject();
       
    85     void qobjectAsActivationObject();
       
    86     void parentContextCallee_QT2270();
       
    87 };
       
    88 
       
    89 tst_QScriptContext::tst_QScriptContext()
       
    90 {
       
    91 }
       
    92 
       
    93 tst_QScriptContext::~tst_QScriptContext()
       
    94 {
       
    95 }
       
    96 
       
    97 static QScriptValue get_callee(QScriptContext *ctx, QScriptEngine *)
       
    98 {
       
    99     return ctx->callee();
       
   100 }
       
   101 
       
   102 static QScriptValue store_callee_and_return_primitive(QScriptContext *ctx, QScriptEngine *eng)
       
   103 {
       
   104     ctx->thisObject().setProperty("callee", ctx->callee());
       
   105     return QScriptValue(eng, 123);
       
   106 }
       
   107 
       
   108 void tst_QScriptContext::callee()
       
   109 {
       
   110     QScriptEngine eng;
       
   111 
       
   112     {
       
   113         QScriptValue fun = eng.newFunction(get_callee);
       
   114         fun.setProperty("foo", QScriptValue(&eng, "bar"));
       
   115         eng.globalObject().setProperty("get_callee", fun);
       
   116 
       
   117         QScriptValue result = eng.evaluate("get_callee()");
       
   118         QCOMPARE(result.isFunction(), true);
       
   119         QCOMPARE(result.property("foo").toString(), QString("bar"));
       
   120     }
       
   121 
       
   122     // callee when toPrimitive() is called internally
       
   123     {
       
   124         QScriptValue fun = eng.newFunction(store_callee_and_return_primitive);
       
   125         QScriptValue obj = eng.newObject();
       
   126         obj.setProperty("toString", fun);
       
   127         QVERIFY(!obj.property("callee").isValid());
       
   128         (void)obj.toString();
       
   129         QVERIFY(obj.property("callee").isFunction());
       
   130         QVERIFY(obj.property("callee").strictlyEquals(fun));
       
   131 
       
   132         obj.setProperty("callee", QScriptValue());
       
   133         QVERIFY(!obj.property("callee").isValid());
       
   134         obj.setProperty("valueOf", fun);
       
   135         (void)obj.toNumber();
       
   136         QVERIFY(obj.property("callee").isFunction());
       
   137         QVERIFY(obj.property("callee").strictlyEquals(fun));
       
   138     }
       
   139 }
       
   140 
       
   141 static QScriptValue get_arguments(QScriptContext *ctx, QScriptEngine *eng)
       
   142 {
       
   143     QScriptValue array = eng->newArray();
       
   144     for (int i = 0; i < ctx->argumentCount(); ++i)
       
   145         array.setProperty(QString::number(i), ctx->argument(i));
       
   146     return array;
       
   147 }
       
   148 
       
   149 static QScriptValue get_argumentsObject(QScriptContext *ctx, QScriptEngine *)
       
   150 {
       
   151     return ctx->argumentsObject();
       
   152 }
       
   153 
       
   154 void tst_QScriptContext::arguments()
       
   155 {
       
   156     QScriptEngine eng;
       
   157 
       
   158     {
       
   159         QScriptValue args = eng.currentContext()->argumentsObject();
       
   160         QVERIFY(args.isObject());
       
   161         QCOMPARE(args.property("length").toInt32(), 0);
       
   162     }
       
   163     {
       
   164         QScriptValue fun = eng.newFunction(get_arguments);
       
   165         eng.globalObject().setProperty("get_arguments", fun);
       
   166     }
       
   167 
       
   168     for (int x = 0; x < 2; ++x) {
       
   169         QString prefix;
       
   170         if (x == 0)
       
   171             prefix = "";
       
   172         else
       
   173             prefix = "new ";
       
   174         {
       
   175             QScriptValue result = eng.evaluate(prefix+"get_arguments()");
       
   176             QCOMPARE(result.isArray(), true);
       
   177             QCOMPARE(result.property("length").toUInt32(), quint32(0));
       
   178         }
       
   179 
       
   180         {
       
   181             QScriptValue result = eng.evaluate(prefix+"get_arguments(123)");
       
   182             QCOMPARE(result.isArray(), true);
       
   183             QCOMPARE(result.property("length").toUInt32(), quint32(1));
       
   184             QCOMPARE(result.property("0").isNumber(), true);
       
   185             QCOMPARE(result.property("0").toNumber(), 123.0);
       
   186         }
       
   187 
       
   188         {
       
   189             QScriptValue result = eng.evaluate(prefix+"get_arguments(\"ciao\", null, true, undefined)");
       
   190             QCOMPARE(result.isArray(), true);
       
   191             QCOMPARE(result.property("length").toUInt32(), quint32(4));
       
   192             QCOMPARE(result.property("0").isString(), true);
       
   193             QCOMPARE(result.property("0").toString(), QString("ciao"));
       
   194             QCOMPARE(result.property("1").isNull(), true);
       
   195             QCOMPARE(result.property("2").isBoolean(), true);
       
   196             QCOMPARE(result.property("2").toBoolean(), true);
       
   197             QCOMPARE(result.property("3").isUndefined(), true);
       
   198         }
       
   199 
       
   200         {
       
   201             QScriptValue fun = eng.newFunction(get_argumentsObject);
       
   202             eng.globalObject().setProperty("get_argumentsObject", fun);
       
   203         }
       
   204 
       
   205         {
       
   206             QScriptValue fun = eng.evaluate("get_argumentsObject");
       
   207             QCOMPARE(fun.isFunction(), true);
       
   208             QScriptValue result = eng.evaluate(prefix+"get_argumentsObject()");
       
   209             QCOMPARE(result.isArray(), false);
       
   210             QVERIFY(result.isObject());
       
   211             QCOMPARE(result.property("length").toUInt32(), quint32(0));
       
   212             QCOMPARE(result.propertyFlags("length"), QScriptValue::SkipInEnumeration);
       
   213             QCOMPARE(result.property("callee").strictlyEquals(fun), true);
       
   214             QCOMPARE(result.propertyFlags("callee"), QScriptValue::SkipInEnumeration);
       
   215             QScriptValue replacedCallee(&eng, 123);
       
   216             result.setProperty("callee", replacedCallee);
       
   217             QVERIFY(result.property("callee").equals(replacedCallee));
       
   218             QScriptValue replacedLength(&eng, 456);
       
   219             result.setProperty("length", replacedLength);
       
   220             QVERIFY(result.property("length").equals(replacedLength));
       
   221             result.setProperty("callee", QScriptValue());
       
   222             QVERIFY(!result.property("callee").isValid());
       
   223             result.setProperty("length", QScriptValue());
       
   224             QVERIFY(!result.property("length").isValid());
       
   225         }
       
   226 
       
   227         {
       
   228             QScriptValue result = eng.evaluate(prefix+"get_argumentsObject(123)");
       
   229             eng.evaluate("function nestedArg(x,y,z) { var w = get_argumentsObject('ABC' , x+y+z); return w; }");
       
   230             QScriptValue result2 = eng.evaluate("nestedArg(1, 'a', 2)");
       
   231             QCOMPARE(result.isArray(), false);
       
   232             QVERIFY(result.isObject());
       
   233             QCOMPARE(result.property("length").toUInt32(), quint32(1));
       
   234             QCOMPARE(result.property("0").isNumber(), true);
       
   235             QCOMPARE(result.property("0").toNumber(), 123.0);
       
   236             QVERIFY(result2.isObject());
       
   237             QCOMPARE(result2.property("length").toUInt32(), quint32(2));
       
   238             QCOMPARE(result2.property("0").toString(), QString::fromLatin1("ABC"));
       
   239             QCOMPARE(result2.property("1").toString(), QString::fromLatin1("1a2"));
       
   240         }
       
   241 
       
   242         {
       
   243             QScriptValue result = eng.evaluate(prefix+"get_argumentsObject(\"ciao\", null, true, undefined)");
       
   244             QCOMPARE(result.isArray(), false);
       
   245             QCOMPARE(result.property("length").toUInt32(), quint32(4));
       
   246             QCOMPARE(result.property("0").isString(), true);
       
   247             QCOMPARE(result.property("0").toString(), QString("ciao"));
       
   248             QCOMPARE(result.property("1").isNull(), true);
       
   249             QCOMPARE(result.property("2").isBoolean(), true);
       
   250             QCOMPARE(result.property("2").toBoolean(), true);
       
   251             QCOMPARE(result.property("3").isUndefined(), true);
       
   252         }
       
   253 
       
   254         // arguments object returned from script
       
   255         {
       
   256             QScriptValue result = eng.evaluate("(function() { return arguments; })(123)");
       
   257             QCOMPARE(result.isArray(), false);
       
   258             QVERIFY(result.isObject());
       
   259             QCOMPARE(result.property("length").toUInt32(), quint32(1));
       
   260             QCOMPARE(result.property("0").isNumber(), true);
       
   261             QCOMPARE(result.property("0").toNumber(), 123.0);
       
   262         }
       
   263 
       
   264         {
       
   265             QScriptValue result = eng.evaluate("(function() { return arguments; })('ciao', null, true, undefined)");
       
   266             QCOMPARE(result.isArray(), false);
       
   267             QCOMPARE(result.property("length").toUInt32(), quint32(4));
       
   268             QCOMPARE(result.property("0").isString(), true);
       
   269             QCOMPARE(result.property("0").toString(), QString("ciao"));
       
   270             QCOMPARE(result.property("1").isNull(), true);
       
   271             QCOMPARE(result.property("2").isBoolean(), true);
       
   272             QCOMPARE(result.property("2").toBoolean(), true);
       
   273             QCOMPARE(result.property("3").isUndefined(), true);
       
   274         }
       
   275     }
       
   276 }
       
   277 
       
   278 static QScriptValue get_thisObject(QScriptContext *ctx, QScriptEngine *)
       
   279 {
       
   280     return ctx->thisObject();
       
   281 }
       
   282 
       
   283 void tst_QScriptContext::thisObject()
       
   284 {
       
   285     QScriptEngine eng;
       
   286 
       
   287     QScriptValue fun = eng.newFunction(get_thisObject);
       
   288     eng.globalObject().setProperty("get_thisObject", fun);
       
   289 
       
   290     {
       
   291         QScriptValue result = eng.evaluate("get_thisObject()");
       
   292         QCOMPARE(result.isObject(), true);
       
   293         QCOMPARE(result.toString(), QString("[object global]"));
       
   294     }
       
   295 
       
   296     {
       
   297         QScriptValue result = eng.evaluate("get_thisObject.apply(new Number(123))");
       
   298         QCOMPARE(result.isObject(), true);
       
   299         QCOMPARE(result.toNumber(), 123.0);
       
   300     }
       
   301 
       
   302     {
       
   303         QScriptValue obj = eng.newObject();
       
   304         eng.currentContext()->setThisObject(obj);
       
   305         QVERIFY(eng.currentContext()->thisObject().equals(obj));
       
   306         eng.currentContext()->setThisObject(QScriptValue());
       
   307         QVERIFY(eng.currentContext()->thisObject().equals(obj));
       
   308 
       
   309         QScriptEngine eng2;
       
   310         QScriptValue obj2 = eng2.newObject();
       
   311         QTest::ignoreMessage(QtWarningMsg, "QScriptContext::setThisObject() failed: cannot set an object created in a different engine");
       
   312         eng.currentContext()->setThisObject(obj2);
       
   313     }
       
   314 }
       
   315 
       
   316 void tst_QScriptContext::returnValue()
       
   317 {
       
   318     QSKIP("Internal function not implemented in JSC-based back-end", SkipAll);
       
   319     QScriptEngine eng;
       
   320     eng.evaluate("123");
       
   321     QCOMPARE(eng.currentContext()->returnValue().toNumber(), 123.0);
       
   322     eng.evaluate("\"ciao\"");
       
   323     QCOMPARE(eng.currentContext()->returnValue().toString(), QString("ciao"));
       
   324 }
       
   325 
       
   326 static QScriptValue throw_Error(QScriptContext *ctx, QScriptEngine *)
       
   327 {
       
   328     return ctx->throwError(QScriptContext::UnknownError, "foo");
       
   329 }
       
   330 
       
   331 static QScriptValue throw_TypeError(QScriptContext *ctx, QScriptEngine *)
       
   332 {
       
   333     return ctx->throwError(QScriptContext::TypeError, "foo");
       
   334 }
       
   335 
       
   336 static QScriptValue throw_ReferenceError(QScriptContext *ctx, QScriptEngine *)
       
   337 {
       
   338     return ctx->throwError(QScriptContext::ReferenceError, "foo");
       
   339 }
       
   340 
       
   341 static QScriptValue throw_SyntaxError(QScriptContext *ctx, QScriptEngine *)
       
   342 {
       
   343     return ctx->throwError(QScriptContext::SyntaxError, "foo");
       
   344 }
       
   345 
       
   346 static QScriptValue throw_RangeError(QScriptContext *ctx, QScriptEngine *)
       
   347 {
       
   348     return ctx->throwError(QScriptContext::RangeError, "foo");
       
   349 }
       
   350 
       
   351 static QScriptValue throw_URIError(QScriptContext *ctx, QScriptEngine *)
       
   352 {
       
   353     return ctx->throwError(QScriptContext::URIError, "foo");
       
   354 }
       
   355 
       
   356 static QScriptValue throw_ErrorAndReturnUndefined(QScriptContext *ctx, QScriptEngine *eng)
       
   357 {
       
   358     ctx->throwError(QScriptContext::UnknownError, "foo");
       
   359     return eng->undefinedValue();
       
   360 }
       
   361 
       
   362 void tst_QScriptContext::throwError()
       
   363 {
       
   364     QScriptEngine eng;
       
   365 
       
   366     {
       
   367         QScriptValue fun = eng.newFunction(throw_Error);
       
   368         eng.globalObject().setProperty("throw_Error", fun);
       
   369         QScriptValue result = eng.evaluate("throw_Error()");
       
   370         QCOMPARE(eng.hasUncaughtException(), true);
       
   371         QCOMPARE(result.isError(), true);
       
   372         QCOMPARE(result.toString(), QString("Error: foo"));
       
   373     }
       
   374 
       
   375     {
       
   376         QScriptValue fun = eng.newFunction(throw_TypeError);
       
   377         eng.globalObject().setProperty("throw_TypeError", fun);
       
   378         QScriptValue result = eng.evaluate("throw_TypeError()");
       
   379         QCOMPARE(eng.hasUncaughtException(), true);
       
   380         QCOMPARE(result.isError(), true);
       
   381         QCOMPARE(result.toString(), QString("TypeError: foo"));
       
   382     }
       
   383 
       
   384     {
       
   385         QScriptValue fun = eng.newFunction(throw_ReferenceError);
       
   386         eng.globalObject().setProperty("throw_ReferenceError", fun);
       
   387         QScriptValue result = eng.evaluate("throw_ReferenceError()");
       
   388         QCOMPARE(eng.hasUncaughtException(), true);
       
   389         QCOMPARE(result.isError(), true);
       
   390         QCOMPARE(result.toString(), QString("ReferenceError: foo"));
       
   391     }
       
   392 
       
   393     {
       
   394         QScriptValue fun = eng.newFunction(throw_SyntaxError);
       
   395         eng.globalObject().setProperty("throw_SyntaxError", fun);
       
   396         QScriptValue result = eng.evaluate("throw_SyntaxError()");
       
   397         QCOMPARE(eng.hasUncaughtException(), true);
       
   398         QCOMPARE(result.isError(), true);
       
   399         QCOMPARE(result.toString(), QString("SyntaxError: foo"));
       
   400     }
       
   401 
       
   402     {
       
   403         QScriptValue fun = eng.newFunction(throw_RangeError);
       
   404         eng.globalObject().setProperty("throw_RangeError", fun);
       
   405         QScriptValue result = eng.evaluate("throw_RangeError()");
       
   406         QCOMPARE(eng.hasUncaughtException(), true);
       
   407         QCOMPARE(result.isError(), true);
       
   408         QCOMPARE(result.toString(), QString("RangeError: foo"));
       
   409     }
       
   410 
       
   411     {
       
   412         QScriptValue fun = eng.newFunction(throw_URIError);
       
   413         eng.globalObject().setProperty("throw_URIError", fun);
       
   414         QScriptValue result = eng.evaluate("throw_URIError()");
       
   415         QCOMPARE(eng.hasUncaughtException(), true);
       
   416         QCOMPARE(result.isError(), true);
       
   417         QCOMPARE(result.toString(), QString("URIError: foo"));
       
   418     }
       
   419 
       
   420     {
       
   421         QScriptValue fun = eng.newFunction(throw_ErrorAndReturnUndefined);
       
   422         eng.globalObject().setProperty("throw_ErrorAndReturnUndefined", fun);
       
   423         QScriptValue result = eng.evaluate("throw_ErrorAndReturnUndefined()");
       
   424         QVERIFY(eng.hasUncaughtException());
       
   425         QVERIFY(result.isError());
       
   426         QCOMPARE(result.toString(), QString("Error: foo"));
       
   427     }
       
   428 
       
   429 }
       
   430 
       
   431 static QScriptValue throw_value(QScriptContext *ctx, QScriptEngine *)
       
   432 {
       
   433     return ctx->throwValue(ctx->argument(0));
       
   434 }
       
   435 
       
   436 void tst_QScriptContext::throwValue()
       
   437 {
       
   438     QScriptEngine eng;
       
   439 
       
   440     QScriptValue fun = eng.newFunction(throw_value);
       
   441     eng.globalObject().setProperty("throw_value", fun);
       
   442 
       
   443     {
       
   444         QScriptValue result = eng.evaluate("throw_value(123)");
       
   445         QCOMPARE(result.isError(), false);
       
   446         QCOMPARE(result.toNumber(), 123.0);
       
   447         QCOMPARE(eng.hasUncaughtException(), true);
       
   448     }
       
   449 }
       
   450 
       
   451 static QScriptValue evaluate(QScriptContext *, QScriptEngine *eng)
       
   452 {
       
   453     return eng->evaluate("a = 123; a");
       
   454 //    return eng->evaluate("a");
       
   455 }
       
   456 
       
   457 void tst_QScriptContext::evaluateInFunction()
       
   458 {
       
   459     QScriptEngine eng;
       
   460 
       
   461     QScriptValue fun = eng.newFunction(evaluate);
       
   462     eng.globalObject().setProperty("evaluate", fun);
       
   463 
       
   464     QScriptValue result = eng.evaluate("evaluate()");
       
   465     QCOMPARE(result.isError(), false);
       
   466     QCOMPARE(result.isNumber(), true);
       
   467     QCOMPARE(result.toNumber(), 123.0);
       
   468     QCOMPARE(eng.hasUncaughtException(), false);
       
   469 
       
   470     QCOMPARE(eng.evaluate("a").toNumber(), 123.0);
       
   471 }
       
   472 
       
   473 void tst_QScriptContext::pushAndPopContext()
       
   474 {
       
   475     QScriptEngine eng;
       
   476     QScriptContext *topLevel = eng.currentContext();
       
   477     QCOMPARE(topLevel->engine(), &eng);
       
   478 
       
   479     QScriptContext *ctx = eng.pushContext();
       
   480     QVERIFY(ctx != 0);
       
   481     QCOMPARE(ctx->parentContext(), topLevel);
       
   482     QCOMPARE(eng.currentContext(), ctx);
       
   483     QCOMPARE(ctx->engine(), &eng);
       
   484     QCOMPARE(ctx->state(), QScriptContext::NormalState);
       
   485     QCOMPARE(ctx->isCalledAsConstructor(), false);
       
   486     QCOMPARE(ctx->argumentCount(), 0);
       
   487     QCOMPARE(ctx->argument(0).isUndefined(), true);
       
   488     QVERIFY(!ctx->argument(-1).isValid());
       
   489     QCOMPARE(ctx->argumentsObject().isObject(), true);
       
   490     QCOMPARE(ctx->activationObject().isObject(), true);
       
   491     QCOMPARE(ctx->callee().isValid(), false);
       
   492     QCOMPARE(ctx->thisObject().strictlyEquals(eng.globalObject()), true);
       
   493     QCOMPARE(ctx->scopeChain().size(), 2);
       
   494     QVERIFY(ctx->scopeChain().at(0).equals(ctx->activationObject()));
       
   495     QVERIFY(ctx->scopeChain().at(1).equals(eng.globalObject()));
       
   496 
       
   497     QScriptContext *ctx2 = eng.pushContext();
       
   498     QCOMPARE(ctx2->parentContext(), ctx);
       
   499     QCOMPARE(eng.currentContext(), ctx2);
       
   500 
       
   501     eng.popContext();
       
   502     QCOMPARE(eng.currentContext(), ctx);
       
   503     eng.popContext();
       
   504     QCOMPARE(eng.currentContext(), topLevel);
       
   505 
       
   506     // popping the top-level context is not allowed
       
   507     QTest::ignoreMessage(QtWarningMsg, "QScriptEngine::popContext() doesn't match with pushContext()");
       
   508     eng.popContext();
       
   509     QCOMPARE(eng.currentContext(), topLevel);
       
   510 
       
   511     {
       
   512         QScriptContext *ctx3 = eng.pushContext();
       
   513         ctx3->activationObject().setProperty("foo", QScriptValue(&eng, 123));
       
   514         QVERIFY(eng.evaluate("foo").strictlyEquals(QScriptValue(&eng, 123)));
       
   515         eng.evaluate("var bar = 'ciao'");
       
   516         QVERIFY(ctx3->activationObject().property("bar", QScriptValue::ResolveLocal).strictlyEquals(QScriptValue(&eng, "ciao")));
       
   517         eng.popContext();
       
   518     }
       
   519 
       
   520     {
       
   521         QScriptContext *ctx4 = eng.pushContext();
       
   522         QScriptValue obj = eng.newObject();
       
   523         obj.setProperty("prop", QScriptValue(&eng, 456));
       
   524         ctx4->setThisObject(obj);
       
   525         QScriptValue ret = eng.evaluate("var tmp = this.prop; tmp + 1");
       
   526         QCOMPARE(eng.currentContext(), ctx4);
       
   527         QVERIFY(ret.strictlyEquals(QScriptValue(&eng, 457)));
       
   528         eng.popContext();
       
   529     }
       
   530 
       
   531     // throwing an exception
       
   532     {
       
   533         QScriptContext *ctx5 = eng.pushContext();
       
   534         QScriptValue ret = eng.evaluate("throw new Error('oops')");
       
   535         QVERIFY(ret.isError());
       
   536         QVERIFY(eng.hasUncaughtException());
       
   537         QCOMPARE(eng.currentContext(), ctx5);
       
   538         eng.popContext();
       
   539     }
       
   540 }
       
   541 
       
   542 void tst_QScriptContext::lineNumber()
       
   543 {
       
   544     QScriptEngine eng;
       
   545 
       
   546     QScriptValue result = eng.evaluate("try { eval(\"foo = 123;\\n this[is{a{syntax|error@#$%@#% \"); } catch (e) { e.lineNumber; }", "foo.qs", 123);
       
   547     QVERIFY(!eng.hasUncaughtException());
       
   548     QVERIFY(result.isNumber());
       
   549     QCOMPARE(result.toInt32(), 2);
       
   550 
       
   551     result = eng.evaluate("foo = 123;\n bar = 42\n0 = 0");
       
   552     QVERIFY(eng.hasUncaughtException());
       
   553     QCOMPARE(eng.uncaughtExceptionLineNumber(), 3);
       
   554     QCOMPARE(result.property("lineNumber").toInt32(), 3);
       
   555 }
       
   556 
       
   557 static QScriptValue getBacktrace(QScriptContext *ctx, QScriptEngine *eng)
       
   558 {
       
   559     return qScriptValueFromValue(eng, ctx->backtrace());
       
   560 }
       
   561 
       
   562 static QScriptValue custom_eval(QScriptContext *ctx, QScriptEngine *eng)
       
   563 {
       
   564     return eng->evaluate(ctx->argumentsObject().property(0).toString(), ctx->argumentsObject().property(1).toString());
       
   565 }
       
   566 
       
   567 static QScriptValue custom_call(QScriptContext *ctx, QScriptEngine *)
       
   568 {
       
   569     return ctx->argumentsObject().property(0).call(QScriptValue(), QScriptValueList() << ctx->argumentsObject().property(1));
       
   570 }
       
   571 
       
   572 static QScriptValue native_recurse(QScriptContext *ctx, QScriptEngine *eng)
       
   573 {
       
   574     QScriptValue func = ctx->argumentsObject().property(0);
       
   575     QScriptValue n = ctx->argumentsObject().property(1);
       
   576 
       
   577     if(n.toUInt32() <= 1) {
       
   578         return func.call(QScriptValue(), QScriptValueList());
       
   579     } else {
       
   580         return eng->evaluate("native_recurse").call(QScriptValue(),
       
   581                                                     QScriptValueList() << func << QScriptValue(n.toUInt32() - 1));
       
   582     }
       
   583 }
       
   584 
       
   585 void tst_QScriptContext::backtrace_data()
       
   586 {
       
   587     QTest::addColumn<QString>("code");
       
   588     QTest::addColumn<QStringList>("expectedbacktrace");
       
   589 
       
   590     {
       
   591         QString source(
       
   592                  "function foo() {\n"
       
   593                  "  return bt(123);\n"
       
   594                  "}\n"
       
   595                  "foo('hello', { })\n"
       
   596                  "var r = 0;");
       
   597 
       
   598         QStringList expected;
       
   599         expected << "<native>(123) at -1"
       
   600                  << "foo('hello', [object Object]) at testfile:2"
       
   601                  << "<global>() at testfile:4";
       
   602 
       
   603 
       
   604         QTest::newRow("simple") << source << expected;
       
   605     }
       
   606 
       
   607     {
       
   608         QStringList expected;
       
   609         QString source = QString(
       
   610             "function foo(arg1 , arg2) {\n"
       
   611             "  return eval(\"%1\");\n"
       
   612             "}\n"
       
   613             "foo('hello', 456)\n"
       
   614             "var a = 0;"
       
   615            ).arg("\\n \\n bt('hey'); \\n");
       
   616 
       
   617            expected << "<native>('hey') at -1"
       
   618                     << "<eval>() at 3"
       
   619                     << QString::fromLatin1("foo(arg1 = 'hello', arg2 = 456) at testfile:%0")
       
   620                // interpreter unfortunately doesn't provide line number for eval()
       
   621                .arg(qt_script_isJITEnabled() ? 2 : -1);
       
   622            expected
       
   623                     << "<global>() at testfile:4";
       
   624 
       
   625             QTest::newRow("eval") << source << expected;
       
   626     }
       
   627 
       
   628     {
       
   629         QString eval_code(
       
   630                 "function bar(a) {\\n"
       
   631                 "  return bt('m');\\n"
       
   632                 "}\\n"
       
   633                 "bar('b'); \\n");
       
   634         QString source = QString(
       
   635                 "function foo() {\n"
       
   636                 "  return custom_eval(\"%1\", 'eval.js');\n"
       
   637                 "}\n"
       
   638                 "foo()"
       
   639             ).arg(eval_code);
       
   640 
       
   641         QStringList expected;
       
   642         expected << "<native>('m') at -1"
       
   643                  << "bar(a = 'b') at eval.js:2"
       
   644                  << "<eval>() at eval.js:4"
       
   645                  << QString("<native>('%1', 'eval.js') at -1").arg(eval_code.replace("\\n", "\n"))
       
   646                  << "foo() at testfile:2"
       
   647                  << "<global>() at testfile:4";
       
   648 
       
   649         QTest::newRow("custom_eval") << source << expected;
       
   650     }
       
   651     {
       
   652         QString f("function (a) {\n return bt(a); \n  }");
       
   653         QString source = QString(
       
   654                 "function foo(f) {\n"
       
   655                 "  return f('b');\n"
       
   656                 "}\n"
       
   657                 "foo(%1)"
       
   658             ).arg(f);
       
   659 
       
   660         QStringList expected;
       
   661         expected << "<native>('b') at -1"
       
   662                  << "<anonymous>(a = 'b') at testfile:5"
       
   663                  << QString("foo(f = %1) at testfile:2").arg(f)
       
   664                  << "<global>() at testfile:6";
       
   665 
       
   666         QTest::newRow("closure") << source << expected;
       
   667     }
       
   668 
       
   669     {
       
   670         QStringList expected;
       
   671         QString source = QString(
       
   672             "var o = new Object;\n"
       
   673             "o.foo = function plop() {\n"
       
   674             "  return eval(\"%1\");\n"
       
   675             "}\n"
       
   676             "o.foo('hello', 456)\n"
       
   677            ).arg("\\n \\n bt('hey'); \\n");
       
   678 
       
   679            expected << "<native>('hey') at -1"
       
   680                     << "<eval>() at 3"
       
   681                     << QString::fromLatin1("plop('hello', 456) at testfile:%0")
       
   682                // interpreter unfortunately doesn't provide line number for eval()
       
   683                .arg(qt_script_isJITEnabled() ? 3 : -1);
       
   684            expected
       
   685                     << "<global>() at testfile:5";
       
   686 
       
   687             QTest::newRow("eval in member") << source << expected;
       
   688     }
       
   689 
       
   690     {
       
   691         QString source(
       
   692                  "function foo(a) {\n"
       
   693                  "  return bt(123);\n"
       
   694                  "}\n"
       
   695                  "function bar() {\n"
       
   696                  "  var v = foo('arg', 4);\n"
       
   697                  "  return v;\n"
       
   698                  "}\n"
       
   699                  "bar('hello', { });\n");
       
   700 
       
   701         QStringList expected;
       
   702         expected << "<native>(123) at -1"
       
   703                  << "foo(a = 'arg', 4) at testfile:2"
       
   704                  << "bar('hello', [object Object]) at testfile:5"
       
   705                  << "<global>() at testfile:8";
       
   706 
       
   707 
       
   708         QTest::newRow("two function") << source << expected;
       
   709     }
       
   710 
       
   711     {
       
   712         QString func("function foo(a, b) {\n"
       
   713                      "  return bt(a);\n"
       
   714                      "}");
       
   715 
       
   716         QString source = func + QString::fromLatin1("\n"
       
   717                  "custom_call(foo, 'hello');\n"
       
   718                  "var a = 1\n");
       
   719 
       
   720         QStringList expected;
       
   721         expected << "<native>('hello') at -1"
       
   722                  << "foo(a = 'hello') at testfile:2"
       
   723                  << QString("<native>(%1, 'hello') at -1").arg(func)
       
   724                  << "<global>() at testfile:4";
       
   725 
       
   726         QTest::newRow("call") << source << expected;
       
   727     }
       
   728 
       
   729     {
       
   730         QString source = QString::fromLatin1("\n"
       
   731         "custom_call(bt, 'hello_world');\n"
       
   732         "var a = 1\n");
       
   733 
       
   734         QStringList expected;
       
   735         expected << "<native>('hello_world') at -1"
       
   736         << "<native>(function () {\n    [native code]\n}, 'hello_world') at -1"
       
   737         << "<global>() at testfile:2";
       
   738 
       
   739         QTest::newRow("call native") << source << expected;
       
   740     }
       
   741 
       
   742     {
       
   743         QLatin1String func( "function f1() {\n"
       
   744             "    eval('var q = 4');\n"
       
   745             "    return custom_call(bt, 22);\n"
       
   746             "}");
       
   747 
       
   748         QString source = QString::fromLatin1("\n"
       
   749             "function f2() {\n"
       
   750             "    func = %1\n"
       
   751             "    return custom_call(func, 12);\n"
       
   752             "}\n"
       
   753             "f2();\n").arg(func);
       
   754 
       
   755         QStringList expected;
       
   756         expected << "<native>(22) at -1"
       
   757             << "<native>(function () {\n    [native code]\n}, 22) at -1"
       
   758             << "f1(12) at testfile:5"
       
   759             << QString::fromLatin1("<native>(%1, 12) at -1").arg(func)
       
   760             << "f2() at testfile:7"
       
   761             << "<global>() at testfile:9";
       
   762 
       
   763 
       
   764         QTest::newRow("calls with closures") << source << expected;
       
   765     }
       
   766 
       
   767     {
       
   768         QLatin1String func( "function js_bt() {\n"
       
   769             "    return bt();\n"
       
   770             "}");
       
   771 
       
   772         QString source = QString::fromLatin1("\n"
       
   773             "%1\n"
       
   774             "function f() {\n"
       
   775             "    return native_recurse(js_bt, 12);\n"
       
   776             "}\n"
       
   777             "f();\n").arg(func);
       
   778 
       
   779         QStringList expected;
       
   780         expected << "<native>() at -1" << "js_bt() at testfile:3";
       
   781         for(int n = 1; n <= 12; n++) {
       
   782             expected << QString::fromLatin1("<native>(%1, %2) at -1")
       
   783                 .arg(func).arg(n);
       
   784         }
       
   785         expected << "f() at testfile:6";
       
   786         expected << "<global>() at testfile:8";
       
   787 
       
   788         QTest::newRow("native recursive") << source << expected;
       
   789     }
       
   790 
       
   791     {
       
   792         QString source = QString::fromLatin1("\n"
       
   793             "function finish() {\n"
       
   794             "    return bt();\n"
       
   795             "}\n"
       
   796             "function rec(n) {\n"
       
   797             "    if(n <= 1)\n"
       
   798             "        return finish();\n"
       
   799             "    else\n"
       
   800             "        return rec (n - 1);\n"
       
   801             "}\n"
       
   802             "function f() {\n"
       
   803             "    return rec(12);\n"
       
   804             "}\n"
       
   805             "f();\n");
       
   806 
       
   807         QStringList expected;
       
   808         expected << "<native>() at -1" << "finish() at testfile:3";
       
   809         for(int n = 1; n <= 12; n++) {
       
   810             expected << QString::fromLatin1("rec(n = %1) at testfile:%2")
       
   811                 .arg(n).arg((n==1) ? 7 : 9);
       
   812         }
       
   813         expected << "f() at testfile:12";
       
   814         expected << "<global>() at testfile:14";
       
   815 
       
   816         QTest::newRow("js recursive") << source << expected;
       
   817     }
       
   818 }
       
   819 
       
   820 
       
   821 void tst_QScriptContext::backtrace()
       
   822 {
       
   823     QFETCH(QString, code);
       
   824     QFETCH(QStringList, expectedbacktrace);
       
   825 
       
   826     QScriptEngine eng;
       
   827     eng.globalObject().setProperty("bt", eng.newFunction(getBacktrace));
       
   828     eng.globalObject().setProperty("custom_eval", eng.newFunction(custom_eval));
       
   829     eng.globalObject().setProperty("custom_call", eng.newFunction(custom_call));
       
   830     eng.globalObject().setProperty("native_recurse", eng.newFunction(native_recurse));
       
   831 
       
   832     QString fileName = "testfile";
       
   833     QScriptValue ret = eng.evaluate(code, fileName);
       
   834     QVERIFY(!eng.hasUncaughtException());
       
   835     QVERIFY(ret.isArray());
       
   836     QStringList slist = qscriptvalue_cast<QStringList>(ret);
       
   837     QCOMPARE(slist, expectedbacktrace);
       
   838 }
       
   839 
       
   840 static QScriptValue getScopeChain(QScriptContext *ctx, QScriptEngine *eng)
       
   841 {
       
   842     return qScriptValueFromValue(eng, ctx->parentContext()->scopeChain());
       
   843 }
       
   844 
       
   845 void tst_QScriptContext::scopeChain()
       
   846 {
       
   847     QScriptEngine eng;
       
   848     {
       
   849         QScriptValueList ret = eng.currentContext()->scopeChain();
       
   850         QCOMPARE(ret.size(), 1);
       
   851         QVERIFY(ret.at(0).strictlyEquals(eng.globalObject()));
       
   852     }
       
   853     {
       
   854         eng.globalObject().setProperty("getScopeChain", eng.newFunction(getScopeChain));
       
   855         QScriptValueList ret = qscriptvalue_cast<QScriptValueList>(eng.evaluate("getScopeChain()"));
       
   856         QCOMPARE(ret.size(), 1);
       
   857         QVERIFY(ret.at(0).strictlyEquals(eng.globalObject()));
       
   858     }
       
   859     {
       
   860         eng.evaluate("function foo() { function bar() { return getScopeChain(); } return bar() }");
       
   861         QScriptValueList ret = qscriptvalue_cast<QScriptValueList>(eng.evaluate("foo()"));
       
   862         QEXPECT_FAIL("", "Number of items in returned scope chain is incorrect", Abort);
       
   863         QCOMPARE(ret.size(), 3);
       
   864         QVERIFY(ret.at(2).strictlyEquals(eng.globalObject()));
       
   865         QCOMPARE(ret.at(1).toString(), QString::fromLatin1("activation"));
       
   866         QVERIFY(ret.at(1).property("arguments").isObject());
       
   867         QCOMPARE(ret.at(0).toString(), QString::fromLatin1("activation"));
       
   868         QVERIFY(ret.at(0).property("arguments").isObject());
       
   869     }
       
   870     {
       
   871         QScriptValueList ret = qscriptvalue_cast<QScriptValueList>(eng.evaluate("o = { x: 123 }; with(o) getScopeChain();"));
       
   872         QCOMPARE(ret.size(), 2);
       
   873         QVERIFY(ret.at(1).strictlyEquals(eng.globalObject()));
       
   874         QVERIFY(ret.at(0).isObject());
       
   875         QCOMPARE(ret.at(0).property("x").toInt32(), 123);
       
   876     }
       
   877     {
       
   878         QScriptValueList ret = qscriptvalue_cast<QScriptValueList>(
       
   879             eng.evaluate("o1 = { x: 123}; o2 = { y: 456 }; with(o1) { with(o2) { getScopeChain(); } }"));
       
   880         QCOMPARE(ret.size(), 3);
       
   881         QVERIFY(ret.at(2).strictlyEquals(eng.globalObject()));
       
   882         QVERIFY(ret.at(1).isObject());
       
   883         QCOMPARE(ret.at(1).property("x").toInt32(), 123);
       
   884         QVERIFY(ret.at(0).isObject());
       
   885         QCOMPARE(ret.at(0).property("y").toInt32(), 456);
       
   886     }
       
   887 }
       
   888 
       
   889 void tst_QScriptContext::pushAndPopScope()
       
   890 {
       
   891     QScriptEngine eng;
       
   892     QScriptContext *ctx = eng.currentContext();
       
   893     QCOMPARE(ctx->scopeChain().size(), 1);
       
   894     QVERIFY(ctx->scopeChain().at(0).strictlyEquals(eng.globalObject()));
       
   895 
       
   896     QVERIFY(ctx->popScope().strictlyEquals(eng.globalObject()));
       
   897     ctx->pushScope(eng.globalObject());
       
   898     QCOMPARE(ctx->scopeChain().size(), 1);
       
   899     QVERIFY(ctx->scopeChain().at(0).strictlyEquals(eng.globalObject()));
       
   900 
       
   901     QScriptValue obj = eng.newObject();
       
   902     ctx->pushScope(obj);
       
   903     QCOMPARE(ctx->scopeChain().size(), 2);
       
   904     QVERIFY(ctx->scopeChain().at(0).strictlyEquals(obj));
       
   905     QVERIFY(ctx->scopeChain().at(1).strictlyEquals(eng.globalObject()));
       
   906 
       
   907     QVERIFY(ctx->popScope().strictlyEquals(obj));
       
   908     QCOMPARE(ctx->scopeChain().size(), 1);
       
   909     QVERIFY(ctx->scopeChain().at(0).strictlyEquals(eng.globalObject()));
       
   910 
       
   911     {
       
   912         QScriptValue ret = eng.evaluate("x");
       
   913         QVERIFY(ret.isError());
       
   914         eng.clearExceptions();
       
   915     }
       
   916     QCOMPARE(ctx->scopeChain().size(), 1);
       
   917     QVERIFY(ctx->scopeChain().at(0).strictlyEquals(eng.globalObject()));
       
   918 
       
   919     // task 236685
       
   920     QScriptValue qobj = eng.newQObject(this, QScriptEngine::QtOwnership, QScriptEngine::AutoCreateDynamicProperties);
       
   921     ctx->pushScope(qobj);
       
   922     QCOMPARE(ctx->scopeChain().size(), 2);
       
   923     QVERIFY(ctx->scopeChain().at(0).strictlyEquals(qobj));
       
   924     QVERIFY(ctx->scopeChain().at(1).strictlyEquals(eng.globalObject()));
       
   925     {
       
   926         QScriptValue ret = eng.evaluate("print");
       
   927         QVERIFY(ret.isFunction());
       
   928     }
       
   929     ctx->popScope();
       
   930 
       
   931     ctx->pushScope(obj);
       
   932     QCOMPARE(ctx->scopeChain().size(), 2);
       
   933     QVERIFY(ctx->scopeChain().at(0).strictlyEquals(obj));
       
   934     obj.setProperty("x", 123);
       
   935     {
       
   936         QScriptValue ret = eng.evaluate("x");
       
   937         QVERIFY(ret.isNumber());
       
   938         QCOMPARE(ret.toInt32(), 123);
       
   939     }
       
   940     QVERIFY(ctx->popScope().strictlyEquals(obj));
       
   941     QCOMPARE(ctx->scopeChain().size(), 1);
       
   942     QVERIFY(ctx->scopeChain().at(0).strictlyEquals(eng.globalObject()));
       
   943 
       
   944     ctx->pushScope(QScriptValue());
       
   945     QCOMPARE(ctx->scopeChain().size(), 1);
       
   946 
       
   947     QVERIFY(ctx->popScope().strictlyEquals(eng.globalObject()));
       
   948     QVERIFY(ctx->scopeChain().isEmpty());
       
   949 
       
   950     // Used to work with old back-end, doesn't with new one because JSC requires that the last object in
       
   951     // a scope chain is the Global Object.
       
   952     QTest::ignoreMessage(QtWarningMsg, "QScriptContext::pushScope() failed: initial object in scope chain has to be the Global Object");
       
   953     ctx->pushScope(obj);
       
   954     QCOMPARE(ctx->scopeChain().size(), 0);
       
   955 
       
   956     QScriptEngine eng2;
       
   957     QScriptValue obj2 = eng2.newObject();
       
   958     QTest::ignoreMessage(QtWarningMsg, "QScriptContext::pushScope() failed: cannot push an object created in a different engine");
       
   959     ctx->pushScope(obj2);
       
   960     QVERIFY(ctx->scopeChain().isEmpty());
       
   961 
       
   962     QVERIFY(!ctx->popScope().isValid());
       
   963 }
       
   964 
       
   965 static QScriptValue get_activationObject(QScriptContext *ctx, QScriptEngine *)
       
   966 {
       
   967     return ctx->activationObject();
       
   968 }
       
   969 
       
   970 void tst_QScriptContext::getSetActivationObject()
       
   971 {
       
   972     QScriptEngine eng;
       
   973     QScriptContext *ctx = eng.currentContext();
       
   974     QVERIFY(ctx->activationObject().equals(eng.globalObject()));
       
   975 
       
   976     ctx->setActivationObject(QScriptValue());
       
   977     QVERIFY(ctx->activationObject().equals(eng.globalObject()));
       
   978     QCOMPARE(ctx->engine(), &eng);
       
   979 
       
   980     QScriptValue obj = eng.newObject();
       
   981     ctx->setActivationObject(obj);
       
   982     QVERIFY(ctx->activationObject().equals(obj));
       
   983     QCOMPARE(ctx->scopeChain().size(), 1);
       
   984     QVERIFY(ctx->scopeChain().at(0).equals(obj));
       
   985 
       
   986     {
       
   987         QScriptEngine eng2;
       
   988         QScriptValue obj2 = eng2.newObject();
       
   989         QTest::ignoreMessage(QtWarningMsg, "QScriptContext::setActivationObject() failed: cannot set an object created in a different engine");
       
   990         QScriptValue was = ctx->activationObject();
       
   991         ctx->setActivationObject(obj2);
       
   992         QVERIFY(ctx->activationObject().equals(was));
       
   993     }
       
   994 
       
   995     ctx->setActivationObject(eng.globalObject());
       
   996     QVERIFY(ctx->activationObject().equals(eng.globalObject()));
       
   997     QScriptValue fun = eng.newFunction(get_activationObject);
       
   998     eng.globalObject().setProperty("get_activationObject", fun);
       
   999     {
       
  1000         QScriptValue ret = eng.evaluate("get_activationObject(1, 2, 3)");
       
  1001         QVERIFY(ret.isObject());
       
  1002         QScriptValue arguments = ret.property("arguments");
       
  1003         QEXPECT_FAIL("", "Getting arguments property of activation object doesn't work", Abort);
       
  1004         QVERIFY(arguments.isObject());
       
  1005         QCOMPARE(arguments.property("length").toInt32(), 3);
       
  1006         QCOMPARE(arguments.property("0").toInt32(), 1);
       
  1007         QCOMPARE(arguments.property("1").toInt32(), 1);
       
  1008         QCOMPARE(arguments.property("2").toInt32(), 1);
       
  1009     }
       
  1010 }
       
  1011 
       
  1012 static QScriptValue myEval(QScriptContext *ctx, QScriptEngine *eng)
       
  1013 {
       
  1014      QString code = ctx->argument(0).toString();
       
  1015      ctx->setActivationObject(ctx->parentContext()->activationObject());
       
  1016      ctx->setThisObject(ctx->parentContext()->thisObject());
       
  1017      return eng->evaluate(code);
       
  1018 }
       
  1019 
       
  1020 void tst_QScriptContext::inheritActivationAndThisObject()
       
  1021 {
       
  1022     QScriptEngine eng;
       
  1023     eng.globalObject().setProperty("myEval", eng.newFunction(myEval));
       
  1024     {
       
  1025         QScriptValue ret = eng.evaluate("var a = 123; myEval('a')");
       
  1026         QVERIFY(ret.isNumber());
       
  1027         QCOMPARE(ret.toInt32(), 123);
       
  1028     }
       
  1029     {
       
  1030         QScriptValue ret = eng.evaluate("(function() { return myEval('this'); }).call(Number)");
       
  1031         QVERIFY(ret.isFunction());
       
  1032         QVERIFY(ret.equals(eng.globalObject().property("Number")));
       
  1033     }
       
  1034     {
       
  1035         QScriptValue ret = eng.evaluate("(function(a) { return myEval('a'); })(123)");
       
  1036         QVERIFY(ret.isNumber());
       
  1037         QCOMPARE(ret.toInt32(), 123);
       
  1038     }
       
  1039 
       
  1040     // QT-2219
       
  1041     {
       
  1042         eng.globalObject().setProperty("a", 123);
       
  1043         QScriptValue ret = eng.evaluate("(function() { myEval('var a = 456'); return a; })()");
       
  1044         QVERIFY(ret.isNumber());
       
  1045         QCOMPARE(ret.toInt32(), 456);
       
  1046         QEXPECT_FAIL("", "QT-2219: Wrong activation object is returned from native function's parent context", Continue);
       
  1047         QVERIFY(eng.globalObject().property("a").strictlyEquals(123));
       
  1048     }
       
  1049 }
       
  1050 
       
  1051 static QScriptValue parentContextToString(QScriptContext *ctx, QScriptEngine *)
       
  1052 {
       
  1053     return ctx->parentContext()->toString();
       
  1054 }
       
  1055 
       
  1056 void tst_QScriptContext::toString()
       
  1057 {
       
  1058     QScriptEngine eng;
       
  1059     eng.globalObject().setProperty("parentContextToString", eng.newFunction(parentContextToString));
       
  1060     QScriptValue ret = eng.evaluate("function foo(first, second, third) {\n"
       
  1061                                     "    return parentContextToString();\n"
       
  1062                                     "}; foo(1, 2, 3)", "script.qs");
       
  1063     QVERIFY(ret.isString());
       
  1064     QCOMPARE(ret.toString(), QString::fromLatin1("foo(first = 1, second = 2, third = 3) at script.qs:2"));
       
  1065 }
       
  1066 
       
  1067 static QScriptValue storeCalledAsConstructor(QScriptContext *ctx, QScriptEngine *eng)
       
  1068 {
       
  1069     ctx->callee().setProperty("calledAsConstructor", ctx->isCalledAsConstructor());
       
  1070     return eng->undefinedValue();
       
  1071 }
       
  1072 
       
  1073 static QScriptValue storeCalledAsConstructorV2(QScriptContext *ctx, QScriptEngine *eng, void *)
       
  1074 {
       
  1075     ctx->callee().setProperty("calledAsConstructor", ctx->isCalledAsConstructor());
       
  1076     return eng->undefinedValue();
       
  1077 }
       
  1078 
       
  1079 static QScriptValue storeCalledAsConstructorV3(QScriptContext *ctx, QScriptEngine *eng)
       
  1080 {
       
  1081     ctx->callee().setProperty("calledAsConstructor", ctx->parentContext()->isCalledAsConstructor());
       
  1082     return eng->undefinedValue();
       
  1083 }
       
  1084 
       
  1085 void tst_QScriptContext::calledAsConstructor()
       
  1086 {
       
  1087     QScriptEngine eng;
       
  1088     QScriptValue fun1 = eng.newFunction(storeCalledAsConstructor);
       
  1089     {
       
  1090         fun1.call();
       
  1091         QVERIFY(!fun1.property("calledAsConstructor").toBool());
       
  1092         fun1.construct();
       
  1093         QVERIFY(fun1.property("calledAsConstructor").toBool());
       
  1094     }
       
  1095     {
       
  1096         QScriptValue fun = eng.newFunction(storeCalledAsConstructorV2, (void*)0);
       
  1097         fun.call();
       
  1098         QVERIFY(!fun.property("calledAsConstructor").toBool());
       
  1099         fun.construct();
       
  1100         QVERIFY(fun.property("calledAsConstructor").toBool());
       
  1101     }
       
  1102     {
       
  1103         eng.globalObject().setProperty("fun1", fun1);
       
  1104         eng.evaluate("fun1();");
       
  1105         QVERIFY(!fun1.property("calledAsConstructor").toBool());
       
  1106         eng.evaluate("new fun1();");
       
  1107         QVERIFY(fun1.property("calledAsConstructor").toBool());
       
  1108     }
       
  1109     {
       
  1110         QScriptValue fun3 = eng.newFunction(storeCalledAsConstructorV3);
       
  1111         eng.globalObject().setProperty("fun3", fun3);
       
  1112         eng.evaluate("function test() { fun3() }");
       
  1113         eng.evaluate("test();");
       
  1114         QVERIFY(!fun3.property("calledAsConstructor").toBool());
       
  1115         eng.evaluate("new test();");
       
  1116         if (qt_script_isJITEnabled())
       
  1117             QEXPECT_FAIL("", "calledAsConstructor is not correctly set for JS functions when JIT is enabled", Continue);
       
  1118         QVERIFY(fun3.property("calledAsConstructor").toBool());
       
  1119     }
       
  1120 
       
  1121 }
       
  1122 
       
  1123 static QScriptValue argumentsObjectInNative_test1(QScriptContext *ctx, QScriptEngine *eng)
       
  1124 {
       
  1125 #define VERIFY(statement) \
       
  1126     do {\
       
  1127         if (!QTest::qVerify((statement), #statement, "", __FILE__, __LINE__))\
       
  1128             return QString::fromLatin1("Failed "  #statement);\
       
  1129     } while (0)
       
  1130 
       
  1131     QScriptValue obj = ctx->argumentsObject();
       
  1132     VERIFY(obj.isObject());
       
  1133     VERIFY(obj.property(0).toUInt32() == 123);
       
  1134     VERIFY(obj.property(1).toString() == QString::fromLatin1("456"));
       
  1135 
       
  1136     obj.setProperty(0, "abc");
       
  1137     VERIFY(eng->evaluate("arguments[0]").toString() == QString::fromLatin1("abc") );
       
  1138 
       
  1139     return QString::fromLatin1("success");
       
  1140 #undef VERIFY
       
  1141 }
       
  1142 
       
  1143 void tst_QScriptContext::argumentsObjectInNative()
       
  1144 {
       
  1145     {
       
  1146         QScriptEngine eng;
       
  1147         QScriptValue fun = eng.newFunction(argumentsObjectInNative_test1);
       
  1148         QScriptValueList args;
       
  1149         args << QScriptValue(&eng, 123.0);
       
  1150         args << QScriptValue(&eng, QString::fromLatin1("456"));
       
  1151         QScriptValue result = fun.call(eng.undefinedValue(), args);
       
  1152         QVERIFY(!eng.hasUncaughtException());
       
  1153         QCOMPARE(result.toString(), QString::fromLatin1("success"));
       
  1154     }
       
  1155     {
       
  1156         QScriptEngine eng;
       
  1157         QScriptValue fun = eng.newFunction(argumentsObjectInNative_test1);
       
  1158         eng.globalObject().setProperty("func", fun);
       
  1159         QScriptValue result = eng.evaluate("func(123.0 , 456);");
       
  1160         QVERIFY(!eng.hasUncaughtException());
       
  1161         QCOMPARE(result.toString(), QString::fromLatin1("success"));
       
  1162     }
       
  1163 }
       
  1164 
       
  1165 static QScriptValue get_jsActivationObject(QScriptContext *ctx, QScriptEngine *)
       
  1166 {
       
  1167     return ctx->parentContext()->parentContext()->activationObject();
       
  1168 }
       
  1169 
       
  1170 void tst_QScriptContext::jsActivationObject()
       
  1171 {
       
  1172     QScriptEngine eng;
       
  1173     eng.globalObject().setProperty("get_jsActivationObject", eng.newFunction(get_jsActivationObject));
       
  1174     eng.evaluate("function f1() { var w = get_jsActivationObject('arg1');  return w; }");
       
  1175     eng.evaluate("function f2(x,y,z) { var v1 = 42;\n"
       
  1176                         //   "function foo() {};\n" //this would avoid JSC to optimize
       
  1177                         "var v2 = f1(); return v2; }");
       
  1178     eng.evaluate("function f3() { var v1 = 'nothing'; return f2(1,2,3); }");
       
  1179     QScriptValue result1 = eng.evaluate("f2('hello', 'useless', 'world')");
       
  1180     QScriptValue result2 = eng.evaluate("f3()");
       
  1181     QVERIFY(result1.isObject());
       
  1182     QEXPECT_FAIL("", "JSC optimize away the activation object", Abort);
       
  1183     QCOMPARE(result1.property("v1").toInt32() , 42);
       
  1184     QCOMPARE(result1.property("arguments").property(1).toString() , QString::fromLatin1("useless"));
       
  1185     QVERIFY(result2.isObject());
       
  1186     QCOMPARE(result2.property("v1").toInt32() , 42);
       
  1187     QCOMPARE(result2.property("arguments").property(1).toString() , QString::fromLatin1("2"));
       
  1188 }
       
  1189 
       
  1190 void tst_QScriptContext::qobjectAsActivationObject()
       
  1191 {
       
  1192     QScriptEngine eng;
       
  1193     QObject object;
       
  1194     QScriptValue scriptObject = eng.newQObject(&object);
       
  1195     QScriptContext *ctx = eng.pushContext();
       
  1196     ctx->setActivationObject(scriptObject);
       
  1197     QVERIFY(ctx->activationObject().equals(scriptObject));
       
  1198 
       
  1199     QVERIFY(!scriptObject.property("foo").isValid());
       
  1200     eng.evaluate("function foo() { return 123; }");
       
  1201     {
       
  1202         QScriptValue val = scriptObject.property("foo");
       
  1203         QVERIFY(val.isValid());
       
  1204         QVERIFY(val.isFunction());
       
  1205     }
       
  1206     QVERIFY(!eng.globalObject().property("foo").isValid());
       
  1207 
       
  1208     QVERIFY(!scriptObject.property("bar").isValid());
       
  1209     eng.evaluate("var bar = 123");
       
  1210     {
       
  1211         QScriptValue val = scriptObject.property("bar");
       
  1212         QVERIFY(val.isValid());
       
  1213         QVERIFY(val.isNumber());
       
  1214         QCOMPARE(val.toInt32(), 123);
       
  1215     }
       
  1216     QVERIFY(!eng.globalObject().property("bar").isValid());
       
  1217 
       
  1218     {
       
  1219         QScriptValue val = eng.evaluate("delete foo");
       
  1220         QVERIFY(val.isBool());
       
  1221         QVERIFY(val.toBool());
       
  1222         QVERIFY(!scriptObject.property("foo").isValid());
       
  1223     }
       
  1224 }
       
  1225 
       
  1226 static QScriptValue getParentContextCallee(QScriptContext *ctx, QScriptEngine *)
       
  1227 {
       
  1228     return ctx->parentContext()->callee();
       
  1229 }
       
  1230 
       
  1231 void tst_QScriptContext::parentContextCallee_QT2270()
       
  1232 {
       
  1233     QScriptEngine engine;
       
  1234     engine.globalObject().setProperty("getParentContextCallee", engine.newFunction(getParentContextCallee));
       
  1235     QScriptValue fun = engine.evaluate("(function() { return getParentContextCallee(); })");
       
  1236     QVERIFY(fun.isFunction());
       
  1237     QScriptValue callee = fun.call();
       
  1238     QVERIFY(callee.equals(fun));
       
  1239 }
       
  1240 
       
  1241 QTEST_MAIN(tst_QScriptContext)
       
  1242 #include "tst_qscriptcontext.moc"