1 /****************************************************************************
3 ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/legal
6 ** This file is part of the test suite of the Qt Toolkit.
8 ** $QT_BEGIN_LICENSE:LGPL$
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 Digia. For licensing terms and
14 ** conditions see http://qt.digia.com/licensing. For further information
15 ** use the contact form at http://qt.digia.com/contact-us.
17 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 2.1 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL included in the
21 ** packaging of this file. Please review the following information to
22 ** ensure the GNU Lesser General Public License version 2.1 requirements
23 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
25 ** In addition, as a special exception, Digia gives you certain additional
26 ** rights. These rights are described in the Digia Qt LGPL Exception
27 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
29 ** GNU General Public License Usage
30 ** Alternatively, this file may be used under the terms of the GNU
31 ** General Public License version 3.0 as published by the Free Software
32 ** Foundation and appearing in the file LICENSE.GPL included in the
33 ** packaging of this file. Please review the following information to
34 ** ensure the GNU General Public License version 3.0 requirements will be
35 ** met: http://www.gnu.org/copyleft/gpl.html.
40 ****************************************************************************/
43 #include <QtTest/QtTest>
45 #include <qjsengine.h>
46 #include <qjsvalueiterator.h>
47 #include <qgraphicsitem.h>
48 #include <qstandarditemmodel.h>
49 #include <QtCore/qnumeric.h>
52 #include <private/v8.h>
54 Q_DECLARE_METATYPE(QList<int>)
55 Q_DECLARE_METATYPE(QObjectList)
57 // The JavaScriptCore GC marks the C stack. To try to ensure that there is
58 // no JSObject* left in stack memory by the compiler, we call this function
59 // to zap some bytes of memory before calling collectGarbage().
60 static void zapSomeStack()
63 memset(buf, 0, sizeof(buf));
66 static void collectGarbage_helper(QJSEngine &eng)
73 extern Q_QML_EXPORT v8::Local<v8::Context> qt_QJSEngineV8Context(QJSEngine *);
74 extern Q_QML_EXPORT v8::Local<v8::Value> qt_QJSValueV8Value(const QJSValue &);
77 class tst_QJSEngine : public QObject
83 virtual ~tst_QJSEngine();
86 void constructWithParent();
89 void newArray_HooliganTask218092();
90 void newArray_HooliganTask233836();
92 void newVariant_valueOfToString();
98 void newQObject_ownership();
99 void newQObject_deletedEngine();
100 void globalObjectProperties();
101 void globalObjectEquals();
102 void globalObjectProperties_enumerate();
103 void createGlobalObjectProperty();
104 void globalObjectWithCustomPrototype();
105 void builtinFunctionNames_data();
106 void builtinFunctionNames();
107 void evaluate_data();
109 void errorMessage_QT679();
110 void valueConversion_basic();
111 void valueConversion_QVariant();
112 void valueConversion_basic2();
113 void valueConversion_dateTime();
114 void valueConversion_regExp();
115 void castWithMultipleInheritance();
116 void collectGarbage();
117 void gcWithNestedDataStructure();
119 void numberParsing_data();
120 void numberParsing();
121 void automaticSemicolonInsertion();
122 void errorConstructors();
123 void argumentsProperty_globalContext();
124 void argumentsProperty_JS();
125 void jsNumberClass();
126 void jsForInStatement_simple();
127 void jsForInStatement_prototypeProperties();
128 void jsForInStatement_mutateWhileIterating();
129 void jsForInStatement_arrays();
130 void jsForInStatement_nullAndUndefined();
131 void jsFunctionDeclarationAsStatement();
132 void stringObjects();
133 void jsStringPrototypeReplaceBugs();
134 void getterSetterThisObject_global();
135 void getterSetterThisObject_plain();
136 void getterSetterThisObject_prototypeChain();
137 void jsContinueInSwitch();
138 void jsShadowReadOnlyPrototypeProperty();
139 void jsReservedWords_data();
140 void jsReservedWords();
141 void jsFutureReservedWords_data();
142 void jsFutureReservedWords();
143 void jsThrowInsideWithStatement();
144 void reentrancy_globalObjectProperties();
145 void reentrancy_Array();
146 void reentrancy_objectCreation();
147 void jsIncDecNonObjectProperty();
149 void qRegExpInport_data();
150 void qRegExpInport();
151 void dateRoundtripJSQtJS();
152 void dateRoundtripQtJSQt();
153 void dateConversionJSQt();
154 void dateConversionQtJS();
155 void functionPrototypeExtensions();
156 void threadedEngine();
158 void v8Context_simple();
159 void v8Context_exception();
160 void v8Context_mixAPIs();
163 tst_QJSEngine::tst_QJSEngine()
167 tst_QJSEngine::~tst_QJSEngine()
171 void tst_QJSEngine::constructWithParent()
173 QPointer<QJSEngine> ptr;
176 QJSEngine *engine = new QJSEngine(&obj);
182 void tst_QJSEngine::newObject()
185 QJSValue object = eng.newObject();
186 QVERIFY(!object.isUndefined());
187 QCOMPARE(object.isObject(), true);
188 QCOMPARE(object.isCallable(), false);
189 // prototype should be Object.prototype
190 QVERIFY(!object.prototype().isUndefined());
191 QCOMPARE(object.prototype().isObject(), true);
192 QCOMPARE(object.prototype().strictlyEquals(eng.evaluate("Object.prototype")), true);
195 void tst_QJSEngine::newArray()
198 QJSValue array = eng.newArray();
199 QVERIFY(!array.isUndefined());
200 QCOMPARE(array.isArray(), true);
201 QCOMPARE(array.isObject(), true);
202 QVERIFY(!array.isCallable());
203 // prototype should be Array.prototype
204 QVERIFY(!array.prototype().isUndefined());
205 QCOMPARE(array.prototype().isArray(), true);
206 QCOMPARE(array.prototype().strictlyEquals(eng.evaluate("Array.prototype")), true);
209 void tst_QJSEngine::newArray_HooliganTask218092()
213 QJSValue ret = eng.evaluate("[].splice(0, 0, 'a')");
214 QVERIFY(ret.isArray());
215 QCOMPARE(ret.property("length").toInt(), 0);
218 QJSValue ret = eng.evaluate("['a'].splice(0, 1, 'b')");
219 QVERIFY(ret.isArray());
220 QCOMPARE(ret.property("length").toInt(), 1);
223 QJSValue ret = eng.evaluate("['a', 'b'].splice(0, 1, 'c')");
224 QVERIFY(ret.isArray());
225 QCOMPARE(ret.property("length").toInt(), 1);
228 QJSValue ret = eng.evaluate("['a', 'b', 'c'].splice(0, 2, 'd')");
229 QVERIFY(ret.isArray());
230 QCOMPARE(ret.property("length").toInt(), 2);
233 QJSValue ret = eng.evaluate("['a', 'b', 'c'].splice(1, 2, 'd', 'e', 'f')");
234 QVERIFY(ret.isArray());
235 QCOMPARE(ret.property("length").toInt(), 2);
239 void tst_QJSEngine::newArray_HooliganTask233836()
243 // According to ECMA-262, this should cause a RangeError.
244 QJSValue ret = eng.evaluate("a = new Array(4294967295); a.push('foo')");
245 QVERIFY(ret.isError() && ret.toString().contains(QLatin1String("RangeError")));
248 QJSValue ret = eng.newArray(0xFFFFFFFF);
249 QEXPECT_FAIL("", "The maximum length of arrays is defined by v8 currently and differs from QtScript", Abort);
250 QCOMPARE(ret.property("length").toUInt(), uint(0xFFFFFFFF));
251 ret.setProperty(0xFFFFFFFF, 123);
252 QCOMPARE(ret.property("length").toUInt(), uint(0xFFFFFFFF));
253 QVERIFY(ret.property(0xFFFFFFFF).isNumber());
254 QCOMPARE(ret.property(0xFFFFFFFF).toInt(), 123);
255 ret.setProperty(123, 456);
256 QCOMPARE(ret.property("length").toUInt(), uint(0xFFFFFFFF));
257 QVERIFY(ret.property(123).isNumber());
258 QCOMPARE(ret.property(123).toInt(), 456);
262 void tst_QJSEngine::newVariant()
266 QJSValue opaque = eng.toScriptValue(QVariant(QPoint(1, 2)));
267 QVERIFY(!opaque.isUndefined());
268 QCOMPARE(opaque.isVariant(), true);
269 QVERIFY(!opaque.isCallable());
270 QCOMPARE(opaque.isObject(), true);
271 QVERIFY(!opaque.prototype().isUndefined());
272 QEXPECT_FAIL("", "FIXME: newly created QObject's prototype is an JS Object", Continue);
273 QCOMPARE(opaque.prototype().isVariant(), true);
274 QVERIFY(opaque.property("valueOf").callWithInstance(opaque).equals(opaque));
278 void tst_QJSEngine::newVariant_valueOfToString()
280 // valueOf() and toString()
283 QJSValue object = eng.toScriptValue(QVariant(QPoint(10, 20)));
284 QJSValue value = object.property("valueOf").callWithInstance(object);
285 QVERIFY(value.isObject());
286 QVERIFY(value.strictlyEquals(object));
287 QCOMPARE(object.toString(), QString::fromLatin1("QVariant(QPoint)"));
291 void tst_QJSEngine::newRegExp()
294 QJSValue rexp = eng.toScriptValue(QRegExp("foo"));
295 QVERIFY(!rexp.isUndefined());
296 QCOMPARE(rexp.isRegExp(), true);
297 QCOMPARE(rexp.isObject(), true);
298 QCOMPARE(rexp.isCallable(), false);
299 // prototype should be RegExp.prototype
300 QVERIFY(!rexp.prototype().isUndefined());
301 QCOMPARE(rexp.prototype().isObject(), true);
302 QCOMPARE(rexp.prototype().isRegExp(), true);
303 // Get [[Class]] internal property of RegExp Prototype Object.
304 // See ECMA-262 Section 8.6.2, "Object Internal Properties and Methods".
305 // See ECMA-262 Section 15.10.6, "Properties of the RegExp Prototype Object".
306 QJSValue r = eng.evaluate("Object.prototype.toString.call(RegExp.prototype)");
307 QCOMPARE(r.toString(), QString::fromLatin1("[object RegExp]"));
308 QCOMPARE(rexp.prototype().strictlyEquals(eng.evaluate("RegExp.prototype")), true);
310 QCOMPARE(qjsvalue_cast<QRegExp>(rexp).pattern(), QRegExp("foo").pattern());
313 void tst_QJSEngine::jsRegExp()
315 // See ECMA-262 Section 15.10, "RegExp Objects".
316 // These should really be JS-only tests, as they test the implementation's
317 // ECMA-compliance, not the C++ API. Compliance should already be covered
318 // by the Mozilla tests (qscriptjstestsuite).
319 // We can consider updating the expected results of this test if the
320 // RegExp implementation changes.
323 QJSValue r = eng.evaluate("/foo/gim");
324 QVERIFY(r.isRegExp());
325 QCOMPARE(r.toString(), QString::fromLatin1("/foo/gim"));
327 QJSValue rxCtor = eng.globalObject().property("RegExp");
328 QJSValue r2 = rxCtor.call(QJSValueList() << r);
329 QVERIFY(r2.isRegExp());
330 QVERIFY(r2.strictlyEquals(r));
332 QJSValue r3 = rxCtor.call(QJSValueList() << r << "gim");
333 QVERIFY(r3.isError());
334 QVERIFY(r3.toString().contains(QString::fromLatin1("TypeError"))); // Cannot supply flags when constructing one RegExp from another
336 QJSValue r4 = rxCtor.call(QJSValueList() << "foo" << "gim");
337 QVERIFY(r4.isRegExp());
339 QJSValue r5 = rxCtor.callAsConstructor(QJSValueList() << r);
340 QVERIFY(r5.isRegExp());
341 QCOMPARE(r5.toString(), QString::fromLatin1("/foo/gim"));
342 // In JSC, constructing a RegExp from another produces the same identical object.
343 // This is different from SpiderMonkey and old back-end.
344 QVERIFY(!r5.strictlyEquals(r));
346 // See ECMA-262 Section 15.10.4.1, "new RegExp(pattern, flags)".
347 QJSValue r6 = rxCtor.callAsConstructor(QJSValueList() << "foo" << "bar");
348 QVERIFY(r6.isError());
349 QVERIFY(r6.toString().contains(QString::fromLatin1("SyntaxError"))); // Invalid regular expression flag
351 QJSValue r7 = eng.evaluate("/foo/gimp");
352 QVERIFY(r7.isError());
353 QVERIFY(r7.toString().contains(QString::fromLatin1("SyntaxError"))); // Invalid regular expression flag
355 QJSValue r8 = eng.evaluate("/foo/migmigmig");
356 QVERIFY(r8.isError());
357 QVERIFY(r8.toString().contains(QString::fromLatin1("SyntaxError"))); // Invalid regular expression flag
359 QJSValue r9 = rxCtor.callAsConstructor();
360 QVERIFY(r9.isRegExp());
361 QCOMPARE(r9.toString(), QString::fromLatin1("/(?:)/"));
363 QJSValue r10 = rxCtor.callAsConstructor(QJSValueList() << "" << "gim");
364 QVERIFY(r10.isRegExp());
365 QCOMPARE(r10.toString(), QString::fromLatin1("/(?:)/gim"));
367 QJSValue r11 = rxCtor.callAsConstructor(QJSValueList() << "{1.*}" << "g");
368 QVERIFY(r11.isRegExp());
369 QCOMPARE(r11.toString(), QString::fromLatin1("/{1.*}/g"));
372 void tst_QJSEngine::newDate()
377 QJSValue date = eng.evaluate("new Date(0)");
378 QVERIFY(!date.isUndefined());
379 QCOMPARE(date.isDate(), true);
380 QCOMPARE(date.isObject(), true);
381 QVERIFY(!date.isCallable());
382 // prototype should be Date.prototype
383 QVERIFY(!date.prototype().isUndefined());
384 QCOMPARE(date.prototype().isDate(), true);
385 QCOMPARE(date.prototype().strictlyEquals(eng.evaluate("Date.prototype")), true);
389 QDateTime dt = QDateTime(QDate(1, 2, 3), QTime(4, 5, 6, 7), Qt::LocalTime);
390 QJSValue date = eng.toScriptValue(dt);
391 QVERIFY(!date.isUndefined());
392 QCOMPARE(date.isDate(), true);
393 QCOMPARE(date.isObject(), true);
394 // prototype should be Date.prototype
395 QVERIFY(!date.prototype().isUndefined());
396 QCOMPARE(date.prototype().isDate(), true);
397 QCOMPARE(date.prototype().strictlyEquals(eng.evaluate("Date.prototype")), true);
399 QCOMPARE(date.toDateTime(), dt);
403 QDateTime dt = QDateTime(QDate(1, 2, 3), QTime(4, 5, 6, 7), Qt::UTC);
404 QJSValue date = eng.toScriptValue(dt);
405 // toDateTime() result should be in local time
406 QCOMPARE(date.toDateTime(), dt.toLocalTime());
410 void tst_QJSEngine::jsParseDate()
413 // Date.parse() should return NaN when it fails
415 QJSValue ret = eng.evaluate("Date.parse()");
416 QVERIFY(ret.isNumber());
417 QVERIFY(qIsNaN(ret.toNumber()));
420 // Date.parse() should be able to parse the output of Date().toString()
422 QJSValue ret = eng.evaluate("var x = new Date(); var s = x.toString(); s == new Date(Date.parse(s)).toString()");
423 QVERIFY(ret.isBool());
424 QCOMPARE(ret.toBool(), true);
428 void tst_QJSEngine::newQObject()
434 QJSValue qobject = eng.newQObject(0);
435 QCOMPARE(qobject.isNull(), true);
436 QCOMPARE(qobject.isObject(), false);
437 QCOMPARE(qobject.toQObject(), (QObject *)0);
440 QJSValue qobject = eng.newQObject(&temp);
441 QVERIFY(!qobject.isUndefined());
442 QCOMPARE(qobject.isQObject(), true);
443 QCOMPARE(qobject.isObject(), true);
444 QCOMPARE(qobject.toQObject(), (QObject *)&temp);
445 QVERIFY(!qobject.isCallable());
446 // prototype should be QObject.prototype
447 QCOMPARE(qobject.prototype().isObject(), true);
448 QEXPECT_FAIL("", "FIXME: newly created QObject's prototype is an JS Object", Continue);
449 QCOMPARE(qobject.prototype().isQObject(), true);
453 void tst_QJSEngine::newQObject_ownership()
457 QPointer<QObject> ptr = new QObject();
460 QJSValue v = eng.newQObject(ptr);
462 collectGarbage_helper(eng);
464 QGuiApplication::sendPostedEvents(ptr, QEvent::DeferredDelete);
468 QPointer<QObject> ptr = new QObject(this);
471 QJSValue v = eng.newQObject(ptr);
473 QObject *before = ptr;
474 collectGarbage_helper(eng);
475 QVERIFY(ptr == before);
479 QObject *parent = new QObject();
480 QObject *child = new QObject(parent);
481 QJSValue v = eng.newQObject(child);
482 QCOMPARE(v.toQObject(), child);
484 QCOMPARE(v.toQObject(), (QObject *)0);
487 QPointer<QObject> ptr = new QObject();
490 QJSValue v = eng.newQObject(ptr);
492 collectGarbage_helper(eng);
493 // no parent, so it should be like ScriptOwnership
495 QGuiApplication::sendPostedEvents(ptr, QEvent::DeferredDelete);
499 QObject *parent = new QObject();
500 QPointer<QObject> child = new QObject(parent);
503 QJSValue v = eng.newQObject(child);
505 collectGarbage_helper(eng);
506 // has parent, so it should be like QtOwnership
512 void tst_QJSEngine::newQObject_deletedEngine()
515 QObject *ptr = new QObject();
516 QSignalSpy spy(ptr, SIGNAL(destroyed()));
519 object = engine.newQObject(ptr);
520 engine.globalObject().setProperty("obj", object);
522 QTRY_VERIFY(spy.count());
525 void tst_QJSEngine::globalObjectProperties()
527 // See ECMA-262 Section 15.1, "The Global Object".
530 QJSValue global = eng.globalObject();
532 QVERIFY(global.property("NaN").isNumber());
533 QVERIFY(qIsNaN(global.property("NaN").toNumber()));
535 QVERIFY(global.property("Infinity").isNumber());
536 QVERIFY(qIsInf(global.property("Infinity").toNumber()));
538 QVERIFY(global.property("undefined").isUndefined());
540 QVERIFY(global.property("eval").isCallable());
542 QVERIFY(global.property("parseInt").isCallable());
544 QVERIFY(global.property("parseFloat").isCallable());
546 QVERIFY(global.property("isNaN").isCallable());
548 QVERIFY(global.property("isFinite").isCallable());
550 QVERIFY(global.property("decodeURI").isCallable());
552 QVERIFY(global.property("decodeURIComponent").isCallable());
554 QVERIFY(global.property("encodeURI").isCallable());
556 QVERIFY(global.property("encodeURIComponent").isCallable());
558 QVERIFY(global.property("Object").isCallable());
559 QVERIFY(global.property("Function").isCallable());
560 QVERIFY(global.property("Array").isCallable());
561 QVERIFY(global.property("String").isCallable());
562 QVERIFY(global.property("Boolean").isCallable());
563 QVERIFY(global.property("Number").isCallable());
564 QVERIFY(global.property("Date").isCallable());
565 QVERIFY(global.property("RegExp").isCallable());
566 QVERIFY(global.property("Error").isCallable());
567 QVERIFY(global.property("EvalError").isCallable());
568 QVERIFY(global.property("RangeError").isCallable());
569 QVERIFY(global.property("ReferenceError").isCallable());
570 QVERIFY(global.property("SyntaxError").isCallable());
571 QVERIFY(global.property("TypeError").isCallable());
572 QVERIFY(global.property("URIError").isCallable());
573 QVERIFY(global.property("Math").isObject());
574 QVERIFY(!global.property("Math").isCallable());
577 void tst_QJSEngine::globalObjectEquals()
580 QJSValue o = eng.globalObject();
581 QVERIFY(o.strictlyEquals(eng.globalObject()));
582 QVERIFY(o.equals(eng.globalObject()));
585 void tst_QJSEngine::globalObjectProperties_enumerate()
588 QJSValue global = eng.globalObject();
590 QSet<QString> expectedNames;
598 << "encodeURIComponent"
615 << "decodeURIComponent"
625 QSet<QString> actualNames;
627 QJSValueIterator it(global);
628 while (it.hasNext()) {
630 actualNames.insert(it.name());
634 QSet<QString> remainingNames = actualNames;
636 QSet<QString>::const_iterator it;
637 for (it = expectedNames.constBegin(); it != expectedNames.constEnd(); ++it) {
639 QVERIFY(actualNames.contains(name));
640 remainingNames.remove(name);
643 QVERIFY(remainingNames.isEmpty());
646 void tst_QJSEngine::createGlobalObjectProperty()
649 QJSValue global = eng.globalObject();
650 // create property with no attributes
652 QString name = QString::fromLatin1("foo");
653 QVERIFY(global.property(name).isUndefined());
655 global.setProperty(name, val);
656 QVERIFY(global.property(name).equals(val));
657 global.deleteProperty(name);
658 QVERIFY(global.property(name).isUndefined());
662 void tst_QJSEngine::globalObjectWithCustomPrototype()
665 QJSValue proto = engine.newObject();
666 proto.setProperty("protoProperty", 123);
667 QJSValue global = engine.globalObject();
668 QJSValue originalProto = global.prototype();
669 global.setPrototype(proto);
671 QJSValue ret = engine.evaluate("protoProperty");
672 QEXPECT_FAIL("", "Replacing the prototype of the global object is currently unsupported (see also v8 issue 1078)", Abort);
673 QVERIFY(ret.isNumber());
674 QVERIFY(ret.strictlyEquals(global.property("protoProperty")));
677 QJSValue ret = engine.evaluate("this.protoProperty");
678 QVERIFY(ret.isNumber());
679 QVERIFY(ret.strictlyEquals(global.property("protoProperty")));
682 QJSValue ret = engine.evaluate("hasOwnProperty('protoProperty')");
683 QVERIFY(ret.isBool());
684 QVERIFY(!ret.toBool());
687 QJSValue ret = engine.evaluate("this.hasOwnProperty('protoProperty')");
688 QVERIFY(ret.isBool());
689 QVERIFY(!ret.toBool());
692 // Custom prototype set from JS
694 QJSValue ret = engine.evaluate("this.__proto__ = { 'a': 123 }; a");
695 QVERIFY(ret.isNumber());
696 QVERIFY(ret.strictlyEquals(global.property("a")));
700 void tst_QJSEngine::builtinFunctionNames_data()
702 QTest::addColumn<QString>("expression");
703 QTest::addColumn<QString>("expectedName");
705 // See ECMA-262 Chapter 15, "Standard Built-in ECMAScript Objects".
707 QTest::newRow("parseInt") << QString("parseInt") << QString("parseInt");
708 QTest::newRow("parseFloat") << QString("parseFloat") << QString("parseFloat");
709 QTest::newRow("isNaN") << QString("isNaN") << QString("isNaN");
710 QTest::newRow("isFinite") << QString("isFinite") << QString("isFinite");
711 QTest::newRow("decodeURI") << QString("decodeURI") << QString("decodeURI");
712 QTest::newRow("decodeURIComponent") << QString("decodeURIComponent") << QString("decodeURIComponent");
713 QTest::newRow("encodeURI") << QString("encodeURI") << QString("encodeURI");
714 QTest::newRow("encodeURIComponent") << QString("encodeURIComponent") << QString("encodeURIComponent");
715 QTest::newRow("escape") << QString("escape") << QString("escape");
716 QTest::newRow("unescape") << QString("unescape") << QString("unescape");
718 QTest::newRow("Array") << QString("Array") << QString("Array");
719 QTest::newRow("Array.prototype.toString") << QString("Array.prototype.toString") << QString("toString");
720 QTest::newRow("Array.prototype.toLocaleString") << QString("Array.prototype.toLocaleString") << QString("toLocaleString");
721 QTest::newRow("Array.prototype.concat") << QString("Array.prototype.concat") << QString("concat");
722 QTest::newRow("Array.prototype.join") << QString("Array.prototype.join") << QString("join");
723 QTest::newRow("Array.prototype.pop") << QString("Array.prototype.pop") << QString("pop");
724 QTest::newRow("Array.prototype.push") << QString("Array.prototype.push") << QString("push");
725 QTest::newRow("Array.prototype.reverse") << QString("Array.prototype.reverse") << QString("reverse");
726 QTest::newRow("Array.prototype.shift") << QString("Array.prototype.shift") << QString("shift");
727 QTest::newRow("Array.prototype.slice") << QString("Array.prototype.slice") << QString("slice");
728 QTest::newRow("Array.prototype.sort") << QString("Array.prototype.sort") << QString("sort");
729 QTest::newRow("Array.prototype.splice") << QString("Array.prototype.splice") << QString("splice");
730 QTest::newRow("Array.prototype.unshift") << QString("Array.prototype.unshift") << QString("unshift");
732 QTest::newRow("Boolean") << QString("Boolean") << QString("Boolean");
733 QTest::newRow("Boolean.prototype.toString") << QString("Boolean.prototype.toString") << QString("toString");
735 QTest::newRow("Date") << QString("Date") << QString("Date");
736 QTest::newRow("Date.prototype.toString") << QString("Date.prototype.toString") << QString("toString");
737 QTest::newRow("Date.prototype.toDateString") << QString("Date.prototype.toDateString") << QString("toDateString");
738 QTest::newRow("Date.prototype.toTimeString") << QString("Date.prototype.toTimeString") << QString("toTimeString");
739 QTest::newRow("Date.prototype.toLocaleString") << QString("Date.prototype.toLocaleString") << QString("toLocaleString");
740 QTest::newRow("Date.prototype.toLocaleDateString") << QString("Date.prototype.toLocaleDateString") << QString("toLocaleDateString");
741 QTest::newRow("Date.prototype.toLocaleTimeString") << QString("Date.prototype.toLocaleTimeString") << QString("toLocaleTimeString");
742 QTest::newRow("Date.prototype.valueOf") << QString("Date.prototype.valueOf") << QString("valueOf");
743 QTest::newRow("Date.prototype.getTime") << QString("Date.prototype.getTime") << QString("getTime");
744 QTest::newRow("Date.prototype.getYear") << QString("Date.prototype.getYear") << QString("getYear");
745 QTest::newRow("Date.prototype.getFullYear") << QString("Date.prototype.getFullYear") << QString("getFullYear");
746 QTest::newRow("Date.prototype.getUTCFullYear") << QString("Date.prototype.getUTCFullYear") << QString("getUTCFullYear");
747 QTest::newRow("Date.prototype.getMonth") << QString("Date.prototype.getMonth") << QString("getMonth");
748 QTest::newRow("Date.prototype.getUTCMonth") << QString("Date.prototype.getUTCMonth") << QString("getUTCMonth");
749 QTest::newRow("Date.prototype.getDate") << QString("Date.prototype.getDate") << QString("getDate");
750 QTest::newRow("Date.prototype.getUTCDate") << QString("Date.prototype.getUTCDate") << QString("getUTCDate");
751 QTest::newRow("Date.prototype.getDay") << QString("Date.prototype.getDay") << QString("getDay");
752 QTest::newRow("Date.prototype.getUTCDay") << QString("Date.prototype.getUTCDay") << QString("getUTCDay");
753 QTest::newRow("Date.prototype.getHours") << QString("Date.prototype.getHours") << QString("getHours");
754 QTest::newRow("Date.prototype.getUTCHours") << QString("Date.prototype.getUTCHours") << QString("getUTCHours");
755 QTest::newRow("Date.prototype.getMinutes") << QString("Date.prototype.getMinutes") << QString("getMinutes");
756 QTest::newRow("Date.prototype.getUTCMinutes") << QString("Date.prototype.getUTCMinutes") << QString("getUTCMinutes");
757 QTest::newRow("Date.prototype.getSeconds") << QString("Date.prototype.getSeconds") << QString("getSeconds");
758 QTest::newRow("Date.prototype.getUTCSeconds") << QString("Date.prototype.getUTCSeconds") << QString("getUTCSeconds");
759 QTest::newRow("Date.prototype.getMilliseconds") << QString("Date.prototype.getMilliseconds") << QString("getMilliseconds");
760 QTest::newRow("Date.prototype.getUTCMilliseconds") << QString("Date.prototype.getUTCMilliseconds") << QString("getUTCMilliseconds");
761 QTest::newRow("Date.prototype.getTimezoneOffset") << QString("Date.prototype.getTimezoneOffset") << QString("getTimezoneOffset");
762 QTest::newRow("Date.prototype.setTime") << QString("Date.prototype.setTime") << QString("setTime");
763 QTest::newRow("Date.prototype.setMilliseconds") << QString("Date.prototype.setMilliseconds") << QString("setMilliseconds");
764 QTest::newRow("Date.prototype.setUTCMilliseconds") << QString("Date.prototype.setUTCMilliseconds") << QString("setUTCMilliseconds");
765 QTest::newRow("Date.prototype.setSeconds") << QString("Date.prototype.setSeconds") << QString("setSeconds");
766 QTest::newRow("Date.prototype.setUTCSeconds") << QString("Date.prototype.setUTCSeconds") << QString("setUTCSeconds");
767 QTest::newRow("Date.prototype.setMinutes") << QString("Date.prototype.setMinutes") << QString("setMinutes");
768 QTest::newRow("Date.prototype.setUTCMinutes") << QString("Date.prototype.setUTCMinutes") << QString("setUTCMinutes");
769 QTest::newRow("Date.prototype.setHours") << QString("Date.prototype.setHours") << QString("setHours");
770 QTest::newRow("Date.prototype.setUTCHours") << QString("Date.prototype.setUTCHours") << QString("setUTCHours");
771 QTest::newRow("Date.prototype.setDate") << QString("Date.prototype.setDate") << QString("setDate");
772 QTest::newRow("Date.prototype.setUTCDate") << QString("Date.prototype.setUTCDate") << QString("setUTCDate");
773 QTest::newRow("Date.prototype.setMonth") << QString("Date.prototype.setMonth") << QString("setMonth");
774 QTest::newRow("Date.prototype.setUTCMonth") << QString("Date.prototype.setUTCMonth") << QString("setUTCMonth");
775 QTest::newRow("Date.prototype.setYear") << QString("Date.prototype.setYear") << QString("setYear");
776 QTest::newRow("Date.prototype.setFullYear") << QString("Date.prototype.setFullYear") << QString("setFullYear");
777 QTest::newRow("Date.prototype.setUTCFullYear") << QString("Date.prototype.setUTCFullYear") << QString("setUTCFullYear");
778 QTest::newRow("Date.prototype.toUTCString") << QString("Date.prototype.toUTCString") << QString("toUTCString");
779 QTest::newRow("Date.prototype.toGMTString") << QString("Date.prototype.toGMTString") << QString("toGMTString");
781 QTest::newRow("Error") << QString("Error") << QString("Error");
782 // QTest::newRow("Error.prototype.backtrace") << QString("Error.prototype.backtrace") << QString("backtrace");
783 QTest::newRow("Error.prototype.toString") << QString("Error.prototype.toString") << QString("toString");
785 QTest::newRow("EvalError") << QString("EvalError") << QString("EvalError");
786 QTest::newRow("RangeError") << QString("RangeError") << QString("RangeError");
787 QTest::newRow("ReferenceError") << QString("ReferenceError") << QString("ReferenceError");
788 QTest::newRow("SyntaxError") << QString("SyntaxError") << QString("SyntaxError");
789 QTest::newRow("TypeError") << QString("TypeError") << QString("TypeError");
790 QTest::newRow("URIError") << QString("URIError") << QString("URIError");
792 QTest::newRow("Function") << QString("Function") << QString("Function");
793 QTest::newRow("Function.prototype.toString") << QString("Function.prototype.toString") << QString("toString");
794 QTest::newRow("Function.prototype.apply") << QString("Function.prototype.apply") << QString("apply");
795 QTest::newRow("Function.prototype.call") << QString("Function.prototype.call") << QString("call");
796 /* In V8, those function are only there for signals
797 QTest::newRow("Function.prototype.connect") << QString("Function.prototype.connect") << QString("connect");
798 QTest::newRow("Function.prototype.disconnect") << QString("Function.prototype.disconnect") << QString("disconnect");*/
800 QTest::newRow("Math.abs") << QString("Math.abs") << QString("abs");
801 QTest::newRow("Math.acos") << QString("Math.acos") << QString("acos");
802 QTest::newRow("Math.asin") << QString("Math.asin") << QString("asin");
803 QTest::newRow("Math.atan") << QString("Math.atan") << QString("atan");
804 QTest::newRow("Math.atan2") << QString("Math.atan2") << QString("atan2");
805 QTest::newRow("Math.ceil") << QString("Math.ceil") << QString("ceil");
806 QTest::newRow("Math.cos") << QString("Math.cos") << QString("cos");
807 QTest::newRow("Math.exp") << QString("Math.exp") << QString("exp");
808 QTest::newRow("Math.floor") << QString("Math.floor") << QString("floor");
809 QTest::newRow("Math.log") << QString("Math.log") << QString("log");
810 QTest::newRow("Math.max") << QString("Math.max") << QString("max");
811 QTest::newRow("Math.min") << QString("Math.min") << QString("min");
812 QTest::newRow("Math.pow") << QString("Math.pow") << QString("pow");
813 QTest::newRow("Math.random") << QString("Math.random") << QString("random");
814 QTest::newRow("Math.round") << QString("Math.round") << QString("round");
815 QTest::newRow("Math.sin") << QString("Math.sin") << QString("sin");
816 QTest::newRow("Math.sqrt") << QString("Math.sqrt") << QString("sqrt");
817 QTest::newRow("Math.tan") << QString("Math.tan") << QString("tan");
819 QTest::newRow("Number") << QString("Number") << QString("Number");
820 QTest::newRow("Number.prototype.toString") << QString("Number.prototype.toString") << QString("toString");
821 QTest::newRow("Number.prototype.toLocaleString") << QString("Number.prototype.toLocaleString") << QString("toLocaleString");
822 QTest::newRow("Number.prototype.valueOf") << QString("Number.prototype.valueOf") << QString("valueOf");
823 QTest::newRow("Number.prototype.toFixed") << QString("Number.prototype.toFixed") << QString("toFixed");
824 QTest::newRow("Number.prototype.toExponential") << QString("Number.prototype.toExponential") << QString("toExponential");
825 QTest::newRow("Number.prototype.toPrecision") << QString("Number.prototype.toPrecision") << QString("toPrecision");
827 QTest::newRow("Object") << QString("Object") << QString("Object");
828 QTest::newRow("Object.prototype.toString") << QString("Object.prototype.toString") << QString("toString");
829 QTest::newRow("Object.prototype.toLocaleString") << QString("Object.prototype.toLocaleString") << QString("toLocaleString");
830 QTest::newRow("Object.prototype.valueOf") << QString("Object.prototype.valueOf") << QString("valueOf");
831 QTest::newRow("Object.prototype.hasOwnProperty") << QString("Object.prototype.hasOwnProperty") << QString("hasOwnProperty");
832 QTest::newRow("Object.prototype.isPrototypeOf") << QString("Object.prototype.isPrototypeOf") << QString("isPrototypeOf");
833 QTest::newRow("Object.prototype.propertyIsEnumerable") << QString("Object.prototype.propertyIsEnumerable") << QString("propertyIsEnumerable");
834 QTest::newRow("Object.prototype.__defineGetter__") << QString("Object.prototype.__defineGetter__") << QString("__defineGetter__");
835 QTest::newRow("Object.prototype.__defineSetter__") << QString("Object.prototype.__defineSetter__") << QString("__defineSetter__");
837 QTest::newRow("RegExp") << QString("RegExp") << QString("RegExp");
838 QTest::newRow("RegExp.prototype.exec") << QString("RegExp.prototype.exec") << QString("exec");
839 QTest::newRow("RegExp.prototype.test") << QString("RegExp.prototype.test") << QString("test");
840 QTest::newRow("RegExp.prototype.toString") << QString("RegExp.prototype.toString") << QString("toString");
842 QTest::newRow("String") << QString("String") << QString("String");
843 QTest::newRow("String.prototype.toString") << QString("String.prototype.toString") << QString("toString");
844 QTest::newRow("String.prototype.valueOf") << QString("String.prototype.valueOf") << QString("valueOf");
845 QTest::newRow("String.prototype.charAt") << QString("String.prototype.charAt") << QString("charAt");
846 QTest::newRow("String.prototype.charCodeAt") << QString("String.prototype.charCodeAt") << QString("charCodeAt");
847 QTest::newRow("String.prototype.concat") << QString("String.prototype.concat") << QString("concat");
848 QTest::newRow("String.prototype.indexOf") << QString("String.prototype.indexOf") << QString("indexOf");
849 QTest::newRow("String.prototype.lastIndexOf") << QString("String.prototype.lastIndexOf") << QString("lastIndexOf");
850 QTest::newRow("String.prototype.localeCompare") << QString("String.prototype.localeCompare") << QString("localeCompare");
851 QTest::newRow("String.prototype.match") << QString("String.prototype.match") << QString("match");
852 QTest::newRow("String.prototype.replace") << QString("String.prototype.replace") << QString("replace");
853 QTest::newRow("String.prototype.search") << QString("String.prototype.search") << QString("search");
854 QTest::newRow("String.prototype.slice") << QString("String.prototype.slice") << QString("slice");
855 QTest::newRow("String.prototype.split") << QString("String.prototype.split") << QString("split");
856 QTest::newRow("String.prototype.substring") << QString("String.prototype.substring") << QString("substring");
857 QTest::newRow("String.prototype.toLowerCase") << QString("String.prototype.toLowerCase") << QString("toLowerCase");
858 QTest::newRow("String.prototype.toLocaleLowerCase") << QString("String.prototype.toLocaleLowerCase") << QString("toLocaleLowerCase");
859 QTest::newRow("String.prototype.toUpperCase") << QString("String.prototype.toUpperCase") << QString("toUpperCase");
860 QTest::newRow("String.prototype.toLocaleUpperCase") << QString("String.prototype.toLocaleUpperCase") << QString("toLocaleUpperCase");
863 void tst_QJSEngine::builtinFunctionNames()
865 QFETCH(QString, expression);
866 QFETCH(QString, expectedName);
868 // The "name" property is actually non-standard, but JSC supports it.
869 QJSValue ret = eng.evaluate(QString::fromLatin1("%0.name").arg(expression));
870 QVERIFY(ret.isString());
871 QCOMPARE(ret.toString(), expectedName);
874 void tst_QJSEngine::evaluate_data()
876 QTest::addColumn<QString>("code");
877 QTest::addColumn<int>("lineNumber");
878 QTest::addColumn<bool>("expectHadError");
879 QTest::addColumn<int>("expectErrorLineNumber");
881 QTest::newRow("(newline)") << QString("\n") << -1 << false << -1;
882 QTest::newRow("0 //") << QString("0 //") << -1 << false << -1;
883 QTest::newRow("/* */") << QString("/* */") << -1 << false << -1;
884 QTest::newRow("//") << QString("//") << -1 << false << -1;
885 QTest::newRow("(spaces)") << QString(" ") << -1 << false << -1;
886 QTest::newRow("(empty)") << QString("") << -1 << false << -1;
887 QTest::newRow("0") << QString("0") << -1 << false << -1;
888 QTest::newRow("0=1") << QString("\n0=1;\n") << -1 << true << 2;
889 QTest::newRow("a=1") << QString("a=1\n") << -1 << false << -1;
890 QTest::newRow("a=1;K") << QString("a=1;\nK") << -1 << true << 2;
892 QTest::newRow("f()") << QString("function f()\n"
895 " var b=\";\n" // here's the error
900 QTest::newRow("0") << QString("0") << 10 << false << -1;
901 QTest::newRow("0=1") << QString("\n\n0=1\n") << 10 << true << 12;
902 QTest::newRow("a=1") << QString("a=1\n") << 10 << false << -1;
903 QTest::newRow("a=1;K") << QString("a=1;\n\nK") << 10 << true << 12;
905 QTest::newRow("f()") << QString("function f()\n"
909 " var b=\";\n" // here's the error
913 QTest::newRow("functionThatDoesntExist()")
914 << QString(";\n;\n;\nfunctionThatDoesntExist()")
916 QTest::newRow("for (var p in this) { continue labelThatDoesntExist; }")
917 << QString("for (var p in this) {\ncontinue labelThatDoesntExist; }")
919 QTest::newRow("duplicateLabel: { duplicateLabel: ; }")
920 << QString("duplicateLabel: { duplicateLabel: ; }")
923 QTest::newRow("/=/") << QString("/=/") << -1 << false << -1;
924 QTest::newRow("/=/g") << QString("/=/g") << -1 << false << -1;
925 QTest::newRow("/a/") << QString("/a/") << -1 << false << -1;
926 QTest::newRow("/a/g") << QString("/a/g") << -1 << false << -1;
927 QTest::newRow("/a/gim") << QString("/a/gim") << -1 << false << -1;
928 QTest::newRow("/a/gimp") << QString("/a/gimp") << 1 << true << 1;
931 void tst_QJSEngine::evaluate()
933 QFETCH(QString, code);
934 QFETCH(int, lineNumber);
935 QFETCH(bool, expectHadError);
936 QFETCH(int, expectErrorLineNumber);
940 if (lineNumber != -1)
941 ret = eng.evaluate(code, /*fileName =*/QString(), lineNumber);
943 ret = eng.evaluate(code);
944 QCOMPARE(ret.isError(), expectHadError);
946 QEXPECT_FAIL("", "we have no more lineNumber property ", Continue);
947 QVERIFY(ret.property("lineNumber").strictlyEquals(eng.toScriptValue(expectErrorLineNumber)));
951 void tst_QJSEngine::errorMessage_QT679()
954 engine.globalObject().setProperty("foo", 15);
955 QJSValue error = engine.evaluate("'hello world';\nfoo.bar.blah");
956 QVERIFY(error.isError());
957 QVERIFY(error.toString().startsWith(QString::fromLatin1("TypeError: ")));
963 Foo() : x(-1), y(-1) { }
966 Q_DECLARE_METATYPE(Foo)
967 Q_DECLARE_METATYPE(Foo*)
969 Q_DECLARE_METATYPE(QLinkedList<QString>)
970 Q_DECLARE_METATYPE(QList<Foo>)
971 Q_DECLARE_METATYPE(QVector<QChar>)
972 Q_DECLARE_METATYPE(QStack<int>)
973 Q_DECLARE_METATYPE(QQueue<char>)
974 Q_DECLARE_METATYPE(QLinkedList<QStack<int> >)
976 void tst_QJSEngine::valueConversion_basic()
980 QJSValue num = eng.toScriptValue(123);
981 QCOMPARE(num.isNumber(), true);
982 QCOMPARE(num.strictlyEquals(eng.toScriptValue(123)), true);
984 int inum = eng.fromScriptValue<int>(num);
987 QString snum = eng.fromScriptValue<QString>(num);
988 QCOMPARE(snum, QLatin1String("123"));
991 QJSValue num = eng.toScriptValue(123);
992 QCOMPARE(num.isNumber(), true);
993 QCOMPARE(num.strictlyEquals(eng.toScriptValue(123)), true);
995 int inum = eng.fromScriptValue<int>(num);
998 QString snum = eng.fromScriptValue<QString>(num);
999 QCOMPARE(snum, QLatin1String("123"));
1002 QJSValue num = eng.toScriptValue(123);
1003 QCOMPARE(eng.fromScriptValue<char>(num), char(123));
1004 QCOMPARE(eng.fromScriptValue<unsigned char>(num), (unsigned char)(123));
1005 QCOMPARE(eng.fromScriptValue<short>(num), short(123));
1006 QCOMPARE(eng.fromScriptValue<unsigned short>(num), (unsigned short)(123));
1007 QCOMPARE(eng.fromScriptValue<float>(num), float(123));
1008 QCOMPARE(eng.fromScriptValue<double>(num), double(123));
1009 QCOMPARE(eng.fromScriptValue<qlonglong>(num), qlonglong(123));
1010 QCOMPARE(eng.fromScriptValue<qulonglong>(num), qulonglong(123));
1014 QCOMPARE(eng.fromScriptValue<char>(num), char(123));
1015 QCOMPARE(eng.fromScriptValue<unsigned char>(num), (unsigned char)(123));
1016 QCOMPARE(eng.fromScriptValue<short>(num), short(123));
1017 QCOMPARE(eng.fromScriptValue<unsigned short>(num), (unsigned short)(123));
1018 QCOMPARE(eng.fromScriptValue<float>(num), float(123));
1019 QCOMPARE(eng.fromScriptValue<double>(num), double(123));
1020 QCOMPARE(eng.fromScriptValue<qlonglong>(num), qlonglong(123));
1021 QCOMPARE(eng.fromScriptValue<qulonglong>(num), qulonglong(123));
1025 QJSValue num = eng.toScriptValue(Q_INT64_C(0x100000000));
1026 QCOMPARE(eng.fromScriptValue<qlonglong>(num), Q_INT64_C(0x100000000));
1027 QCOMPARE(eng.fromScriptValue<qulonglong>(num), Q_UINT64_C(0x100000000));
1031 QChar c = QLatin1Char('c');
1032 QJSValue str = eng.toScriptValue(QString::fromLatin1("ciao"));
1033 QCOMPARE(eng.fromScriptValue<QChar>(str), c);
1034 QJSValue code = eng.toScriptValue(c.unicode());
1035 QCOMPARE(eng.fromScriptValue<QChar>(code), c);
1036 QCOMPARE(eng.fromScriptValue<QChar>(eng.toScriptValue(c)), c);
1039 QVERIFY(eng.toScriptValue(static_cast<void *>(0)).isNull());
1042 void tst_QJSEngine::valueConversion_QVariant()
1045 // qScriptValueFromValue() should be "smart" when the argument is a QVariant
1047 QJSValue val = eng.toScriptValue(QVariant());
1048 QVERIFY(!val.isVariant());
1049 QVERIFY(val.isUndefined());
1051 // Checking nested QVariants
1054 QVariant tmp2(QMetaType::QVariant, &tmp1);
1055 QVERIFY(QMetaType::Type(tmp2.type()) == QMetaType::QVariant);
1057 QJSValue val1 = eng.toScriptValue(tmp1);
1058 QJSValue val2 = eng.toScriptValue(tmp2);
1059 QVERIFY(val1.isUndefined());
1060 QEXPECT_FAIL("", "Variant are unrwapped, maybe we should not...", Continue);
1061 QVERIFY(!val2.isUndefined());
1062 QVERIFY(!val1.isVariant());
1063 QEXPECT_FAIL("", "Variant are unrwapped, maybe we should not...", Continue);
1064 QVERIFY(val2.isVariant());
1068 QVariant tmp2(QMetaType::QVariant, &tmp1);
1069 QVariant tmp3(QMetaType::QVariant, &tmp2);
1070 QVERIFY(QMetaType::Type(tmp1.type()) == QMetaType::Int);
1071 QVERIFY(QMetaType::Type(tmp2.type()) == QMetaType::QVariant);
1072 QVERIFY(QMetaType::Type(tmp3.type()) == QMetaType::QVariant);
1074 QJSValue val1 = eng.toScriptValue(tmp2);
1075 QJSValue val2 = eng.toScriptValue(tmp3);
1076 QVERIFY(!val1.isUndefined());
1077 QVERIFY(!val2.isUndefined());
1078 QEXPECT_FAIL("", "Variant are unrwapped, maybe we should not...", Continue);
1079 QVERIFY(val1.isVariant());
1080 QEXPECT_FAIL("", "Variant are unrwapped, maybe we should not...", Continue);
1081 QVERIFY(val2.isVariant());
1082 QVERIFY(val1.toVariant().toInt() == 123);
1083 QVERIFY(eng.toScriptValue(val2.toVariant()).toVariant().toInt() == 123);
1086 QJSValue val = eng.toScriptValue(QVariant(true));
1087 QVERIFY(!val.isVariant());
1088 QVERIFY(val.isBool());
1089 QCOMPARE(val.toBool(), true);
1092 QJSValue val = eng.toScriptValue(QVariant(int(123)));
1093 QVERIFY(!val.isVariant());
1094 QVERIFY(val.isNumber());
1095 QCOMPARE(val.toNumber(), qreal(123));
1098 QJSValue val = eng.toScriptValue(QVariant(qreal(1.25)));
1099 QVERIFY(!val.isVariant());
1100 QVERIFY(val.isNumber());
1101 QCOMPARE(val.toNumber(), qreal(1.25));
1104 QString str = QString::fromLatin1("ciao");
1105 QJSValue val = eng.toScriptValue(QVariant(str));
1106 QVERIFY(!val.isVariant());
1107 QVERIFY(val.isString());
1108 QCOMPARE(val.toString(), str);
1111 QJSValue val = eng.toScriptValue(qVariantFromValue((QObject*)this));
1112 QVERIFY(!val.isVariant());
1113 QVERIFY(val.isQObject());
1114 QCOMPARE(val.toQObject(), (QObject*)this);
1117 QVariant var = qVariantFromValue(QPoint(123, 456));
1118 QJSValue val = eng.toScriptValue(var);
1119 QVERIFY(val.isVariant());
1120 QCOMPARE(val.toVariant(), var);
1123 QCOMPARE(qjsvalue_cast<QVariant>(QJSValue(123)), QVariant(123));
1125 QVERIFY(eng.toScriptValue(QVariant(QMetaType::VoidStar, 0)).isNull());
1128 void tst_QJSEngine::valueConversion_basic2()
1131 // more built-in types
1133 QJSValue val = eng.toScriptValue(uint(123));
1134 QVERIFY(val.isNumber());
1135 QCOMPARE(val.toInt(), 123);
1138 QJSValue val = eng.toScriptValue(qulonglong(123));
1139 QVERIFY(val.isNumber());
1140 QCOMPARE(val.toInt(), 123);
1143 QJSValue val = eng.toScriptValue(float(123));
1144 QVERIFY(val.isNumber());
1145 QCOMPARE(val.toInt(), 123);
1148 QJSValue val = eng.toScriptValue(short(123));
1149 QVERIFY(val.isNumber());
1150 QCOMPARE(val.toInt(), 123);
1153 QJSValue val = eng.toScriptValue(ushort(123));
1154 QVERIFY(val.isNumber());
1155 QCOMPARE(val.toInt(), 123);
1158 QJSValue val = eng.toScriptValue(char(123));
1159 QVERIFY(val.isNumber());
1160 QCOMPARE(val.toInt(), 123);
1163 QJSValue val = eng.toScriptValue(uchar(123));
1164 QVERIFY(val.isNumber());
1165 QCOMPARE(val.toInt(), 123);
1169 void tst_QJSEngine::valueConversion_dateTime()
1173 QDateTime in = QDateTime::currentDateTime();
1174 QJSValue val = eng.toScriptValue(in);
1175 QVERIFY(val.isDate());
1176 QCOMPARE(val.toDateTime(), in);
1179 QDate in = QDate::currentDate();
1180 QJSValue val = eng.toScriptValue(in);
1181 QVERIFY(val.isDate());
1182 QCOMPARE(val.toDateTime().date(), in);
1186 void tst_QJSEngine::valueConversion_regExp()
1190 QRegExp in = QRegExp("foo");
1191 QJSValue val = eng.toScriptValue(in);
1192 QVERIFY(val.isRegExp());
1193 QRegExp out = qjsvalue_cast<QRegExp>(val);
1194 QEXPECT_FAIL("", "QTBUG-6136: JSC-based back-end doesn't preserve QRegExp::patternSyntax (always uses RegExp2)", Continue);
1195 QCOMPARE(out.patternSyntax(), in.patternSyntax());
1196 QCOMPARE(out.pattern(), in.pattern());
1197 QCOMPARE(out.caseSensitivity(), in.caseSensitivity());
1198 QCOMPARE(out.isMinimal(), in.isMinimal());
1201 QRegExp in = QRegExp("foo", Qt::CaseSensitive, QRegExp::RegExp2);
1202 QJSValue val = eng.toScriptValue(in);
1203 QVERIFY(val.isRegExp());
1204 QCOMPARE(qjsvalue_cast<QRegExp>(val), in);
1207 QRegExp in = QRegExp("foo");
1208 in.setMinimal(true);
1209 QJSValue val = eng.toScriptValue(in);
1210 QVERIFY(val.isRegExp());
1211 QEXPECT_FAIL("", "QTBUG-6136: JSC-based back-end doesn't preserve QRegExp::minimal (always false)", Continue);
1212 QCOMPARE(qjsvalue_cast<QRegExp>(val).isMinimal(), in.isMinimal());
1216 Q_DECLARE_METATYPE(QGradient)
1217 Q_DECLARE_METATYPE(QGradient*)
1218 Q_DECLARE_METATYPE(QLinearGradient)
1220 class Klazz : public QWidget,
1221 public QStandardItem,
1222 public QGraphicsItem
1224 Q_INTERFACES(QGraphicsItem)
1227 Klazz(QWidget *parent = 0) : QWidget(parent) { }
1228 virtual QRectF boundingRect() const { return QRectF(); }
1229 virtual void paint(QPainter*, const QStyleOptionGraphicsItem*, QWidget*) { }
1232 Q_DECLARE_METATYPE(Klazz*)
1233 Q_DECLARE_METATYPE(QStandardItem*)
1235 void tst_QJSEngine::castWithMultipleInheritance()
1239 QJSValue v = eng.newQObject(&klz);
1241 QCOMPARE(qjsvalue_cast<Klazz*>(v), &klz);
1242 QCOMPARE(qjsvalue_cast<QWidget*>(v), (QWidget *)&klz);
1243 QCOMPARE(qjsvalue_cast<QObject*>(v), (QObject *)&klz);
1244 QCOMPARE(qjsvalue_cast<QStandardItem*>(v), (QStandardItem *)&klz);
1245 QCOMPARE(qjsvalue_cast<QGraphicsItem*>(v), (QGraphicsItem *)&klz);
1248 void tst_QJSEngine::collectGarbage()
1251 eng.evaluate("a = new Object(); a = new Object(); a = new Object()");
1252 QJSValue a = eng.newObject();
1253 a = eng.newObject();
1254 a = eng.newObject();
1255 QPointer<QObject> ptr = new QObject();
1257 (void)eng.newQObject(ptr);
1258 collectGarbage_helper(eng);
1260 QGuiApplication::sendPostedEvents(ptr, QEvent::DeferredDelete);
1264 void tst_QJSEngine::gcWithNestedDataStructure()
1266 // The GC must be able to traverse deeply nested objects, otherwise this
1267 // test would crash.
1269 QJSValue ret = eng.evaluate(
1270 "function makeList(size)"
1274 " for (var i = 0; i < size; ++i) {"
1275 " l.data = i + \"\";"
1276 " l.next = { }; l = l.next;"
1281 QVERIFY(!ret.isError());
1282 const int size = 200;
1283 QJSValue head = eng.evaluate(QString::fromLatin1("makeList(%0)").arg(size));
1284 QVERIFY(!head.isError());
1285 for (int x = 0; x < 2; ++x) {
1287 eng.evaluate("gc()");
1289 // Make sure all the nodes are still alive.
1290 for (int i = 0; i < 200; ++i) {
1291 QCOMPARE(l.property("data").toString(), QString::number(i));
1292 l = l.property("next");
1297 void tst_QJSEngine::stacktrace()
1299 QString script = QString::fromLatin1(
1300 "function foo(counter) {\n"
1301 " switch (counter) {\n"
1302 " case 0: foo(counter+1); break;\n"
1303 " case 1: foo(counter+1); break;\n"
1304 " case 2: foo(counter+1); break;\n"
1305 " case 3: foo(counter+1); break;\n"
1306 " case 4: foo(counter+1); break;\n"
1308 " throw new Error('blah');\n"
1313 const QString fileName("testfile");
1315 QStringList backtrace;
1316 backtrace << "foo(5)@testfile:9"
1317 << "foo(4)@testfile:7"
1318 << "foo(3)@testfile:6"
1319 << "foo(2)@testfile:5"
1320 << "foo(1)@testfile:4"
1321 << "foo(0)@testfile:3"
1322 << "<global>()@testfile:12";
1325 QJSValue result = eng.evaluate(script, fileName);
1326 QVERIFY(result.isError());
1328 QJSValue stack = result.property("stack");
1330 QJSValueIterator it(stack);
1332 while (it.hasNext()) {
1334 QJSValue obj = it.value();
1335 QJSValue frame = obj.property("frame");
1337 QCOMPARE(obj.property("fileName").toString(), fileName);
1339 QJSValue callee = frame.property("arguments").property("callee");
1340 QVERIFY(callee.strictlyEquals(eng.globalObject().property("foo")));
1341 QCOMPARE(obj.property("functionName").toString(), QString("foo"));
1342 int line = obj.property("lineNumber").toInt();
1346 QCOMPARE(line, 3 + counter);
1348 QVERIFY(frame.strictlyEquals(eng.globalObject()));
1349 QVERIFY(obj.property("functionName").toString().isEmpty());
1355 // throw something that isn't an Error object
1356 // ###FIXME: No uncaughtExceptionBacktrace: QVERIFY(eng.uncaughtExceptionBacktrace().isEmpty());
1357 QString script2 = QString::fromLatin1(
1358 "function foo(counter) {\n"
1359 " switch (counter) {\n"
1360 " case 0: foo(counter+1); break;\n"
1361 " case 1: foo(counter+1); break;\n"
1362 " case 2: foo(counter+1); break;\n"
1363 " case 3: foo(counter+1); break;\n"
1364 " case 4: foo(counter+1); break;\n"
1366 " throw 'just a string';\n"
1371 QJSValue result2 = eng.evaluate(script2, fileName);
1372 QVERIFY(!result2.isError());
1373 QVERIFY(result2.isString());
1376 void tst_QJSEngine::numberParsing_data()
1378 QTest::addColumn<QString>("string");
1379 QTest::addColumn<qreal>("expect");
1381 QTest::newRow("decimal 0") << QString("0") << qreal(0);
1382 QTest::newRow("octal 0") << QString("00") << qreal(00);
1383 QTest::newRow("hex 0") << QString("0x0") << qreal(0x0);
1384 QTest::newRow("decimal 100") << QString("100") << qreal(100);
1385 QTest::newRow("hex 100") << QString("0x100") << qreal(0x100);
1386 QTest::newRow("octal 100") << QString("0100") << qreal(0100);
1387 QTest::newRow("decimal 4G") << QString("4294967296") << qreal(Q_UINT64_C(4294967296));
1388 QTest::newRow("hex 4G") << QString("0x100000000") << qreal(Q_UINT64_C(0x100000000));
1389 QTest::newRow("octal 4G") << QString("040000000000") << qreal(Q_UINT64_C(040000000000));
1390 QTest::newRow("0.5") << QString("0.5") << qreal(0.5);
1391 QTest::newRow("1.5") << QString("1.5") << qreal(1.5);
1392 QTest::newRow("1e2") << QString("1e2") << qreal(100);
1395 void tst_QJSEngine::numberParsing()
1397 QFETCH(QString, string);
1398 QFETCH(qreal, expect);
1401 QJSValue ret = eng.evaluate(string);
1402 QVERIFY(ret.isNumber());
1403 qreal actual = ret.toNumber();
1404 QCOMPARE(actual, expect);
1407 // see ECMA-262, section 7.9
1408 // This is testing ECMA compliance, not our C++ API, but it's important that
1409 // the back-end is conformant in this regard.
1410 void tst_QJSEngine::automaticSemicolonInsertion()
1414 QJSValue ret = eng.evaluate("{ 1 2 } 3");
1415 QVERIFY(ret.isError());
1416 QVERIFY(ret.toString().contains("SyntaxError"));
1419 QJSValue ret = eng.evaluate("{ 1\n2 } 3");
1420 QVERIFY(ret.isNumber());
1421 QCOMPARE(ret.toInt(), 3);
1424 QJSValue ret = eng.evaluate("for (a; b\n)");
1425 QVERIFY(ret.isError());
1426 QVERIFY(ret.toString().contains("SyntaxError"));
1429 QJSValue ret = eng.evaluate("(function() { return\n1 + 2 })()");
1430 QVERIFY(ret.isUndefined());
1433 eng.evaluate("c = 2; b = 1");
1434 QJSValue ret = eng.evaluate("a = b\n++c");
1435 QVERIFY(ret.isNumber());
1436 QCOMPARE(ret.toInt(), 3);
1439 QJSValue ret = eng.evaluate("if (a > b)\nelse c = d");
1440 QVERIFY(ret.isError());
1441 QVERIFY(ret.toString().contains("SyntaxError"));
1444 eng.evaluate("function c() { return { foo: function() { return 5; } } }");
1445 eng.evaluate("b = 1; d = 2; e = 3");
1446 QJSValue ret = eng.evaluate("a = b + c\n(d + e).foo()");
1447 QVERIFY(ret.isNumber());
1448 QCOMPARE(ret.toInt(), 6);
1451 QJSValue ret = eng.evaluate("throw\n1");
1452 QVERIFY(ret.isError());
1453 QVERIFY(ret.toString().contains("SyntaxError"));
1456 QJSValue ret = eng.evaluate("a = Number(1)\n++a");
1457 QVERIFY(ret.isNumber());
1458 QCOMPARE(ret.toInt(), 2);
1461 // "a semicolon is never inserted automatically if the semicolon
1462 // would then be parsed as an empty statement"
1464 eng.evaluate("a = 123");
1465 QJSValue ret = eng.evaluate("if (0)\n ++a; a");
1466 QVERIFY(ret.isNumber());
1467 QCOMPARE(ret.toInt(), 123);
1470 eng.evaluate("a = 123");
1471 QJSValue ret = eng.evaluate("if (0)\n --a; a");
1472 QVERIFY(ret.isNumber());
1473 QCOMPARE(ret.toInt(), 123);
1476 eng.evaluate("a = 123");
1477 QJSValue ret = eng.evaluate("if ((0))\n ++a; a");
1478 QVERIFY(ret.isNumber());
1479 QCOMPARE(ret.toInt(), 123);
1482 eng.evaluate("a = 123");
1483 QJSValue ret = eng.evaluate("if ((0))\n --a; a");
1484 QVERIFY(ret.isNumber());
1485 QCOMPARE(ret.toInt(), 123);
1488 eng.evaluate("a = 123");
1489 QJSValue ret = eng.evaluate("if (0\n)\n ++a; a");
1490 QVERIFY(ret.isNumber());
1491 QCOMPARE(ret.toInt(), 123);
1494 eng.evaluate("a = 123");
1495 QJSValue ret = eng.evaluate("if (0\n ++a; a");
1496 QVERIFY(ret.isError());
1499 eng.evaluate("a = 123");
1500 QJSValue ret = eng.evaluate("if (0))\n ++a; a");
1501 QVERIFY(ret.isError());
1504 QJSValue ret = eng.evaluate("n = 0; for (i = 0; i < 10; ++i)\n ++n; n");
1505 QVERIFY(ret.isNumber());
1506 QCOMPARE(ret.toInt(), 10);
1509 QJSValue ret = eng.evaluate("n = 30; for (i = 0; i < 10; ++i)\n --n; n");
1510 QVERIFY(ret.isNumber());
1511 QCOMPARE(ret.toInt(), 20);
1514 QJSValue ret = eng.evaluate("n = 0; for (var i = 0; i < 10; ++i)\n ++n; n");
1515 QVERIFY(ret.isNumber());
1516 QCOMPARE(ret.toInt(), 10);
1519 QJSValue ret = eng.evaluate("n = 30; for (var i = 0; i < 10; ++i)\n --n; n");
1520 QVERIFY(ret.isNumber());
1521 QCOMPARE(ret.toInt(), 20);
1524 QJSValue ret = eng.evaluate("n = 0; i = 0; while (i++ < 10)\n ++n; n");
1525 QVERIFY(ret.isNumber());
1526 QCOMPARE(ret.toInt(), 10);
1529 QJSValue ret = eng.evaluate("n = 30; i = 0; while (i++ < 10)\n --n; n");
1530 QVERIFY(ret.isNumber());
1531 QCOMPARE(ret.toInt(), 20);
1534 QJSValue ret = eng.evaluate("o = { a: 0, b: 1, c: 2 }; n = 0; for (i in o)\n ++n; n");
1535 QVERIFY(ret.isNumber());
1536 QCOMPARE(ret.toInt(), 3);
1539 QJSValue ret = eng.evaluate("o = { a: 0, b: 1, c: 2 }; n = 9; for (i in o)\n --n; n");
1540 QVERIFY(ret.isNumber());
1541 QCOMPARE(ret.toInt(), 6);
1544 QJSValue ret = eng.evaluate("o = { a: 0, b: 1, c: 2 }; n = 0; for (var i in o)\n ++n; n");
1545 QVERIFY(ret.isNumber());
1546 QCOMPARE(ret.toInt(), 3);
1549 QJSValue ret = eng.evaluate("o = { a: 0, b: 1, c: 2 }; n = 9; for (var i in o)\n --n; n");
1550 QVERIFY(ret.isNumber());
1551 QCOMPARE(ret.toInt(), 6);
1554 QJSValue ret = eng.evaluate("o = { n: 3 }; n = 5; with (o)\n ++n; n");
1555 QVERIFY(ret.isNumber());
1556 QCOMPARE(ret.toInt(), 5);
1559 QJSValue ret = eng.evaluate("o = { n: 3 }; n = 10; with (o)\n --n; n");
1560 QVERIFY(ret.isNumber());
1561 QCOMPARE(ret.toInt(), 10);
1564 QJSValue ret = eng.evaluate("n = 5; i = 0; do\n ++n; while (++i < 10); n");
1565 QVERIFY(ret.isNumber());
1566 QCOMPARE(ret.toInt(), 15);
1569 QJSValue ret = eng.evaluate("n = 20; i = 0; do\n --n; while (++i < 10); n");
1570 QVERIFY(ret.isNumber());
1571 QCOMPARE(ret.toInt(), 10);
1575 QJSValue ret = eng.evaluate("n = 1; i = 0; if (n) i\n++n; n");
1576 QVERIFY(ret.isNumber());
1577 QCOMPARE(ret.toInt(), 2);
1580 QJSValue ret = eng.evaluate("n = 1; i = 0; if (n) i\n--n; n");
1581 QVERIFY(ret.isNumber());
1582 QCOMPARE(ret.toInt(), 0);
1586 QJSValue ret = eng.evaluate("if (0)");
1587 QVERIFY(ret.isError());
1590 QJSValue ret = eng.evaluate("while (0)");
1591 QVERIFY(ret.isError());
1594 QJSValue ret = eng.evaluate("for (;;)");
1595 QVERIFY(ret.isError());
1598 QJSValue ret = eng.evaluate("for (p in this)");
1599 QVERIFY(ret.isError());
1602 QJSValue ret = eng.evaluate("with (this)");
1603 QVERIFY(ret.isError());
1606 QJSValue ret = eng.evaluate("do");
1607 QVERIFY(ret.isError());
1611 void tst_QJSEngine::errorConstructors()
1614 QStringList prefixes;
1615 prefixes << "" << "Eval" << "Range" << "Reference" << "Syntax" << "Type" << "URI";
1616 for (int x = 0; x < 3; ++x) {
1617 for (int i = 0; i < prefixes.size(); ++i) {
1618 QString name = prefixes.at(i) + QLatin1String("Error");
1619 QString code = QString(i+1, QLatin1Char('\n'));
1621 code += QLatin1String("throw ");
1623 code += QLatin1String("new ");
1624 code += name + QLatin1String("()");
1625 QJSValue ret = eng.evaluate(code);
1626 QVERIFY(ret.isError());
1627 QVERIFY(ret.toString().startsWith(name));
1628 //QTBUG-6138: JSC doesn't assign lineNumber when errors are not thrown
1629 QEXPECT_FAIL("", "we have no more lineNumber property ", Continue);
1630 QCOMPARE(ret.property("lineNumber").toInt(), i+2);
1635 void tst_QJSEngine::argumentsProperty_globalContext()
1639 // Unlike function calls, the global context doesn't have an
1640 // arguments property.
1641 QJSValue ret = eng.evaluate("arguments");
1642 QVERIFY(ret.isError());
1643 QVERIFY(ret.toString().contains(QString::fromLatin1("ReferenceError")));
1645 eng.evaluate("arguments = 10");
1647 QJSValue ret = eng.evaluate("arguments");
1648 QVERIFY(ret.isNumber());
1649 QCOMPARE(ret.toInt(), 10);
1651 QVERIFY(eng.evaluate("delete arguments").toBool());
1652 QVERIFY(eng.globalObject().property("arguments").isUndefined());
1655 void tst_QJSEngine::argumentsProperty_JS()
1659 eng.evaluate("o = { arguments: 123 }");
1660 QJSValue ret = eng.evaluate("with (o) { arguments; }");
1661 QVERIFY(ret.isNumber());
1662 QCOMPARE(ret.toInt(), 123);
1666 QVERIFY(eng.globalObject().property("arguments").isUndefined());
1667 // This is testing ECMA-262 compliance. In function calls, "arguments"
1668 // appears like a local variable, and it can be replaced.
1669 QJSValue ret = eng.evaluate("(function() { arguments = 456; return arguments; })()");
1670 QVERIFY(ret.isNumber());
1671 QCOMPARE(ret.toInt(), 456);
1672 QVERIFY(eng.globalObject().property("arguments").isUndefined());
1676 void tst_QJSEngine::jsNumberClass()
1678 // See ECMA-262 Section 15.7, "Number Objects".
1682 QJSValue ctor = eng.globalObject().property("Number");
1683 QVERIFY(ctor.property("length").isNumber());
1684 QCOMPARE(ctor.property("length").toNumber(), qreal(1));
1685 QJSValue proto = ctor.property("prototype");
1686 QVERIFY(proto.isObject());
1688 QVERIFY(ctor.property("MAX_VALUE").isNumber());
1689 QVERIFY(ctor.property("MIN_VALUE").isNumber());
1690 QVERIFY(ctor.property("NaN").isNumber());
1691 QVERIFY(ctor.property("NEGATIVE_INFINITY").isNumber());
1692 QVERIFY(ctor.property("POSITIVE_INFINITY").isNumber());
1694 QCOMPARE(proto.toNumber(), qreal(0));
1695 QVERIFY(proto.property("constructor").strictlyEquals(ctor));
1698 QJSValue ret = eng.evaluate("Number()");
1699 QVERIFY(ret.isNumber());
1700 QCOMPARE(ret.toNumber(), qreal(0));
1703 QJSValue ret = eng.evaluate("Number(123)");
1704 QVERIFY(ret.isNumber());
1705 QCOMPARE(ret.toNumber(), qreal(123));
1708 QJSValue ret = eng.evaluate("Number('456')");
1709 QVERIFY(ret.isNumber());
1710 QCOMPARE(ret.toNumber(), qreal(456));
1713 QJSValue ret = eng.evaluate("new Number()");
1714 QVERIFY(!ret.isNumber());
1715 QVERIFY(ret.isObject());
1716 QCOMPARE(ret.toNumber(), qreal(0));
1719 QJSValue ret = eng.evaluate("new Number(123)");
1720 QVERIFY(!ret.isNumber());
1721 QVERIFY(ret.isObject());
1722 QCOMPARE(ret.toNumber(), qreal(123));
1725 QJSValue ret = eng.evaluate("new Number('456')");
1726 QVERIFY(!ret.isNumber());
1727 QVERIFY(ret.isObject());
1728 QCOMPARE(ret.toNumber(), qreal(456));
1731 QVERIFY(proto.property("toString").isCallable());
1733 QJSValue ret = eng.evaluate("new Number(123).toString()");
1734 QVERIFY(ret.isString());
1735 QCOMPARE(ret.toString(), QString::fromLatin1("123"));
1738 QJSValue ret = eng.evaluate("new Number(123).toString(8)");
1739 QVERIFY(ret.isString());
1740 QCOMPARE(ret.toString(), QString::fromLatin1("173"));
1743 QJSValue ret = eng.evaluate("new Number(123).toString(16)");
1744 QVERIFY(ret.isString());
1745 QCOMPARE(ret.toString(), QString::fromLatin1("7b"));
1747 QVERIFY(proto.property("toLocaleString").isCallable());
1749 QJSValue ret = eng.evaluate("new Number(123).toLocaleString()");
1750 QVERIFY(ret.isString());
1751 QCOMPARE(ret.toString(), QString::fromLatin1("123"));
1753 QVERIFY(proto.property("valueOf").isCallable());
1755 QJSValue ret = eng.evaluate("new Number(123).valueOf()");
1756 QVERIFY(ret.isNumber());
1757 QCOMPARE(ret.toNumber(), qreal(123));
1759 QVERIFY(proto.property("toExponential").isCallable());
1761 QJSValue ret = eng.evaluate("new Number(123).toExponential()");
1762 QVERIFY(ret.isString());
1763 QCOMPARE(ret.toString(), QString::fromLatin1("1.23e+2"));
1765 QVERIFY(proto.property("toFixed").isCallable());
1767 QJSValue ret = eng.evaluate("new Number(123).toFixed()");
1768 QVERIFY(ret.isString());
1769 QCOMPARE(ret.toString(), QString::fromLatin1("123"));
1771 QVERIFY(proto.property("toPrecision").isCallable());
1773 QJSValue ret = eng.evaluate("new Number(123).toPrecision()");
1774 QVERIFY(ret.isString());
1775 QCOMPARE(ret.toString(), QString::fromLatin1("123"));
1779 void tst_QJSEngine::jsForInStatement_simple()
1783 QJSValue ret = eng.evaluate("o = { }; r = []; for (var p in o) r[r.length] = p; r");
1784 QStringList lst = qjsvalue_cast<QStringList>(ret);
1785 QVERIFY(lst.isEmpty());
1788 QJSValue ret = eng.evaluate("o = { p: 123 }; r = [];"
1789 "for (var p in o) r[r.length] = p; r");
1790 QStringList lst = qjsvalue_cast<QStringList>(ret);
1791 QCOMPARE(lst.size(), 1);
1792 QCOMPARE(lst.at(0), QString::fromLatin1("p"));
1795 QJSValue ret = eng.evaluate("o = { p: 123, q: 456 }; r = [];"
1796 "for (var p in o) r[r.length] = p; r");
1797 QStringList lst = qjsvalue_cast<QStringList>(ret);
1798 QCOMPARE(lst.size(), 2);
1799 QCOMPARE(lst.at(0), QString::fromLatin1("p"));
1800 QCOMPARE(lst.at(1), QString::fromLatin1("q"));
1804 void tst_QJSEngine::jsForInStatement_prototypeProperties()
1808 QJSValue ret = eng.evaluate("o = { }; o.__proto__ = { p: 123 }; r = [];"
1809 "for (var p in o) r[r.length] = p; r");
1810 QStringList lst = qjsvalue_cast<QStringList>(ret);
1811 QCOMPARE(lst.size(), 1);
1812 QCOMPARE(lst.at(0), QString::fromLatin1("p"));
1815 QJSValue ret = eng.evaluate("o = { p: 123 }; o.__proto__ = { q: 456 }; r = [];"
1816 "for (var p in o) r[r.length] = p; r");
1817 QStringList lst = qjsvalue_cast<QStringList>(ret);
1818 QCOMPARE(lst.size(), 2);
1819 QCOMPARE(lst.at(0), QString::fromLatin1("p"));
1820 QCOMPARE(lst.at(1), QString::fromLatin1("q"));
1823 // shadowed property
1824 QJSValue ret = eng.evaluate("o = { p: 123 }; o.__proto__ = { p: 456 }; r = [];"
1825 "for (var p in o) r[r.length] = p; r");
1826 QStringList lst = qjsvalue_cast<QStringList>(ret);
1827 QCOMPARE(lst.size(), 1);
1828 QCOMPARE(lst.at(0), QString::fromLatin1("p"));
1833 void tst_QJSEngine::jsForInStatement_mutateWhileIterating()
1836 // deleting property during enumeration
1838 QJSValue ret = eng.evaluate("o = { p: 123 }; r = [];"
1839 "for (var p in o) { r[r.length] = p; delete r[p]; } r");
1840 QStringList lst = qjsvalue_cast<QStringList>(ret);
1841 QCOMPARE(lst.size(), 1);
1842 QCOMPARE(lst.at(0), QString::fromLatin1("p"));
1845 QJSValue ret = eng.evaluate("o = { p: 123, q: 456 }; r = [];"
1846 "for (var p in o) { r[r.length] = p; delete o.q; } r");
1847 QStringList lst = qjsvalue_cast<QStringList>(ret);
1848 QCOMPARE(lst.size(), 1);
1849 QCOMPARE(lst.at(0), QString::fromLatin1("p"));
1852 // adding property during enumeration
1854 QJSValue ret = eng.evaluate("o = { p: 123 }; r = [];"
1855 "for (var p in o) { r[r.length] = p; o.q = 456; } r");
1856 QStringList lst = qjsvalue_cast<QStringList>(ret);
1857 QCOMPARE(lst.size(), 1);
1858 QCOMPARE(lst.at(0), QString::fromLatin1("p"));
1863 void tst_QJSEngine::jsForInStatement_arrays()
1867 QJSValue ret = eng.evaluate("a = [123, 456]; r = [];"
1868 "for (var p in a) r[r.length] = p; r");
1869 QStringList lst = qjsvalue_cast<QStringList>(ret);
1870 QCOMPARE(lst.size(), 2);
1871 QCOMPARE(lst.at(0), QString::fromLatin1("0"));
1872 QCOMPARE(lst.at(1), QString::fromLatin1("1"));
1875 QJSValue ret = eng.evaluate("a = [123, 456]; a.foo = 'bar'; r = [];"
1876 "for (var p in a) r[r.length] = p; r");
1877 QStringList lst = qjsvalue_cast<QStringList>(ret);
1878 QCOMPARE(lst.size(), 3);
1879 QCOMPARE(lst.at(0), QString::fromLatin1("0"));
1880 QCOMPARE(lst.at(1), QString::fromLatin1("1"));
1881 QCOMPARE(lst.at(2), QString::fromLatin1("foo"));
1884 QJSValue ret = eng.evaluate("a = [123, 456]; a.foo = 'bar';"
1885 "b = [111, 222, 333]; b.bar = 'baz';"
1886 "a.__proto__ = b; r = [];"
1887 "for (var p in a) r[r.length] = p; r");
1888 QStringList lst = qjsvalue_cast<QStringList>(ret);
1889 QCOMPARE(lst.size(), 5);
1890 QCOMPARE(lst.at(0), QString::fromLatin1("0"));
1891 QCOMPARE(lst.at(1), QString::fromLatin1("1"));
1892 QCOMPARE(lst.at(2), QString::fromLatin1("foo"));
1893 QCOMPARE(lst.at(3), QString::fromLatin1("2"));
1894 QCOMPARE(lst.at(4), QString::fromLatin1("bar"));
1898 void tst_QJSEngine::jsForInStatement_nullAndUndefined()
1902 QJSValue ret = eng.evaluate("r = true; for (var p in undefined) r = false; r");
1903 QVERIFY(ret.isBool());
1904 QVERIFY(ret.toBool());
1907 QJSValue ret = eng.evaluate("r = true; for (var p in null) r = false; r");
1908 QVERIFY(ret.isBool());
1909 QVERIFY(ret.toBool());
1913 void tst_QJSEngine::jsFunctionDeclarationAsStatement()
1915 // ECMA-262 does not allow function declarations to be used as statements,
1916 // but several popular implementations (including JSC) do. See the NOTE
1917 // at the beginning of chapter 12 in ECMA-262 5th edition, where it's
1918 // recommended that implementations either disallow this usage or issue
1920 // Since we had a bug report long ago about QtScript not supporting this
1921 // "feature" (and thus deviating from other implementations), we still
1922 // check this behavior.
1925 QVERIFY(eng.globalObject().property("bar").isUndefined());
1926 eng.evaluate("function foo(arg) {\n"
1927 " if (arg == 'bar')\n"
1928 " function bar() { return 'bar'; }\n"
1930 " function baz() { return 'baz'; }\n"
1931 " return (arg == 'bar') ? bar : baz;\n"
1933 QVERIFY(eng.globalObject().property("bar").isUndefined());
1934 QVERIFY(eng.globalObject().property("baz").isUndefined());
1935 QVERIFY(eng.evaluate("foo").isCallable());
1937 QJSValue ret = eng.evaluate("foo('bar')");
1938 QVERIFY(ret.isCallable());
1939 QJSValue ret2 = ret.call();
1940 QCOMPARE(ret2.toString(), QString::fromLatin1("bar"));
1941 QVERIFY(eng.globalObject().property("bar").isUndefined());
1942 QVERIFY(eng.globalObject().property("baz").isUndefined());
1945 QJSValue ret = eng.evaluate("foo('baz')");
1946 QVERIFY(ret.isCallable());
1947 QJSValue ret2 = ret.call();
1948 QCOMPARE(ret2.toString(), QString::fromLatin1("baz"));
1949 QVERIFY(eng.globalObject().property("bar").isUndefined());
1950 QVERIFY(eng.globalObject().property("baz").isUndefined());
1954 void tst_QJSEngine::stringObjects()
1956 // See ECMA-262 Section 15.5, "String Objects".
1959 QString str("ciao");
1962 QJSValue obj = eng.evaluate(QString::fromLatin1("new String('%0')").arg(str));
1963 QCOMPARE(obj.property("length").toInt(), str.length());
1964 for (int i = 0; i < str.length(); ++i) {
1965 QString pname = QString::number(i);
1966 QVERIFY(obj.property(pname).isString());
1967 QCOMPARE(obj.property(pname).toString(), QString(str.at(i)));
1968 QVERIFY(!obj.deleteProperty(pname));
1969 obj.setProperty(pname, 123);
1970 QVERIFY(obj.property(pname).isString());
1971 QCOMPARE(obj.property(pname).toString(), QString(str.at(i)));
1973 QVERIFY(obj.property("-1").isUndefined());
1974 QVERIFY(obj.property(QString::number(str.length())).isUndefined());
1976 QJSValue val = eng.toScriptValue(123);
1977 obj.setProperty("-1", val);
1978 QVERIFY(obj.property("-1").strictlyEquals(val));
1979 obj.setProperty("100", val);
1980 QVERIFY(obj.property("100").strictlyEquals(val));
1984 QJSValue ret = eng.evaluate("s = new String('ciao'); r = []; for (var p in s) r.push(p); r");
1985 QVERIFY(ret.isArray());
1986 QStringList lst = qjsvalue_cast<QStringList>(ret);
1987 QCOMPARE(lst.size(), str.length());
1988 for (int i = 0; i < str.length(); ++i)
1989 QCOMPARE(lst.at(i), QString::number(i));
1991 QJSValue ret2 = eng.evaluate("s[0] = 123; s[0]");
1992 QVERIFY(ret2.isString());
1993 QCOMPARE(ret2.toString().length(), 1);
1994 QCOMPARE(ret2.toString().at(0), str.at(0));
1996 QJSValue ret3 = eng.evaluate("s[-1] = 123; s[-1]");
1997 QVERIFY(ret3.isNumber());
1998 QCOMPARE(ret3.toInt(), 123);
2000 QJSValue ret4 = eng.evaluate("s[s.length] = 456; s[s.length]");
2001 QVERIFY(ret4.isNumber());
2002 QCOMPARE(ret4.toInt(), 456);
2004 QJSValue ret5 = eng.evaluate("delete s[0]");
2005 QVERIFY(ret5.isBool());
2006 QVERIFY(!ret5.toBool());
2008 QJSValue ret6 = eng.evaluate("delete s[-1]");
2009 QVERIFY(ret6.isBool());
2010 QVERIFY(ret6.toBool());
2012 QJSValue ret7 = eng.evaluate("delete s[s.length]");
2013 QVERIFY(ret7.isBool());
2014 QVERIFY(ret7.toBool());
2018 void tst_QJSEngine::jsStringPrototypeReplaceBugs()
2023 QJSValue ret = eng.evaluate("replace_args = []; \"a a a\".replace(/(a)/g, function() { replace_args.push(arguments); }); replace_args");
2024 QVERIFY(ret.isArray());
2025 int len = ret.property("length").toInt();
2027 for (int i = 0; i < len; ++i) {
2028 QJSValue args = ret.property(i);
2029 QCOMPARE(args.property("length").toInt(), 4);
2030 QCOMPARE(args.property(0).toString(), QString::fromLatin1("a")); // matched string
2031 QCOMPARE(args.property(1).toString(), QString::fromLatin1("a")); // capture
2032 QVERIFY(args.property(2).isNumber());
2033 QCOMPARE(args.property(2).toInt(), i*2); // index of match
2034 QCOMPARE(args.property(3).toString(), QString::fromLatin1("a a a"));
2039 QJSValue ret = eng.evaluate("\"foo\".replace(/a/g, function() {})");
2040 QVERIFY(ret.isString());
2041 QCOMPARE(ret.toString(), QString::fromLatin1("foo"));
2045 void tst_QJSEngine::getterSetterThisObject_global()
2050 eng.evaluate("__defineGetter__('x', function() { return this; });");
2052 QJSValue ret = eng.evaluate("x");
2053 QVERIFY(ret.equals(eng.globalObject()));
2056 QJSValue ret = eng.evaluate("(function() { return x; })()");
2057 QVERIFY(ret.equals(eng.globalObject()));
2060 QJSValue ret = eng.evaluate("with (this) x");
2061 QVERIFY(ret.equals(eng.globalObject()));
2064 QJSValue ret = eng.evaluate("with ({}) x");
2065 QVERIFY(ret.equals(eng.globalObject()));
2068 QJSValue ret = eng.evaluate("(function() { with ({}) return x; })()");
2069 QVERIFY(ret.equals(eng.globalObject()));
2072 eng.evaluate("__defineSetter__('x', function() { return this; });");
2074 QJSValue ret = eng.evaluate("x = 'foo'");
2075 // SpiderMonkey says setter return value, JSC says RHS.
2076 QVERIFY(ret.isString());
2077 QCOMPARE(ret.toString(), QString::fromLatin1("foo"));
2080 QJSValue ret = eng.evaluate("(function() { return x = 'foo'; })()");
2081 // SpiderMonkey says setter return value, JSC says RHS.
2082 QVERIFY(ret.isString());
2083 QCOMPARE(ret.toString(), QString::fromLatin1("foo"));
2086 QJSValue ret = eng.evaluate("with (this) x = 'foo'");
2087 // SpiderMonkey says setter return value, JSC says RHS.
2088 QVERIFY(ret.isString());
2089 QCOMPARE(ret.toString(), QString::fromLatin1("foo"));
2092 QJSValue ret = eng.evaluate("with ({}) x = 'foo'");
2093 // SpiderMonkey says setter return value, JSC says RHS.
2094 QVERIFY(ret.isString());
2095 QCOMPARE(ret.toString(), QString::fromLatin1("foo"));
2098 QJSValue ret = eng.evaluate("(function() { with ({}) return x = 'foo'; })()");
2099 // SpiderMonkey says setter return value, JSC says RHS.
2100 QVERIFY(ret.isString());
2101 QCOMPARE(ret.toString(), QString::fromLatin1("foo"));
2106 void tst_QJSEngine::getterSetterThisObject_plain()
2110 eng.evaluate("o = {}");
2112 eng.evaluate("o.__defineGetter__('x', function() { return this; })");
2113 QVERIFY(eng.evaluate("o.x === o").toBool());
2114 QVERIFY(eng.evaluate("with (o) x").equals(eng.evaluate("o")));
2115 QVERIFY(eng.evaluate("(function() { with (o) return x; })() === o").toBool());
2116 eng.evaluate("q = {}; with (o) with (q) x").equals(eng.evaluate("o"));
2118 eng.evaluate("o.__defineSetter__('x', function() { return this; });");
2119 // SpiderMonkey says setter return value, JSC says RHS.
2120 QVERIFY(eng.evaluate("(o.x = 'foo') === 'foo'").toBool());
2121 QVERIFY(eng.evaluate("with (o) x = 'foo'").equals("foo"));
2122 QVERIFY(eng.evaluate("with (o) with (q) x = 'foo'").equals("foo"));
2126 void tst_QJSEngine::getterSetterThisObject_prototypeChain()
2130 eng.evaluate("o = {}; p = {}; o.__proto__ = p");
2132 eng.evaluate("p.__defineGetter__('x', function() { return this; })");
2133 QVERIFY(eng.evaluate("o.x === o").toBool());
2134 QVERIFY(eng.evaluate("with (o) x").equals(eng.evaluate("o")));
2135 QVERIFY(eng.evaluate("(function() { with (o) return x; })() === o").toBool());
2136 eng.evaluate("q = {}; with (o) with (q) x").equals(eng.evaluate("o"));
2137 eng.evaluate("with (q) with (o) x").equals(eng.evaluate("o"));
2139 eng.evaluate("o.__defineSetter__('x', function() { return this; });");
2140 // SpiderMonkey says setter return value, JSC says RHS.
2141 QVERIFY(eng.evaluate("(o.x = 'foo') === 'foo'").toBool());
2142 QVERIFY(eng.evaluate("with (o) x = 'foo'").equals("foo"));
2143 QVERIFY(eng.evaluate("with (o) with (q) x = 'foo'").equals("foo"));
2147 void tst_QJSEngine::jsContinueInSwitch()
2149 // This is testing ECMA-262 compliance, not C++ API.
2152 // switch - continue
2154 QJSValue ret = eng.evaluate("switch (1) { default: continue; }");
2155 QVERIFY(ret.isError());
2157 // for - switch - case - continue
2159 QJSValue ret = eng.evaluate("j = 0; for (i = 0; i < 100000; ++i) {\n"
2161 " case 1: ++j; continue;\n"
2162 " case 100: ++j; continue;\n"
2163 " case 1000: ++j; continue;\n"
2166 QVERIFY(ret.isNumber());
2167 QCOMPARE(ret.toInt(), 3);
2169 // for - switch - case - default - continue
2171 QJSValue ret = eng.evaluate("j = 0; for (i = 0; i < 100000; ++i) {\n"
2173 " case 1: ++j; continue;\n"
2174 " case 100: ++j; continue;\n"
2175 " case 1000: ++j; continue;\n"
2176 " default: if (i < 50000) break; else continue;\n"
2179 QVERIFY(ret.isNumber());
2180 QCOMPARE(ret.toInt(), 3);
2182 // switch - for - continue
2184 QJSValue ret = eng.evaluate("j = 123; switch (j) {\n"
2186 " for (i = 0; i < 100000; ++i) {\n"
2190 QVERIFY(ret.isNumber());
2191 QCOMPARE(ret.toInt(), 100000);
2193 // switch - switch - continue
2195 QJSValue ret = eng.evaluate("i = 1; switch (i) { default: switch (i) { case 1: continue; } }");
2196 QVERIFY(ret.isError());
2198 // for - switch - switch - continue
2200 QJSValue ret = eng.evaluate("j = 0; for (i = 0; i < 100000; ++i) {\n"
2204 " case 1: ++j; continue;\n"
2208 QVERIFY(ret.isNumber());
2209 QCOMPARE(ret.toInt(), 1);
2211 // switch - for - switch - continue
2213 QJSValue ret = eng.evaluate("j = 123; switch (j) {\n"
2215 " for (i = 0; i < 100000; ++i) {\n"
2222 QVERIFY(ret.isNumber());
2223 QCOMPARE(ret.toInt(), 100000);
2227 void tst_QJSEngine::jsShadowReadOnlyPrototypeProperty()
2229 // SpiderMonkey has different behavior than JSC and V8; it disallows
2230 // creating a property on the instance if there's a property with the
2231 // same name in the prototype, and that property is read-only. We
2232 // adopted that behavior in the old (4.5) QtScript back-end, but it
2233 // just seems weird -- and non-compliant. Adopt the JSC behavior instead.
2235 QVERIFY(eng.evaluate("o = {}; o.__proto__ = parseInt; o.length").isNumber());
2236 QCOMPARE(eng.evaluate("o.length = 123; o.length").toInt(), 123);
2237 QVERIFY(eng.evaluate("o.hasOwnProperty('length')").toBool());
2240 void tst_QJSEngine::jsReservedWords_data()
2242 QTest::addColumn<QString>("word");
2243 QTest::newRow("break") << QString("break");
2244 QTest::newRow("case") << QString("case");
2245 QTest::newRow("catch") << QString("catch");
2246 QTest::newRow("continue") << QString("continue");
2247 QTest::newRow("default") << QString("default");
2248 QTest::newRow("delete") << QString("delete");
2249 QTest::newRow("do") << QString("do");
2250 QTest::newRow("else") << QString("else");
2251 QTest::newRow("false") << QString("false");
2252 QTest::newRow("finally") << QString("finally");
2253 QTest::newRow("for") << QString("for");
2254 QTest::newRow("function") << QString("function");
2255 QTest::newRow("if") << QString("if");
2256 QTest::newRow("in") << QString("in");
2257 QTest::newRow("instanceof") << QString("instanceof");
2258 QTest::newRow("new") << QString("new");
2259 QTest::newRow("null") << QString("null");
2260 QTest::newRow("return") << QString("return");
2261 QTest::newRow("switch") << QString("switch");
2262 QTest::newRow("this") << QString("this");
2263 QTest::newRow("throw") << QString("throw");
2264 QTest::newRow("true") << QString("true");
2265 QTest::newRow("try") << QString("try");
2266 QTest::newRow("typeof") << QString("typeof");
2267 QTest::newRow("var") << QString("var");
2268 QTest::newRow("void") << QString("void");
2269 QTest::newRow("while") << QString("while");
2270 QTest::newRow("with") << QString("with");
2273 void tst_QJSEngine::jsReservedWords()
2275 // See ECMA-262 Section 7.6.1, "Reserved Words".
2276 // We prefer that the implementation is less strict than the spec; e.g.
2277 // it's good to allow reserved words as identifiers in object literals,
2278 // and when accessing properties using dot notation.
2280 QFETCH(QString, word);
2283 QJSValue ret = eng.evaluate(word + " = 123");
2284 QVERIFY(ret.isError());
2285 QString str = ret.toString();
2286 QVERIFY(str.startsWith("SyntaxError") || str.startsWith("ReferenceError"));
2290 QJSValue ret = eng.evaluate("var " + word + " = 123");
2291 QVERIFY(ret.isError());
2292 QVERIFY(ret.toString().startsWith("SyntaxError"));
2296 QJSValue ret = eng.evaluate("o = {}; o." + word + " = 123");
2297 // in the old back-end, in SpiderMonkey and in v8, this is allowed, but not in JSC
2298 QVERIFY(!ret.isError());
2299 QVERIFY(ret.strictlyEquals(eng.evaluate("o." + word)));
2303 QJSValue ret = eng.evaluate("o = { " + word + ": 123 }");
2304 // in the old back-end, in SpiderMonkey and in v8, this is allowed, but not in JSC
2305 QVERIFY(!ret.isError());
2306 QVERIFY(ret.property(word).isNumber());
2309 // SpiderMonkey allows this, but we don't
2311 QJSValue ret = eng.evaluate("function " + word + "() {}");
2312 QVERIFY(ret.isError());
2313 QVERIFY(ret.toString().startsWith("SyntaxError"));
2317 void tst_QJSEngine::jsFutureReservedWords_data()
2319 QTest::addColumn<QString>("word");
2320 QTest::addColumn<bool>("allowed");
2321 QTest::newRow("abstract") << QString("abstract") << true;
2322 QTest::newRow("boolean") << QString("boolean") << true;
2323 QTest::newRow("byte") << QString("byte") << true;
2324 QTest::newRow("char") << QString("char") << true;
2325 QTest::newRow("class") << QString("class") << false;
2326 QTest::newRow("const") << QString("const") << false;
2327 QTest::newRow("debugger") << QString("debugger") << false;
2328 QTest::newRow("double") << QString("double") << true;
2329 QTest::newRow("enum") << QString("enum") << false;
2330 QTest::newRow("export") << QString("export") << false;
2331 QTest::newRow("extends") << QString("extends") << false;
2332 QTest::newRow("final") << QString("final") << true;
2333 QTest::newRow("float") << QString("float") << true;
2334 QTest::newRow("goto") << QString("goto") << true;
2335 QTest::newRow("implements") << QString("implements") << true;
2336 QTest::newRow("import") << QString("import") << false;
2337 QTest::newRow("int") << QString("int") << true;
2338 QTest::newRow("interface") << QString("interface") << true;
2339 QTest::newRow("long") << QString("long") << true;
2340 QTest::newRow("native") << QString("native") << true;
2341 QTest::newRow("package") << QString("package") << true;
2342 QTest::newRow("private") << QString("private") << true;
2343 QTest::newRow("protected") << QString("protected") << true;
2344 QTest::newRow("public") << QString("public") << true;
2345 QTest::newRow("short") << QString("short") << true;
2346 QTest::newRow("static") << QString("static") << true;
2347 QTest::newRow("super") << QString("super") << false;
2348 QTest::newRow("synchronized") << QString("synchronized") << true;
2349 QTest::newRow("throws") << QString("throws") << true;
2350 QTest::newRow("transient") << QString("transient") << true;
2351 QTest::newRow("volatile") << QString("volatile") << true;
2354 void tst_QJSEngine::jsFutureReservedWords()
2356 // See ECMA-262 Section 7.6.1.2, "Future Reserved Words".
2357 // In real-world implementations, most of these words are
2358 // actually allowed as normal identifiers.
2360 QFETCH(QString, word);
2361 QFETCH(bool, allowed);
2364 QJSValue ret = eng.evaluate(word + " = 123");
2365 QCOMPARE(!ret.isError(), allowed);
2369 QJSValue ret = eng.evaluate("var " + word + " = 123");
2370 QCOMPARE(!ret.isError(), allowed);
2373 // this should probably be allowed (see task 162567)
2375 QJSValue ret = eng.evaluate("o = {}; o." + word + " = 123");
2377 QEXPECT_FAIL("class", "QTBUG-27193", Abort);
2378 QEXPECT_FAIL("const", "QTBUG-27193", Abort);
2379 QEXPECT_FAIL("debugger", "QTBUG-27193", Abort);
2380 QEXPECT_FAIL("enum", "QTBUG-27193", Abort);
2381 QEXPECT_FAIL("export", "QTBUG-27193", Abort);
2382 QEXPECT_FAIL("extends", "QTBUG-27193", Abort);
2383 QEXPECT_FAIL("import", "QTBUG-27193", Abort);
2384 QEXPECT_FAIL("super", "QTBUG-27193", Abort);
2386 QCOMPARE(ret.isNumber(), allowed);
2387 QCOMPARE(!ret.isError(), allowed);
2390 // this should probably be allowed (see task 162567)
2392 QJSValue ret = eng.evaluate("o = { " + word + ": 123 }");
2393 QCOMPARE(!ret.isError(), allowed);
2397 void tst_QJSEngine::jsThrowInsideWithStatement()
2399 // This is testing ECMA-262 compliance, not C++ API.
2404 QJSValue ret = eng.evaluate(
2406 " o = { bad : \"bug\" };"
2413 QVERIFY(ret.isError());
2414 QVERIFY(ret.toString().contains(QString::fromLatin1("ReferenceError")));
2417 QJSValue ret = eng.evaluate(
2419 " o = { bad : \"bug\" };"
2426 QVERIFY(ret.isError());
2427 QVERIFY(ret.toString().contains(QString::fromLatin1("ReferenceError")));
2430 QJSValue ret = eng.evaluate(
2431 "o = { bug : \"no bug\" };"
2439 QVERIFY(!ret.isError());
2440 QVERIFY(ret.isNumber());
2441 QCOMPARE(ret.toInt(), 123);
2444 QJSValue ret = eng.evaluate(
2445 "o = { bug : \"no bug\" };"
2449 QVERIFY(ret.isNumber());
2450 QJSValue ret2 = eng.evaluate("bug");
2451 QVERIFY(ret2.isError());
2452 QVERIFY(ret2.toString().contains(QString::fromLatin1("ReferenceError")));
2456 void tst_QJSEngine::reentrancy_globalObjectProperties()
2460 QVERIFY(eng2.globalObject().property("a").isUndefined());
2461 eng1.evaluate("a = 10");
2462 QVERIFY(eng1.globalObject().property("a").isNumber());
2463 QVERIFY(eng2.globalObject().property("a").isUndefined());
2464 eng2.evaluate("a = 20");
2465 QVERIFY(eng2.globalObject().property("a").isNumber());
2466 QCOMPARE(eng1.globalObject().property("a").toInt(), 10);
2469 void tst_QJSEngine::reentrancy_Array()
2471 // weird bug with JSC backend
2474 QCOMPARE(eng.evaluate("Array()").toString(), QString());
2475 eng.evaluate("Array.prototype.toString");
2476 QCOMPARE(eng.evaluate("Array()").toString(), QString());
2480 QCOMPARE(eng.evaluate("Array()").toString(), QString());
2484 void tst_QJSEngine::reentrancy_objectCreation()
2486 // Owned by JS engine, as newQObject() sets JS ownership explicitly
2487 QObject *temp = new QObject();
2492 QDateTime dt = QDateTime::currentDateTime();
2493 QJSValue d1 = eng1.toScriptValue(dt);
2494 QJSValue d2 = eng2.toScriptValue(dt);
2495 QCOMPARE(d1.toDateTime(), d2.toDateTime());
2496 QCOMPARE(d2.toDateTime(), d1.toDateTime());
2499 QJSValue r1 = eng1.evaluate("new RegExp('foo', 'gim')");
2500 QJSValue r2 = eng2.evaluate("new RegExp('foo', 'gim')");
2501 QCOMPARE(qjsvalue_cast<QRegExp>(r1), qjsvalue_cast<QRegExp>(r2));
2502 QCOMPARE(qjsvalue_cast<QRegExp>(r2), qjsvalue_cast<QRegExp>(r1));
2505 QJSValue o1 = eng1.newQObject(temp);
2506 QJSValue o2 = eng2.newQObject(temp);
2507 QCOMPARE(o1.toQObject(), o2.toQObject());
2508 QCOMPARE(o2.toQObject(), o1.toQObject());
2512 void tst_QJSEngine::jsIncDecNonObjectProperty()
2514 // This is testing ECMA-262 compliance, not C++ API.
2518 QJSValue ret = eng.evaluate("var a; a.n++");
2519 QVERIFY(ret.isError());
2520 QVERIFY(ret.toString().contains(QString::fromLatin1("TypeError")));
2523 QJSValue ret = eng.evaluate("var a; a.n--");
2524 QVERIFY(ret.isError());
2525 QVERIFY(ret.toString().contains(QString::fromLatin1("TypeError")));
2528 QJSValue ret = eng.evaluate("var a = null; a.n++");
2529 QVERIFY(ret.isError());
2530 QVERIFY(ret.toString().contains(QString::fromLatin1("TypeError")));
2533 QJSValue ret = eng.evaluate("var a = null; a.n--");
2534 QVERIFY(ret.isError());
2535 QVERIFY(ret.toString().contains(QString::fromLatin1("TypeError")));
2538 QJSValue ret = eng.evaluate("var a; ++a.n");
2539 QVERIFY(ret.isError());
2540 QVERIFY(ret.toString().contains(QString::fromLatin1("TypeError")));
2543 QJSValue ret = eng.evaluate("var a; --a.n");
2544 QVERIFY(ret.isError());
2545 QVERIFY(ret.toString().contains(QString::fromLatin1("TypeError")));
2548 QJSValue ret = eng.evaluate("var a; a.n += 1");
2549 QVERIFY(ret.isError());
2550 QVERIFY(ret.toString().contains(QString::fromLatin1("TypeError")));
2553 QJSValue ret = eng.evaluate("var a; a.n -= 1");
2554 QVERIFY(ret.isError());
2555 QVERIFY(ret.toString().contains(QString::fromLatin1("TypeError")));
2558 QJSValue ret = eng.evaluate("var a = 'ciao'; a.length++");
2559 QVERIFY(ret.isNumber());
2560 QCOMPARE(ret.toInt(), 4);
2563 QJSValue ret = eng.evaluate("var a = 'ciao'; a.length--");
2564 QVERIFY(ret.isNumber());
2565 QCOMPARE(ret.toInt(), 4);
2568 QJSValue ret = eng.evaluate("var a = 'ciao'; ++a.length");
2569 QVERIFY(ret.isNumber());
2570 QCOMPARE(ret.toInt(), 5);
2573 QJSValue ret = eng.evaluate("var a = 'ciao'; --a.length");
2574 QVERIFY(ret.isNumber());
2575 QCOMPARE(ret.toInt(), 3);
2579 static QRegExp minimal(QRegExp r) { r.setMinimal(true); return r; }
2581 void tst_QJSEngine::qRegExpInport_data()
2583 QTest::addColumn<QRegExp>("rx");
2584 QTest::addColumn<QString>("string");
2585 QTest::addColumn<QString>("matched");
2587 QTest::newRow("normal") << QRegExp("(test|foo)") << "test _ foo _ test _ Foo";
2588 QTest::newRow("normal2") << QRegExp("(Test|Foo)") << "test _ foo _ test _ Foo";
2589 QTest::newRow("case insensitive)") << QRegExp("(test|foo)", Qt::CaseInsensitive) << "test _ foo _ test _ Foo";
2590 QTest::newRow("case insensitive2)") << QRegExp("(Test|Foo)", Qt::CaseInsensitive) << "test _ foo _ test _ Foo";
2591 QTest::newRow("b(a*)(b*)") << QRegExp("b(a*)(b*)", Qt::CaseInsensitive) << "aaabbBbaAabaAaababaaabbaaab";
2592 QTest::newRow("greedy") << QRegExp("a*(a*)", Qt::CaseInsensitive, QRegExp::RegExp2) << "aaaabaaba";
2593 QTest::newRow("willcard") << QRegExp("*.txt", Qt::CaseSensitive, QRegExp::Wildcard) << "file.txt";
2594 QTest::newRow("willcard 2") << QRegExp("a?b.txt", Qt::CaseSensitive, QRegExp::Wildcard) << "ab.txt abb.rtc acb.txt";
2595 QTest::newRow("slash") << QRegExp("g/.*/s", Qt::CaseInsensitive, QRegExp::RegExp2) << "string/string/string";
2596 QTest::newRow("slash2") << QRegExp("g / .* / s", Qt::CaseInsensitive, QRegExp::RegExp2) << "string / string / string";
2597 QTest::newRow("fixed") << QRegExp("a*aa.a(ba)*a\\ba", Qt::CaseInsensitive, QRegExp::FixedString) << "aa*aa.a(ba)*a\\ba";
2598 QTest::newRow("fixed insensitive") << QRegExp("A*A", Qt::CaseInsensitive, QRegExp::FixedString) << "a*A A*a A*A a*a";
2599 QTest::newRow("fixed sensitive") << QRegExp("A*A", Qt::CaseSensitive, QRegExp::FixedString) << "a*A A*a A*A a*a";
2600 QTest::newRow("html") << QRegExp("<b>(.*)</b>", Qt::CaseSensitive, QRegExp::RegExp2) << "<b>bold</b><i>italic</i><b>bold</b>";
2601 QTest::newRow("html minimal") << minimal(QRegExp("<b>(.*)</b>", Qt::CaseSensitive, QRegExp::RegExp2)) << "<b>bold</b><i>italic</i><b>bold</b>";
2602 QTest::newRow("aaa") << QRegExp("a{2,5}") << "aAaAaaaaaAa";
2603 QTest::newRow("aaa minimal") << minimal(QRegExp("a{2,5}")) << "aAaAaaaaaAa";
2604 QTest::newRow("minimal") << minimal(QRegExp(".*\\} [*8]")) << "}?} ?} *";
2605 QTest::newRow(".? minimal") << minimal(QRegExp(".?")) << ".?";
2606 QTest::newRow(".+ minimal") << minimal(QRegExp(".+")) << ".+";
2607 QTest::newRow("[.?] minimal") << minimal(QRegExp("[.?]")) << ".?";
2608 QTest::newRow("[.+] minimal") << minimal(QRegExp("[.+]")) << ".+";
2611 void tst_QJSEngine::qRegExpInport()
2613 QFETCH(QRegExp, rx);
2614 QFETCH(QString, string);
2618 rexp = eng.toScriptValue(rx);
2620 QCOMPARE(rexp.isRegExp(), true);
2621 QCOMPARE(rexp.isCallable(), false);
2623 QJSValue func = eng.evaluate("(function(string, regexp) { return string.match(regexp); })");
2624 QJSValue result = func.call(QJSValueList() << string << rexp);
2627 for (int i = 0; i <= rx.captureCount(); i++) {
2628 QCOMPARE(result.property(i).toString(), rx.cap(i));
2632 // QScriptValue::toDateTime() returns a local time, whereas JS dates
2633 // are always stored as UTC. QtScript must respect the current time
2634 // zone, and correctly adjust for daylight saving time that may be in
2635 // effect at a given date (QTBUG-9770).
2636 void tst_QJSEngine::dateRoundtripJSQtJS()
2638 uint secs = QDateTime(QDate(2009, 1, 1)).toUTC().toTime_t();
2640 for (int i = 0; i < 8000; ++i) {
2641 QJSValue jsDate = eng.evaluate(QString::fromLatin1("new Date(%0)").arg(secs * 1000.0));
2642 QDateTime qtDate = jsDate.toDateTime();
2643 QJSValue jsDate2 = eng.toScriptValue(qtDate);
2644 if (jsDate2.toNumber() != jsDate.toNumber())
2645 QFAIL(qPrintable(jsDate.toString()));
2650 void tst_QJSEngine::dateRoundtripQtJSQt()
2652 QDateTime qtDate = QDateTime(QDate(2009, 1, 1));
2654 for (int i = 0; i < 8000; ++i) {
2655 QJSValue jsDate = eng.toScriptValue(qtDate);
2656 QDateTime qtDate2 = jsDate.toDateTime();
2657 if (qtDate2 != qtDate)
2658 QFAIL(qPrintable(qtDate.toString()));
2659 qtDate = qtDate.addSecs(2*60*60);
2663 void tst_QJSEngine::dateConversionJSQt()
2665 uint secs = QDateTime(QDate(2009, 1, 1)).toUTC().toTime_t();
2667 for (int i = 0; i < 8000; ++i) {
2668 QJSValue jsDate = eng.evaluate(QString::fromLatin1("new Date(%0)").arg(secs * 1000.0));
2669 QDateTime qtDate = jsDate.toDateTime();
2670 QString qtUTCDateStr = qtDate.toUTC().toString(Qt::ISODate);
2671 QString jsUTCDateStr = jsDate.property("toISOString").callWithInstance(jsDate).toString();
2672 jsUTCDateStr.remove(jsUTCDateStr.length() - 5, 4); // get rid of milliseconds (".000")
2673 if (qtUTCDateStr != jsUTCDateStr)
2674 QFAIL(qPrintable(jsDate.toString()));
2679 void tst_QJSEngine::dateConversionQtJS()
2681 QDateTime qtDate = QDateTime(QDate(2009, 1, 1));
2683 for (int i = 0; i < 8000; ++i) {
2684 QJSValue jsDate = eng.toScriptValue(qtDate);
2685 QString jsUTCDateStr = jsDate.property("toISOString").callWithInstance(jsDate).toString();
2686 jsUTCDateStr.remove(jsUTCDateStr.length() - 5, 4); // get rid of milliseconds (".000")
2687 QString qtUTCDateStr = qtDate.toUTC().toString(Qt::ISODate);
2688 if (jsUTCDateStr != qtUTCDateStr)
2689 QFAIL(qPrintable(qtDate.toString()));
2690 qtDate = qtDate.addSecs(2*60*60);
2694 void tst_QJSEngine::functionPrototypeExtensions()
2696 // QJS adds connect and disconnect properties to Function.prototype.
2698 QJSValue funProto = eng.globalObject().property("Function").property("prototype");
2699 QVERIFY(funProto.isCallable());
2700 QVERIFY(funProto.property("connect").isCallable());
2701 QVERIFY(funProto.property("disconnect").isCallable());
2703 // No properties should appear in for-in statements.
2704 QJSValue props = eng.evaluate("props = []; for (var p in Function.prototype) props.push(p); props");
2705 QVERIFY(props.isArray());
2706 QCOMPARE(props.property("length").toInt(), 0);
2709 class ThreadedTestEngine : public QThread {
2715 ThreadedTestEngine()
2719 QJSEngine firstEngine;
2720 QJSEngine secondEngine;
2721 QJSValue value = firstEngine.evaluate("1");
2722 result = secondEngine.evaluate("1 + " + QString::number(value.toInt())).toInt();
2726 void tst_QJSEngine::threadedEngine()
2728 ThreadedTestEngine thread1;
2729 ThreadedTestEngine thread2;
2734 QCOMPARE(thread1.result, 2);
2735 QCOMPARE(thread2.result, 2);
2738 void tst_QJSEngine::v8Context_simple()
2742 v8::HandleScope handleScope;
2743 v8::Local<v8::Context> context = QT_PREPEND_NAMESPACE(qt_QJSEngineV8Context(&eng));
2744 v8::Context::Scope contextScope(context);
2746 v8::Local<v8::Script> script = v8::Script::Compile(
2747 v8::String::New("({ foo: 123, bar: 'ciao', baz: true })"));
2750 v8::Local<v8::Value> result = script->Run();
2752 QVERIFY(!tc.HasCaught());
2753 QVERIFY(result->IsObject());
2755 v8::Local<v8::Object> object = result.As<v8::Object>();
2756 QVERIFY(object->Get(v8::String::New("foo"))->Equals(v8::Number::New(123)));
2757 QVERIFY(object->Get(v8::String::New("bar"))->Equals(v8::String::New("ciao")));
2758 QVERIFY(object->Get(v8::String::New("baz"))->IsTrue());
2761 void tst_QJSEngine::v8Context_exception()
2765 v8::HandleScope handleScope;
2766 v8::Local<v8::Context> context = qt_QJSEngineV8Context(&eng);
2767 v8::Context::Scope contextScope(context);
2769 int startLineNumber = 42;
2770 v8::ScriptOrigin origin(v8::String::New("test.js"), v8::Integer::New(startLineNumber));
2771 v8::Local<v8::Script> script = v8::Script::Compile(
2773 "function foo(i) {\n"
2775 " throw Error('Catch me if you can');\n"
2781 // QJS does this for us:
2782 // v8::V8::SetCaptureStackTraceForUncaughtExceptions(true);
2785 v8::Local<v8::Value> result = script->Run();
2787 QVERIFY(tc.HasCaught());
2788 QVERIFY(result.IsEmpty());
2790 v8::Local<v8::Message> message = tc.Message();
2791 QVERIFY(!message.IsEmpty());
2792 QCOMPARE(*v8::String::AsciiValue(message->Get()), "Uncaught Error: Catch me if you can");
2793 QCOMPARE(*v8::String::AsciiValue(message->GetScriptResourceName()), "test.js");
2794 QCOMPARE(message->GetLineNumber(), startLineNumber + 3);
2797 void tst_QJSEngine::v8Context_mixAPIs()
2801 v8::HandleScope handleScope;
2802 v8::Local<v8::Context> context = qt_QJSEngineV8Context(&eng);
2803 v8::Context::Scope contextScope(context);
2805 QJSValue globalQJS = eng.globalObject();
2806 v8::Local<v8::Value> globalV8Value = qt_QJSValueV8Value(globalQJS);
2807 QVERIFY(!globalV8Value.IsEmpty());
2808 QVERIFY(globalV8Value->IsObject());
2809 v8::Local<v8::Object> globalV8 = globalV8Value.As<v8::Object>();
2811 QVERIFY(globalQJS.property("foo").isUndefined());
2812 QVERIFY(globalV8->Get(v8::String::New("foo"))->IsUndefined());
2814 globalQJS.setProperty("foo", 123);
2815 QVERIFY(globalV8->Get(v8::String::New("foo"))->Equals(v8::Number::New(123)));
2817 globalV8->Set(v8::String::New("bar"), v8::String::New("ciao"));
2818 QVERIFY(globalQJS.property("bar").equals("ciao"));
2820 QJSValue arrayQJS = eng.newArray(10);
2821 v8::Local<v8::Value> arrayV8Value = qt_QJSValueV8Value(arrayQJS);
2822 QVERIFY(!arrayV8Value.IsEmpty());
2823 QVERIFY(arrayV8Value->IsArray());
2824 v8::Local<v8::Array> arrayV8 = arrayV8Value.As<v8::Array>();
2826 QCOMPARE(int(arrayV8->Length()), 10);
2827 arrayV8->Set(5, v8::Null());
2828 QVERIFY(arrayQJS.property(5).isNull());
2831 QTEST_MAIN(tst_QJSEngine)
2833 #include "tst_qjsengine.moc"