]>
Commit | Line | Data |
---|---|---|
f9bf01c6 A |
1 | /* |
2 | Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies) | |
3 | ||
4 | This library is free software; you can redistribute it and/or | |
5 | modify it under the terms of the GNU Library General Public | |
6 | License as published by the Free Software Foundation; either | |
7 | version 2 of the License, or (at your option) any later version. | |
8 | ||
9 | This library is distributed in the hope that it will be useful, | |
10 | but WITHOUT ANY WARRANTY; without even the implied warranty of | |
11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
12 | Library General Public License for more details. | |
13 | ||
14 | You should have received a copy of the GNU Library General Public License | |
15 | along with this library; see the file COPYING.LIB. If not, write to | |
16 | the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, | |
17 | Boston, MA 02110-1301, USA. | |
18 | */ | |
19 | ||
20 | #include "qscriptengine.h" | |
4e4e5a6f A |
21 | #include "qscriptprogram.h" |
22 | #include "qscriptsyntaxcheckresult.h" | |
f9bf01c6 | 23 | #include "qscriptvalue.h" |
14957cd0 | 24 | #include <QtCore/qnumeric.h> |
f9bf01c6 A |
25 | #include <QtTest/qtest.h> |
26 | ||
27 | class tst_QScriptEngine : public QObject { | |
28 | Q_OBJECT | |
29 | ||
30 | public: | |
31 | tst_QScriptEngine() {} | |
32 | virtual ~tst_QScriptEngine() {} | |
33 | ||
34 | public slots: | |
35 | void init() {} | |
36 | void cleanup() {} | |
37 | ||
38 | private slots: | |
14957cd0 A |
39 | void newFunction(); |
40 | void newObject(); | |
4e4e5a6f | 41 | void globalObject(); |
f9bf01c6 A |
42 | void evaluate(); |
43 | void collectGarbage(); | |
4e4e5a6f A |
44 | void reportAdditionalMemoryCost(); |
45 | void nullValue(); | |
46 | void undefinedValue(); | |
47 | void evaluateProgram(); | |
48 | void checkSyntax_data(); | |
49 | void checkSyntax(); | |
14957cd0 A |
50 | void toObject(); |
51 | void toObjectTwoEngines(); | |
52 | void newArray(); | |
53 | void uncaughtException(); | |
54 | void newDate(); | |
f9bf01c6 A |
55 | }; |
56 | ||
57 | /* Evaluating a script that throw an unhandled exception should return an invalid value. */ | |
58 | void tst_QScriptEngine::evaluate() | |
59 | { | |
60 | QScriptEngine engine; | |
61 | QVERIFY2(engine.evaluate("1+1").isValid(), "the expression should be evaluated and an valid result should be returned"); | |
62 | QVERIFY2(engine.evaluate("ping").isValid(), "Script throwing an unhandled exception should return an exception value"); | |
63 | } | |
64 | ||
14957cd0 A |
65 | static QScriptValue myFunction(QScriptContext*, QScriptEngine* eng) |
66 | { | |
67 | return eng->nullValue(); | |
68 | } | |
69 | ||
70 | static QScriptValue myFunctionWithArg(QScriptContext*, QScriptEngine* eng, void* arg) | |
71 | { | |
72 | int* result = reinterpret_cast<int*>(arg); | |
73 | return QScriptValue(eng, *result); | |
74 | } | |
75 | ||
76 | static QScriptValue myFunctionThatReturns(QScriptContext*, QScriptEngine* eng) | |
77 | { | |
78 | return QScriptValue(eng, 42); | |
79 | } | |
80 | ||
81 | static QScriptValue myFunctionThatReturnsWithoutEngine(QScriptContext*, QScriptEngine*) | |
82 | { | |
83 | return QScriptValue(1024); | |
84 | } | |
85 | ||
86 | static QScriptValue myFunctionThatReturnsWrongEngine(QScriptContext*, QScriptEngine*, void* arg) | |
87 | { | |
88 | QScriptEngine* wrongEngine = reinterpret_cast<QScriptEngine*>(arg); | |
89 | return QScriptValue(wrongEngine, 42); | |
90 | } | |
91 | ||
92 | void tst_QScriptEngine::newFunction() | |
93 | { | |
94 | QScriptEngine eng; | |
95 | { | |
96 | QScriptValue fun = eng.newFunction(myFunction); | |
97 | QCOMPARE(fun.isValid(), true); | |
98 | QCOMPARE(fun.isFunction(), true); | |
99 | QCOMPARE(fun.isObject(), true); | |
100 | // QCOMPARE(fun.scriptClass(), (QScriptClass*)0); | |
101 | // a prototype property is automatically constructed | |
102 | { | |
103 | QScriptValue prot = fun.property("prototype", QScriptValue::ResolveLocal); | |
104 | QVERIFY(prot.isObject()); | |
105 | QVERIFY(prot.property("constructor").strictlyEquals(fun)); | |
106 | QEXPECT_FAIL("", "JSCallbackObject::getOwnPropertyDescriptor() doesn't return correct information yet", Continue); | |
107 | QCOMPARE(fun.propertyFlags("prototype"), QScriptValue::Undeletable); | |
108 | QEXPECT_FAIL("", "WebKit bug: 40613 (The JSObjectSetProperty doesn't overwrite property flags)", Continue); | |
109 | QCOMPARE(prot.propertyFlags("constructor"), QScriptValue::PropertyFlags(QScriptValue::Undeletable | QScriptValue::SkipInEnumeration)); | |
110 | } | |
111 | // prototype should be Function.prototype | |
112 | QCOMPARE(fun.prototype().isValid(), true); | |
113 | QCOMPARE(fun.prototype().isFunction(), true); | |
114 | QCOMPARE(fun.prototype().strictlyEquals(eng.evaluate("Function.prototype")), true); | |
115 | ||
116 | QCOMPARE(fun.call().isNull(), true); | |
117 | // QCOMPARE(fun.construct().isObject(), true); | |
118 | } | |
119 | // the overload that takes an extra argument | |
120 | { | |
121 | int expectedResult = 42; | |
122 | QScriptValue fun = eng.newFunction(myFunctionWithArg, reinterpret_cast<void*>(&expectedResult)); | |
123 | QVERIFY(fun.isFunction()); | |
124 | // QCOMPARE(fun.scriptClass(), (QScriptClass*)0); | |
125 | // a prototype property is automatically constructed | |
126 | { | |
127 | QScriptValue prot = fun.property("prototype", QScriptValue::ResolveLocal); | |
128 | QVERIFY(prot.isObject()); | |
129 | QVERIFY(prot.property("constructor").strictlyEquals(fun)); | |
130 | QEXPECT_FAIL("", "JSCallbackObject::getOwnPropertyDescriptor() doesn't return correct information yet", Continue); | |
131 | QCOMPARE(fun.propertyFlags("prototype"), QScriptValue::Undeletable); | |
132 | QEXPECT_FAIL("", "WebKit bug: 40613 (The JSObjectSetProperty doesn't overwrite property flags)", Continue); | |
133 | QCOMPARE(prot.propertyFlags("constructor"), QScriptValue::PropertyFlags(QScriptValue::Undeletable | QScriptValue::SkipInEnumeration)); | |
134 | } | |
135 | // prototype should be Function.prototype | |
136 | QCOMPARE(fun.prototype().isValid(), true); | |
137 | QCOMPARE(fun.prototype().isFunction(), true); | |
138 | QCOMPARE(fun.prototype().strictlyEquals(eng.evaluate("Function.prototype")), true); | |
139 | ||
140 | QScriptValue result = fun.call(); | |
141 | QCOMPARE(result.isNumber(), true); | |
142 | QCOMPARE(result.toInt32(), expectedResult); | |
143 | } | |
144 | // the overload that takes a prototype | |
145 | { | |
146 | QScriptValue proto = eng.newObject(); | |
147 | QScriptValue fun = eng.newFunction(myFunction, proto); | |
148 | QCOMPARE(fun.isValid(), true); | |
149 | QCOMPARE(fun.isFunction(), true); | |
150 | QCOMPARE(fun.isObject(), true); | |
151 | // internal prototype should be Function.prototype | |
152 | QCOMPARE(fun.prototype().isValid(), true); | |
153 | QCOMPARE(fun.prototype().isFunction(), true); | |
154 | QCOMPARE(fun.prototype().strictlyEquals(eng.evaluate("Function.prototype")), true); | |
155 | // public prototype should be the one we passed | |
156 | QCOMPARE(fun.property("prototype").strictlyEquals(proto), true); | |
157 | QEXPECT_FAIL("", "JSCallbackObject::getOwnPropertyDescriptor() doesn't return correct information yet", Continue); | |
158 | QCOMPARE(fun.propertyFlags("prototype"), QScriptValue::Undeletable); | |
159 | QCOMPARE(proto.property("constructor").strictlyEquals(fun), true); | |
160 | QEXPECT_FAIL("", "WebKit bug: 40613 (The JSObjectSetProperty doesn't overwrite property flags)", Continue); | |
161 | QCOMPARE(proto.propertyFlags("constructor"), QScriptValue::PropertyFlags(QScriptValue::Undeletable | QScriptValue::SkipInEnumeration)); | |
162 | ||
163 | QCOMPARE(fun.call().isNull(), true); | |
164 | // QCOMPARE(fun.construct().isObject(), true); | |
165 | } | |
166 | // whether the return value is correct | |
167 | { | |
168 | QScriptValue fun = eng.newFunction(myFunctionThatReturns); | |
169 | QCOMPARE(fun.isValid(), true); | |
170 | QCOMPARE(fun.isFunction(), true); | |
171 | QCOMPARE(fun.isObject(), true); | |
172 | ||
173 | QScriptValue result = fun.call(); | |
174 | QCOMPARE(result.isNumber(), true); | |
175 | QCOMPARE(result.toInt32(), 42); | |
176 | } | |
177 | // whether the return value is assigned to the correct engine | |
178 | { | |
179 | QScriptValue fun = eng.newFunction(myFunctionThatReturnsWithoutEngine); | |
180 | QCOMPARE(fun.isValid(), true); | |
181 | QCOMPARE(fun.isFunction(), true); | |
182 | QCOMPARE(fun.isObject(), true); | |
183 | ||
184 | QScriptValue result = fun.call(); | |
185 | QCOMPARE(result.engine(), &eng); | |
186 | QCOMPARE(result.isNumber(), true); | |
187 | QCOMPARE(result.toInt32(), 1024); | |
188 | } | |
189 | // whether the return value is undefined when returning a value with wrong engine | |
190 | { | |
191 | QScriptEngine wrongEngine; | |
192 | ||
193 | QScriptValue fun = eng.newFunction(myFunctionThatReturnsWrongEngine, reinterpret_cast<void*>(&wrongEngine)); | |
194 | QCOMPARE(fun.isValid(), true); | |
195 | QCOMPARE(fun.isFunction(), true); | |
196 | QCOMPARE(fun.isObject(), true); | |
197 | ||
198 | QTest::ignoreMessage(QtWarningMsg, "Value from different engine returned from native function, returning undefined value instead."); | |
199 | QScriptValue result = fun.call(); | |
200 | QCOMPARE(result.isValid(), true); | |
201 | QCOMPARE(result.isUndefined(), true); | |
202 | } | |
203 | } | |
204 | ||
205 | void tst_QScriptEngine::newObject() | |
206 | { | |
207 | QScriptEngine engine; | |
208 | QScriptValue object = engine.newObject(); | |
209 | QVERIFY(object.isObject()); | |
210 | QVERIFY(object.engine() == &engine); | |
211 | QVERIFY(!object.isError()); | |
212 | QVERIFY(!object.equals(engine.newObject())); | |
213 | QVERIFY(!object.strictlyEquals(engine.newObject())); | |
214 | QCOMPARE(object.toString(), QString::fromAscii("[object Object]")); | |
215 | } | |
216 | ||
4e4e5a6f A |
217 | void tst_QScriptEngine::globalObject() |
218 | { | |
219 | QScriptEngine engine; | |
220 | QScriptValue global = engine.globalObject(); | |
221 | QScriptValue self = engine.evaluate("this"); | |
222 | QVERIFY(global.isObject()); | |
223 | QVERIFY(engine.globalObject().equals(engine.evaluate("this"))); | |
4e4e5a6f A |
224 | QVERIFY(engine.globalObject().strictlyEquals(self)); |
225 | } | |
226 | ||
f9bf01c6 A |
227 | /* Test garbage collection, at least try to not crash. */ |
228 | void tst_QScriptEngine::collectGarbage() | |
229 | { | |
230 | QScriptEngine engine; | |
231 | QScriptValue foo = engine.evaluate("( function foo() {return 'pong';} )"); | |
232 | QVERIFY(foo.isFunction()); | |
233 | engine.collectGarbage(); | |
234 | QCOMPARE(foo.call().toString(), QString::fromAscii("pong")); | |
235 | } | |
4e4e5a6f A |
236 | |
237 | void tst_QScriptEngine::reportAdditionalMemoryCost() | |
238 | { | |
239 | // There isn't any easy way to test the responsiveness of the GC; | |
240 | // just try to call the function a few times with various sizes. | |
241 | QScriptEngine eng; | |
242 | for (int i = 0; i < 100; ++i) { | |
243 | eng.reportAdditionalMemoryCost(0); | |
244 | eng.reportAdditionalMemoryCost(10); | |
245 | eng.reportAdditionalMemoryCost(1000); | |
246 | eng.reportAdditionalMemoryCost(10000); | |
247 | eng.reportAdditionalMemoryCost(100000); | |
248 | eng.reportAdditionalMemoryCost(1000000); | |
249 | eng.reportAdditionalMemoryCost(10000000); | |
250 | eng.reportAdditionalMemoryCost(-1); | |
251 | eng.reportAdditionalMemoryCost(-1000); | |
252 | QScriptValue obj = eng.evaluate("new Object"); | |
253 | eng.collectGarbage(); | |
254 | } | |
255 | } | |
256 | ||
257 | void tst_QScriptEngine::nullValue() | |
258 | { | |
259 | QScriptEngine engine; | |
260 | QScriptValue value = engine.nullValue(); | |
261 | QVERIFY(value.isValid()); | |
262 | QVERIFY(value.isNull()); | |
263 | } | |
264 | ||
265 | void tst_QScriptEngine::undefinedValue() | |
266 | { | |
267 | QScriptEngine engine; | |
268 | QScriptValue value = engine.undefinedValue(); | |
269 | QVERIFY(value.isValid()); | |
270 | QVERIFY(value.isUndefined()); | |
271 | } | |
272 | ||
273 | void tst_QScriptEngine::evaluateProgram() | |
274 | { | |
275 | QScriptEngine eng; | |
276 | { | |
277 | QString code("1 + 2"); | |
278 | QString fileName("hello.js"); | |
279 | int lineNumber = 123; | |
280 | QScriptProgram program(code, fileName, lineNumber); | |
281 | QVERIFY(!program.isNull()); | |
282 | QCOMPARE(program.sourceCode(), code); | |
283 | QCOMPARE(program.fileName(), fileName); | |
284 | QCOMPARE(program.firstLineNumber(), lineNumber); | |
285 | ||
286 | QScriptValue expected = eng.evaluate(code); | |
287 | for (int x = 0; x < 10; ++x) { | |
288 | QScriptValue ret = eng.evaluate(program); | |
289 | QVERIFY(ret.equals(expected)); | |
290 | } | |
291 | ||
292 | // operator= | |
293 | QScriptProgram sameProgram = program; | |
294 | QVERIFY(sameProgram == program); | |
295 | QVERIFY(eng.evaluate(sameProgram).equals(expected)); | |
296 | ||
297 | // copy constructor | |
298 | QScriptProgram sameProgram2(program); | |
299 | QVERIFY(sameProgram2 == program); | |
300 | QVERIFY(eng.evaluate(sameProgram2).equals(expected)); | |
301 | ||
302 | QScriptProgram differentProgram("2 + 3"); | |
303 | QVERIFY(differentProgram != program); | |
304 | QVERIFY(!eng.evaluate(differentProgram).equals(expected)); | |
305 | } | |
306 | ||
307 | // Program that accesses variable in the scope | |
308 | { | |
309 | QScriptProgram program("a"); | |
310 | QVERIFY(!program.isNull()); | |
311 | { | |
312 | QScriptValue ret = eng.evaluate(program); | |
313 | QVERIFY(ret.isError()); | |
314 | QCOMPARE(ret.toString(), QString::fromLatin1("ReferenceError: Can't find variable: a")); | |
315 | } | |
316 | { | |
317 | QScriptValue ret = eng.evaluate(program); | |
318 | QVERIFY(ret.isError()); | |
319 | } | |
320 | eng.evaluate("a = 456"); | |
321 | { | |
322 | QScriptValue ret = eng.evaluate(program); | |
323 | QVERIFY(!ret.isError()); | |
324 | QCOMPARE(ret.toNumber(), 456.0); | |
325 | } | |
326 | } | |
327 | ||
328 | // Program that creates closure | |
329 | { | |
330 | QScriptProgram program("(function() { var count = 0; return function() { return count++; }; })"); | |
331 | QVERIFY(!program.isNull()); | |
332 | QScriptValue createCounter = eng.evaluate(program); | |
333 | QVERIFY(createCounter.isFunction()); | |
334 | QScriptValue counter = createCounter.call(); | |
335 | QVERIFY(counter.isFunction()); | |
336 | { | |
337 | QScriptValue ret = counter.call(); | |
338 | QVERIFY(ret.isNumber()); | |
339 | } | |
340 | QScriptValue counter2 = createCounter.call(); | |
341 | QVERIFY(counter2.isFunction()); | |
342 | QVERIFY(!counter2.equals(counter)); | |
343 | { | |
344 | QScriptValue ret = counter2.call(); | |
345 | QVERIFY(ret.isNumber()); | |
346 | } | |
347 | } | |
348 | ||
349 | // Same program run in different engines | |
350 | { | |
351 | QString code("1 + 2"); | |
352 | QScriptProgram program(code); | |
353 | QVERIFY(!program.isNull()); | |
354 | double expected = eng.evaluate(program).toNumber(); | |
355 | for (int x = 0; x < 2; ++x) { | |
356 | QScriptEngine eng2; | |
357 | for (int y = 0; y < 2; ++y) { | |
358 | double ret = eng2.evaluate(program).toNumber(); | |
359 | QCOMPARE(ret, expected); | |
360 | } | |
361 | } | |
362 | } | |
363 | ||
364 | // No program | |
365 | { | |
366 | QScriptProgram program; | |
367 | QVERIFY(program.isNull()); | |
368 | QScriptValue ret = eng.evaluate(program); | |
369 | QVERIFY(!ret.isValid()); | |
370 | } | |
371 | } | |
372 | ||
373 | void tst_QScriptEngine::checkSyntax_data() | |
374 | { | |
375 | QTest::addColumn<QString>("code"); | |
376 | QTest::addColumn<int>("expectedState"); | |
377 | QTest::addColumn<int>("errorLineNumber"); | |
378 | QTest::addColumn<int>("errorColumnNumber"); | |
379 | QTest::addColumn<QString>("errorMessage"); | |
380 | ||
381 | QTest::newRow("0") | |
382 | << QString("0") << int(QScriptSyntaxCheckResult::Valid) | |
383 | << -1 << -1 << ""; | |
384 | QTest::newRow("if (") | |
385 | << QString("if (\n") << int(QScriptSyntaxCheckResult::Intermediate) | |
386 | << 1 << 4 << ""; | |
387 | QTest::newRow("if else") | |
388 | << QString("\nif else") << int(QScriptSyntaxCheckResult::Error) | |
389 | << 2 << 4 << "SyntaxError: Parse error"; | |
390 | QTest::newRow("{if}") | |
391 | << QString("{\n{\nif\n}\n") << int(QScriptSyntaxCheckResult::Error) | |
392 | << 4 << 1 << "SyntaxError: Parse error"; | |
393 | QTest::newRow("foo[") | |
394 | << QString("foo[") << int(QScriptSyntaxCheckResult::Error) | |
395 | << 1 << 4 << "SyntaxError: Parse error"; | |
396 | QTest::newRow("foo['bar']") | |
397 | << QString("foo['bar']") << int(QScriptSyntaxCheckResult::Valid) | |
398 | << -1 << -1 << ""; | |
399 | ||
400 | QTest::newRow("/*") | |
401 | << QString("/*") << int(QScriptSyntaxCheckResult::Intermediate) | |
402 | << 1 << 1 << "Unclosed comment at end of file"; | |
403 | QTest::newRow("/*\nMy comment") | |
404 | << QString("/*\nMy comment") << int(QScriptSyntaxCheckResult::Intermediate) | |
405 | << 1 << 1 << "Unclosed comment at end of file"; | |
406 | QTest::newRow("/*\nMy comment */\nfoo = 10") | |
407 | << QString("/*\nMy comment */\nfoo = 10") << int(QScriptSyntaxCheckResult::Valid) | |
408 | << -1 << -1 << ""; | |
409 | QTest::newRow("foo = 10 /*") | |
410 | << QString("foo = 10 /*") << int(QScriptSyntaxCheckResult::Intermediate) | |
411 | << -1 << -1 << ""; | |
412 | QTest::newRow("foo = 10; /*") | |
413 | << QString("foo = 10; /*") << int(QScriptSyntaxCheckResult::Intermediate) | |
414 | << 1 << 11 << "Expected `end of file'"; | |
415 | QTest::newRow("foo = 10 /* My comment */") | |
416 | << QString("foo = 10 /* My comment */") << int(QScriptSyntaxCheckResult::Valid) | |
417 | << -1 << -1 << ""; | |
418 | ||
419 | QTest::newRow("/=/") | |
420 | << QString("/=/") << int(QScriptSyntaxCheckResult::Valid) << -1 << -1 << ""; | |
421 | QTest::newRow("/=/g") | |
422 | << QString("/=/g") << int(QScriptSyntaxCheckResult::Valid) << -1 << -1 << ""; | |
423 | QTest::newRow("/a/") | |
424 | << QString("/a/") << int(QScriptSyntaxCheckResult::Valid) << -1 << -1 << ""; | |
425 | QTest::newRow("/a/g") | |
426 | << QString("/a/g") << int(QScriptSyntaxCheckResult::Valid) << -1 << -1 << ""; | |
427 | } | |
428 | ||
429 | void tst_QScriptEngine::checkSyntax() | |
430 | { | |
431 | QFETCH(QString, code); | |
432 | QFETCH(int, expectedState); | |
433 | QFETCH(int, errorLineNumber); | |
434 | QFETCH(int, errorColumnNumber); | |
435 | QFETCH(QString, errorMessage); | |
436 | ||
437 | QScriptSyntaxCheckResult result = QScriptEngine::checkSyntax(code); | |
438 | ||
439 | // assignment | |
440 | { | |
441 | QScriptSyntaxCheckResult copy = result; | |
442 | QCOMPARE(copy.state(), result.state()); | |
443 | QCOMPARE(copy.errorLineNumber(), result.errorLineNumber()); | |
444 | QCOMPARE(copy.errorColumnNumber(), result.errorColumnNumber()); | |
445 | QCOMPARE(copy.errorMessage(), result.errorMessage()); | |
446 | } | |
447 | { | |
448 | QScriptSyntaxCheckResult copy(result); | |
449 | QCOMPARE(copy.state(), result.state()); | |
450 | QCOMPARE(copy.errorLineNumber(), result.errorLineNumber()); | |
451 | QCOMPARE(copy.errorColumnNumber(), result.errorColumnNumber()); | |
452 | QCOMPARE(copy.errorMessage(), result.errorMessage()); | |
453 | } | |
454 | ||
455 | if (expectedState == QScriptSyntaxCheckResult::Intermediate) | |
456 | QEXPECT_FAIL("", "QScriptSyntaxCheckResult::state() doesn't return the Intermediate state", Abort); | |
457 | QCOMPARE(result.state(), QScriptSyntaxCheckResult::State(expectedState)); | |
458 | QCOMPARE(result.errorLineNumber(), errorLineNumber); | |
459 | if (expectedState != QScriptSyntaxCheckResult::Valid && errorColumnNumber != 1) | |
460 | QEXPECT_FAIL("", "QScriptSyntaxCheckResult::errorColumnNumber() doesn't return correct value", Continue); | |
461 | QCOMPARE(result.errorColumnNumber(), errorColumnNumber); | |
462 | QCOMPARE(result.errorMessage(), errorMessage); | |
463 | } | |
464 | ||
14957cd0 A |
465 | void tst_QScriptEngine::toObject() |
466 | { | |
467 | QScriptEngine eng; | |
468 | QVERIFY(!eng.toObject(eng.undefinedValue()).isValid()); | |
469 | QVERIFY(!eng.toObject(eng.nullValue()).isValid()); | |
470 | QVERIFY(!eng.toObject(QScriptValue()).isValid()); | |
471 | ||
472 | QScriptValue falskt(false); | |
473 | { | |
474 | QScriptValue tmp = eng.toObject(falskt); | |
475 | QVERIFY(tmp.isObject()); | |
476 | QVERIFY(!falskt.isObject()); | |
477 | QVERIFY(!falskt.engine()); | |
478 | QCOMPARE(tmp.toNumber(), falskt.toNumber()); | |
479 | } | |
480 | ||
481 | QScriptValue sant(true); | |
482 | { | |
483 | QScriptValue tmp = eng.toObject(sant); | |
484 | QVERIFY(tmp.isObject()); | |
485 | QVERIFY(!sant.isObject()); | |
486 | QVERIFY(!sant.engine()); | |
487 | QCOMPARE(tmp.toNumber(), sant.toNumber()); | |
488 | } | |
489 | ||
490 | QScriptValue number(123.0); | |
491 | { | |
492 | QScriptValue tmp = eng.toObject(number); | |
493 | QVERIFY(tmp.isObject()); | |
494 | QVERIFY(!number.isObject()); | |
495 | QVERIFY(!number.engine()); | |
496 | QCOMPARE(tmp.toNumber(), number.toNumber()); | |
497 | } | |
498 | ||
499 | QScriptValue str = QScriptValue(&eng, QString("ciao")); | |
500 | { | |
501 | QScriptValue tmp = eng.toObject(str); | |
502 | QVERIFY(tmp.isObject()); | |
503 | QVERIFY(!str.isObject()); | |
504 | QCOMPARE(tmp.toString(), str.toString()); | |
505 | } | |
506 | ||
507 | QScriptValue object = eng.evaluate("new Object"); | |
508 | { | |
509 | QScriptValue tmp = eng.toObject(object); | |
510 | QVERIFY(tmp.isObject()); | |
511 | QVERIFY(object.isObject()); | |
512 | QVERIFY(tmp.strictlyEquals(object)); | |
513 | } | |
514 | } | |
515 | ||
516 | void tst_QScriptEngine::toObjectTwoEngines() | |
517 | { | |
518 | QScriptEngine engine1; | |
519 | QScriptEngine engine2; | |
520 | ||
521 | { | |
522 | QScriptValue null = engine1.nullValue(); | |
523 | QTest::ignoreMessage(QtWarningMsg, "QScriptEngine::toObject: cannot convert value created in a different engine"); | |
524 | QVERIFY(!engine2.toObject(null).isValid()); | |
525 | QVERIFY(null.isValid()); | |
526 | QTest::ignoreMessage(QtWarningMsg, "QScriptEngine::toObject: cannot convert value created in a different engine"); | |
527 | QVERIFY(engine2.toObject(null).engine() != &engine2); | |
528 | } | |
529 | { | |
530 | QScriptValue undefined = engine1.undefinedValue(); | |
531 | QTest::ignoreMessage(QtWarningMsg, "QScriptEngine::toObject: cannot convert value created in a different engine"); | |
532 | QVERIFY(!engine2.toObject(undefined).isValid()); | |
533 | QVERIFY(undefined.isValid()); | |
534 | QTest::ignoreMessage(QtWarningMsg, "QScriptEngine::toObject: cannot convert value created in a different engine"); | |
535 | QVERIFY(engine2.toObject(undefined).engine() != &engine2); | |
536 | } | |
537 | { | |
538 | QScriptValue value = engine1.evaluate("1"); | |
539 | QTest::ignoreMessage(QtWarningMsg, "QScriptEngine::toObject: cannot convert value created in a different engine"); | |
540 | QVERIFY(engine2.toObject(value).engine() != &engine2); | |
541 | QVERIFY(!value.isObject()); | |
542 | } | |
543 | { | |
544 | QScriptValue string = engine1.evaluate("'Qt'"); | |
545 | QTest::ignoreMessage(QtWarningMsg, "QScriptEngine::toObject: cannot convert value created in a different engine"); | |
546 | QVERIFY(engine2.toObject(string).engine() != &engine2); | |
547 | QVERIFY(!string.isObject()); | |
548 | } | |
549 | { | |
550 | QScriptValue object = engine1.evaluate("new Object"); | |
551 | QTest::ignoreMessage(QtWarningMsg, "QScriptEngine::toObject: cannot convert value created in a different engine"); | |
552 | QVERIFY(engine2.toObject(object).engine() != &engine2); | |
553 | QVERIFY(object.isObject()); | |
554 | } | |
555 | } | |
556 | ||
557 | void tst_QScriptEngine::newArray() | |
558 | { | |
559 | QScriptEngine eng; | |
560 | QScriptValue array = eng.newArray(); | |
561 | QCOMPARE(array.isValid(), true); | |
562 | QCOMPARE(array.isArray(), true); | |
563 | QCOMPARE(array.isObject(), true); | |
564 | QVERIFY(!array.isFunction()); | |
565 | // QCOMPARE(array.scriptClass(), (QScriptClass*)0); | |
566 | ||
567 | // Prototype should be Array.prototype. | |
568 | QCOMPARE(array.prototype().isValid(), true); | |
569 | QCOMPARE(array.prototype().isArray(), true); | |
570 | QCOMPARE(array.prototype().strictlyEquals(eng.evaluate("Array.prototype")), true); | |
571 | ||
572 | QScriptValue arrayWithSize = eng.newArray(42); | |
573 | QCOMPARE(arrayWithSize.isValid(), true); | |
574 | QCOMPARE(arrayWithSize.isArray(), true); | |
575 | QCOMPARE(arrayWithSize.isObject(), true); | |
576 | QCOMPARE(arrayWithSize.property("length").toInt32(), 42); | |
577 | ||
578 | // task 218092 | |
579 | { | |
580 | QScriptValue ret = eng.evaluate("[].splice(0, 0, 'a')"); | |
581 | QVERIFY(ret.isArray()); | |
582 | QCOMPARE(ret.property("length").toInt32(), 0); | |
583 | } | |
584 | { | |
585 | QScriptValue ret = eng.evaluate("['a'].splice(0, 1, 'b')"); | |
586 | QVERIFY(ret.isArray()); | |
587 | QCOMPARE(ret.property("length").toInt32(), 1); | |
588 | } | |
589 | { | |
590 | QScriptValue ret = eng.evaluate("['a', 'b'].splice(0, 1, 'c')"); | |
591 | QVERIFY(ret.isArray()); | |
592 | QCOMPARE(ret.property("length").toInt32(), 1); | |
593 | } | |
594 | { | |
595 | QScriptValue ret = eng.evaluate("['a', 'b', 'c'].splice(0, 2, 'd')"); | |
596 | QVERIFY(ret.isArray()); | |
597 | QCOMPARE(ret.property("length").toInt32(), 2); | |
598 | } | |
599 | { | |
600 | QScriptValue ret = eng.evaluate("['a', 'b', 'c'].splice(1, 2, 'd', 'e', 'f')"); | |
601 | QVERIFY(ret.isArray()); | |
602 | QCOMPARE(ret.property("length").toInt32(), 2); | |
603 | } | |
604 | } | |
605 | ||
606 | void tst_QScriptEngine::uncaughtException() | |
607 | { | |
608 | QScriptEngine eng; | |
609 | QScriptValue fun = eng.evaluate("(function foo () { return null; });"); | |
610 | QVERIFY(!eng.uncaughtException().isValid()); | |
611 | QVERIFY(fun.isFunction()); | |
612 | QScriptValue throwFun = eng.evaluate("( function() { throw new Error('Pong'); });"); | |
613 | QVERIFY(throwFun.isFunction()); | |
614 | { | |
615 | eng.evaluate("a = 10"); | |
616 | QVERIFY(!eng.hasUncaughtException()); | |
617 | QVERIFY(!eng.uncaughtException().isValid()); | |
618 | } | |
619 | { | |
620 | eng.evaluate("1 = 2"); | |
621 | QVERIFY(eng.hasUncaughtException()); | |
622 | eng.clearExceptions(); | |
623 | QVERIFY(!eng.hasUncaughtException()); | |
624 | } | |
625 | { | |
626 | // Check if the call or toString functions can remove the last exception. | |
627 | QVERIFY(throwFun.call().isError()); | |
628 | QVERIFY(eng.hasUncaughtException()); | |
629 | QScriptValue exception = eng.uncaughtException(); | |
630 | fun.call(); | |
631 | exception.toString(); | |
632 | QVERIFY(eng.hasUncaughtException()); | |
633 | QVERIFY(eng.uncaughtException().strictlyEquals(exception)); | |
634 | } | |
635 | eng.clearExceptions(); | |
636 | { | |
637 | // Check if in the call function a new exception can override an existing one. | |
638 | throwFun.call(); | |
639 | QVERIFY(eng.hasUncaughtException()); | |
640 | QScriptValue exception = eng.uncaughtException(); | |
641 | throwFun.call(); | |
642 | QVERIFY(eng.hasUncaughtException()); | |
643 | QVERIFY(!exception.strictlyEquals(eng.uncaughtException())); | |
644 | } | |
645 | { | |
646 | eng.evaluate("throwFun = (function foo () { throw new Error('bla') });"); | |
647 | eng.evaluate("1;\nthrowFun();"); | |
648 | QVERIFY(eng.hasUncaughtException()); | |
649 | QCOMPARE(eng.uncaughtExceptionLineNumber(), 1); | |
650 | eng.clearExceptions(); | |
651 | QVERIFY(!eng.hasUncaughtException()); | |
652 | } | |
653 | for (int x = 1; x < 4; ++x) { | |
654 | QScriptValue ret = eng.evaluate("a = 10;\nb = 20;\n0 = 0;\n", | |
655 | QString::fromLatin1("FooScript") + QString::number(x), | |
656 | /* lineNumber */ x); | |
657 | QVERIFY(eng.hasUncaughtException()); | |
658 | QCOMPARE(eng.uncaughtExceptionLineNumber(), x + 2); | |
659 | QVERIFY(eng.uncaughtException().strictlyEquals(ret)); | |
660 | QVERIFY(eng.hasUncaughtException()); | |
661 | QVERIFY(eng.uncaughtException().strictlyEquals(ret)); | |
662 | QString backtrace = QString::fromLatin1("<anonymous>()@FooScript") + QString::number(x) + ":" + QString::number(x + 2); | |
663 | QCOMPARE(eng.uncaughtExceptionBacktrace().join(""), backtrace); | |
664 | QVERIFY(fun.call().isNull()); | |
665 | QVERIFY(eng.hasUncaughtException()); | |
666 | QCOMPARE(eng.uncaughtExceptionLineNumber(), x + 2); | |
667 | QVERIFY(eng.uncaughtException().strictlyEquals(ret)); | |
668 | eng.clearExceptions(); | |
669 | QVERIFY(!eng.hasUncaughtException()); | |
670 | QCOMPARE(eng.uncaughtExceptionLineNumber(), -1); | |
671 | QVERIFY(!eng.uncaughtException().isValid()); | |
672 | eng.evaluate("2 = 3"); | |
673 | QVERIFY(eng.hasUncaughtException()); | |
674 | QScriptValue ret2 = throwFun.call(); | |
675 | QVERIFY(ret2.isError()); | |
676 | QVERIFY(eng.hasUncaughtException()); | |
677 | QVERIFY(eng.uncaughtException().strictlyEquals(ret2)); | |
678 | QCOMPARE(eng.uncaughtExceptionLineNumber(), 1); | |
679 | eng.clearExceptions(); | |
680 | QVERIFY(!eng.hasUncaughtException()); | |
681 | eng.evaluate("1 + 2"); | |
682 | QVERIFY(!eng.hasUncaughtException()); | |
683 | } | |
684 | } | |
685 | ||
686 | void tst_QScriptEngine::newDate() | |
687 | { | |
688 | QScriptEngine eng; | |
689 | { | |
690 | QScriptValue date = eng.newDate(0); | |
691 | QCOMPARE(date.isValid(), true); | |
692 | QCOMPARE(date.isDate(), true); | |
693 | QCOMPARE(date.isObject(), true); | |
694 | QVERIFY(!date.isFunction()); | |
695 | // prototype should be Date.prototype | |
696 | QCOMPARE(date.prototype().isValid(), true); | |
697 | QCOMPARE(date.prototype().isDate(), true); | |
698 | QCOMPARE(date.prototype().strictlyEquals(eng.evaluate("Date.prototype")), true); | |
699 | } | |
700 | { | |
701 | QDateTime dt = QDateTime(QDate(1, 2, 3), QTime(4, 5, 6, 7), Qt::LocalTime); | |
702 | QScriptValue date = eng.newDate(dt); | |
703 | QCOMPARE(date.isValid(), true); | |
704 | QCOMPARE(date.isDate(), true); | |
705 | QCOMPARE(date.isObject(), true); | |
706 | // prototype should be Date.prototype | |
707 | QCOMPARE(date.prototype().isValid(), true); | |
708 | QCOMPARE(date.prototype().isDate(), true); | |
709 | QCOMPARE(date.prototype().strictlyEquals(eng.evaluate("Date.prototype")), true); | |
710 | ||
711 | QCOMPARE(date.toDateTime(), dt); | |
712 | } | |
713 | { | |
714 | QDateTime dt = QDateTime(QDate(1, 2, 3), QTime(4, 5, 6, 7), Qt::UTC); | |
715 | QScriptValue date = eng.newDate(dt); | |
716 | // toDateTime() result should be in local time | |
717 | QCOMPARE(date.toDateTime(), dt.toLocalTime()); | |
718 | } | |
719 | // Date.parse() should return NaN when it fails | |
720 | { | |
721 | QScriptValue ret = eng.evaluate("Date.parse()"); | |
722 | QVERIFY(ret.isNumber()); | |
723 | QVERIFY(qIsNaN(ret.toNumber())); | |
724 | } | |
725 | // Date.parse() should be able to parse the output of Date().toString() | |
726 | { | |
727 | QScriptValue ret = eng.evaluate("var x = new Date(); var s = x.toString(); s == new Date(Date.parse(s)).toString()"); | |
728 | QVERIFY(ret.isBoolean()); | |
729 | QCOMPARE(ret.toBoolean(), true); | |
730 | } | |
731 | } | |
4e4e5a6f | 732 | |
f9bf01c6 A |
733 | QTEST_MAIN(tst_QScriptEngine) |
734 | #include "tst_qscriptengine.moc" |