1/****************************************************************************
2**
3** Copyright (C) 2018 The Qt Company Ltd.
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the test suite of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:GPL-EXCEPT$
9** Commercial License Usage
10** Licensees holding valid commercial Qt licenses may use this file in
11** accordance with the commercial license agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and The Qt Company. For licensing terms
14** and conditions see https://www.qt.io/terms-conditions. For further
15** information use the contact form at https://www.qt.io/contact-us.
16**
17** GNU General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU
19** General Public License version 3 as published by the Free Software
20** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
21** included in the packaging of this file. Please review the following
22** information to ensure the GNU General Public License requirements will
23** be met: https://www.gnu.org/licenses/gpl-3.0.html.
24**
25** $QT_END_LICENSE$
26**
27****************************************************************************/
28
29
30#include <QtTest/QtTest>
31
32#include <QtScript/qscriptcontext.h>
33#include <QtScript/qscriptengine.h>
34#include <QtScript/qscriptvalueiterator.h>
35
36Q_DECLARE_METATYPE(QScriptValueList)
37Q_DECLARE_METATYPE(QScriptContext::Error)
38
39QT_BEGIN_NAMESPACE
40extern bool qt_script_isJITEnabled();
41QT_END_NAMESPACE
42
43class tst_QScriptContext : public QObject
44{
45 Q_OBJECT
46
47public:
48 tst_QScriptContext();
49 virtual ~tst_QScriptContext();
50
51private slots:
52 void callee();
53 void callee_implicitCall();
54 void arguments();
55 void argumentsInJS();
56 void thisObject();
57 void returnValue();
58 void throwError_data();
59 void throwError_fromEvaluate_data();
60 void throwError_fromEvaluate();
61 void throwError_fromCpp_data();
62 void throwError_fromCpp();
63 void throwValue();
64 void evaluateInFunction();
65 void pushAndPopContext();
66 void pushAndPopContext_variablesInActivation();
67 void pushAndPopContext_setThisObject();
68 void pushAndPopContext_throwException();
69 void lineNumber();
70 void backtrace_data();
71 void backtrace();
72 void scopeChain_globalContext();
73 void scopeChain_closure();
74 void scopeChain_withStatement();
75 void pushAndPopScope_globalContext();
76 void pushAndPopScope_globalContext2();
77 void getSetActivationObject_globalContext();
78 void pushScopeEvaluate();
79 void pushScopeCall();
80 void popScopeSimple();
81 void pushAndPopGlobalObjectSimple();
82 void pushAndPopIterative();
83 void getSetActivationObject_customContext();
84 void inheritActivationAndThisObject();
85 void toString();
86 void calledAsConstructor_fromCpp();
87 void calledAsConstructor_fromJS();
88 void calledAsConstructor_parentContext();
89 void argumentsObjectInNative();
90 void jsActivationObject();
91 void qobjectAsActivationObject();
92 void parentContextCallee_QT2270();
93 void popNativeContextScope();
94 void throwErrorInGlobalContext();
95 void throwErrorWithTypeInGlobalContext_data();
96 void throwErrorWithTypeInGlobalContext();
97 void throwValueInGlobalContext();
98};
99
100tst_QScriptContext::tst_QScriptContext()
101{
102}
103
104tst_QScriptContext::~tst_QScriptContext()
105{
106}
107
108static QScriptValue get_callee(QScriptContext *ctx, QScriptEngine *)
109{
110 return ctx->callee();
111}
112
113static QScriptValue store_callee_and_return_primitive(QScriptContext *ctx, QScriptEngine *eng)
114{
115 ctx->thisObject().setProperty(name: "callee", value: ctx->callee());
116 return QScriptValue(eng, 123);
117}
118
119void tst_QScriptContext::callee()
120{
121 QScriptEngine eng;
122
123 QScriptValue fun = eng.newFunction(signature: get_callee);
124 fun.setProperty(name: "foo", value: QScriptValue(&eng, "bar"));
125 eng.globalObject().setProperty(name: "get_callee", value: fun);
126
127 QScriptValue result = eng.evaluate(program: "get_callee()");
128 QCOMPARE(result.isFunction(), true);
129 QCOMPARE(result.property("foo").toString(), QString("bar"));
130}
131
132void tst_QScriptContext::callee_implicitCall()
133{
134 QScriptEngine eng;
135 // callee when toPrimitive() is called internally
136 QScriptValue fun = eng.newFunction(signature: store_callee_and_return_primitive);
137 QScriptValue obj = eng.newObject();
138 obj.setProperty(name: "toString", value: fun);
139 QVERIFY(!obj.property("callee").isValid());
140 (void)obj.toString();
141 QVERIFY(obj.property("callee").isFunction());
142 QVERIFY(obj.property("callee").strictlyEquals(fun));
143
144 obj.setProperty(name: "callee", value: QScriptValue());
145 QVERIFY(!obj.property("callee").isValid());
146 obj.setProperty(name: "valueOf", value: fun);
147 (void)obj.toNumber();
148 QVERIFY(obj.property("callee").isFunction());
149 QVERIFY(obj.property("callee").strictlyEquals(fun));
150}
151
152static QScriptValue get_arguments(QScriptContext *ctx, QScriptEngine *eng)
153{
154 QScriptValue array = eng->newArray();
155 for (int i = 0; i < ctx->argumentCount(); ++i)
156 array.setProperty(name: QString::number(i), value: ctx->argument(index: i));
157 return array;
158}
159
160static QScriptValue get_argumentsObject(QScriptContext *ctx, QScriptEngine *)
161{
162 return ctx->argumentsObject();
163}
164
165void tst_QScriptContext::arguments()
166{
167 QScriptEngine eng;
168
169 // See section 10.6 ("Arguments Object") of ECMA-262.
170
171 {
172 QScriptValue args = eng.currentContext()->argumentsObject();
173 QVERIFY(args.isObject());
174 QCOMPARE(args.property("length").toInt32(), 0);
175 }
176 {
177 QScriptValue fun = eng.newFunction(signature: get_arguments);
178 eng.globalObject().setProperty(name: "get_arguments", value: fun);
179 }
180
181 for (int x = 0; x < 2; ++x) {
182 // The expected arguments array should be the same regardless of
183 // whether get_arguments() is called as a constructor.
184 QString prefix;
185 if (x == 0)
186 prefix = "";
187 else
188 prefix = "new ";
189 {
190 QScriptValue result = eng.evaluate(program: prefix+"get_arguments()");
191 QCOMPARE(result.isArray(), true);
192 QCOMPARE(result.property("length").toUInt32(), quint32(0));
193 }
194
195 {
196 QScriptValue result = eng.evaluate(program: prefix+"get_arguments(123)");
197 QCOMPARE(result.isArray(), true);
198 QCOMPARE(result.property("length").toUInt32(), quint32(1));
199 QCOMPARE(result.property("0").isNumber(), true);
200 QCOMPARE(result.property("0").toNumber(), 123.0);
201 }
202
203 {
204 QScriptValue result = eng.evaluate(program: prefix+"get_arguments(\"ciao\", null, true, undefined)");
205 QCOMPARE(result.isArray(), true);
206 QCOMPARE(result.property("length").toUInt32(), quint32(4));
207 QCOMPARE(result.property("0").isString(), true);
208 QCOMPARE(result.property("0").toString(), QString("ciao"));
209 QCOMPARE(result.property("1").isNull(), true);
210 QCOMPARE(result.property("2").isBoolean(), true);
211 QCOMPARE(result.property("2").toBoolean(), true);
212 QCOMPARE(result.property("3").isUndefined(), true);
213 }
214
215 {
216 QScriptValue fun = eng.newFunction(signature: get_argumentsObject);
217 eng.globalObject().setProperty(name: "get_argumentsObject", value: fun);
218 }
219
220 {
221 QScriptValue fun = eng.evaluate(program: "get_argumentsObject");
222 QCOMPARE(fun.isFunction(), true);
223 QScriptValue result = eng.evaluate(program: prefix+"get_argumentsObject()");
224 QCOMPARE(result.isArray(), false);
225 QVERIFY(result.isObject());
226 QCOMPARE(result.property("length").toUInt32(), quint32(0));
227 QCOMPARE(result.propertyFlags("length"), QScriptValue::SkipInEnumeration);
228 QCOMPARE(result.property("callee").strictlyEquals(fun), true);
229 QCOMPARE(result.propertyFlags("callee"), QScriptValue::SkipInEnumeration);
230
231 // callee and length properties should be writable.
232 QScriptValue replacedCallee(&eng, 123);
233 result.setProperty(name: "callee", value: replacedCallee);
234 QVERIFY(result.property("callee").equals(replacedCallee));
235 QScriptValue replacedLength(&eng, 456);
236 result.setProperty(name: "length", value: replacedLength);
237
238 // callee and length properties should be deletable.
239 QVERIFY(result.property("length").equals(replacedLength));
240 result.setProperty(name: "callee", value: QScriptValue());
241 QVERIFY(!result.property("callee").isValid());
242 result.setProperty(name: "length", value: QScriptValue());
243 QVERIFY(!result.property("length").isValid());
244 }
245
246 {
247 QScriptValue result = eng.evaluate(program: prefix+"get_argumentsObject(123)");
248 eng.evaluate(program: "function nestedArg(x,y,z) { var w = get_argumentsObject('ABC' , x+y+z); return w; }");
249 QScriptValue result2 = eng.evaluate(program: "nestedArg(1, 'a', 2)");
250 QCOMPARE(result.isArray(), false);
251 QVERIFY(result.isObject());
252 QCOMPARE(result.property("length").toUInt32(), quint32(1));
253 QCOMPARE(result.property("0").isNumber(), true);
254 QCOMPARE(result.property("0").toNumber(), 123.0);
255 QVERIFY(result2.isObject());
256 QCOMPARE(result2.property("length").toUInt32(), quint32(2));
257 QCOMPARE(result2.property("0").toString(), QString::fromLatin1("ABC"));
258 QCOMPARE(result2.property("1").toString(), QString::fromLatin1("1a2"));
259 }
260
261 {
262 QScriptValue result = eng.evaluate(program: prefix+"get_argumentsObject(\"ciao\", null, true, undefined)");
263 QCOMPARE(result.isArray(), false);
264 QCOMPARE(result.property("length").toUInt32(), quint32(4));
265 QCOMPARE(result.property("0").isString(), true);
266 QCOMPARE(result.property("0").toString(), QString("ciao"));
267 QCOMPARE(result.property("1").isNull(), true);
268 QCOMPARE(result.property("2").isBoolean(), true);
269 QCOMPARE(result.property("2").toBoolean(), true);
270 QCOMPARE(result.property("3").isUndefined(), true);
271 }
272 }
273}
274
275void tst_QScriptContext::argumentsInJS()
276{
277 QScriptEngine eng;
278 {
279 QScriptValue result = eng.evaluate(program: "(function() { return arguments; })(123)");
280 QCOMPARE(result.isArray(), false);
281 QVERIFY(result.isObject());
282 QCOMPARE(result.property("length").toUInt32(), quint32(1));
283 QCOMPARE(result.property("0").isNumber(), true);
284 QCOMPARE(result.property("0").toNumber(), 123.0);
285 }
286
287 {
288 QScriptValue result = eng.evaluate(program: "(function() { return arguments; })('ciao', null, true, undefined)");
289 QCOMPARE(result.isArray(), false);
290 QCOMPARE(result.property("length").toUInt32(), quint32(4));
291 QCOMPARE(result.property("0").isString(), true);
292 QCOMPARE(result.property("0").toString(), QString("ciao"));
293 QCOMPARE(result.property("1").isNull(), true);
294 QCOMPARE(result.property("2").isBoolean(), true);
295 QCOMPARE(result.property("2").toBoolean(), true);
296 QCOMPARE(result.property("3").isUndefined(), true);
297 }
298}
299
300static QScriptValue get_thisObject(QScriptContext *ctx, QScriptEngine *)
301{
302 return ctx->thisObject();
303}
304
305void tst_QScriptContext::thisObject()
306{
307 QScriptEngine eng;
308
309 QScriptValue fun = eng.newFunction(signature: get_thisObject);
310 eng.globalObject().setProperty(name: "get_thisObject", value: fun);
311
312 {
313 QScriptValue result = eng.evaluate(program: "get_thisObject()");
314 QCOMPARE(result.isObject(), true);
315 QCOMPARE(result.toString(), QString("[object global]"));
316 }
317
318 {
319 QScriptValue result = eng.evaluate(program: "get_thisObject.apply(new Number(123))");
320 QCOMPARE(result.isObject(), true);
321 QCOMPARE(result.toNumber(), 123.0);
322 }
323
324 {
325 QScriptValue obj = eng.newObject();
326 eng.currentContext()->setThisObject(obj);
327 QVERIFY(eng.currentContext()->thisObject().equals(obj));
328 eng.currentContext()->setThisObject(QScriptValue());
329 QVERIFY(eng.currentContext()->thisObject().equals(obj));
330
331 QScriptEngine eng2;
332 QScriptValue obj2 = eng2.newObject();
333 QTest::ignoreMessage(type: QtWarningMsg, message: "QScriptContext::setThisObject() failed: cannot set an object created in a different engine");
334 eng.currentContext()->setThisObject(obj2);
335 }
336}
337
338void tst_QScriptContext::returnValue()
339{
340 QSKIP("Internal function not implemented in JSC-based back-end");
341 QScriptEngine eng;
342 eng.evaluate(program: "123");
343 QCOMPARE(eng.currentContext()->returnValue().toNumber(), 123.0);
344 eng.evaluate(program: "\"ciao\"");
345 QCOMPARE(eng.currentContext()->returnValue().toString(), QString("ciao"));
346}
347
348static QScriptValue throw_Error(QScriptContext *ctx, QScriptEngine *)
349{
350 return ctx->throwError(error: QScriptContext::UnknownError, text: "foo");
351}
352
353static QScriptValue throw_TypeError(QScriptContext *ctx, QScriptEngine *)
354{
355 return ctx->throwError(error: QScriptContext::TypeError, text: "foo");
356}
357
358static QScriptValue throw_ReferenceError(QScriptContext *ctx, QScriptEngine *)
359{
360 return ctx->throwError(error: QScriptContext::ReferenceError, text: "foo");
361}
362
363static QScriptValue throw_SyntaxError(QScriptContext *ctx, QScriptEngine *)
364{
365 return ctx->throwError(error: QScriptContext::SyntaxError, text: "foo");
366}
367
368static QScriptValue throw_RangeError(QScriptContext *ctx, QScriptEngine *)
369{
370 return ctx->throwError(error: QScriptContext::RangeError, text: "foo");
371}
372
373static QScriptValue throw_URIError(QScriptContext *ctx, QScriptEngine *)
374{
375 return ctx->throwError(error: QScriptContext::URIError, text: "foo");
376}
377
378static QScriptValue throw_ErrorAndReturnUndefined(QScriptContext *ctx, QScriptEngine *eng)
379{
380 ctx->throwError(error: QScriptContext::UnknownError, text: "foo");
381 return eng->undefinedValue();
382}
383
384static QScriptValue throw_ErrorAndReturnString(QScriptContext *ctx, QScriptEngine *)
385{
386 return ctx->throwError(error: QScriptContext::UnknownError, text: "foo").toString();
387}
388
389static QScriptValue throw_ErrorAndReturnObject(QScriptContext *ctx, QScriptEngine *eng)
390{
391 ctx->throwError(error: QScriptContext::UnknownError, text: "foo");
392 return eng->newObject();
393}
394
395void tst_QScriptContext::throwError_data()
396{
397 QTest::addColumn<void*>(name: "nativeFunctionPtr");
398 QTest::addColumn<QString>(name: "stringRepresentation");
399
400 QTest::newRow(dataTag: "Error") << reinterpret_cast<void*>(throw_Error) << QString("Error: foo");
401 QTest::newRow(dataTag: "TypeError") << reinterpret_cast<void*>(throw_TypeError) << QString("TypeError: foo");
402 QTest::newRow(dataTag: "ReferenceError") << reinterpret_cast<void*>(throw_ReferenceError) << QString("ReferenceError: foo");
403 QTest::newRow(dataTag: "SyntaxError") << reinterpret_cast<void*>(throw_SyntaxError) << QString("SyntaxError: foo");
404 QTest::newRow(dataTag: "RangeError") << reinterpret_cast<void*>(throw_RangeError) << QString("RangeError: foo");
405 QTest::newRow(dataTag: "URIError") << reinterpret_cast<void*>(throw_URIError) << QString("URIError: foo");
406 QTest::newRow(dataTag: "ErrorAndReturnUndefined") << reinterpret_cast<void*>(throw_ErrorAndReturnUndefined) << QString("Error: foo");
407 QTest::newRow(dataTag: "ErrorAndReturnString") << reinterpret_cast<void*>(throw_ErrorAndReturnString) << QString("Error: foo");
408 QTest::newRow(dataTag: "ErrorAndReturnObject") << reinterpret_cast<void*>(throw_ErrorAndReturnObject) << QString("Error: foo");
409}
410
411void tst_QScriptContext::throwError_fromEvaluate_data()
412{
413 throwError_data();
414}
415
416void tst_QScriptContext::throwError_fromEvaluate()
417{
418 QFETCH(void*, nativeFunctionPtr);
419 QScriptEngine::FunctionSignature nativeFunction = reinterpret_cast<QScriptEngine::FunctionSignature>(nativeFunctionPtr);
420 QFETCH(QString, stringRepresentation);
421 QScriptEngine engine;
422
423 QScriptValue fun = engine.newFunction(signature: nativeFunction);
424 engine.globalObject().setProperty(name: "throw_Error", value: fun);
425 QScriptValue result = engine.evaluate(program: "throw_Error()");
426 QCOMPARE(engine.hasUncaughtException(), true);
427 QCOMPARE(result.isError(), true);
428 QCOMPARE(result.toString(), stringRepresentation);
429}
430
431void tst_QScriptContext::throwError_fromCpp_data()
432{
433 throwError_data();
434}
435
436void tst_QScriptContext::throwError_fromCpp()
437{
438 QFETCH(void*, nativeFunctionPtr);
439 QScriptEngine::FunctionSignature nativeFunction = reinterpret_cast<QScriptEngine::FunctionSignature>(nativeFunctionPtr);
440 QFETCH(QString, stringRepresentation);
441 QScriptEngine engine;
442
443 QScriptValue fun = engine.newFunction(signature: nativeFunction);
444 engine.globalObject().setProperty(name: "throw_Error", value: fun);
445 QScriptValue result = fun.call();
446 QCOMPARE(engine.hasUncaughtException(), true);
447 QCOMPARE(result.isError(), true);
448 QCOMPARE(result.toString(), stringRepresentation);
449}
450
451static QScriptValue throw_value(QScriptContext *ctx, QScriptEngine *)
452{
453 return ctx->throwValue(value: ctx->argument(index: 0));
454}
455
456void tst_QScriptContext::throwValue()
457{
458 QScriptEngine eng;
459
460 QScriptValue fun = eng.newFunction(signature: throw_value);
461 eng.globalObject().setProperty(name: "throw_value", value: fun);
462
463 {
464 QScriptValue result = eng.evaluate(program: "throw_value(123)");
465 QCOMPARE(result.isError(), false);
466 QCOMPARE(result.toNumber(), 123.0);
467 QCOMPARE(eng.hasUncaughtException(), true);
468 }
469}
470
471static QScriptValue evaluate(QScriptContext *, QScriptEngine *eng)
472{
473 return eng->evaluate(program: "a = 123; a");
474// return eng->evaluate("a");
475}
476
477void tst_QScriptContext::evaluateInFunction()
478{
479 QScriptEngine eng;
480
481 QScriptValue fun = eng.newFunction(signature: evaluate);
482 eng.globalObject().setProperty(name: "evaluate", value: fun);
483
484 QScriptValue result = eng.evaluate(program: "evaluate()");
485 QCOMPARE(result.isError(), false);
486 QCOMPARE(result.isNumber(), true);
487 QCOMPARE(result.toNumber(), 123.0);
488 QCOMPARE(eng.hasUncaughtException(), false);
489
490 QCOMPARE(eng.evaluate("a").toNumber(), 123.0);
491}
492
493void tst_QScriptContext::pushAndPopContext()
494{
495 QScriptEngine eng;
496 QScriptContext *topLevel = eng.currentContext();
497 QCOMPARE(topLevel->engine(), &eng);
498
499 QScriptContext *ctx = eng.pushContext();
500 QVERIFY(ctx != 0);
501 QCOMPARE(ctx->parentContext(), topLevel);
502 QCOMPARE(eng.currentContext(), ctx);
503 QCOMPARE(ctx->engine(), &eng);
504 QCOMPARE(ctx->state(), QScriptContext::NormalState);
505 QCOMPARE(ctx->isCalledAsConstructor(), false);
506 QCOMPARE(ctx->argumentCount(), 0);
507 QCOMPARE(ctx->argument(0).isUndefined(), true);
508 QVERIFY(!ctx->argument(-1).isValid());
509 QCOMPARE(ctx->argumentsObject().isObject(), true);
510 QCOMPARE(ctx->activationObject().isObject(), true);
511 QCOMPARE(ctx->callee().isValid(), false);
512 QCOMPARE(ctx->thisObject().strictlyEquals(eng.globalObject()), true);
513 QCOMPARE(ctx->scopeChain().size(), 2);
514 QVERIFY(ctx->scopeChain().at(0).equals(ctx->activationObject()));
515 QVERIFY(ctx->scopeChain().at(1).equals(eng.globalObject()));
516
517 QScriptContext *ctx2 = eng.pushContext();
518 QCOMPARE(ctx2->parentContext(), ctx);
519 QCOMPARE(eng.currentContext(), ctx2);
520
521 eng.popContext();
522 QCOMPARE(eng.currentContext(), ctx);
523 eng.popContext();
524 QCOMPARE(eng.currentContext(), topLevel);
525
526 // popping the top-level context is not allowed
527 QTest::ignoreMessage(type: QtWarningMsg, message: "QScriptEngine::popContext() doesn't match with pushContext()");
528 eng.popContext();
529 QCOMPARE(eng.currentContext(), topLevel);
530}
531
532void tst_QScriptContext::pushAndPopContext_variablesInActivation()
533{
534 QScriptEngine eng;
535 QScriptContext *ctx = eng.pushContext();
536 ctx->activationObject().setProperty(name: "foo", value: QScriptValue(&eng, 123));
537 // evaluate() should use the current context.
538 QVERIFY(eng.evaluate("foo").strictlyEquals(QScriptValue(&eng, 123)));
539 QCOMPARE(ctx->activationObject().propertyFlags("foo"), QScriptValue::PropertyFlags(0));
540
541 ctx->activationObject().setProperty(arrayIndex: 4, value: 456);
542 QVERIFY(ctx->activationObject().property(4, QScriptValue::ResolveLocal).equals(456));
543
544 // New JS variables should become properties of the current context's activation.
545 eng.evaluate(program: "var bar = 'ciao'");
546 QVERIFY(ctx->activationObject().property("bar", QScriptValue::ResolveLocal).strictlyEquals(QScriptValue(&eng, "ciao")));
547
548 ctx->activationObject().setProperty(name: "baz", value: 789, flags: QScriptValue::ReadOnly);
549 QVERIFY(eng.evaluate("baz").equals(789));
550 QCOMPARE(ctx->activationObject().propertyFlags("baz"), QScriptValue::ReadOnly);
551
552 QSet<QString> activationPropertyNames;
553 QScriptValueIterator it(ctx->activationObject());
554 while (it.hasNext()) {
555 it.next();
556 activationPropertyNames.insert(value: it.name());
557 }
558 QCOMPARE(activationPropertyNames.size(), 4);
559 QVERIFY(activationPropertyNames.contains("foo"));
560 QVERIFY(activationPropertyNames.contains("4"));
561 QVERIFY(activationPropertyNames.contains("bar"));
562 QVERIFY(activationPropertyNames.contains("baz"));
563
564 eng.popContext();
565}
566
567void tst_QScriptContext::pushAndPopContext_setThisObject()
568{
569 QScriptEngine eng;
570 QScriptContext *ctx = eng.pushContext();
571 QScriptValue obj = eng.newObject();
572 obj.setProperty(name: "prop", value: QScriptValue(&eng, 456));
573 ctx->setThisObject(obj);
574 QScriptValue ret = eng.evaluate(program: "var tmp = this.prop; tmp + 1");
575 QCOMPARE(eng.currentContext(), ctx);
576 QVERIFY(ret.strictlyEquals(QScriptValue(&eng, 457)));
577 eng.popContext();
578}
579
580void tst_QScriptContext::pushAndPopContext_throwException()
581{
582 QScriptEngine eng;
583 QScriptContext *ctx = eng.pushContext();
584 QScriptValue ret = eng.evaluate(program: "throw new Error('oops')");
585 QVERIFY(ret.isError());
586 QVERIFY(eng.hasUncaughtException());
587 QCOMPARE(eng.currentContext(), ctx);
588 eng.popContext();
589}
590
591void tst_QScriptContext::popNativeContextScope()
592{
593 QScriptEngine eng;
594 QScriptContext *ctx = eng.pushContext();
595 QVERIFY(ctx->popScope().isObject()); // the activation object
596
597 QCOMPARE(ctx->scopeChain().size(), 1);
598 QVERIFY(ctx->scopeChain().at(0).strictlyEquals(eng.globalObject()));
599 // This was different in 4.5: scope and activation were decoupled
600 QVERIFY(ctx->activationObject().strictlyEquals(eng.globalObject()));
601
602 QVERIFY(!eng.evaluate("var foo = 123; function bar() {}").isError());
603 QVERIFY(eng.globalObject().property("foo").isNumber());
604 QVERIFY(eng.globalObject().property("bar").isFunction());
605
606 QScriptValue customScope = eng.newObject();
607 ctx->pushScope(object: customScope);
608 QCOMPARE(ctx->scopeChain().size(), 2);
609 QVERIFY(ctx->scopeChain().at(0).strictlyEquals(customScope));
610 QVERIFY(ctx->scopeChain().at(1).strictlyEquals(eng.globalObject()));
611 QVERIFY(ctx->activationObject().strictlyEquals(eng.globalObject()));
612 ctx->setActivationObject(customScope);
613 QVERIFY(ctx->activationObject().strictlyEquals(customScope));
614 QCOMPARE(ctx->scopeChain().size(), 2);
615 QVERIFY(ctx->scopeChain().at(0).strictlyEquals(customScope));
616 QEXPECT_FAIL("", "QTBUG-11012: Global object is replaced in scope chain", Continue);
617 QVERIFY(ctx->scopeChain().at(1).strictlyEquals(eng.globalObject()));
618
619 QVERIFY(!eng.evaluate("baz = 456; var foo = 789; function barbar() {}").isError());
620 QEXPECT_FAIL("", "QTBUG-11012", Continue);
621 QVERIFY(eng.globalObject().property("baz").isNumber());
622 QVERIFY(customScope.property("foo").isNumber());
623 QVERIFY(customScope.property("barbar").isFunction());
624
625 QVERIFY(ctx->popScope().strictlyEquals(customScope));
626 QCOMPARE(ctx->scopeChain().size(), 1);
627 QEXPECT_FAIL("", "QTBUG-11012", Continue);
628 QVERIFY(ctx->scopeChain().at(0).strictlyEquals(eng.globalObject()));
629
630 // Need to push another object, otherwise we crash in popContext() (QTBUG-11012)
631 ctx->pushScope(object: customScope);
632 eng.popContext();
633}
634
635void tst_QScriptContext::lineNumber()
636{
637 QScriptEngine eng;
638
639 QScriptValue result = eng.evaluate(program: "try { eval(\"foo = 123;\\n this[is{a{syntax|error@#$%@#% \"); } catch (e) { e.lineNumber; }", fileName: "foo.qs", lineNumber: 123);
640 QVERIFY(!eng.hasUncaughtException());
641 QVERIFY(result.isNumber());
642 QCOMPARE(result.toInt32(), 2);
643
644 result = eng.evaluate(program: "foo = 123;\n bar = 42\n0 = 0");
645 QVERIFY(eng.hasUncaughtException());
646 QCOMPARE(eng.uncaughtExceptionLineNumber(), 3);
647 QCOMPARE(result.property("lineNumber").toInt32(), 3);
648}
649
650static QScriptValue getBacktrace(QScriptContext *ctx, QScriptEngine *eng)
651{
652 return qScriptValueFromValue(engine: eng, t: ctx->backtrace());
653}
654
655static QScriptValue custom_eval(QScriptContext *ctx, QScriptEngine *eng)
656{
657 return eng->evaluate(program: ctx->argumentsObject().property(arrayIndex: 0).toString(), fileName: ctx->argumentsObject().property(arrayIndex: 1).toString());
658}
659
660static QScriptValue custom_call(QScriptContext *ctx, QScriptEngine *)
661{
662 return ctx->argumentsObject().property(arrayIndex: 0).call(thisObject: QScriptValue(), args: QScriptValueList() << ctx->argumentsObject().property(arrayIndex: 1));
663}
664
665static QScriptValue native_recurse(QScriptContext *ctx, QScriptEngine *eng)
666{
667 QScriptValue func = ctx->argumentsObject().property(arrayIndex: 0);
668 QScriptValue n = ctx->argumentsObject().property(arrayIndex: 1);
669
670 if(n.toUInt32() <= 1) {
671 return func.call(thisObject: QScriptValue(), args: QScriptValueList());
672 } else {
673 return eng->evaluate(program: "native_recurse").call(thisObject: QScriptValue(),
674 args: QScriptValueList() << func << QScriptValue(n.toUInt32() - 1));
675 }
676}
677
678void tst_QScriptContext::backtrace_data()
679{
680 QTest::addColumn<QString>(name: "code");
681 QTest::addColumn<QStringList>(name: "expectedbacktrace");
682
683 {
684 QString source(
685 "function foo() {\n"
686 " return bt(123);\n"
687 "}\n"
688 "foo('hello', { })\n"
689 "var r = 0;");
690
691 QStringList expected;
692 expected << "<native>(123) at -1"
693 << "foo('hello', [object Object]) at testfile:2"
694 << "<global>() at testfile:4";
695
696
697 QTest::newRow(dataTag: "simple") << source << expected;
698 }
699
700 {
701 QStringList expected;
702 QString source = QString(
703 "function foo(arg1 , arg2) {\n"
704 " return eval(\"%1\");\n"
705 "}\n"
706 "foo('hello', 456)\n"
707 "var a = 0;"
708 ).arg(a: "\\n \\n bt('hey'); \\n");
709
710 expected << "<native>('hey') at -1"
711 << "<eval>() at 3"
712 << "foo(arg1 = 'hello', arg2 = 456) at testfile:2"
713 << "<global>() at testfile:4";
714
715 QTest::newRow(dataTag: "eval") << source << expected;
716 }
717
718 {
719 QString eval_code(
720 "function bar(a) {\\n"
721 " return bt('m');\\n"
722 "}\\n"
723 "bar('b'); \\n");
724 QString source = QString(
725 "function foo() {\n"
726 " return custom_eval(\"%1\", 'eval.js');\n"
727 "}\n"
728 "foo()"
729 ).arg(a: eval_code);
730
731 QStringList expected;
732 expected << "<native>('m') at -1"
733 << "bar(a = 'b') at eval.js:2"
734 << "<eval>() at eval.js:4"
735 << QString("<native>('%1', 'eval.js') at -1").arg(a: eval_code.replace(before: "\\n", after: "\n"))
736 << "foo() at testfile:2"
737 << "<global>() at testfile:4";
738
739 QTest::newRow(dataTag: "custom_eval") << source << expected;
740 }
741 {
742 QString f("function (a) {\n return bt(a); \n }");
743 QString source = QString(
744 "function foo(f) {\n"
745 " return f('b');\n"
746 "}\n"
747 "foo(%1)"
748 ).arg(a: f);
749
750 QStringList expected;
751 expected << "<native>('b') at -1"
752 << "<anonymous>(a = 'b') at testfile:5"
753 << QString("foo(f = %1) at testfile:2").arg(a: f)
754 << "<global>() at testfile:6";
755
756 QTest::newRow(dataTag: "closure") << source << expected;
757 }
758
759 {
760 QStringList expected;
761 QString source = QString(
762 "var o = new Object;\n"
763 "o.foo = function plop() {\n"
764 " return eval(\"%1\");\n"
765 "}\n"
766 "o.foo('hello', 456)\n"
767 ).arg(a: "\\n \\n bt('hey'); \\n");
768
769 expected << "<native>('hey') at -1"
770 << "<eval>() at 3"
771 << "plop('hello', 456) at testfile:3"
772 << "<global>() at testfile:5";
773
774 QTest::newRow(dataTag: "eval in member") << source << expected;
775 }
776
777 {
778 QString source(
779 "function foo(a) {\n"
780 " return bt(123);\n"
781 "}\n"
782 "function bar() {\n"
783 " var v = foo('arg', 4);\n"
784 " return v;\n"
785 "}\n"
786 "bar('hello', { });\n");
787
788 QStringList expected;
789 expected << "<native>(123) at -1"
790 << "foo(a = 'arg', 4) at testfile:2"
791 << "bar('hello', [object Object]) at testfile:5"
792 << "<global>() at testfile:8";
793
794
795 QTest::newRow(dataTag: "two function") << source << expected;
796 }
797
798 {
799 QString func("function foo(a, b) {\n"
800 " return bt(a);\n"
801 "}");
802
803 QString source = func + QString::fromLatin1(str: "\n"
804 "custom_call(foo, 'hello');\n"
805 "var a = 1\n");
806
807 QStringList expected;
808 expected << "<native>('hello') at -1"
809 << "foo(a = 'hello') at testfile:2"
810 << QString("<native>(%1, 'hello') at -1").arg(a: func)
811 << "<global>() at testfile:4";
812
813 QTest::newRow(dataTag: "call") << source << expected;
814 }
815
816 {
817 QString source = QString::fromLatin1(str: "\n"
818 "custom_call(bt, 'hello_world');\n"
819 "var a = 1\n");
820
821 QStringList expected;
822 expected << "<native>('hello_world') at -1"
823 << "<native>(function () {\n [native code]\n}, 'hello_world') at -1"
824 << "<global>() at testfile:2";
825
826 QTest::newRow(dataTag: "call native") << source << expected;
827 }
828
829 {
830 QLatin1String func( "function f1() {\n"
831 " eval('var q = 4');\n"
832 " return custom_call(bt, 22);\n"
833 "}");
834
835 QString source = QString::fromLatin1(str: "\n"
836 "function f2() {\n"
837 " func = %1\n"
838 " return custom_call(func, 12);\n"
839 "}\n"
840 "f2();\n").arg(a: func);
841
842 QStringList expected;
843 expected << "<native>(22) at -1"
844 << "<native>(function () {\n [native code]\n}, 22) at -1"
845 << "f1(12) at testfile:5"
846 << QString::fromLatin1(str: "<native>(%1, 12) at -1").arg(a: func)
847 << "f2() at testfile:7"
848 << "<global>() at testfile:9";
849
850
851 QTest::newRow(dataTag: "calls with closures") << source << expected;
852 }
853
854 {
855 QLatin1String func( "function js_bt() {\n"
856 " return bt();\n"
857 "}");
858
859 QString source = QString::fromLatin1(str: "\n"
860 "%1\n"
861 "function f() {\n"
862 " return native_recurse(js_bt, 12);\n"
863 "}\n"
864 "f();\n").arg(a: func);
865
866 QStringList expected;
867 expected << "<native>() at -1" << "js_bt() at testfile:3";
868 for(int n = 1; n <= 12; n++) {
869 expected << QString::fromLatin1(str: "<native>(%1, %2) at -1")
870 .arg(a: func).arg(a: n);
871 }
872 expected << "f() at testfile:6";
873 expected << "<global>() at testfile:8";
874
875 QTest::newRow(dataTag: "native recursive") << source << expected;
876 }
877
878 {
879 QString source = QString::fromLatin1(str: "\n"
880 "function finish() {\n"
881 " return bt();\n"
882 "}\n"
883 "function rec(n) {\n"
884 " if(n <= 1)\n"
885 " return finish();\n"
886 " else\n"
887 " return rec (n - 1);\n"
888 "}\n"
889 "function f() {\n"
890 " return rec(12);\n"
891 "}\n"
892 "f();\n");
893
894 QStringList expected;
895 expected << "<native>() at -1" << "finish() at testfile:3";
896 for(int n = 1; n <= 12; n++) {
897 expected << QString::fromLatin1(str: "rec(n = %1) at testfile:%2")
898 .arg(a: n).arg(a: (n==1) ? 7 : 9);
899 }
900 expected << "f() at testfile:12";
901 expected << "<global>() at testfile:14";
902
903 QTest::newRow(dataTag: "js recursive") << source << expected;
904 }
905
906 {
907 QString source = QString::fromLatin1(
908 str: "[0].forEach(\n"
909 " function() {\n"
910 " result = bt();\n"
911 "}); result");
912
913 QStringList expected;
914 expected << "<native>() at -1"
915 << "<anonymous>(0, 0, 0) at testfile:3"
916 << QString::fromLatin1(str: "forEach(%0) at -1")
917 // Because the JIT doesn't store the arguments in the frame
918 // for built-in functions, arguments are not available.
919 // Will work when the copy of JavaScriptCore is updated
920 // (QTBUG-16568).
921 .arg(a: qt_script_isJITEnabled()
922 ? ""
923 : "function () {\n result = bt();\n}")
924 << "<global>() at testfile:4";
925 QTest::newRow(dataTag: "js callback from built-in") << source << expected;
926 }
927
928 {
929 QString source = QString::fromLatin1(
930 str: "[10,20].forEach(\n"
931 " function() {\n"
932 " result = bt();\n"
933 "}); result");
934
935 QStringList expected;
936 expected << "<native>() at -1"
937 << "<anonymous>(20, 1, 10,20) at testfile:3"
938 << QString::fromLatin1(str: "forEach(%0) at -1")
939 // Because the JIT doesn't store the arguments in the frame
940 // for built-in functions, arguments are not available.
941 // Will work when the copy of JavaScriptCore is updated
942 // (QTBUG-16568).
943 .arg(a: qt_script_isJITEnabled()
944 ? ""
945 : "function () {\n result = bt();\n}")
946 << "<global>() at testfile:4";
947 QTest::newRow(dataTag: "js callback from built-in") << source << expected;
948 }
949}
950
951
952void tst_QScriptContext::backtrace()
953{
954 QFETCH(QString, code);
955 QFETCH(QStringList, expectedbacktrace);
956
957 QScriptEngine eng;
958 eng.globalObject().setProperty(name: "bt", value: eng.newFunction(signature: getBacktrace));
959 eng.globalObject().setProperty(name: "custom_eval", value: eng.newFunction(signature: custom_eval));
960 eng.globalObject().setProperty(name: "custom_call", value: eng.newFunction(signature: custom_call));
961 eng.globalObject().setProperty(name: "native_recurse", value: eng.newFunction(signature: native_recurse));
962
963 QString fileName = "testfile";
964 QScriptValue ret = eng.evaluate(program: code, fileName);
965 QVERIFY(!eng.hasUncaughtException());
966 QVERIFY(ret.isArray());
967 QStringList slist = qscriptvalue_cast<QStringList>(value: ret);
968 QEXPECT_FAIL("eval", "QTBUG-17842: Missing line number in backtrace when function calls eval()", Continue);
969 QEXPECT_FAIL("eval in member", "QTBUG-17842: Missing line number in backtrace when function calls eval()", Continue);
970 QCOMPARE(slist, expectedbacktrace);
971}
972
973static QScriptValue getScopeChain(QScriptContext *ctx, QScriptEngine *eng)
974{
975 return qScriptValueFromValue(engine: eng, t: ctx->parentContext()->scopeChain());
976}
977
978void tst_QScriptContext::scopeChain_globalContext()
979{
980 QScriptEngine eng;
981 {
982 QScriptValueList ret = eng.currentContext()->scopeChain();
983 QCOMPARE(ret.size(), 1);
984 QVERIFY(ret.at(0).strictlyEquals(eng.globalObject()));
985 }
986 {
987 eng.globalObject().setProperty(name: "getScopeChain", value: eng.newFunction(signature: getScopeChain));
988 QScriptValueList ret = qscriptvalue_cast<QScriptValueList>(value: eng.evaluate(program: "getScopeChain()"));
989 QCOMPARE(ret.size(), 1);
990 QVERIFY(ret.at(0).strictlyEquals(eng.globalObject()));
991 }
992}
993
994void tst_QScriptContext::scopeChain_closure()
995{
996 QScriptEngine eng;
997 eng.globalObject().setProperty(name: "getScopeChain", value: eng.newFunction(signature: getScopeChain));
998
999 eng.evaluate(program: "function foo() { function bar() { return getScopeChain(); } return bar() }");
1000 QScriptValueList ret = qscriptvalue_cast<QScriptValueList>(value: eng.evaluate(program: "foo()"));
1001 // JSC will not create an activation for bar() unless we insert a call
1002 // to eval() in the function body; JSC has no way of knowing that the
1003 // native function will be asking for the activation, and we don't want
1004 // to needlessly create it.
1005 QEXPECT_FAIL("", "QTBUG-10313: JSC optimizes away the activation object", Abort);
1006 QCOMPARE(ret.size(), 3);
1007 QVERIFY(ret.at(2).strictlyEquals(eng.globalObject()));
1008 QCOMPARE(ret.at(1).toString(), QString::fromLatin1("activation"));
1009 QVERIFY(ret.at(1).property("arguments").isObject());
1010 QCOMPARE(ret.at(0).toString(), QString::fromLatin1("activation"));
1011 QVERIFY(ret.at(0).property("arguments").isObject());
1012}
1013
1014void tst_QScriptContext::scopeChain_withStatement()
1015{
1016 QScriptEngine eng;
1017 eng.globalObject().setProperty(name: "getScopeChain", value: eng.newFunction(signature: getScopeChain));
1018 {
1019 QScriptValueList ret = qscriptvalue_cast<QScriptValueList>(value: eng.evaluate(program: "o = { x: 123 }; with(o) getScopeChain();"));
1020 QEXPECT_FAIL("", "QTBUG-17131: with-scope isn't reflected by QScriptContext", Abort);
1021 QCOMPARE(ret.size(), 2);
1022 QVERIFY(ret.at(1).strictlyEquals(eng.globalObject()));
1023 QVERIFY(ret.at(0).isObject());
1024 QCOMPARE(ret.at(0).property("x").toInt32(), 123);
1025 }
1026 {
1027 QScriptValueList ret = qscriptvalue_cast<QScriptValueList>(
1028 value: eng.evaluate(program: "o1 = { x: 123}; o2 = { y: 456 }; with(o1) { with(o2) { getScopeChain(); } }"));
1029 QCOMPARE(ret.size(), 3);
1030 QVERIFY(ret.at(2).strictlyEquals(eng.globalObject()));
1031 QVERIFY(ret.at(1).isObject());
1032 QCOMPARE(ret.at(1).property("x").toInt32(), 123);
1033 QVERIFY(ret.at(0).isObject());
1034 QCOMPARE(ret.at(0).property("y").toInt32(), 456);
1035 }
1036}
1037
1038void tst_QScriptContext::pushScopeEvaluate()
1039{
1040 QScriptEngine engine;
1041 QScriptValue object = engine.newObject();
1042 object.setProperty(name: "foo", value: 1234);
1043 object.setProperty(arrayIndex: 1, value: 1234);
1044 engine.currentContext()->pushScope(object);
1045 object.setProperty(name: "bar", value: 4321);
1046 object.setProperty(arrayIndex: 2, value: 4321);
1047 QVERIFY(engine.evaluate("foo").equals(1234));
1048 QVERIFY(engine.evaluate("bar").equals(4321));
1049}
1050
1051void tst_QScriptContext::pushScopeCall()
1052{
1053 QScriptEngine engine;
1054 QScriptValue object = engine.globalObject();
1055 QScriptValue thisObject = engine.newObject();
1056 QScriptValue function = engine.evaluate(program: "(function(property){return this[property]; })");
1057 QVERIFY(function.isFunction());
1058 object.setProperty(name: "foo", value: 1234);
1059 thisObject.setProperty(name: "foo", value: "foo");
1060 engine.currentContext()->pushScope(object);
1061 object.setProperty(name: "bar", value: 4321);
1062 thisObject.setProperty(name: "bar", value: "bar");
1063 QVERIFY(function.call(QScriptValue(), QScriptValueList() << "foo").equals(1234));
1064 QVERIFY(function.call(QScriptValue(), QScriptValueList() << "bar").equals(4321));
1065 QVERIFY(function.call(thisObject, QScriptValueList() << "foo").equals("foo"));
1066 QVERIFY(function.call(thisObject, QScriptValueList() << "bar").equals("bar"));
1067}
1068
1069void tst_QScriptContext::popScopeSimple()
1070{
1071 QScriptEngine engine;
1072 QScriptValue object = engine.newObject();
1073 QScriptValue globalObject = engine.globalObject();
1074 engine.currentContext()->pushScope(object);
1075 QVERIFY(engine.currentContext()->popScope().strictlyEquals(object));
1076 QVERIFY(engine.globalObject().strictlyEquals(globalObject));
1077}
1078
1079void tst_QScriptContext::pushAndPopGlobalObjectSimple()
1080{
1081 QScriptEngine engine;
1082 QScriptValue globalObject = engine.globalObject();
1083 engine.currentContext()->pushScope(object: globalObject);
1084 QVERIFY(engine.currentContext()->popScope().strictlyEquals(globalObject));
1085 QVERIFY(engine.globalObject().strictlyEquals(globalObject));
1086}
1087
1088void tst_QScriptContext::pushAndPopIterative()
1089{
1090 QScriptEngine engine;
1091 for (uint repeat = 0; repeat < 2; ++repeat) {
1092 for (uint i = 1; i < 11; ++i) {
1093 QScriptValue object = engine.newObject();
1094 object.setProperty(name: "x", value: i + 10 * repeat);
1095 engine.currentContext()->pushScope(object);
1096 }
1097 for (uint i = 10; i > 0; --i) {
1098 QScriptValue object = engine.currentContext()->popScope();
1099 QVERIFY(object.property("x").equals(i + 10 * repeat));
1100 }
1101 }
1102}
1103
1104void tst_QScriptContext::pushAndPopScope_globalContext()
1105{
1106 QScriptEngine eng;
1107 QScriptContext *ctx = eng.currentContext();
1108 QCOMPARE(ctx->scopeChain().size(), 1);
1109 QVERIFY(ctx->scopeChain().at(0).strictlyEquals(eng.globalObject()));
1110
1111 QVERIFY(ctx->popScope().strictlyEquals(eng.globalObject()));
1112 ctx->pushScope(object: eng.globalObject());
1113 QCOMPARE(ctx->scopeChain().size(), 1);
1114 QVERIFY(ctx->scopeChain().at(0).strictlyEquals(eng.globalObject()));
1115
1116 QScriptValue obj = eng.newObject();
1117 ctx->pushScope(object: obj);
1118 QCOMPARE(ctx->scopeChain().size(), 2);
1119 QVERIFY(ctx->scopeChain().at(0).strictlyEquals(obj));
1120 QVERIFY(ctx->scopeChain().at(1).strictlyEquals(eng.globalObject()));
1121
1122 QVERIFY(ctx->popScope().strictlyEquals(obj));
1123 QCOMPARE(ctx->scopeChain().size(), 1);
1124 QVERIFY(ctx->scopeChain().at(0).strictlyEquals(eng.globalObject()));
1125
1126 {
1127 QScriptValue ret = eng.evaluate(program: "x");
1128 QVERIFY(ret.isError());
1129 eng.clearExceptions();
1130 }
1131 QCOMPARE(ctx->scopeChain().size(), 1);
1132 QVERIFY(ctx->scopeChain().at(0).strictlyEquals(eng.globalObject()));
1133
1134 // task 236685
1135 QScriptValue qobj = eng.newQObject(object: this, ownership: QScriptEngine::QtOwnership, options: QScriptEngine::AutoCreateDynamicProperties);
1136 ctx->pushScope(object: qobj);
1137 QCOMPARE(ctx->scopeChain().size(), 2);
1138 QVERIFY(ctx->scopeChain().at(0).strictlyEquals(qobj));
1139 QVERIFY(ctx->scopeChain().at(1).strictlyEquals(eng.globalObject()));
1140 {
1141 QScriptValue ret = eng.evaluate(program: "print");
1142 QVERIFY(ret.isFunction());
1143 }
1144 ctx->popScope();
1145
1146 ctx->pushScope(object: obj);
1147 QCOMPARE(ctx->scopeChain().size(), 2);
1148 QVERIFY(ctx->scopeChain().at(0).strictlyEquals(obj));
1149 obj.setProperty(name: "x", value: 123);
1150 {
1151 QScriptValue ret = eng.evaluate(program: "x");
1152 QVERIFY(ret.isNumber());
1153 QCOMPARE(ret.toInt32(), 123);
1154 }
1155 QVERIFY(ctx->popScope().strictlyEquals(obj));
1156 QCOMPARE(ctx->scopeChain().size(), 1);
1157 QVERIFY(ctx->scopeChain().at(0).strictlyEquals(eng.globalObject()));
1158
1159 ctx->pushScope(object: QScriptValue());
1160 QCOMPARE(ctx->scopeChain().size(), 1);
1161}
1162
1163void tst_QScriptContext::pushAndPopScope_globalContext2()
1164{
1165 QScriptEngine eng;
1166 QScriptContext *ctx = eng.currentContext();
1167 QVERIFY(ctx->popScope().strictlyEquals(eng.globalObject()));
1168 QVERIFY(ctx->scopeChain().isEmpty());
1169
1170 // Used to work with old back-end, doesn't with new one because JSC requires that the last object in
1171 // a scope chain is the Global Object.
1172 QTest::ignoreMessage(type: QtWarningMsg, message: "QScriptContext::pushScope() failed: initial object in scope chain has to be the Global Object");
1173 ctx->pushScope(object: eng.newObject());
1174 QCOMPARE(ctx->scopeChain().size(), 0);
1175
1176 QScriptEngine eng2;
1177 QScriptValue obj2 = eng2.newObject();
1178 QTest::ignoreMessage(type: QtWarningMsg, message: "QScriptContext::pushScope() failed: cannot push an object created in a different engine");
1179 ctx->pushScope(object: obj2);
1180 QVERIFY(ctx->scopeChain().isEmpty());
1181
1182 QVERIFY(!ctx->popScope().isValid());
1183}
1184
1185static QScriptValue get_activationObject(QScriptContext *ctx, QScriptEngine *)
1186{
1187 return ctx->activationObject();
1188}
1189
1190void tst_QScriptContext::getSetActivationObject_globalContext()
1191{
1192 QScriptEngine eng;
1193 QScriptContext *ctx = eng.currentContext();
1194 QVERIFY(ctx->activationObject().equals(eng.globalObject()));
1195
1196 ctx->setActivationObject(QScriptValue());
1197 QVERIFY(ctx->activationObject().equals(eng.globalObject()));
1198 QCOMPARE(ctx->engine(), &eng);
1199
1200 QScriptValue obj = eng.newObject();
1201 ctx->setActivationObject(obj);
1202 QVERIFY(ctx->activationObject().equals(obj));
1203 QCOMPARE(ctx->scopeChain().size(), 1);
1204 QVERIFY(ctx->scopeChain().at(0).equals(obj));
1205
1206 {
1207 QScriptEngine eng2;
1208 QScriptValue obj2 = eng2.newObject();
1209 QTest::ignoreMessage(type: QtWarningMsg, message: "QScriptContext::setActivationObject() failed: cannot set an object created in a different engine");
1210 QScriptValue was = ctx->activationObject();
1211 ctx->setActivationObject(obj2);
1212 QVERIFY(ctx->activationObject().equals(was));
1213 }
1214
1215 ctx->setActivationObject(eng.globalObject());
1216 QVERIFY(ctx->activationObject().equals(eng.globalObject()));
1217 QScriptValue fun = eng.newFunction(signature: get_activationObject);
1218 eng.globalObject().setProperty(name: "get_activationObject", value: fun);
1219 {
1220 QScriptValue ret = eng.evaluate(program: "get_activationObject(1, 2, 3)");
1221 QVERIFY(ret.isObject());
1222 QScriptValue arguments = ret.property(name: "arguments");
1223 QEXPECT_FAIL("", "QTBUG-17136: arguments property of activation object is missing", Abort);
1224 QVERIFY(arguments.isObject());
1225 QCOMPARE(arguments.property("length").toInt32(), 3);
1226 QCOMPARE(arguments.property("0").toInt32(), 1);
1227 QCOMPARE(arguments.property("1").toInt32(), 1);
1228 QCOMPARE(arguments.property("2").toInt32(), 1);
1229 }
1230}
1231
1232void tst_QScriptContext::getSetActivationObject_customContext()
1233{
1234 QScriptEngine eng;
1235 QScriptContext *ctx = eng.pushContext();
1236 QVERIFY(ctx->activationObject().isObject());
1237 QScriptValue act = eng.newObject();
1238 ctx->setActivationObject(act);
1239 QVERIFY(ctx->activationObject().equals(act));
1240 eng.evaluate(program: "var foo = 123");
1241 QCOMPARE(act.property("foo").toInt32(), 123);
1242 eng.popContext();
1243 QCOMPARE(act.property("foo").toInt32(), 123);
1244}
1245
1246// Helper function that's intended to have the same behavior
1247// as the built-in eval() function.
1248static QScriptValue myEval(QScriptContext *ctx, QScriptEngine *eng)
1249{
1250 QString code = ctx->argument(index: 0).toString();
1251 ctx->setActivationObject(ctx->parentContext()->activationObject());
1252 ctx->setThisObject(ctx->parentContext()->thisObject());
1253 return eng->evaluate(program: code);
1254}
1255
1256void tst_QScriptContext::inheritActivationAndThisObject()
1257{
1258 QScriptEngine eng;
1259 eng.globalObject().setProperty(name: "myEval", value: eng.newFunction(signature: myEval));
1260 {
1261 QScriptValue ret = eng.evaluate(program: "var a = 123; myEval('a')");
1262 QVERIFY(ret.isNumber());
1263 QCOMPARE(ret.toInt32(), 123);
1264 }
1265 {
1266 QScriptValue ret = eng.evaluate(program: "(function() { return myEval('this'); }).call(Number)");
1267 QVERIFY(ret.isFunction());
1268 QVERIFY(ret.equals(eng.globalObject().property("Number")));
1269 }
1270 {
1271 QScriptValue ret = eng.evaluate(program: "(function(a) { return myEval('a'); })(123)");
1272 QVERIFY(ret.isNumber());
1273 QCOMPARE(ret.toInt32(), 123);
1274 }
1275
1276 {
1277 eng.globalObject().setProperty(name: "a", value: 123);
1278 QScriptValue ret = eng.evaluate(program: "(function() { myEval('var a = 456'); return a; })()");
1279 QVERIFY(ret.isNumber());
1280 QCOMPARE(ret.toInt32(), 456);
1281 // Since JSC doesn't create an activation object for the anonymous function call,
1282 // myEval() will use the global object as the activation, which is wrong.
1283 QEXPECT_FAIL("", "QTBUG-10313: Wrong activation object is returned from native function's parent context", Continue);
1284 QVERIFY(eng.globalObject().property("a").strictlyEquals(123));
1285 }
1286}
1287
1288static QScriptValue parentContextToString(QScriptContext *ctx, QScriptEngine *)
1289{
1290 return ctx->parentContext()->toString();
1291}
1292
1293void tst_QScriptContext::toString()
1294{
1295 QScriptEngine eng;
1296 eng.globalObject().setProperty(name: "parentContextToString", value: eng.newFunction(signature: parentContextToString));
1297 QScriptValue ret = eng.evaluate(program: "function foo(first, second, third) {\n"
1298 " return parentContextToString();\n"
1299 "}; foo(1, 2, 3)", fileName: "script.qs");
1300 QVERIFY(ret.isString());
1301 QCOMPARE(ret.toString(), QString::fromLatin1("foo(first = 1, second = 2, third = 3) at script.qs:2"));
1302}
1303
1304static QScriptValue storeCalledAsConstructor(QScriptContext *ctx, QScriptEngine *eng)
1305{
1306 ctx->callee().setProperty(name: "calledAsConstructor", value: ctx->isCalledAsConstructor());
1307 return eng->undefinedValue();
1308}
1309
1310static QScriptValue storeCalledAsConstructorV2(QScriptContext *ctx, QScriptEngine *eng, void *)
1311{
1312 ctx->callee().setProperty(name: "calledAsConstructor", value: ctx->isCalledAsConstructor());
1313 return eng->undefinedValue();
1314}
1315
1316static QScriptValue storeCalledAsConstructorV3(QScriptContext *ctx, QScriptEngine *eng)
1317{
1318 ctx->callee().setProperty(name: "calledAsConstructor", value: ctx->parentContext()->isCalledAsConstructor());
1319 return eng->undefinedValue();
1320}
1321
1322void tst_QScriptContext::calledAsConstructor_fromCpp()
1323{
1324 QScriptEngine eng;
1325 {
1326 QScriptValue fun1 = eng.newFunction(signature: storeCalledAsConstructor);
1327 fun1.call();
1328 QVERIFY(!fun1.property("calledAsConstructor").toBool());
1329 fun1.construct();
1330 QVERIFY(fun1.property("calledAsConstructor").toBool());
1331 }
1332 {
1333 QScriptValue fun = eng.newFunction(signature: storeCalledAsConstructorV2, arg: (void*)0);
1334 fun.call();
1335 QVERIFY(!fun.property("calledAsConstructor").toBool());
1336 fun.construct();
1337 QVERIFY(fun.property("calledAsConstructor").toBool());
1338 }
1339}
1340
1341void tst_QScriptContext::calledAsConstructor_fromJS()
1342{
1343 QScriptEngine eng;
1344 QScriptValue fun1 = eng.newFunction(signature: storeCalledAsConstructor);
1345 eng.globalObject().setProperty(name: "fun1", value: fun1);
1346 eng.evaluate(program: "fun1();");
1347 QVERIFY(!fun1.property("calledAsConstructor").toBool());
1348 eng.evaluate(program: "new fun1();");
1349 QVERIFY(fun1.property("calledAsConstructor").toBool());
1350}
1351
1352void tst_QScriptContext::calledAsConstructor_parentContext()
1353{
1354 QScriptEngine eng;
1355 QScriptValue fun3 = eng.newFunction(signature: storeCalledAsConstructorV3);
1356 eng.globalObject().setProperty(name: "fun3", value: fun3);
1357 eng.evaluate(program: "function test() { fun3() }");
1358 eng.evaluate(program: "test();");
1359 QVERIFY(!fun3.property("calledAsConstructor").toBool());
1360 eng.evaluate(program: "new test();");
1361 if (qt_script_isJITEnabled())
1362 QEXPECT_FAIL("", "QTBUG-6132: calledAsConstructor is not correctly set for JS functions when JIT is enabled", Continue);
1363 QVERIFY(fun3.property("calledAsConstructor").toBool());
1364}
1365
1366static QScriptValue argumentsObjectInNative_test1(QScriptContext *ctx, QScriptEngine *eng)
1367{
1368#define VERIFY(statement) \
1369 do {\
1370 if (!QTest::qVerify((statement), #statement, "", __FILE__, __LINE__))\
1371 return QString::fromLatin1("Failed " #statement);\
1372 } while (0)
1373
1374 QScriptValue obj = ctx->argumentsObject();
1375 VERIFY(obj.isObject());
1376 VERIFY(obj.property(0).toUInt32() == 123);
1377 VERIFY(obj.property(1).toString() == QString::fromLatin1("456"));
1378
1379 obj.setProperty(arrayIndex: 0, value: "abc");
1380 VERIFY(eng->evaluate("arguments[0]").toString() == QString::fromLatin1("abc") );
1381
1382 return QString::fromLatin1(str: "success");
1383#undef VERIFY
1384}
1385
1386void tst_QScriptContext::argumentsObjectInNative()
1387{
1388 {
1389 QScriptEngine eng;
1390 QScriptValue fun = eng.newFunction(signature: argumentsObjectInNative_test1);
1391 QScriptValueList args;
1392 args << QScriptValue(&eng, 123.0);
1393 args << QScriptValue(&eng, QString::fromLatin1(str: "456"));
1394 QScriptValue result = fun.call(thisObject: eng.undefinedValue(), args);
1395 QVERIFY(!eng.hasUncaughtException());
1396 QCOMPARE(result.toString(), QString::fromLatin1("success"));
1397 }
1398 {
1399 QScriptEngine eng;
1400 QScriptValue fun = eng.newFunction(signature: argumentsObjectInNative_test1);
1401 eng.globalObject().setProperty(name: "func", value: fun);
1402 QScriptValue result = eng.evaluate(program: "func(123.0 , 456);");
1403 QVERIFY(!eng.hasUncaughtException());
1404 QCOMPARE(result.toString(), QString::fromLatin1("success"));
1405 }
1406}
1407
1408static QScriptValue get_jsActivationObject(QScriptContext *ctx, QScriptEngine *)
1409{
1410 return ctx->parentContext()->parentContext()->activationObject();
1411}
1412
1413void tst_QScriptContext::jsActivationObject()
1414{
1415 QScriptEngine eng;
1416 eng.globalObject().setProperty(name: "get_jsActivationObject", value: eng.newFunction(signature: get_jsActivationObject));
1417 eng.evaluate(program: "function f1() { var w = get_jsActivationObject('arg1'); return w; }");
1418 eng.evaluate(program: "function f2(x,y,z) { var v1 = 42;\n"
1419 // "function foo() {};\n" //this would avoid JSC to optimize
1420 "var v2 = f1(); return v2; }");
1421 eng.evaluate(program: "function f3() { var v1 = 'nothing'; return f2(1,2,3); }");
1422 QScriptValue result1 = eng.evaluate(program: "f2('hello', 'useless', 'world')");
1423 QScriptValue result2 = eng.evaluate(program: "f3()");
1424 QVERIFY(result1.isObject());
1425 QEXPECT_FAIL("", "QTBUG-10313: JSC optimizes away the activation object", Abort);
1426 QCOMPARE(result1.property("v1").toInt32() , 42);
1427 QCOMPARE(result1.property("arguments").property(1).toString() , QString::fromLatin1("useless"));
1428 QVERIFY(result2.isObject());
1429 QCOMPARE(result2.property("v1").toInt32() , 42);
1430 QCOMPARE(result2.property("arguments").property(1).toString() , QString::fromLatin1("2"));
1431}
1432
1433void tst_QScriptContext::qobjectAsActivationObject()
1434{
1435 QScriptEngine eng;
1436 QObject object;
1437 QScriptValue scriptObject = eng.newQObject(object: &object);
1438 QScriptContext *ctx = eng.pushContext();
1439 ctx->setActivationObject(scriptObject);
1440 QVERIFY(ctx->activationObject().equals(scriptObject));
1441
1442 QVERIFY(!scriptObject.property("foo").isValid());
1443 eng.evaluate(program: "function foo() { return 123; }");
1444 {
1445 QScriptValue val = scriptObject.property(name: "foo");
1446 QVERIFY(val.isValid());
1447 QVERIFY(val.isFunction());
1448 }
1449 QVERIFY(!eng.globalObject().property("foo").isValid());
1450
1451 QVERIFY(!scriptObject.property("bar").isValid());
1452 eng.evaluate(program: "var bar = 123");
1453 {
1454 QScriptValue val = scriptObject.property(name: "bar");
1455 QVERIFY(val.isValid());
1456 QVERIFY(val.isNumber());
1457 QCOMPARE(val.toInt32(), 123);
1458 }
1459 QVERIFY(!eng.globalObject().property("bar").isValid());
1460
1461 {
1462 QScriptValue val = eng.evaluate(program: "delete foo");
1463 QVERIFY(val.isBool());
1464 QVERIFY(val.toBool());
1465 QVERIFY(!scriptObject.property("foo").isValid());
1466 }
1467}
1468
1469static QScriptValue getParentContextCallee(QScriptContext *ctx, QScriptEngine *)
1470{
1471 return ctx->parentContext()->callee();
1472}
1473
1474void tst_QScriptContext::parentContextCallee_QT2270()
1475{
1476 QScriptEngine engine;
1477 engine.globalObject().setProperty(name: "getParentContextCallee", value: engine.newFunction(signature: getParentContextCallee));
1478 QScriptValue fun = engine.evaluate(program: "(function() { return getParentContextCallee(); })");
1479 QVERIFY(fun.isFunction());
1480 QScriptValue callee = fun.call();
1481 QVERIFY(callee.equals(fun));
1482}
1483
1484void tst_QScriptContext::throwErrorInGlobalContext()
1485{
1486 QScriptEngine eng;
1487 QScriptValue ret = eng.currentContext()->throwError(text: "foo");
1488 QVERIFY(ret.isError());
1489 QVERIFY(eng.hasUncaughtException());
1490 QVERIFY(eng.uncaughtException().strictlyEquals(ret));
1491 QCOMPARE(ret.toString(), QString::fromLatin1("Error: foo"));
1492}
1493
1494void tst_QScriptContext::throwErrorWithTypeInGlobalContext_data()
1495{
1496 QTest::addColumn<QScriptContext::Error>(name: "error");
1497 QTest::addColumn<QString>(name: "stringRepresentation");
1498 QTest::newRow(dataTag: "ReferenceError") << QScriptContext::ReferenceError << QString::fromLatin1(str: "ReferenceError: foo");
1499 QTest::newRow(dataTag: "SyntaxError") << QScriptContext::SyntaxError << QString::fromLatin1(str: "SyntaxError: foo");
1500 QTest::newRow(dataTag: "TypeError") << QScriptContext::TypeError << QString::fromLatin1(str: "TypeError: foo");
1501 QTest::newRow(dataTag: "RangeError") << QScriptContext::RangeError << QString::fromLatin1(str: "RangeError: foo");
1502 QTest::newRow(dataTag: "URIError") << QScriptContext::URIError << QString::fromLatin1(str: "URIError: foo");
1503 QTest::newRow(dataTag: "UnknownError") << QScriptContext::UnknownError << QString::fromLatin1(str: "Error: foo");
1504}
1505
1506void tst_QScriptContext::throwErrorWithTypeInGlobalContext()
1507{
1508 QFETCH(QScriptContext::Error, error);
1509 QFETCH(QString, stringRepresentation);
1510 QScriptEngine eng;
1511 QScriptValue ret = eng.currentContext()->throwError(error, text: "foo");
1512 QVERIFY(ret.isError());
1513 QVERIFY(eng.hasUncaughtException());
1514 QVERIFY(eng.uncaughtException().strictlyEquals(ret));
1515 QCOMPARE(ret.toString(), stringRepresentation);
1516}
1517
1518void tst_QScriptContext::throwValueInGlobalContext()
1519{
1520 QScriptEngine eng;
1521 QScriptValue val(&eng, 123);
1522 QScriptValue ret = eng.currentContext()->throwValue(value: val);
1523 QVERIFY(ret.strictlyEquals(val));
1524 QVERIFY(eng.hasUncaughtException());
1525 QVERIFY(eng.uncaughtException().strictlyEquals(val));
1526}
1527
1528QTEST_MAIN(tst_QScriptContext)
1529#include "tst_qscriptcontext.moc"
1530

source code of qtscript/tests/auto/qscriptcontext/tst_qscriptcontext.cpp