Don't crash when creating backtrace for built-in JS function
[qt:qt.git] / tests / auto / qscriptcontext / tst_qscriptcontext.cpp
1 /****************************************************************************
2 **
3 ** Copyright (C) 2011 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     void popNativeContextScope();
88 };
89
90 tst_QScriptContext::tst_QScriptContext()
91 {
92 }
93
94 tst_QScriptContext::~tst_QScriptContext()
95 {
96 }
97
98 static QScriptValue get_callee(QScriptContext *ctx, QScriptEngine *)
99 {
100     return ctx->callee();
101 }
102
103 static QScriptValue store_callee_and_return_primitive(QScriptContext *ctx, QScriptEngine *eng)
104 {
105     ctx->thisObject().setProperty("callee", ctx->callee());
106     return QScriptValue(eng, 123);
107 }
108
109 void tst_QScriptContext::callee()
110 {
111     QScriptEngine eng;
112
113     {
114         QScriptValue fun = eng.newFunction(get_callee);
115         fun.setProperty("foo", QScriptValue(&eng, "bar"));
116         eng.globalObject().setProperty("get_callee", fun);
117
118         QScriptValue result = eng.evaluate("get_callee()");
119         QCOMPARE(result.isFunction(), true);
120         QCOMPARE(result.property("foo").toString(), QString("bar"));
121     }
122
123     // callee when toPrimitive() is called internally
124     {
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));
132
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));
139     }
140 }
141
142 static QScriptValue get_arguments(QScriptContext *ctx, QScriptEngine *eng)
143 {
144     QScriptValue array = eng->newArray();
145     for (int i = 0; i < ctx->argumentCount(); ++i)
146         array.setProperty(QString::number(i), ctx->argument(i));
147     return array;
148 }
149
150 static QScriptValue get_argumentsObject(QScriptContext *ctx, QScriptEngine *)
151 {
152     return ctx->argumentsObject();
153 }
154
155 void tst_QScriptContext::arguments()
156 {
157     QScriptEngine eng;
158
159     {
160         QScriptValue args = eng.currentContext()->argumentsObject();
161         QVERIFY(args.isObject());
162         QCOMPARE(args.property("length").toInt32(), 0);
163     }
164     {
165         QScriptValue fun = eng.newFunction(get_arguments);
166         eng.globalObject().setProperty("get_arguments", fun);
167     }
168
169     for (int x = 0; x < 2; ++x) {
170         QString prefix;
171         if (x == 0)
172             prefix = "";
173         else
174             prefix = "new ";
175         {
176             QScriptValue result = eng.evaluate(prefix+"get_arguments()");
177             QCOMPARE(result.isArray(), true);
178             QCOMPARE(result.property("length").toUInt32(), quint32(0));
179         }
180
181         {
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);
187         }
188
189         {
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);
199         }
200
201         {
202             QScriptValue fun = eng.newFunction(get_argumentsObject);
203             eng.globalObject().setProperty("get_argumentsObject", fun);
204         }
205
206         {
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());
226         }
227
228         {
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"));
241         }
242
243         {
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);
253         }
254
255         // arguments object returned from script
256         {
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);
263         }
264
265         {
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);
275         }
276     }
277 }
278
279 static QScriptValue get_thisObject(QScriptContext *ctx, QScriptEngine *)
280 {
281     return ctx->thisObject();
282 }
283
284 void tst_QScriptContext::thisObject()
285 {
286     QScriptEngine eng;
287
288     QScriptValue fun = eng.newFunction(get_thisObject);
289     eng.globalObject().setProperty("get_thisObject", fun);
290
291     {
292         QScriptValue result = eng.evaluate("get_thisObject()");
293         QCOMPARE(result.isObject(), true);
294         QCOMPARE(result.toString(), QString("[object global]"));
295     }
296
297     {
298         QScriptValue result = eng.evaluate("get_thisObject.apply(new Number(123))");
299         QCOMPARE(result.isObject(), true);
300         QCOMPARE(result.toNumber(), 123.0);
301     }
302
303     {
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));
309
310         QScriptEngine eng2;
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);
314     }
315 }
316
317 void tst_QScriptContext::returnValue()
318 {
319     QSKIP("Internal function not implemented in JSC-based back-end", SkipAll);
320     QScriptEngine eng;
321     eng.evaluate("123");
322     QCOMPARE(eng.currentContext()->returnValue().toNumber(), 123.0);
323     eng.evaluate("\"ciao\"");
324     QCOMPARE(eng.currentContext()->returnValue().toString(), QString("ciao"));
325 }
326
327 static QScriptValue throw_Error(QScriptContext *ctx, QScriptEngine *)
328 {
329     return ctx->throwError(QScriptContext::UnknownError, "foo");
330 }
331
332 static QScriptValue throw_TypeError(QScriptContext *ctx, QScriptEngine *)
333 {
334     return ctx->throwError(QScriptContext::TypeError, "foo");
335 }
336
337 static QScriptValue throw_ReferenceError(QScriptContext *ctx, QScriptEngine *)
338 {
339     return ctx->throwError(QScriptContext::ReferenceError, "foo");
340 }
341
342 static QScriptValue throw_SyntaxError(QScriptContext *ctx, QScriptEngine *)
343 {
344     return ctx->throwError(QScriptContext::SyntaxError, "foo");
345 }
346
347 static QScriptValue throw_RangeError(QScriptContext *ctx, QScriptEngine *)
348 {
349     return ctx->throwError(QScriptContext::RangeError, "foo");
350 }
351
352 static QScriptValue throw_URIError(QScriptContext *ctx, QScriptEngine *)
353 {
354     return ctx->throwError(QScriptContext::URIError, "foo");
355 }
356
357 static QScriptValue throw_ErrorAndReturnUndefined(QScriptContext *ctx, QScriptEngine *eng)
358 {
359     ctx->throwError(QScriptContext::UnknownError, "foo");
360     return eng->undefinedValue();
361 }
362
363 void tst_QScriptContext::throwError()
364 {
365     QScriptEngine eng;
366
367     {
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"));
374     }
375
376     {
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"));
383     }
384
385     {
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"));
392     }
393
394     {
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"));
401     }
402
403     {
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"));
410     }
411
412     {
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"));
419     }
420
421     {
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"));
428     }
429
430 }
431
432 static QScriptValue throw_value(QScriptContext *ctx, QScriptEngine *)
433 {
434     return ctx->throwValue(ctx->argument(0));
435 }
436
437 void tst_QScriptContext::throwValue()
438 {
439     QScriptEngine eng;
440
441     QScriptValue fun = eng.newFunction(throw_value);
442     eng.globalObject().setProperty("throw_value", fun);
443
444     {
445         QScriptValue result = eng.evaluate("throw_value(123)");
446         QCOMPARE(result.isError(), false);
447         QCOMPARE(result.toNumber(), 123.0);
448         QCOMPARE(eng.hasUncaughtException(), true);
449     }
450 }
451
452 static QScriptValue evaluate(QScriptContext *, QScriptEngine *eng)
453 {
454     return eng->evaluate("a = 123; a");
455 //    return eng->evaluate("a");
456 }
457
458 void tst_QScriptContext::evaluateInFunction()
459 {
460     QScriptEngine eng;
461
462     QScriptValue fun = eng.newFunction(evaluate);
463     eng.globalObject().setProperty("evaluate", fun);
464
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);
470
471     QCOMPARE(eng.evaluate("a").toNumber(), 123.0);
472 }
473
474 void tst_QScriptContext::pushAndPopContext()
475 {
476     QScriptEngine eng;
477     QScriptContext *topLevel = eng.currentContext();
478     QCOMPARE(topLevel->engine(), &eng);
479
480     QScriptContext *ctx = eng.pushContext();
481     QVERIFY(ctx != 0);
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()));
497
498     QScriptContext *ctx2 = eng.pushContext();
499     QCOMPARE(ctx2->parentContext(), ctx);
500     QCOMPARE(eng.currentContext(), ctx2);
501
502     eng.popContext();
503     QCOMPARE(eng.currentContext(), ctx);
504     eng.popContext();
505     QCOMPARE(eng.currentContext(), topLevel);
506
507     // popping the top-level context is not allowed
508     QTest::ignoreMessage(QtWarningMsg, "QScriptEngine::popContext() doesn't match with pushContext()");
509     eng.popContext();
510     QCOMPARE(eng.currentContext(), topLevel);
511
512     {
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")));
518         eng.popContext();
519     }
520
521     {
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)));
529         eng.popContext();
530     }
531
532     // throwing an exception
533     {
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);
539         eng.popContext();
540     }
541 }
542
543 void tst_QScriptContext::popNativeContextScope()
544 {
545     QScriptEngine eng;
546     QScriptContext *ctx = eng.pushContext();
547     QVERIFY(ctx->popScope().isObject()); // the activation object
548
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()));
553
554     QVERIFY(!eng.evaluate("var foo = 123; function bar() {}").isError());
555     QVERIFY(eng.globalObject().property("foo").isNumber());
556     QVERIFY(eng.globalObject().property("bar").isFunction());
557
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()));
570
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());
576
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()));
581
582     // Need to push another object, otherwise we crash in popContext() (QTBUG-11012)
583     ctx->pushScope(customScope);
584     eng.popContext();
585 }
586
587 void tst_QScriptContext::lineNumber()
588 {
589     QScriptEngine eng;
590
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);
595
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);
600 }
601
602 static QScriptValue getBacktrace(QScriptContext *ctx, QScriptEngine *eng)
603 {
604     return qScriptValueFromValue(eng, ctx->backtrace());
605 }
606
607 static QScriptValue custom_eval(QScriptContext *ctx, QScriptEngine *eng)
608 {
609     return eng->evaluate(ctx->argumentsObject().property(0).toString(), ctx->argumentsObject().property(1).toString());
610 }
611
612 static QScriptValue custom_call(QScriptContext *ctx, QScriptEngine *)
613 {
614     return ctx->argumentsObject().property(0).call(QScriptValue(), QScriptValueList() << ctx->argumentsObject().property(1));
615 }
616
617 static QScriptValue native_recurse(QScriptContext *ctx, QScriptEngine *eng)
618 {
619     QScriptValue func = ctx->argumentsObject().property(0);
620     QScriptValue n = ctx->argumentsObject().property(1);
621
622     if(n.toUInt32() <= 1) {
623         return func.call(QScriptValue(), QScriptValueList());
624     } else {
625         return eng->evaluate("native_recurse").call(QScriptValue(),
626                                                     QScriptValueList() << func << QScriptValue(n.toUInt32() - 1));
627     }
628 }
629
630 void tst_QScriptContext::backtrace_data()
631 {
632     QTest::addColumn<QString>("code");
633     QTest::addColumn<QStringList>("expectedbacktrace");
634
635     {
636         QString source(
637                  "function foo() {\n"
638                  "  return bt(123);\n"
639                  "}\n"
640                  "foo('hello', { })\n"
641                  "var r = 0;");
642
643         QStringList expected;
644         expected << "<native>(123) at -1"
645                  << "foo('hello', [object Object]) at testfile:2"
646                  << "<global>() at testfile:4";
647
648
649         QTest::newRow("simple") << source << expected;
650     }
651
652     {
653         QStringList expected;
654         QString source = QString(
655             "function foo(arg1 , arg2) {\n"
656             "  return eval(\"%1\");\n"
657             "}\n"
658             "foo('hello', 456)\n"
659             "var a = 0;"
660            ).arg("\\n \\n bt('hey'); \\n");
661
662            expected << "<native>('hey') at -1"
663                     << "<eval>() at 3"
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);
667            expected
668                     << "<global>() at testfile:4";
669
670             QTest::newRow("eval") << source << expected;
671     }
672
673     {
674         QString eval_code(
675                 "function bar(a) {\\n"
676                 "  return bt('m');\\n"
677                 "}\\n"
678                 "bar('b'); \\n");
679         QString source = QString(
680                 "function foo() {\n"
681                 "  return custom_eval(\"%1\", 'eval.js');\n"
682                 "}\n"
683                 "foo()"
684             ).arg(eval_code);
685
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";
693
694         QTest::newRow("custom_eval") << source << expected;
695     }
696     {
697         QString f("function (a) {\n return bt(a); \n  }");
698         QString source = QString(
699                 "function foo(f) {\n"
700                 "  return f('b');\n"
701                 "}\n"
702                 "foo(%1)"
703             ).arg(f);
704
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";
710
711         QTest::newRow("closure") << source << expected;
712     }
713
714     {
715         QStringList expected;
716         QString source = QString(
717             "var o = new Object;\n"
718             "o.foo = function plop() {\n"
719             "  return eval(\"%1\");\n"
720             "}\n"
721             "o.foo('hello', 456)\n"
722            ).arg("\\n \\n bt('hey'); \\n");
723
724            expected << "<native>('hey') at -1"
725                     << "<eval>() at 3"
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);
729            expected
730                     << "<global>() at testfile:5";
731
732             QTest::newRow("eval in member") << source << expected;
733     }
734
735     {
736         QString source(
737                  "function foo(a) {\n"
738                  "  return bt(123);\n"
739                  "}\n"
740                  "function bar() {\n"
741                  "  var v = foo('arg', 4);\n"
742                  "  return v;\n"
743                  "}\n"
744                  "bar('hello', { });\n");
745
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";
751
752
753         QTest::newRow("two function") << source << expected;
754     }
755
756     {
757         QString func("function foo(a, b) {\n"
758                      "  return bt(a);\n"
759                      "}");
760
761         QString source = func + QString::fromLatin1("\n"
762                  "custom_call(foo, 'hello');\n"
763                  "var a = 1\n");
764
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";
770
771         QTest::newRow("call") << source << expected;
772     }
773
774     {
775         QString source = QString::fromLatin1("\n"
776         "custom_call(bt, 'hello_world');\n"
777         "var a = 1\n");
778
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";
783
784         QTest::newRow("call native") << source << expected;
785     }
786
787     {
788         QLatin1String func( "function f1() {\n"
789             "    eval('var q = 4');\n"
790             "    return custom_call(bt, 22);\n"
791             "}");
792
793         QString source = QString::fromLatin1("\n"
794             "function f2() {\n"
795             "    func = %1\n"
796             "    return custom_call(func, 12);\n"
797             "}\n"
798             "f2();\n").arg(func);
799
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";
807
808
809         QTest::newRow("calls with closures") << source << expected;
810     }
811
812     {
813         QLatin1String func( "function js_bt() {\n"
814             "    return bt();\n"
815             "}");
816
817         QString source = QString::fromLatin1("\n"
818             "%1\n"
819             "function f() {\n"
820             "    return native_recurse(js_bt, 12);\n"
821             "}\n"
822             "f();\n").arg(func);
823
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")
828                 .arg(func).arg(n);
829         }
830         expected << "f() at testfile:6";
831         expected << "<global>() at testfile:8";
832
833         QTest::newRow("native recursive") << source << expected;
834     }
835
836     {
837         QString source = QString::fromLatin1("\n"
838             "function finish() {\n"
839             "    return bt();\n"
840             "}\n"
841             "function rec(n) {\n"
842             "    if(n <= 1)\n"
843             "        return finish();\n"
844             "    else\n"
845             "        return rec (n - 1);\n"
846             "}\n"
847             "function f() {\n"
848             "    return rec(12);\n"
849             "}\n"
850             "f();\n");
851
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);
857         }
858         expected << "f() at testfile:12";
859         expected << "<global>() at testfile:14";
860
861         QTest::newRow("js recursive") << source << expected;
862     }
863
864     {
865         QString source = QString::fromLatin1(
866             "[0].forEach(\n"
867             "    function() {\n"
868             "        result = bt();\n"
869             "}); result");
870
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;
877     }
878 }
879
880
881 void tst_QScriptContext::backtrace()
882 {
883     QFETCH(QString, code);
884     QFETCH(QStringList, expectedbacktrace);
885
886     QScriptEngine eng;
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));
891
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);
898 }
899
900 static QScriptValue getScopeChain(QScriptContext *ctx, QScriptEngine *eng)
901 {
902     return qScriptValueFromValue(eng, ctx->parentContext()->scopeChain());
903 }
904
905 void tst_QScriptContext::scopeChain()
906 {
907     QScriptEngine eng;
908     {
909         QScriptValueList ret = eng.currentContext()->scopeChain();
910         QCOMPARE(ret.size(), 1);
911         QVERIFY(ret.at(0).strictlyEquals(eng.globalObject()));
912     }
913     {
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()));
918     }
919     {
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());
929     }
930     {
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);
936     }
937     {
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);
946     }
947 }
948
949 void tst_QScriptContext::pushAndPopScope()
950 {
951     QScriptEngine eng;
952     QScriptContext *ctx = eng.currentContext();
953     QCOMPARE(ctx->scopeChain().size(), 1);
954     QVERIFY(ctx->scopeChain().at(0).strictlyEquals(eng.globalObject()));
955
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()));
960
961     QScriptValue obj = eng.newObject();
962     ctx->pushScope(obj);
963     QCOMPARE(ctx->scopeChain().size(), 2);
964     QVERIFY(ctx->scopeChain().at(0).strictlyEquals(obj));
965     QVERIFY(ctx->scopeChain().at(1).strictlyEquals(eng.globalObject()));
966
967     QVERIFY(ctx->popScope().strictlyEquals(obj));
968     QCOMPARE(ctx->scopeChain().size(), 1);
969     QVERIFY(ctx->scopeChain().at(0).strictlyEquals(eng.globalObject()));
970
971     {
972         QScriptValue ret = eng.evaluate("x");
973         QVERIFY(ret.isError());
974         eng.clearExceptions();
975     }
976     QCOMPARE(ctx->scopeChain().size(), 1);
977     QVERIFY(ctx->scopeChain().at(0).strictlyEquals(eng.globalObject()));
978
979     // task 236685
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()));
985     {
986         QScriptValue ret = eng.evaluate("print");
987         QVERIFY(ret.isFunction());
988     }
989     ctx->popScope();
990
991     ctx->pushScope(obj);
992     QCOMPARE(ctx->scopeChain().size(), 2);
993     QVERIFY(ctx->scopeChain().at(0).strictlyEquals(obj));
994     obj.setProperty("x", 123);
995     {
996         QScriptValue ret = eng.evaluate("x");
997         QVERIFY(ret.isNumber());
998         QCOMPARE(ret.toInt32(), 123);
999     }
1000     QVERIFY(ctx->popScope().strictlyEquals(obj));
1001     QCOMPARE(ctx->scopeChain().size(), 1);
1002     QVERIFY(ctx->scopeChain().at(0).strictlyEquals(eng.globalObject()));
1003
1004     ctx->pushScope(QScriptValue());
1005     QCOMPARE(ctx->scopeChain().size(), 1);
1006
1007     QVERIFY(ctx->popScope().strictlyEquals(eng.globalObject()));
1008     QVERIFY(ctx->scopeChain().isEmpty());
1009
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);
1015
1016     QScriptEngine eng2;
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());
1021
1022     QVERIFY(!ctx->popScope().isValid());
1023 }
1024
1025 static QScriptValue get_activationObject(QScriptContext *ctx, QScriptEngine *)
1026 {
1027     return ctx->activationObject();
1028 }
1029
1030 void tst_QScriptContext::getSetActivationObject()
1031 {
1032     QScriptEngine eng;
1033     QScriptContext *ctx = eng.currentContext();
1034     QVERIFY(ctx->activationObject().equals(eng.globalObject()));
1035
1036     ctx->setActivationObject(QScriptValue());
1037     QVERIFY(ctx->activationObject().equals(eng.globalObject()));
1038     QCOMPARE(ctx->engine(), &eng);
1039
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));
1045
1046     {
1047         QScriptEngine eng2;
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));
1053     }
1054
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);
1059     {
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);
1069     }
1070 }
1071
1072 static QScriptValue myEval(QScriptContext *ctx, QScriptEngine *eng)
1073 {
1074      QString code = ctx->argument(0).toString();
1075      ctx->setActivationObject(ctx->parentContext()->activationObject());
1076      ctx->setThisObject(ctx->parentContext()->thisObject());
1077      return eng->evaluate(code);
1078 }
1079
1080 void tst_QScriptContext::inheritActivationAndThisObject()
1081 {
1082     QScriptEngine eng;
1083     eng.globalObject().setProperty("myEval", eng.newFunction(myEval));
1084     {
1085         QScriptValue ret = eng.evaluate("var a = 123; myEval('a')");
1086         QVERIFY(ret.isNumber());
1087         QCOMPARE(ret.toInt32(), 123);
1088     }
1089     {
1090         QScriptValue ret = eng.evaluate("(function() { return myEval('this'); }).call(Number)");
1091         QVERIFY(ret.isFunction());
1092         QVERIFY(ret.equals(eng.globalObject().property("Number")));
1093     }
1094     {
1095         QScriptValue ret = eng.evaluate("(function(a) { return myEval('a'); })(123)");
1096         QVERIFY(ret.isNumber());
1097         QCOMPARE(ret.toInt32(), 123);
1098     }
1099
1100     // QT-2219
1101     {
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));
1108     }
1109 }
1110
1111 static QScriptValue parentContextToString(QScriptContext *ctx, QScriptEngine *)
1112 {
1113     return ctx->parentContext()->toString();
1114 }
1115
1116 void tst_QScriptContext::toString()
1117 {
1118     QScriptEngine eng;
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"));
1125 }
1126
1127 static QScriptValue storeCalledAsConstructor(QScriptContext *ctx, QScriptEngine *eng)
1128 {
1129     ctx->callee().setProperty("calledAsConstructor", ctx->isCalledAsConstructor());
1130     return eng->undefinedValue();
1131 }
1132
1133 static QScriptValue storeCalledAsConstructorV2(QScriptContext *ctx, QScriptEngine *eng, void *)
1134 {
1135     ctx->callee().setProperty("calledAsConstructor", ctx->isCalledAsConstructor());
1136     return eng->undefinedValue();
1137 }
1138
1139 static QScriptValue storeCalledAsConstructorV3(QScriptContext *ctx, QScriptEngine *eng)
1140 {
1141     ctx->callee().setProperty("calledAsConstructor", ctx->parentContext()->isCalledAsConstructor());
1142     return eng->undefinedValue();
1143 }
1144
1145 void tst_QScriptContext::calledAsConstructor()
1146 {
1147     QScriptEngine eng;
1148     QScriptValue fun1 = eng.newFunction(storeCalledAsConstructor);
1149     {
1150         fun1.call();
1151         QVERIFY(!fun1.property("calledAsConstructor").toBool());
1152         fun1.construct();
1153         QVERIFY(fun1.property("calledAsConstructor").toBool());
1154     }
1155     {
1156         QScriptValue fun = eng.newFunction(storeCalledAsConstructorV2, (void*)0);
1157         fun.call();
1158         QVERIFY(!fun.property("calledAsConstructor").toBool());
1159         fun.construct();
1160         QVERIFY(fun.property("calledAsConstructor").toBool());
1161     }
1162     {
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());
1168     }
1169     {
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());
1179     }
1180
1181 }
1182
1183 static QScriptValue argumentsObjectInNative_test1(QScriptContext *ctx, QScriptEngine *eng)
1184 {
1185 #define VERIFY(statement) \
1186     do {\
1187         if (!QTest::qVerify((statement), #statement, "", __FILE__, __LINE__))\
1188             return QString::fromLatin1("Failed "  #statement);\
1189     } while (0)
1190
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"));
1195
1196     obj.setProperty(0, "abc");
1197     VERIFY(eng->evaluate("arguments[0]").toString() == QString::fromLatin1("abc") );
1198
1199     return QString::fromLatin1("success");
1200 #undef VERIFY
1201 }
1202
1203 void tst_QScriptContext::argumentsObjectInNative()
1204 {
1205     {
1206         QScriptEngine eng;
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"));
1214     }
1215     {
1216         QScriptEngine eng;
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"));
1222     }
1223 }
1224
1225 static QScriptValue get_jsActivationObject(QScriptContext *ctx, QScriptEngine *)
1226 {
1227     return ctx->parentContext()->parentContext()->activationObject();
1228 }
1229
1230 void tst_QScriptContext::jsActivationObject()
1231 {
1232     QScriptEngine eng;
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"));
1248 }
1249
1250 void tst_QScriptContext::qobjectAsActivationObject()
1251 {
1252     QScriptEngine eng;
1253     QObject object;
1254     QScriptValue scriptObject = eng.newQObject(&object);
1255     QScriptContext *ctx = eng.pushContext();
1256     ctx->setActivationObject(scriptObject);
1257     QVERIFY(ctx->activationObject().equals(scriptObject));
1258
1259     QVERIFY(!scriptObject.property("foo").isValid());
1260     eng.evaluate("function foo() { return 123; }");
1261     {
1262         QScriptValue val = scriptObject.property("foo");
1263         QVERIFY(val.isValid());
1264         QVERIFY(val.isFunction());
1265     }
1266     QVERIFY(!eng.globalObject().property("foo").isValid());
1267
1268     QVERIFY(!scriptObject.property("bar").isValid());
1269     eng.evaluate("var bar = 123");
1270     {
1271         QScriptValue val = scriptObject.property("bar");
1272         QVERIFY(val.isValid());
1273         QVERIFY(val.isNumber());
1274         QCOMPARE(val.toInt32(), 123);
1275     }
1276     QVERIFY(!eng.globalObject().property("bar").isValid());
1277
1278     {
1279         QScriptValue val = eng.evaluate("delete foo");
1280         QVERIFY(val.isBool());
1281         QVERIFY(val.toBool());
1282         QVERIFY(!scriptObject.property("foo").isValid());
1283     }
1284 }
1285
1286 static QScriptValue getParentContextCallee(QScriptContext *ctx, QScriptEngine *)
1287 {
1288     return ctx->parentContext()->callee();
1289 }
1290
1291 void tst_QScriptContext::parentContextCallee_QT2270()
1292 {
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));
1299 }
1300
1301 QTEST_MAIN(tst_QScriptContext)
1302 #include "tst_qscriptcontext.moc"