1// Copyright (C) 2016 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
4#include "qv4runtime_p.h"
5
6#include <private/qqmlengine_p.h>
7#include <private/qqmljavascriptexpression_p.h>
8#include <private/qqmljsast_p.h>
9#include <private/qqmltypewrapper_p.h>
10#include <private/qqmlvaluetypewrapper_p.h>
11#include <private/qv4argumentsobject_p.h>
12#include <private/qv4engine_p.h>
13#include <private/qv4function_p.h>
14#include <private/qv4generatorobject_p.h>
15#include <private/qv4global_p.h>
16#include <private/qv4globalobject_p.h>
17#include <private/qv4jscall_p.h>
18#include <private/qv4lookup_p.h>
19#include <private/qv4math_p.h>
20#include <private/qv4object_p.h>
21#include <private/qv4qmlcontext_p.h>
22#include <private/qv4qobjectwrapper_p.h>
23#include <private/qv4regexp_p.h>
24#include <private/qv4regexpobject_p.h>
25#include <private/qv4scopedvalue_p.h>
26#include <private/qv4stackframe_p.h>
27#include <private/qv4symbol_p.h>
28
29#include <wtf/MathExtras.h>
30
31#include <QtCore/private/qlocale_tools_p.h>
32#include <QtCore/qdebug.h>
33
34#ifdef QV4_COUNT_RUNTIME_FUNCTIONS
35# include <QtCore/qbuffer.h>
36#endif // QV4_COUNT_RUNTIME_FUNCTIONS
37
38#include <cassert>
39#include <cstdio>
40#include <stdlib.h>
41
42QT_BEGIN_NAMESPACE
43
44Q_LOGGING_CATEGORY(lcCoercingTypeAssertion, "qt.qml.coercingTypeAssertion");
45
46namespace QV4 {
47
48#ifdef QV4_COUNT_RUNTIME_FUNCTIONS
49struct RuntimeCounters::Data {
50 enum Type {
51 None = 0,
52 Undefined = 1,
53 Null = 2,
54 Boolean = 3,
55 Integer = 4,
56 Managed = 5,
57 Double = 7
58 };
59
60 static const char *pretty(Type t) {
61 switch (t) {
62 case None: return "";
63 case Undefined: return "Undefined";
64 case Null: return "Null";
65 case Boolean: return "Boolean";
66 case Integer: return "Integer";
67 case Managed: return "Managed";
68 case Double: return "Double";
69 default: return "Unknown";
70 }
71 }
72
73 static unsigned mangle(unsigned tag) {
74 switch (tag) {
75 case Value::Undefined_Type: return Undefined;
76 case Value::Null_Type: return Null;
77 case Value::Boolean_Type: return Boolean;
78 case Value::Integer_Type: return Integer;
79 case Value::Managed_Type: return Managed;
80 default: return Double;
81 }
82 }
83
84 static unsigned mangle(unsigned tag1, unsigned tag2) {
85 return (mangle(tag1) << 3) | mangle(tag2);
86 }
87
88 static void unmangle(unsigned signature, Type &tag1, Type &tag2) {
89 tag1 = Type((signature >> 3) & 7);
90 tag2 = Type(signature & 7);
91 }
92
93 typedef QVector<quint64> Counters;
94 QHash<const char *, Counters> counters;
95
96 inline void count(const char *func) {
97 QVector<quint64> &cnt = counters[func];
98 if (cnt.isEmpty())
99 cnt.resize(64);
100 cnt[0] += 1;
101 }
102
103 inline void count(const char *func, unsigned tag) {
104 QVector<quint64> &cnt = counters[func];
105 if (cnt.isEmpty())
106 cnt.resize(64);
107 cnt[mangle(tag)] += 1;
108 }
109
110 inline void count(const char *func, unsigned tag1, unsigned tag2) {
111 QVector<quint64> &cnt = counters[func];
112 if (cnt.isEmpty())
113 cnt.resize(64);
114 cnt[mangle(tag1, tag2)] += 1;
115 }
116
117 struct Line {
118 const char *func;
119 Type tag1, tag2;
120 quint64 count;
121
122 static bool less(const Line &line1, const Line &line2) {
123 return line1.count > line2.count;
124 }
125 };
126
127 void dump() const {
128 QBuffer buf;
129 buf.open(QIODevice::WriteOnly);
130 QTextStream outs(&buf);
131 QList<Line> lines;
132 for (auto it = counters.cbegin(), end = counters.cend(); it != end; ++it) {
133 const Counters &fCount = it.value();
134 for (int i = 0, ei = fCount.size(); i != ei; ++i) {
135 quint64 count = fCount[i];
136 if (!count)
137 continue;
138 Line line;
139 line.func = it.key();
140 unmangle(i, line.tag1, line.tag2);
141 line.count = count;
142 lines.append(line);
143 }
144 }
145 std::sort(lines.begin(), lines.end(), Line::less);
146 outs << lines.size() << " counters:" << endl;
147 for (const Line &line : std::as_const(lines))
148 outs << qSetFieldWidth(10) << line.count << qSetFieldWidth(0)
149 << " | " << line.func
150 << " | " << pretty(line.tag1)
151 << " | " << pretty(line.tag2)
152 << endl;
153 qDebug("%s", buf.data().constData());
154 }
155};
156
157RuntimeCounters *RuntimeCounters::instance = 0;
158static RuntimeCounters runtimeCountersInstance;
159RuntimeCounters::RuntimeCounters()
160 : d(new Data)
161{
162 if (!instance)
163 instance = this;
164}
165
166RuntimeCounters::~RuntimeCounters()
167{
168 d->dump();
169 delete d;
170}
171
172void RuntimeCounters::count(const char *func)
173{
174 d->count(func);
175}
176
177void RuntimeCounters::count(const char *func, uint tag)
178{
179 d->count(func, tag);
180}
181
182void RuntimeCounters::count(const char *func, uint tag1, uint tag2)
183{
184 d->count(func, tag1, tag2);
185}
186
187#endif // QV4_COUNT_RUNTIME_FUNCTIONS
188
189static QV4::Lookup *runtimeLookup(Function *f, uint i)
190{
191 return f->executableCompilationUnit()->runtimeLookups + i;
192}
193
194void RuntimeHelpers::numberToString(QString *result, double num, int radix)
195{
196 Q_ASSERT(result);
197
198 if (std::isnan(x: num)) {
199 *result = QStringLiteral("NaN");
200 return;
201 } else if (qt_is_inf(d: num)) {
202 *result = num < 0 ? QStringLiteral("-Infinity") : QStringLiteral("Infinity");
203 return;
204 }
205
206 if (radix == 10) {
207 // We cannot use our usual locale->toString(...) here, because EcmaScript has special rules
208 // about the longest permissible number, depending on if it's <0 or >0.
209 const int ecma_shortest_low = -6;
210 const int ecma_shortest_high = 21;
211
212 const QLatin1Char zero('0');
213 const QLatin1Char dot('.');
214
215 int decpt = 0;
216 int sign = 0;
217 *result = qdtoa(d: num, decpt: &decpt, sign: &sign);
218
219 if (decpt <= ecma_shortest_low || decpt > ecma_shortest_high) {
220 if (result->size() > 1)
221 result->insert(i: 1, c: dot);
222 result->append(c: QLatin1Char('e'));
223 if (decpt > 0)
224 result->append(c: QLatin1Char('+'));
225 result->append(s: QString::number(decpt - 1));
226 } else if (decpt <= 0) {
227 result->prepend(s: QLatin1String("0.") + QString(-decpt, zero));
228 } else if (decpt < result->size()) {
229 result->insert(i: decpt, c: dot);
230 } else {
231 result->append(s: QString(decpt - result->size(), zero));
232 }
233
234 if (sign && num)
235 result->prepend(c: QLatin1Char('-'));
236
237 return;
238 }
239
240 result->clear();
241 bool negative = false;
242
243 if (num < 0) {
244 negative = true;
245 num = -num;
246 }
247
248 double frac = num - ::floor(x: num);
249 num = Value::toInteger(d: num);
250
251 do {
252 char c = (char)::fmod(x: num, y: radix);
253 c = (c < 10) ? (c + '0') : (c - 10 + 'a');
254 result->prepend(c: QLatin1Char(c));
255 num = ::floor(x: num / radix);
256 } while (num != 0);
257
258 if (frac != 0) {
259 result->append(c: QLatin1Char('.'));
260 double magnitude = 1;
261 double next = frac;
262 do {
263 next *= radix;
264 const int floored = ::floor(x: next);
265 char c = char(floored);
266 c = (c < 10) ? (c + '0') : (c - 10 + 'a');
267 result->append(c: QLatin1Char(c));
268 magnitude /= radix;
269 frac -= double(floored) * magnitude;
270 next -= double(floored);
271
272 // The next digit still makes a difference
273 // if a value of "radix" for it would change frac.
274 // Otherwise we've reached the limit of numerical precision.
275 } while (frac > 0 && frac - magnitude != frac);
276 }
277
278 if (negative)
279 result->prepend(c: QLatin1Char('-'));
280}
281
282ReturnedValue Runtime::Closure::call(ExecutionEngine *engine, int functionId)
283{
284 QV4::Function *clos = engine->currentStackFrame->v4Function->executableCompilationUnit()
285 ->runtimeFunctions[functionId];
286 Q_ASSERT(clos);
287 ExecutionContext *current = engine->currentContext();
288 Scope s(engine);
289 QV4::ScopedObject closure(s);
290
291 if (clos->isGenerator())
292 closure = GeneratorFunction::create(scope: current, function: clos)->asReturnedValue();
293 else
294 closure = FunctionObject::createScriptFunction(scope: current, function: clos)->asReturnedValue();
295 // ### TODO: only keep reference to scripts if actually needed; see QTBUG-130795
296 Scoped<QV4::QmlContext> callingQmlContext(s, s.engine->qmlContext());
297 if (callingQmlContext) {
298 // ### TODO: It would be more efficient to use custom Function prototypes instead of setting a property
299 // ==> QTBUG-130798
300 Scoped<QV4::QQmlContextWrapper> qmlContextWrapper(s, callingQmlContext->d()->qml());
301 const QV4::QQmlContextWrapper *resource = qmlContextWrapper;
302 QQmlRefPointer<QQmlContextData> context = resource->getContext();
303 /* we can use fromReturnedValue here, as isNullOrUndefined doesn't allocate.
304 fetching importedScripts twvice is less overhead than unconditionally creating a scope,
305 as script imports are rather rare*/
306 if (!QV4::Value::fromReturnedValue(val: context->importedScripts()).isNullOrUndefined()) {
307 QV4::ScopedString name(s, engine->newString(s: QLatin1StringView("$importedScripts")));
308 QV4::ScopedObject scripts(s, context->importedScripts());
309 closure->insertMember(s: name, v: scripts, attributes: Attr_Invalid);
310 }
311 }
312 return closure.asReturnedValue();
313}
314
315Bool Runtime::DeleteProperty_NoThrow::call(ExecutionEngine *engine, const Value &base, const Value &index)
316{
317 Scope scope(engine);
318 ScopedObject o(scope, base.toObject(e: engine));
319 if (scope.hasException())
320 return Encode::undefined();
321 Q_ASSERT(o);
322
323 ScopedPropertyKey key(scope, index.toPropertyKey(e: engine));
324 if (engine->hasException)
325 return false;
326 return o->deleteProperty(id: key);
327}
328
329ReturnedValue Runtime::DeleteProperty::call(ExecutionEngine *engine, QV4::Function *function, const QV4::Value &base, const QV4::Value &index)
330{
331 if (!Runtime::DeleteProperty_NoThrow::call(engine, base, index)) {
332 if (function->isStrict())
333 engine->throwTypeError();
334 return Encode(false);
335 } else {
336 return Encode(true);
337 }
338}
339
340Bool Runtime::DeleteName_NoThrow::call(ExecutionEngine *engine, int nameIndex)
341{
342 Scope scope(engine);
343 ScopedString name(scope, engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[nameIndex]);
344 return engine->currentContext()->deleteProperty(name);
345}
346
347ReturnedValue Runtime::DeleteName::call(ExecutionEngine *engine, Function *function, int name)
348{
349 if (!Runtime::DeleteName_NoThrow::call(engine, nameIndex: name)) {
350 if (function->isStrict())
351 engine->throwTypeError();
352 return Encode(false);
353 } else {
354 return Encode(true);
355 }
356}
357
358static QV4::ReturnedValue doInstanceof(ExecutionEngine *engine, const Value &lval, const Value &rval)
359{
360 // 11.8.6, 5: rval must be an Object
361 const Object *rhs = rval.as<Object>();
362 if (!rhs)
363 return engine->throwTypeError();
364
365 const FunctionObject *f = rhs->as<FunctionObject>();
366 // shortcut hasInstance evaluation. In this case we know that we are calling the regular hasInstance()
367 // method of the FunctionPrototype
368 if (f && f->d()->prototype() == engine->functionPrototype()->d() && !f->hasHasInstanceProperty())
369 return Object::checkedInstanceOf(engine, typeObject: f, var: lval);
370
371 Scope scope(engine);
372 ScopedValue hasInstance(scope, rhs->get(name: engine->symbol_hasInstance()));
373 if (hasInstance->isUndefined())
374 return Encode(rhs->instanceOf(var: lval));
375
376 FunctionObject *fHasInstance = hasInstance->as<FunctionObject>();
377 if (!fHasInstance)
378 return engine->throwTypeError();
379
380 return Encode(fHasInstance->call(thisObject: &rval, argv: &lval, argc: 1));
381}
382
383QV4::ReturnedValue Runtime::Instanceof::call(ExecutionEngine *engine, const Value &lval, const Value &rval)
384{
385 Scope scope(engine);
386 ScopedValue result(scope, doInstanceof(engine, lval, rval));
387 return scope.hasException() ? Encode::undefined() : Encode(result->toBoolean());
388}
389
390QV4::ReturnedValue Runtime::As::call(ExecutionEngine *engine, const Value &lval, const Value &rval)
391{
392 Scope scope(engine);
393 ScopedValue result(scope, doInstanceof(engine, lval, rval));
394
395 if (scope.hasException()) {
396 // "foo instanceof valueType" must not throw an exception.
397 // So this can only be an object type.
398 engine->catchException();
399 return Encode::null();
400 }
401
402 if (result->toBoolean())
403 return lval.asReturnedValue();
404 else if (result->isBoolean())
405 return Encode::null();
406
407 if (engine->callingQmlContext()->valueTypesAreAssertable())
408 return Encode::undefined();
409
410 // Try to convert the value type
411 Scoped<QQmlTypeWrapper> typeWrapper(scope, rval);
412 if (!typeWrapper)
413 return Encode::undefined();
414
415 const auto *stackFrame = engine->currentStackFrame;
416 if (lval.as<QQmlValueTypeWrapper>()) {
417 qCWarning(lcCoercingTypeAssertion).nospace().noquote()
418 << stackFrame->source() << ':' << stackFrame->lineNumber() << ':'
419 << " Coercing between incompatible value types mistakenly yields null rather than"
420 << " undefined. Add 'pragma ValueTypeBehavior: Assertable' to prevent this.";
421 return Encode::null();
422 }
423
424 if (lval.as<QV4::QObjectWrapper>()) {
425 qCWarning(lcCoercingTypeAssertion).nospace().noquote()
426 << stackFrame->source() << ':' << stackFrame->lineNumber() << ':'
427 << " Coercing from instances of object types to value types mistakenly yields null"
428 << " rather than undefined. Add 'pragma ValueTypeBehavior: Assertable' to prevent"
429 << " this.";
430 return Encode::null();
431 }
432
433 result = coerce(engine, value: lval, qmlType: typeWrapper->d()->type(), isList: false);
434 if (result->isUndefined())
435 return Encode::undefined();
436
437 qCWarning(lcCoercingTypeAssertion).nospace().noquote()
438 << stackFrame->source() << ':' << stackFrame->lineNumber() << ':'
439 << " Coercing a value to " << typeWrapper->toQStringNoThrow()
440 << " using a type assertion. This behavior is deprecated."
441 << " Add 'pragma ValueTypeBehavior: Assertable' to prevent it.";
442 return result->asReturnedValue();
443}
444
445QV4::ReturnedValue Runtime::In::call(ExecutionEngine *engine, const Value &left, const Value &right)
446{
447 Object *ro = right.objectValue();
448 if (!ro)
449 return engine->throwTypeError();
450 Scope scope(engine);
451 ScopedPropertyKey s(scope, left.toPropertyKey(e: engine));
452 if (scope.hasException())
453 return Encode::undefined();
454 bool r = ro->hasProperty(id: s);
455 return Encode(r);
456}
457
458double RuntimeHelpers::stringToNumber(const QString &string)
459{
460 // The actual maximum valid length is certainly shorter, but due to the sheer number of
461 // different number formatting variants, we rather err on the side of caution here.
462 // For example, you can have up to 772 valid decimal digits left of the dot, as stated in the
463 // libdoubleconversion sources. The same maximum value would be represented by roughly 3.5 times
464 // as many binary digits.
465 const int excessiveLength = 16 * 1024;
466 if (string.size() > excessiveLength)
467 return qQNaN();
468
469 const QStringView s = QStringView(string).trimmed();
470 if (s.startsWith(c: QLatin1Char('0'))) {
471 int base = -1;
472 if (s.startsWith(s: QLatin1String("0x")) || s.startsWith(s: QLatin1String("0X")))
473 base = 16;
474 else if (s.startsWith(s: QLatin1String("0o")) || s.startsWith(s: QLatin1String("0O")))
475 base = 8;
476 else if (s.startsWith(s: QLatin1String("0b")) || s.startsWith(s: QLatin1String("0B")))
477 base = 2;
478 if (base > 0) {
479 bool ok = true;
480 qlonglong num;
481 num = s.mid(pos: 2).toLongLong(ok: &ok, base);
482 if (!ok)
483 return qQNaN();
484 return num;
485 }
486 }
487 bool ok = false;
488 QByteArray ba = s.toLatin1();
489 const char *begin = ba.constData();
490 const char *end = nullptr;
491 double d = qstrtod(s00: begin, se: &end, ok: &ok);
492 if (end - begin != ba.size()) {
493 if (ba == "Infinity" || ba == "+Infinity")
494 d = Q_INFINITY;
495 else if (ba == "-Infinity")
496 d = -Q_INFINITY;
497 else
498 d = std::numeric_limits<double>::quiet_NaN();
499 }
500 return d;
501}
502
503Heap::String *RuntimeHelpers::stringFromNumber(ExecutionEngine *engine, double number)
504{
505 QString qstr;
506 RuntimeHelpers::numberToString(result: &qstr, num: number, radix: 10);
507 return engine->newString(s: qstr);
508}
509
510ReturnedValue RuntimeHelpers::objectDefaultValue(const Object *object, int typeHint)
511{
512 ExecutionEngine *engine = object->internalClass()->engine;
513 if (engine->hasException)
514 return Encode::undefined();
515
516 String *hint;
517 switch (typeHint) {
518 case STRING_HINT:
519 hint = engine->id_string();
520 break;
521 case NUMBER_HINT:
522 hint = engine->id_number();
523 break;
524 default:
525 hint = engine->id_default();
526 break;
527 }
528
529 Scope scope(engine);
530 ScopedFunctionObject toPrimitive(scope, object->get(name: engine->symbol_toPrimitive()));
531 if (engine->hasException)
532 return Encode::undefined();
533 if (toPrimitive) {
534 ScopedValue result(scope, toPrimitive->call(thisObject: object, argv: hint, argc: 1));
535 if (engine->hasException)
536 return Encode::undefined();
537 if (!result->isPrimitive())
538 return engine->throwTypeError();
539 return result->asReturnedValue();
540 }
541
542 if (hint == engine->id_default())
543 hint = engine->id_number();
544 return ordinaryToPrimitive(engine, object, typeHint: hint);
545}
546
547
548ReturnedValue RuntimeHelpers::ordinaryToPrimitive(ExecutionEngine *engine, const Object *object, String *typeHint)
549{
550 Q_ASSERT(!engine->hasException);
551
552 String *meth1 = engine->id_toString();
553 String *meth2 = engine->id_valueOf();
554
555 if (typeHint->propertyKey() == engine->id_number()->propertyKey()) {
556 qSwap(value1&: meth1, value2&: meth2);
557 } else {
558 Q_ASSERT(typeHint->propertyKey() == engine->id_string()->propertyKey());
559 }
560
561 Scope scope(engine);
562 ScopedValue result(scope);
563
564 ScopedValue conv(scope, object->get(name: meth1));
565 if (FunctionObject *o = conv->as<FunctionObject>()) {
566 result = o->call(thisObject: object, argv: nullptr, argc: 0);
567 if (engine->hasException)
568 return Encode::undefined();
569 if (result->isPrimitive())
570 return result->asReturnedValue();
571 }
572
573 if (engine->hasException)
574 return Encode::undefined();
575
576 conv = object->get(name: meth2);
577 if (FunctionObject *o = conv->as<FunctionObject>()) {
578 result = o->call(thisObject: object, argv: nullptr, argc: 0);
579 if (engine->hasException)
580 return Encode::undefined();
581 if (result->isPrimitive())
582 return result->asReturnedValue();
583 }
584
585 return engine->throwTypeError();
586}
587
588
589Heap::Object *RuntimeHelpers::convertToObject(ExecutionEngine *engine, const Value &value)
590{
591 Q_ASSERT(!value.isObject());
592 switch (value.type()) {
593 case Value::Undefined_Type:
594 engine->throwTypeError(message: QLatin1String("Value is undefined and could not be converted to an object"));
595 return nullptr;
596 case Value::Null_Type:
597 engine->throwTypeError(message: QLatin1String("Value is null and could not be converted to an object"));
598 return nullptr;
599 case Value::Boolean_Type:
600 return engine->newBooleanObject(b: value.booleanValue());
601 case Value::Managed_Type:
602 Q_ASSERT(value.isStringOrSymbol());
603 if (!value.isString())
604 return engine->newSymbolObject(symbol: value.symbolValue());
605 return engine->newStringObject(string: value.stringValue());
606 case Value::Integer_Type:
607 default: // double
608 return engine->newNumberObject(value: value.asDouble());
609 }
610}
611
612Heap::String *RuntimeHelpers::convertToString(ExecutionEngine *engine, Value value, TypeHint hint)
613{
614 redo:
615 switch (value.type()) {
616 case Value::Empty_Type:
617 Q_ASSERT(!"empty Value encountered");
618 Q_UNREACHABLE();
619 case Value::Undefined_Type:
620 return engine->id_undefined()->d();
621 case Value::Null_Type:
622 return engine->id_null()->d();
623 case Value::Boolean_Type:
624 if (value.booleanValue())
625 return engine->id_true()->d();
626 else
627 return engine->id_false()->d();
628 case Value::Managed_Type: {
629 if (value.isString())
630 return static_cast<const String &>(value).d();
631 if (value.isSymbol()) {
632 engine->throwTypeError(message: QLatin1String("Cannot convert a symbol to a string."));
633 return nullptr;
634 }
635 value = Value::fromReturnedValue(val: RuntimeHelpers::toPrimitive(value, typeHint: hint));
636 Q_ASSERT(value.isPrimitive());
637 if (value.isString())
638 return static_cast<const String &>(value).d();
639 goto redo;
640 }
641 case Value::Integer_Type:
642 return engine->newString(s: QString::number(value.int_32()));
643 default: // double
644 return RuntimeHelpers::stringFromNumber(engine, number: value.doubleValue());
645 } // switch
646}
647
648// This is slightly different from the method above, as
649// the + operator requires a slightly different conversion
650static Heap::String *convert_to_string_add(ExecutionEngine *engine, Value value)
651{
652 return RuntimeHelpers::convertToString(engine, value, hint: PREFERREDTYPE_HINT);
653}
654
655QV4::ReturnedValue RuntimeHelpers::addHelper(ExecutionEngine *engine, const Value &left, const Value &right)
656{
657 Scope scope(engine);
658
659 ScopedValue pleft(scope, RuntimeHelpers::toPrimitive(value: left, typeHint: PREFERREDTYPE_HINT));
660 ScopedValue pright(scope, RuntimeHelpers::toPrimitive(value: right, typeHint: PREFERREDTYPE_HINT));
661 String *sleft = pleft->stringValue();
662 String *sright = pright->stringValue();
663 if (sleft || sright) {
664 if (!sleft) {
665 pleft = convert_to_string_add(engine, value: pleft);
666 sleft = static_cast<String *>(pleft.ptr);
667 }
668 if (!sright) {
669 pright = convert_to_string_add(engine, value: pright);
670 sright = static_cast<String *>(pright.ptr);
671 }
672 if (engine->hasException)
673 return Encode::undefined();
674 if (!sleft->d()->length())
675 return sright->asReturnedValue();
676 if (!sright->d()->length())
677 return sleft->asReturnedValue();
678 MemoryManager *mm = engine->memoryManager;
679 return (mm->alloc<ComplexString>(args: sleft->d(), args: sright->d()))->asReturnedValue();
680 }
681 double x = RuntimeHelpers::toNumber(value: pleft);
682 double y = RuntimeHelpers::toNumber(value: pright);
683 return Encode(x + y);
684}
685
686ReturnedValue Runtime::GetTemplateObject::call(Function *function, int index)
687{
688 return function->executableCompilationUnit()->templateObjectAt(index)->asReturnedValue();
689}
690
691void Runtime::StoreProperty::call(ExecutionEngine *engine, const Value &object, int nameIndex, const Value &value)
692{
693 Scope scope(engine);
694 QV4::Function *v4Function = engine->currentStackFrame->v4Function;
695 ScopedString name(scope, v4Function->compilationUnit->runtimeStrings[nameIndex]);
696 ScopedObject o(scope, object);
697 if (!o) {
698 if (v4Function->isStrict()) {
699 engine->throwTypeError();
700 return;
701 }
702 o = object.toObject(e: engine);
703 }
704 if ((!o || !o->put(name, v: value)) && v4Function->isStrict())
705 engine->throwTypeError();
706}
707
708static Q_NEVER_INLINE ReturnedValue getElementIntFallback(ExecutionEngine *engine, const Value &object, uint idx)
709{
710 Q_ASSERT(idx < UINT_MAX);
711 Scope scope(engine);
712
713 ScopedObject o(scope, object);
714 if (!o) {
715 if (const String *str = object.as<String>()) {
716 if (idx >= (uint)str->toQString().size()) {
717 return Encode::undefined();
718 }
719 const QString s = str->toQString().mid(position: idx, n: 1);
720 return scope.engine->newString(s)->asReturnedValue();
721 }
722
723 if (object.isNullOrUndefined()) {
724 QString message = QStringLiteral("Cannot read property '%1' of %2").arg(a: idx).arg(a: object.toQStringNoThrow());
725 return engine->throwTypeError(message);
726 }
727
728 o = RuntimeHelpers::convertToObject(engine: scope.engine, value: object);
729 Q_ASSERT(!!o); // can't fail as null/undefined is covered above
730 }
731
732 if (o->arrayData() && !o->arrayData()->attrs) {
733 ScopedValue v(scope, o->arrayData()->get(i: idx));
734 if (!v->isEmpty())
735 return v->asReturnedValue();
736 }
737
738 return o->get(idx);
739}
740
741static Q_NEVER_INLINE ReturnedValue getElementFallback(ExecutionEngine *engine, const Value &object, const Value &index)
742{
743 Q_ASSERT(!index.isPositiveInt());
744
745 Scope scope(engine);
746
747 ScopedObject o(scope, object);
748 if (!o) {
749 if (object.isNullOrUndefined()) {
750 QString message = QStringLiteral("Cannot read property '%1' of %2").arg(a: index.toQStringNoThrow()).arg(a: object.toQStringNoThrow());
751 return engine->throwTypeError(message);
752 }
753
754 o = RuntimeHelpers::convertToObject(engine: scope.engine, value: object);
755 Q_ASSERT(!!o); // can't fail as null/undefined is covered above
756 }
757
758 ScopedPropertyKey name(scope, index.toPropertyKey(e: engine));
759 if (scope.hasException())
760 return Encode::undefined();
761 return o->get(id: name);
762}
763
764ReturnedValue Runtime::LoadElement::call(ExecutionEngine *engine, const Value &object, const Value &index)
765{
766 if (index.isPositiveInt()) {
767 uint idx = static_cast<uint>(index.int_32());
768 if (Heap::Base *b = object.heapObject()) {
769 if (b->internalClass->vtable->isObject) {
770 Heap::Object *o = static_cast<Heap::Object *>(b);
771 if (o->arrayData && o->arrayData->type == Heap::ArrayData::Simple) {
772 Heap::SimpleArrayData *s = o->arrayData.cast<Heap::SimpleArrayData>();
773 if (idx < s->values.size)
774 if (!s->data(index: idx).isEmpty())
775 return s->data(index: idx).asReturnedValue();
776 }
777 }
778 }
779 return getElementIntFallback(engine, object, idx);
780 }
781
782 return getElementFallback(engine, object, index);
783}
784
785static Q_NEVER_INLINE bool setElementFallback(ExecutionEngine *engine, const Value &object, const Value &index, const Value &value)
786{
787 Scope scope(engine);
788 ScopedObject o(scope, object);
789 if (!o) {
790 if (engine->currentStackFrame->v4Function->isStrict()) {
791 engine->throwTypeError();
792 return false;
793 }
794
795 o = object.toObject(e: engine);
796 }
797 if (engine->hasException)
798 return false;
799
800 if (index.isPositiveInt()) {
801 uint idx = static_cast<uint>(index.int_32());
802 if (o->d()->arrayData && o->d()->arrayData->type == Heap::ArrayData::Simple) {
803 Heap::SimpleArrayData *s = o->d()->arrayData.cast<Heap::SimpleArrayData>();
804 if (idx < s->values.size) {
805 s->setData(e: engine, index: idx, newVal: value);
806 return true;
807 }
808 }
809 return o->put(idx, v: value);
810 }
811
812 ScopedPropertyKey name(scope, index.toPropertyKey(e: engine));
813 if (engine->hasException)
814 return false;
815 return o->put(id: name, v: value);
816}
817
818void Runtime::StoreElement::call(ExecutionEngine *engine, const Value &object, const Value &index, const Value &value)
819{
820 if (index.isPositiveInt()) {
821 uint idx = static_cast<uint>(index.int_32());
822 if (Heap::Base *b = object.heapObject()) {
823 if (b->internalClass->vtable->isObject) {
824 Heap::Object *o = static_cast<Heap::Object *>(b);
825 if (o->arrayData && o->arrayData->type == Heap::ArrayData::Simple) {
826 Heap::SimpleArrayData *s = o->arrayData.cast<Heap::SimpleArrayData>();
827 if (idx < s->values.size) {
828 s->setData(e: engine, index: idx, newVal: value);
829 return;
830 }
831 }
832 }
833 }
834 }
835
836 if (!setElementFallback(engine, object, index, value) && engine->currentStackFrame->v4Function->isStrict())
837 engine->throwTypeError();
838}
839
840ReturnedValue Runtime::GetIterator::call(ExecutionEngine *engine, const Value &in, int iterator)
841{
842 Scope scope(engine);
843 ScopedObject o(scope, (Object *)nullptr);
844 if (!in.isNullOrUndefined())
845 o = in.toObject(e: engine);
846 if (engine->hasException)
847 return Encode::undefined();
848 if (iterator == static_cast<int>(QQmlJS::AST::ForEachType::Of)) {
849 if (!o)
850 return engine->throwTypeError();
851 ScopedFunctionObject f(scope, o->get(name: engine->symbol_iterator()));
852 if (!f)
853 return engine->throwTypeError();
854 JSCallData cData(o, nullptr, 0);
855 ScopedObject it(scope, f->call(data: cData));
856 if (engine->hasException)
857 return Encode::undefined();
858 if (!it)
859 return engine->throwTypeError();
860 return it->asReturnedValue();
861 }
862 return engine->newForInIteratorObject(o)->asReturnedValue();
863}
864
865ReturnedValue Runtime::IteratorNext::call(ExecutionEngine *engine, const Value &iterator, Value *value)
866{
867 // if we throw an exception from here, return true, not undefined. This is to ensure iteratorDone is set to true
868 // and the stack unwinding won't close the iterator
869 Q_ASSERT(iterator.isObject());
870
871 Scope scope(engine);
872 ScopedFunctionObject f(scope, static_cast<const Object &>(iterator).get(name: engine->id_next()));
873 if (!f) {
874 engine->throwTypeError();
875 return Encode(true);
876 }
877 JSCallData cData(&iterator, nullptr, 0);
878 ScopedObject o(scope, f->call(data: cData));
879 if (scope.hasException())
880 return Encode(true);
881 if (!o) {
882 engine->throwTypeError();
883 return Encode(true);
884 }
885
886 ScopedValue d(scope, o->get(name: engine->id_done()));
887 if (scope.hasException())
888 return Encode(true);
889 bool done = d->toBoolean();
890 if (done) {
891 *value = Encode::undefined();
892 return Encode(true);
893 }
894
895 *value = o->get(name: engine->id_value());
896 if (scope.hasException())
897 return Encode(true);
898 return Encode(false);
899}
900
901ReturnedValue Runtime::IteratorNextForYieldStar::call(ExecutionEngine *engine, const Value &received, const Value &iterator, Value *object)
902{
903 // the return value encodes how to continue the yield* iteration.
904 // true implies iteration is done, false for iteration to continue
905 // a return value of undefines is a special marker, that the iterator has been invoked with return()
906
907 Scope scope(engine);
908 Q_ASSERT(iterator.isObject());
909
910 const Value *arg = &received;
911 bool returnCalled = false;
912 FunctionObject *f = nullptr;
913 if (engine->hasException) {
914 if (engine->exceptionValue->isEmpty()) {
915 // generator called with return()
916 *engine->exceptionValue = Encode::undefined();
917 engine->hasException = false;
918
919 ScopedValue ret(scope, static_cast<const Object &>(iterator).get(name: engine->id_return()));
920 if (engine->hasException)
921 return Encode(true);
922 if (ret->isUndefined()) {
923 // propagate return()
924 return Encode::undefined();
925 }
926 returnCalled = true;
927 f = ret->as<FunctionObject>();
928 } else {
929 // generator called with throw
930 ScopedValue exceptionValue(scope, *engine->exceptionValue);
931 *engine->exceptionValue = Encode::undefined();
932 engine->hasException = false;
933
934 ScopedValue t(scope, static_cast<const Object &>(iterator).get(name: engine->id_throw()));
935 if (engine->hasException)
936 return Encode(true);
937 if (t->isUndefined()) {
938 // no throw method on the iterator
939 IteratorClose::call(engine, iterator);
940 if (!engine->hasException)
941 engine->throwTypeError();
942 return Encode(true);
943 }
944 f = t->as<FunctionObject>();
945 arg = exceptionValue;
946 }
947 } else {
948 // generator called with next()
949 ScopedFunctionObject next(scope, static_cast<const Object &>(iterator).get(name: engine->id_next()));
950 f = next->as<FunctionObject>();
951 }
952
953 if (!f) {
954 engine->throwTypeError();
955 return Encode(true);
956 }
957
958 ScopedObject o(scope, f->call(thisObject: &iterator, argv: arg, argc: 1));
959 if (scope.hasException())
960 return Encode(true);
961 if (!o) {
962 engine->throwTypeError();
963 return Encode(true);
964 }
965
966 ScopedValue d(scope, o->get(name: engine->id_done()));
967 if (scope.hasException())
968 return Encode(true);
969 bool done = d->toBoolean();
970 if (done) {
971 *object = o->get(name: engine->id_value());
972 return (returnCalled && !engine->hasException) ? Encode::undefined() : Encode(true);
973 }
974 *object = o;
975 return Encode(false);
976}
977
978ReturnedValue Runtime::IteratorClose::call(ExecutionEngine *engine, const Value &iterator)
979{
980 Q_ASSERT(iterator.isObject());
981
982 Scope scope(engine);
983 ScopedValue e(scope);
984 bool hadException = engine->hasException;
985 if (hadException) {
986 e = *engine->exceptionValue;
987 engine->hasException = false;
988 }
989
990 auto originalCompletion = [=]() {
991 if (hadException) {
992 *engine->exceptionValue = e;
993 engine->hasException = hadException;
994 }
995 return Encode::undefined();
996 };
997
998 ScopedValue ret(scope, static_cast<const Object &>(iterator).get(name: engine->id_return()));
999 ScopedObject o(scope);
1000 if (!ret->isUndefined()) {
1001 FunctionObject *f = ret->as<FunctionObject>();
1002 o = f->call(thisObject: &iterator, argv: nullptr, argc: 0);
1003 if (engine->hasException && !hadException)
1004 return Encode::undefined();
1005 }
1006 if (hadException || ret->isUndefined())
1007 return originalCompletion();
1008
1009 if (!o)
1010 return engine->throwTypeError();
1011
1012 return originalCompletion();
1013}
1014
1015ReturnedValue Runtime::DestructureRestElement::call(ExecutionEngine *engine, const Value &iterator)
1016{
1017 Q_ASSERT(iterator.isObject());
1018
1019 Scope scope(engine);
1020 ScopedArrayObject array(scope, engine->newArrayObject());
1021 array->arrayCreate();
1022 uint index = 0;
1023 while (1) {
1024 ScopedValue n(scope);
1025 ScopedValue done(scope, IteratorNext::call(engine, iterator, value: n));
1026 if (engine->hasException)
1027 return Encode::undefined();
1028 Q_ASSERT(done->isBoolean());
1029 if (done->booleanValue())
1030 break;
1031 array->arraySet(index, value: n);
1032 ++index;
1033 }
1034 return array->asReturnedValue();
1035}
1036
1037void Runtime::StoreNameSloppy::call(ExecutionEngine *engine, int nameIndex, const Value &value)
1038{
1039 Scope scope(engine);
1040 ScopedString name(scope, engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[nameIndex]);
1041 ExecutionContext::Error e = engine->currentContext()->setProperty(name, value);
1042
1043 if (e == ExecutionContext::RangeError)
1044 engine->globalObject->put(name, v: value);
1045}
1046
1047void Runtime::StoreNameStrict::call(ExecutionEngine *engine, int nameIndex, const Value &value)
1048{
1049 Scope scope(engine);
1050 ScopedString name(scope, engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[nameIndex]);
1051 ExecutionContext::Error e = engine->currentContext()->setProperty(name, value);
1052 if (e == ExecutionContext::TypeError)
1053 engine->throwTypeError();
1054 else if (e == ExecutionContext::RangeError)
1055 engine->throwReferenceError(value: name);
1056}
1057
1058ReturnedValue Runtime::LoadProperty::call(ExecutionEngine *engine, const Value &object, int nameIndex)
1059{
1060 Scope scope(engine);
1061 ScopedString name(scope, engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[nameIndex]);
1062
1063 ScopedObject o(scope, object);
1064 if (o)
1065 return o->get(name);
1066
1067 if (object.isNullOrUndefined()) {
1068 QString message = QStringLiteral("Cannot read property '%1' of %2").arg(a: name->toQString()).arg(a: object.toQStringNoThrow());
1069 return engine->throwTypeError(message);
1070 }
1071
1072 o = RuntimeHelpers::convertToObject(engine: scope.engine, value: object);
1073 if (!o) // type error
1074 return Encode::undefined();
1075 return o->get(name);
1076}
1077
1078ReturnedValue Runtime::LoadName::call(ExecutionEngine *engine, int nameIndex)
1079{
1080 Scope scope(engine);
1081 ScopedString name(scope, engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[nameIndex]);
1082 return engine->currentContext()->getProperty(name);
1083}
1084
1085static Object *getSuperBase(Scope &scope)
1086{
1087 Scoped<JavaScriptFunctionObject> f(scope);
1088 ScopedObject homeObject(scope);
1089 if (scope.engine->currentStackFrame->isJSTypesFrame()) {
1090 JSTypesStackFrame *frame = static_cast<JSTypesStackFrame *>(
1091 scope.engine->currentStackFrame);
1092
1093 if (frame->jsFrame->thisObject.isEmpty()) {
1094 scope.engine->throwReferenceError(
1095 QStringLiteral("Missing call to super()."), fileName: QString(), lineNumber: 0, column: 0);
1096 return nullptr;
1097 }
1098
1099 f = Value::fromStaticValue(staticValue: frame->jsFrame->function);
1100 homeObject = f->getHomeObject();
1101 } else {
1102 Q_ASSERT(scope.engine->currentStackFrame->isMetaTypesFrame());
1103 MetaTypesStackFrame *frame = static_cast<MetaTypesStackFrame *>(
1104 scope.engine->currentStackFrame);
1105 if (frame->thisObject() == nullptr) {
1106 scope.engine->throwReferenceError(
1107 QStringLiteral("Missing call to super()."), fileName: QString(), lineNumber: 0, column: 0);
1108 return nullptr;
1109 }
1110 }
1111
1112 if (!homeObject) {
1113 ScopedContext ctx(scope, scope.engine->currentContext());
1114 Q_ASSERT(ctx);
1115 while (ctx) {
1116 if (CallContext *c = ctx->asCallContext()) {
1117 f = c->d()->function;
1118 QV4::Function *fn = f->function();
1119 if (fn && !fn->isArrowFunction() && fn->kind != Function::Eval)
1120 break;
1121 }
1122 ctx = ctx->d()->outer;
1123 }
1124 if (f)
1125 homeObject = f->getHomeObject();
1126 }
1127 if (!homeObject) {
1128 scope.engine->throwTypeError();
1129 return nullptr;
1130 }
1131 Q_ASSERT(homeObject);
1132 ScopedObject proto(scope, homeObject->getPrototypeOf());
1133 if (!proto) {
1134 scope.engine->throwTypeError();
1135 return nullptr;
1136 }
1137 return proto;
1138}
1139
1140ReturnedValue Runtime::LoadSuperProperty::call(ExecutionEngine *engine, const Value &property)
1141{
1142 Scope scope(engine);
1143 Object *base = getSuperBase(scope);
1144 if (!base)
1145 return Encode::undefined();
1146 ScopedPropertyKey key(scope, property.toPropertyKey(e: engine));
1147 if (engine->hasException)
1148 return Encode::undefined();
1149
1150 if (scope.engine->currentStackFrame->isJSTypesFrame()) {
1151 JSTypesStackFrame *frame = static_cast<JSTypesStackFrame *>(
1152 scope.engine->currentStackFrame);
1153 return base->get(id: key, receiver: &(frame->jsFrame->thisObject.asValue<Value>()));
1154 } else {
1155 Q_ASSERT(scope.engine->currentStackFrame->isMetaTypesFrame());
1156 MetaTypesStackFrame *frame = static_cast<MetaTypesStackFrame *>(
1157 scope.engine->currentStackFrame);
1158 Scoped<QObjectWrapper> wrapper(scope, QObjectWrapper::wrap(engine, object: frame->thisObject()));
1159 return base->get(id: key, receiver: wrapper);
1160 }
1161}
1162
1163void Runtime::StoreSuperProperty::call(ExecutionEngine *engine, const Value &property, const Value &value)
1164{
1165 Scope scope(engine);
1166 Object *base = getSuperBase(scope);
1167 if (!base)
1168 return;
1169 ScopedPropertyKey key(scope, property.toPropertyKey(e: engine));
1170 if (engine->hasException)
1171 return;
1172
1173 bool result;
1174 if (scope.engine->currentStackFrame->isJSTypesFrame()) {
1175 JSTypesStackFrame *frame = static_cast<JSTypesStackFrame *>(
1176 scope.engine->currentStackFrame);
1177 result = base->put(id: key, v: value, receiver: &(frame->jsFrame->thisObject.asValue<Value>()));
1178 } else {
1179 Q_ASSERT(scope.engine->currentStackFrame->isMetaTypesFrame());
1180 MetaTypesStackFrame *frame = static_cast<MetaTypesStackFrame *>(
1181 scope.engine->currentStackFrame);
1182 Scoped<QObjectWrapper> wrapper(scope, QObjectWrapper::wrap(engine, object: frame->thisObject()));
1183 result = base->put(id: key, v: value, receiver: wrapper);
1184 }
1185
1186 if (!result && engine->currentStackFrame->v4Function->isStrict())
1187 engine->throwTypeError();
1188}
1189
1190ReturnedValue Runtime::LoadGlobalLookup::call(ExecutionEngine *engine, Function *f, int index)
1191{
1192 Lookup *l = runtimeLookup(f, i: index);
1193 return l->globalGetter(l, engine);
1194}
1195
1196ReturnedValue Runtime::LoadQmlContextPropertyLookup::call(ExecutionEngine *engine, uint index)
1197{
1198 Lookup *l = runtimeLookup(f: engine->currentStackFrame->v4Function, i: index);
1199 return l->qmlContextPropertyGetter(l, engine, nullptr);
1200}
1201
1202ReturnedValue Runtime::GetLookup::call(ExecutionEngine *engine, Function *f, const Value &base, int index)
1203{
1204 Lookup *l = runtimeLookup(f, i: index);
1205 return l->getter(l, engine, base);
1206}
1207
1208void Runtime::SetLookupSloppy::call(Function *f, const Value &base, int index, const Value &value)
1209{
1210 ExecutionEngine *engine = f->internalClass->engine;
1211 QV4::Lookup *l = runtimeLookup(f, i: index);
1212 l->setter(l, engine, const_cast<Value &>(base), value);
1213}
1214
1215void Runtime::SetLookupStrict::call(Function *f, const Value &base, int index, const Value &value)
1216{
1217 ExecutionEngine *engine = f->internalClass->engine;
1218 QV4::Lookup *l = runtimeLookup(f, i: index);
1219 if (!l->setter(l, engine, const_cast<Value &>(base), value))
1220 engine->throwTypeError();
1221}
1222
1223ReturnedValue Runtime::LoadSuperConstructor::call(ExecutionEngine *engine, const Value &t)
1224{
1225 if (engine->currentStackFrame->isJSTypesFrame()) {
1226 JSTypesStackFrame *frame = static_cast<JSTypesStackFrame *>(engine->currentStackFrame);
1227 if (frame->thisObject() != Value::emptyValue().asReturnedValue()) {
1228 // ### TODO: fix line number
1229 return engine->throwReferenceError(
1230 QStringLiteral("super() already called."), fileName: QString(), lineNumber: 0, column: 0);
1231 }
1232 } else {
1233 Q_ASSERT(engine->currentStackFrame->isMetaTypesFrame());
1234 MetaTypesStackFrame *frame = static_cast<MetaTypesStackFrame *>(engine->currentStackFrame);
1235 if (frame->thisObject() != nullptr) {
1236 // ### TODO: fix line number
1237 return engine->throwReferenceError(
1238 QStringLiteral("super() already called."), fileName: QString(), lineNumber: 0, column: 0);
1239 }
1240 }
1241
1242 const FunctionObject *f = t.as<FunctionObject>();
1243 if (!f)
1244 return engine->throwTypeError();
1245 Heap::Object *c = static_cast<const Object &>(t).getPrototypeOf();
1246 if (!c->vtable()->callAsConstructor)
1247 return engine->throwTypeError();
1248 return c->asReturnedValue();
1249}
1250
1251uint RuntimeHelpers::equalHelper(const Value &x, const Value &y)
1252{
1253 Q_ASSERT(x.type() != y.type() || (x.isManaged() && (x.isString() != y.isString())));
1254
1255 if (x.isNumber() && y.isNumber())
1256 return x.asDouble() == y.asDouble();
1257 if (x.isNull() && y.isUndefined()) {
1258 return true;
1259 } else if (x.isUndefined() && y.isNull()) {
1260 return true;
1261 } else if (x.isNumber() && y.isString()) {
1262 double dy = RuntimeHelpers::toNumber(value: y);
1263 return x.asDouble() == dy;
1264 } else if (x.isString() && y.isNumber()) {
1265 double dx = RuntimeHelpers::toNumber(value: x);
1266 return dx == y.asDouble();
1267 } else if (x.isBoolean()) {
1268 return Runtime::CompareEqual::call(Value::fromDouble(d: (double) x.booleanValue()), y);
1269 } else if (y.isBoolean()) {
1270 return Runtime::CompareEqual::call(x, Value::fromDouble(d: (double) y.booleanValue()));
1271 } else {
1272 Object *xo = x.objectValue();
1273 Object *yo = y.objectValue();
1274 if (yo && (x.isNumber() || x.isString())) {
1275 Scope scope(yo->engine());
1276 ScopedValue py(scope, RuntimeHelpers::objectDefaultValue(object: yo, typeHint: PREFERREDTYPE_HINT));
1277 return Runtime::CompareEqual::call(x, py);
1278 } else if (xo && (y.isNumber() || y.isString())) {
1279 Scope scope(xo->engine());
1280 ScopedValue px(scope, RuntimeHelpers::objectDefaultValue(object: xo, typeHint: PREFERREDTYPE_HINT));
1281 return Runtime::CompareEqual::call(px, y);
1282 }
1283 }
1284
1285 return false;
1286}
1287
1288Bool RuntimeHelpers::strictEqual(const Value &x, const Value &y)
1289{
1290 TRACE2(x, y);
1291
1292 if (x.rawValue() == y.rawValue())
1293 // NaN != NaN
1294 return !x.isNaN();
1295
1296 if (x.isNumber())
1297 return y.isNumber() && x.asDouble() == y.asDouble();
1298 if (x.isManaged()) {
1299 return y.isManaged() && x.cast<Managed>()->isEqualTo(other: y.cast<Managed>());
1300 }
1301 return false;
1302}
1303
1304QV4::Bool Runtime::CompareGreaterThan::call(const Value &l, const Value &r)
1305{
1306 TRACE2(l, r);
1307 if (l.isInteger() && r.isInteger())
1308 return l.integerValue() > r.integerValue();
1309 if (l.isNumber() && r.isNumber())
1310 return l.asDouble() > r.asDouble();
1311 String *sl = l.stringValue();
1312 String *sr = r.stringValue();
1313 if (sl && sr) {
1314 return sr->lessThan(other: sl);
1315 }
1316
1317 Object *ro = r.objectValue();
1318 Object *lo = l.objectValue();
1319 if (ro || lo) {
1320 QV4::ExecutionEngine *e = (lo ? lo : ro)->engine();
1321 QV4::Scope scope(e);
1322 QV4::ScopedValue pl(scope, lo ? RuntimeHelpers::objectDefaultValue(object: lo, typeHint: QV4::NUMBER_HINT) : l.asReturnedValue());
1323 QV4::ScopedValue pr(scope, ro ? RuntimeHelpers::objectDefaultValue(object: ro, typeHint: QV4::NUMBER_HINT) : r.asReturnedValue());
1324 return Runtime::CompareGreaterThan::call(l: pl, r: pr);
1325 }
1326
1327 double dl = RuntimeHelpers::toNumber(value: l);
1328 double dr = RuntimeHelpers::toNumber(value: r);
1329 return dl > dr;
1330}
1331
1332QV4::Bool Runtime::CompareLessThan::call(const Value &l, const Value &r)
1333{
1334 TRACE2(l, r);
1335 if (l.isInteger() && r.isInteger())
1336 return l.integerValue() < r.integerValue();
1337 if (l.isNumber() && r.isNumber())
1338 return l.asDouble() < r.asDouble();
1339 String *sl = l.stringValue();
1340 String *sr = r.stringValue();
1341 if (sl && sr) {
1342 return sl->lessThan(other: sr);
1343 }
1344
1345 Object *ro = r.objectValue();
1346 Object *lo = l.objectValue();
1347 if (ro || lo) {
1348 QV4::ExecutionEngine *e = (lo ? lo : ro)->engine();
1349 QV4::Scope scope(e);
1350 QV4::ScopedValue pl(scope, lo ? RuntimeHelpers::objectDefaultValue(object: lo, typeHint: QV4::NUMBER_HINT) : l.asReturnedValue());
1351 QV4::ScopedValue pr(scope, ro ? RuntimeHelpers::objectDefaultValue(object: ro, typeHint: QV4::NUMBER_HINT) : r.asReturnedValue());
1352 return Runtime::CompareLessThan::call(l: pl, r: pr);
1353 }
1354
1355 double dl = RuntimeHelpers::toNumber(value: l);
1356 double dr = RuntimeHelpers::toNumber(value: r);
1357 return dl < dr;
1358}
1359
1360QV4::Bool Runtime::CompareGreaterEqual::call(const Value &l, const Value &r)
1361{
1362 TRACE2(l, r);
1363 if (l.isInteger() && r.isInteger())
1364 return l.integerValue() >= r.integerValue();
1365 if (l.isNumber() && r.isNumber())
1366 return l.asDouble() >= r.asDouble();
1367 String *sl = l.stringValue();
1368 String *sr = r.stringValue();
1369 if (sl && sr) {
1370 return !sl->lessThan(other: sr);
1371 }
1372
1373 Object *ro = r.objectValue();
1374 Object *lo = l.objectValue();
1375 if (ro || lo) {
1376 QV4::ExecutionEngine *e = (lo ? lo : ro)->engine();
1377 QV4::Scope scope(e);
1378 QV4::ScopedValue pl(scope, lo ? RuntimeHelpers::objectDefaultValue(object: lo, typeHint: QV4::NUMBER_HINT) : l.asReturnedValue());
1379 QV4::ScopedValue pr(scope, ro ? RuntimeHelpers::objectDefaultValue(object: ro, typeHint: QV4::NUMBER_HINT) : r.asReturnedValue());
1380 return Runtime::CompareGreaterEqual::call(l: pl, r: pr);
1381 }
1382
1383 double dl = RuntimeHelpers::toNumber(value: l);
1384 double dr = RuntimeHelpers::toNumber(value: r);
1385 return dl >= dr;
1386}
1387
1388QV4::Bool Runtime::CompareLessEqual::call(const Value &l, const Value &r)
1389{
1390 TRACE2(l, r);
1391 if (l.isInteger() && r.isInteger())
1392 return l.integerValue() <= r.integerValue();
1393 if (l.isNumber() && r.isNumber())
1394 return l.asDouble() <= r.asDouble();
1395 String *sl = l.stringValue();
1396 String *sr = r.stringValue();
1397 if (sl && sr) {
1398 return !sr->lessThan(other: sl);
1399 }
1400
1401 Object *ro = r.objectValue();
1402 Object *lo = l.objectValue();
1403 if (ro || lo) {
1404 QV4::ExecutionEngine *e = (lo ? lo : ro)->engine();
1405 QV4::Scope scope(e);
1406 QV4::ScopedValue pl(scope, lo ? RuntimeHelpers::objectDefaultValue(object: lo, typeHint: QV4::NUMBER_HINT) : l.asReturnedValue());
1407 QV4::ScopedValue pr(scope, ro ? RuntimeHelpers::objectDefaultValue(object: ro, typeHint: QV4::NUMBER_HINT) : r.asReturnedValue());
1408 return Runtime::CompareLessEqual::call(l: pl, r: pr);
1409 }
1410
1411 double dl = RuntimeHelpers::toNumber(value: l);
1412 double dr = RuntimeHelpers::toNumber(value: r);
1413 return dl <= dr;
1414}
1415
1416Bool Runtime::CompareInstanceof::call(ExecutionEngine *engine, const Value &left, const Value &right)
1417{
1418 TRACE2(left, right);
1419
1420 Scope scope(engine);
1421 ScopedValue v(scope, Instanceof::call(engine, lval: left, rval: right));
1422 return v->booleanValue();
1423}
1424
1425uint Runtime::CompareIn::call(ExecutionEngine *engine, const Value &left, const Value &right)
1426{
1427 TRACE2(left, right);
1428
1429 Scope scope(engine);
1430 ScopedValue v(scope, In::call(engine, left, right));
1431 return v->booleanValue();
1432}
1433
1434static ReturnedValue throwPropertyIsNotAFunctionTypeError(ExecutionEngine *engine, Value *thisObject, const QString &propertyName)
1435{
1436 QString objectAsString = QStringLiteral("[null]");
1437 if (!thisObject->isUndefined())
1438 objectAsString = thisObject->toQStringNoThrow();
1439 QString msg = QStringLiteral("Property '%1' of object %2 is not a function")
1440 .arg(args: propertyName, args&: objectAsString);
1441 return engine->throwTypeError(message: msg);
1442}
1443
1444ReturnedValue Runtime::CallGlobalLookup::call(ExecutionEngine *engine, uint index, Value argv[], int argc)
1445{
1446 Scope scope(engine);
1447 Lookup *l = runtimeLookup(f: engine->currentStackFrame->v4Function, i: index);
1448 Value function = Value::fromReturnedValue(val: l->globalGetter(l, engine));
1449 Value thisObject = Value::undefinedValue();
1450 if (!function.isFunctionObject()) {
1451 return throwPropertyIsNotAFunctionTypeError(engine, thisObject: &thisObject,
1452 propertyName: engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[l->nameIndex]->toQString());
1453 }
1454
1455 return checkedResult(v4: engine, result: static_cast<FunctionObject &>(function).call(
1456 thisObject: &thisObject, argv, argc));
1457}
1458
1459ReturnedValue Runtime::CallQmlContextPropertyLookup::call(ExecutionEngine *engine, uint index,
1460 Value *argv, int argc)
1461{
1462 Scope scope(engine);
1463 ScopedValue thisObject(scope);
1464 Lookup *l = runtimeLookup(f: engine->currentStackFrame->v4Function, i: index);
1465 Value function = Value::fromReturnedValue(val: l->qmlContextPropertyGetter(l, engine, thisObject));
1466 if (!function.isFunctionObject()) {
1467 return throwPropertyIsNotAFunctionTypeError(engine, thisObject,
1468 propertyName: engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[l->nameIndex]->toQString());
1469 }
1470
1471 return checkedResult(v4: engine, result: static_cast<FunctionObject &>(function).call(
1472 thisObject, argv, argc));
1473}
1474
1475ReturnedValue Runtime::CallPossiblyDirectEval::call(ExecutionEngine *engine, Value *argv, int argc)
1476{
1477 Scope scope(engine);
1478 ScopedValue thisObject(scope);
1479
1480 ScopedFunctionObject function(
1481 scope, engine->currentContext()->getPropertyAndBase(name: engine->id_eval(), base: thisObject));
1482 if (engine->hasException)
1483 return Encode::undefined();
1484
1485 if (!function)
1486 return throwPropertyIsNotAFunctionTypeError(engine, thisObject, propertyName: QLatin1String("eval"));
1487
1488 if (function->d() == engine->evalFunction()->d())
1489 return static_cast<EvalFunction *>(function.getPointer())->evalCall(thisObject, argv, argc, directCall: true);
1490
1491 return checkedResult(v4: engine, result: function->call(thisObject, argv, argc));
1492}
1493
1494ReturnedValue Runtime::CallName::call(ExecutionEngine *engine, int nameIndex, Value *argv, int argc)
1495{
1496 Scope scope(engine);
1497 ScopedValue thisObject(scope);
1498 ScopedString name(scope, engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[nameIndex]);
1499
1500 ScopedFunctionObject f(scope, engine->currentContext()->getPropertyAndBase(name, base: thisObject));
1501 if (engine->hasException)
1502 return Encode::undefined();
1503
1504 if (!f) {
1505 return throwPropertyIsNotAFunctionTypeError(
1506 engine, thisObject, propertyName: engine->currentStackFrame->v4Function->compilationUnit
1507 ->runtimeStrings[nameIndex]->toQString());
1508 }
1509
1510 return checkedResult(v4: engine, result: f->call(thisObject, argv, argc));
1511}
1512
1513ReturnedValue Runtime::CallProperty::call(ExecutionEngine *engine, const Value &baseRef, int nameIndex, Value *argv, int argc)
1514{
1515 const Value *base = &baseRef;
1516 Scope scope(engine);
1517 ScopedString name(
1518 scope,
1519 engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[nameIndex]);
1520 ScopedObject lookupObject(scope, base);
1521
1522 if (!lookupObject) {
1523 Q_ASSERT(!base->isEmpty());
1524 if (base->isNullOrUndefined()) {
1525 QString message = QStringLiteral("Cannot call method '%1' of %2")
1526 .arg(args: name->toQString(), args: base->toQStringNoThrow());
1527 return engine->throwTypeError(message);
1528 }
1529
1530 if (base->isManaged()) {
1531 const Managed *m = static_cast<const Managed *>(base);
1532 lookupObject = m->internalClass()->prototype;
1533 Q_ASSERT(m->internalClass()->prototype);
1534 } else {
1535 lookupObject = RuntimeHelpers::convertToObject(engine, value: *base);
1536 if (engine->hasException) // type error
1537 return Encode::undefined();
1538 if (!engine->currentStackFrame->v4Function->isStrict())
1539 base = lookupObject;
1540 }
1541 }
1542
1543 ScopedFunctionObject f(scope, static_cast<Object *>(lookupObject)->get(name));
1544
1545 if (!f) {
1546 QString error = QStringLiteral("Property '%1' of object %2 is not a function")
1547 .arg(args: name->toQString(),
1548 args: base->toQStringNoThrow());
1549 return engine->throwTypeError(message: error);
1550 }
1551
1552 return checkedResult(v4: engine, result: f->call(thisObject: base, argv, argc));
1553}
1554
1555ReturnedValue Runtime::CallPropertyLookup::call(ExecutionEngine *engine, const Value &base, uint index, Value *argv, int argc)
1556{
1557 Lookup *l = runtimeLookup(f: engine->currentStackFrame->v4Function, i: index);
1558 // ok to have the value on the stack here
1559 Value f = Value::fromReturnedValue(val: l->getter(l, engine, base));
1560
1561 if (Q_LIKELY(f.isFunctionObject()))
1562 return checkedResult(v4: engine, result: static_cast<FunctionObject &>(f).call(thisObject: &base, argv, argc));
1563
1564 if (QmlSignalHandler *handler = f.as<QmlSignalHandler>())
1565 return checkedResult(v4: engine, result: handler->call(thisObject: &base, argv, argc));
1566
1567 const QString message = QStringLiteral("Property '%1' of object %2 is not a function")
1568 .arg(a: engine->currentStackFrame->v4Function->compilationUnit
1569 ->runtimeStrings[l->nameIndex]->toQString())
1570 .arg(a: base.toQStringNoThrow());
1571 return engine->throwTypeError(message);
1572}
1573
1574ReturnedValue Runtime::CallValue::call(ExecutionEngine *engine, const Value &func, Value *argv, int argc)
1575{
1576 if (!func.isFunctionObject())
1577 return engine->throwTypeError(QStringLiteral("%1 is not a function").arg(a: func.toQStringNoThrow()));
1578 Value undef = Value::undefinedValue();
1579 return checkedResult(v4: engine, result: static_cast<const FunctionObject &>(func).call(
1580 thisObject: &undef, argv, argc));
1581}
1582
1583ReturnedValue Runtime::CallWithReceiver::call(ExecutionEngine *engine, const Value &func,
1584 const Value &thisObject, Value argv[], int argc)
1585{
1586 if (!func.isFunctionObject())
1587 return engine->throwTypeError(QStringLiteral("%1 is not a function").arg(a: func.toQStringNoThrow()));
1588 return checkedResult(v4: engine, result: static_cast<const FunctionObject &>(func).call(
1589 thisObject: &thisObject, argv, argc));
1590}
1591
1592struct CallArgs {
1593 Value *argv;
1594 int argc;
1595};
1596
1597static CallArgs createSpreadArguments(Scope &scope, Value *argv, int argc)
1598{
1599 ScopedValue it(scope);
1600 ScopedValue done(scope);
1601
1602 int argCount = 0;
1603
1604 Value *v = scope.alloc<Scope::Uninitialized>();
1605 Value *arguments = v;
1606 for (int i = 0; i < argc; ++i) {
1607 if (!argv[i].isEmpty()) {
1608 *v = argv[i];
1609 ++argCount;
1610 v = scope.alloc<Scope::Uninitialized>();
1611 continue;
1612 }
1613 // spread element
1614 ++i;
1615 it = Runtime::GetIterator::call(engine: scope.engine, in: argv[i], /* ForInIterator */ iterator: 1);
1616 if (scope.hasException())
1617 return { .argv: nullptr, .argc: 0 };
1618 while (1) {
1619 done = Runtime::IteratorNext::call(engine: scope.engine, iterator: it, value: v);
1620 if (scope.hasException())
1621 return { .argv: nullptr, .argc: 0 };
1622 Q_ASSERT(done->isBoolean());
1623 if (done->booleanValue())
1624 break;
1625 ++argCount;
1626 constexpr auto safetyMargin = 100; // leave some space on the stack for actual work with the elements
1627 if (qint64(scope.engine->jsStackLimit - scope.engine->jsStackTop) < safetyMargin) {
1628 scope.engine->throwRangeError(message: QLatin1String("Too many elements in array to use it with the spread operator"));
1629 return { .argv: nullptr, .argc: 0 };
1630 }
1631 v = scope.alloc<Scope::Uninitialized>();
1632 }
1633 }
1634 return { .argv: arguments, .argc: argCount };
1635}
1636
1637ReturnedValue Runtime::CallWithSpread::call(ExecutionEngine *engine, const Value &function, const Value &thisObject, Value *argv, int argc)
1638{
1639 Q_ASSERT(argc >= 1);
1640 if (!function.isFunctionObject())
1641 return engine->throwTypeError();
1642
1643 Scope scope(engine);
1644 CallArgs arguments = createSpreadArguments(scope, argv, argc);
1645 if (engine->hasException)
1646 return Encode::undefined();
1647
1648 return checkedResult(v4: engine, result: static_cast<const FunctionObject &>(function).call(
1649 thisObject: &thisObject, argv: arguments.argv, argc: arguments.argc));
1650}
1651
1652ReturnedValue Runtime::Construct::call(ExecutionEngine *engine, const Value &function, const Value &newTarget, Value *argv, int argc)
1653{
1654 if (!function.isFunctionObject())
1655 return engine->throwTypeError();
1656
1657 return static_cast<const FunctionObject &>(function).callAsConstructor(argv, argc, newTarget: &newTarget);
1658}
1659
1660ReturnedValue Runtime::ConstructWithSpread::call(ExecutionEngine *engine, const Value &function, const Value &newTarget, Value *argv, int argc)
1661{
1662 if (!function.isFunctionObject())
1663 return engine->throwTypeError();
1664
1665 Scope scope(engine);
1666 CallArgs arguments = createSpreadArguments(scope, argv, argc);
1667 if (engine->hasException)
1668 return Encode::undefined();
1669
1670 return static_cast<const FunctionObject &>(function).callAsConstructor(argv: arguments.argv, argc: arguments.argc, newTarget: &newTarget);
1671}
1672
1673ReturnedValue Runtime::TailCall::call(JSTypesStackFrame *frame, ExecutionEngine *engine)
1674{
1675 // IMPORTANT! The JIT assumes that this method has the same amount (or less) arguments than
1676 // the jitted function, so it can safely do a tail call.
1677
1678 Value *tos = engine->jsStackTop;
1679 const Value &function = tos[StackOffsets::tailCall_function];
1680 const Value &thisObject = tos[StackOffsets::tailCall_thisObject];
1681 Value *argv = reinterpret_cast<Value *>(frame->jsFrame) + tos[StackOffsets::tailCall_argv].int_32();
1682 int argc = tos[StackOffsets::tailCall_argc].int_32();
1683 Q_ASSERT(argc >= 0);
1684
1685 const JavaScriptFunctionObject *jsfo = function.as<JavaScriptFunctionObject>();
1686 if (!jsfo) {
1687 if (const FunctionObject *fo = function.as<FunctionObject>())
1688 return checkedResult(v4: engine, result: fo->call(thisObject: &thisObject, argv, argc));
1689 return engine->throwTypeError();
1690 }
1691
1692 if (!frame->callerCanHandleTailCall() || !jsfo->canBeTailCalled() || engine->debugger()
1693 || unsigned(argc) > jsfo->formalParameterCount()) {
1694 // Cannot tailcall, do a normal call:
1695 return checkedResult(v4: engine, result: jsfo->call(thisObject: &thisObject, argv, argc));
1696 }
1697
1698 memmove(dest: frame->jsFrame->args, src: argv, n: argc * sizeof(Value));
1699 frame->init(v4Function: jsfo->function(), argv: frame->jsFrame->argValues<Value>(), argc,
1700 callerCanHandleTailCall: frame->callerCanHandleTailCall());
1701 frame->setupJSFrame(stackSpace: frame->framePointer(), function: *jsfo, scope: jsfo->scope(), thisObject,
1702 newTarget: Primitive::undefinedValue());
1703 engine->jsStackTop = frame->framePointer() + frame->requiredJSStackFrameSize();
1704 frame->setPendingTailCall(true);
1705 return Encode::undefined();
1706}
1707
1708void Runtime::ThrowException::call(ExecutionEngine *engine, const Value &value)
1709{
1710 if (!value.isEmpty())
1711 engine->throwError(value);
1712}
1713
1714ReturnedValue Runtime::TypeofValue::call(ExecutionEngine *engine, const Value &value)
1715{
1716 Scope scope(engine);
1717 ScopedString res(scope);
1718 switch (value.type()) {
1719 case Value::Undefined_Type:
1720 res = engine->id_undefined();
1721 break;
1722 case Value::Null_Type:
1723 res = engine->id_object();
1724 break;
1725 case Value::Boolean_Type:
1726 res = engine->id_boolean();
1727 break;
1728 case Value::Managed_Type:
1729 if (value.isString())
1730 res = engine->id_string();
1731 else if (value.isSymbol())
1732 res = engine->id_symbol();
1733 else if (value.objectValue()->as<FunctionObject>())
1734 res = engine->id_function();
1735 else
1736 res = engine->id_object(); // ### implementation-defined
1737 break;
1738 default:
1739 res = engine->id_number();
1740 break;
1741 }
1742 return res.asReturnedValue();
1743}
1744
1745QV4::ReturnedValue Runtime::TypeofName::call(ExecutionEngine *engine, int nameIndex)
1746{
1747 Scope scope(engine);
1748 ScopedString name(scope, engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[nameIndex]);
1749 ScopedValue prop(scope, engine->currentContext()->getProperty(name));
1750 // typeof doesn't throw. clear any possible exception
1751 scope.engine->hasException = false;
1752 return TypeofValue::call(engine, value: prop);
1753}
1754
1755void Runtime::PushCallContext::call(JSTypesStackFrame *frame)
1756{
1757 frame->jsFrame->context = ExecutionContext::newCallContext(frame)->asReturnedValue();
1758}
1759
1760ReturnedValue Runtime::PushWithContext::call(ExecutionEngine *engine, const Value &acc)
1761{
1762 Q_ASSERT(engine->currentStackFrame->isJSTypesFrame());
1763 CallData *jsFrame = static_cast<JSTypesStackFrame *>(engine->currentStackFrame)->jsFrame;
1764 Value &newAcc = jsFrame->accumulator.asValue<Value>();
1765 newAcc = Value::fromHeapObject(m: acc.toObject(e: engine));
1766 if (!engine->hasException) {
1767 Q_ASSERT(newAcc.isObject());
1768 const Object &obj = static_cast<const Object &>(newAcc);
1769 Value &context = jsFrame->context.asValue<Value>();
1770 auto ec = static_cast<const ExecutionContext *>(&context);
1771 context = ec->newWithContext(with: obj.d())->asReturnedValue();
1772 }
1773 return newAcc.asReturnedValue();
1774}
1775
1776void Runtime::PushCatchContext::call(ExecutionEngine *engine, int blockIndex, int exceptionVarNameIndex)
1777{
1778 Q_ASSERT(engine->currentStackFrame->isJSTypesFrame());
1779 auto name = engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[exceptionVarNameIndex];
1780 static_cast<JSTypesStackFrame *>(engine->currentStackFrame)->jsFrame->context
1781 = ExecutionContext::newCatchContext(frame: engine->currentStackFrame, blockIndex, exceptionVarName: name)->asReturnedValue();
1782}
1783
1784void Runtime::PushBlockContext::call(ExecutionEngine *engine, int index)
1785{
1786 Q_ASSERT(engine->currentStackFrame->isJSTypesFrame());
1787 static_cast<JSTypesStackFrame *>(engine->currentStackFrame)->jsFrame->context
1788 = ExecutionContext::newBlockContext(frame: engine->currentStackFrame, blockIndex: index)->asReturnedValue();
1789}
1790
1791void Runtime::CloneBlockContext::call(ExecutionEngine *engine)
1792{
1793 Q_ASSERT(engine->currentStackFrame->isJSTypesFrame());
1794 auto frame = static_cast<JSTypesStackFrame *>(engine->currentStackFrame);
1795 auto context = static_cast<Heap::CallContext *>(
1796 Value::fromStaticValue(staticValue: frame->jsFrame->context).m());
1797 frame->jsFrame->context =
1798 ExecutionContext::cloneBlockContext(engine, callContext: context)->asReturnedValue();
1799}
1800
1801void Runtime::PushScriptContext::call(ExecutionEngine *engine, int index)
1802{
1803 Q_ASSERT(engine->currentStackFrame->isJSTypesFrame());
1804 Q_ASSERT(engine->currentContext()->d()->type == Heap::ExecutionContext::Type_GlobalContext ||
1805 engine->currentContext()->d()->type == Heap::ExecutionContext::Type_QmlContext);
1806 ReturnedValue c = ExecutionContext::newBlockContext(frame: engine->currentStackFrame, blockIndex: index)->asReturnedValue();
1807 engine->setScriptContext(c);
1808 static_cast<JSTypesStackFrame *>(engine->currentStackFrame)->jsFrame->context = c;
1809}
1810
1811void Runtime::PopScriptContext::call(ExecutionEngine *engine)
1812{
1813 Q_ASSERT(engine->currentStackFrame->isJSTypesFrame());
1814 ReturnedValue root = engine->rootContext()->asReturnedValue();
1815 engine->setScriptContext(root);
1816 static_cast<JSTypesStackFrame *>(engine->currentStackFrame)->jsFrame->context = root;
1817}
1818
1819void Runtime::ThrowReferenceError::call(ExecutionEngine *engine, int nameIndex)
1820{
1821 Scope scope(engine);
1822 ScopedString name(scope, engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[nameIndex]);
1823 engine->throwReferenceError(value: name);
1824}
1825
1826void Runtime::ThrowOnNullOrUndefined::call(ExecutionEngine *engine, const Value &v)
1827{
1828 if (v.isNullOrUndefined())
1829 engine->throwTypeError();
1830}
1831
1832void Runtime::MarkCustom::call(const Value &toBeMarked)
1833{
1834 auto *h = toBeMarked.heapObject();
1835 if (!h)
1836 return;
1837 Q_ASSERT(h->internalClass);
1838 auto engine = h->internalClass->engine;
1839 Q_ASSERT(engine);
1840 // runtime function is only meant to be called while gc is ongoing
1841 Q_ASSERT(engine->isGCOngoing);
1842 QV4::WriteBarrier::markCustom(engine, markFunction: [&](QV4::MarkStack *ms) {
1843 h->mark(markStack: ms);
1844 });
1845}
1846
1847ReturnedValue Runtime::ConvertThisToObject::call(ExecutionEngine *engine, const Value &t)
1848{
1849 if (!t.isObject()) {
1850 if (t.isNullOrUndefined()) {
1851 return engine->globalObject->asReturnedValue();
1852 } else {
1853 return t.toObject(e: engine)->asReturnedValue();
1854 }
1855 }
1856 return t.asReturnedValue();
1857}
1858
1859void Runtime::DeclareVar::call(ExecutionEngine *engine, Bool deletable, int nameIndex)
1860{
1861 Scope scope(engine);
1862 ScopedString name(scope, engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[nameIndex]);
1863 engine->currentContext()->createMutableBinding(name, deletable);
1864}
1865
1866ReturnedValue Runtime::ArrayLiteral::call(ExecutionEngine *engine, Value *values, uint length)
1867{
1868 return engine->newArrayObject(values, length)->asReturnedValue();
1869}
1870
1871ReturnedValue Runtime::ObjectLiteral::call(ExecutionEngine *engine, int classId, QV4::Value args[], int argc)
1872{
1873 Scope scope(engine);
1874 Scoped<InternalClass> klass(scope, engine->currentStackFrame->v4Function->compilationUnit->runtimeClasses[classId]);
1875 ScopedObject o(scope, engine->newObject(internalClass: klass->d()));
1876
1877 Q_ASSERT(uint(argc) >= klass->d()->size);
1878
1879 for (uint i = 0; i < klass->d()->size; ++i)
1880 o->setProperty(index: i, v: *args++);
1881
1882 Q_ASSERT((argc - klass->d()->size) % 3 == 0);
1883 int additionalArgs = (argc - int(klass->d()->size))/3;
1884
1885 if (!additionalArgs)
1886 return o->asReturnedValue();
1887
1888 ScopedPropertyKey name(scope);
1889 ScopedProperty pd(scope);
1890 ScopedFunctionObject fn(scope);
1891 ScopedString fnName(scope);
1892 ScopedValue value(scope);
1893 for (int i = 0; i < additionalArgs; ++i) {
1894 Q_ASSERT(args->isInteger());
1895 ObjectLiteralArgument arg = ObjectLiteralArgument(args->integerValue());
1896 name = args[1].toPropertyKey(e: engine);
1897 value = args[2];
1898 if (engine->hasException)
1899 return Encode::undefined();
1900 if (arg != ObjectLiteralArgument::Value) {
1901 Q_ASSERT(args[2].isInteger());
1902 int functionId = args[2].integerValue();
1903 QV4::Function *clos = engine->currentStackFrame->v4Function->executableCompilationUnit()
1904 ->runtimeFunctions[functionId];
1905 Q_ASSERT(clos);
1906
1907 PropertyKey::FunctionNamePrefix prefix = PropertyKey::None;
1908 if (arg == ObjectLiteralArgument::Getter)
1909 prefix = PropertyKey::Getter;
1910 else if (arg == ObjectLiteralArgument::Setter)
1911 prefix = PropertyKey::Setter;
1912 else
1913 arg = ObjectLiteralArgument::Value;
1914 fnName = name->asFunctionName(e: engine, prefix);
1915
1916 ExecutionContext *current = engine->currentContext();
1917 if (clos->isGenerator())
1918 value = MemberGeneratorFunction::create(scope: current, function: clos, homeObject: o, name: fnName)->asReturnedValue();
1919 else
1920 value = FunctionObject::createMemberFunction(scope: current, function: clos, homeObject: o, name: fnName)->asReturnedValue();
1921 } else if (args[2].isFunctionObject()) {
1922 fn = static_cast<const FunctionObject &>(args[2]);
1923
1924 fnName = name->asFunctionName(e: engine, prefix: PropertyKey::None);
1925 fn->setName(fnName);
1926 }
1927 Q_ASSERT(arg != ObjectLiteralArgument::Method);
1928 Q_ASSERT(arg == ObjectLiteralArgument::Value || value->isFunctionObject());
1929 if (arg == ObjectLiteralArgument::Value || arg == ObjectLiteralArgument::Getter) {
1930 pd->value = value;
1931 pd->set = Value::emptyValue();
1932 } else {
1933 pd->value = Value::emptyValue();
1934 pd->set = value;
1935 }
1936 bool ok = o->defineOwnProperty(id: name, p: pd, attrs: (arg == ObjectLiteralArgument::Value ? Attr_Data : Attr_Accessor));
1937 if (!ok)
1938 return engine->throwTypeError();
1939
1940 args += 3;
1941 }
1942 return o.asReturnedValue();
1943}
1944
1945ReturnedValue Runtime::CreateClass::call(ExecutionEngine *engine, int classIndex,
1946 const Value &superClass, Value computedNames[])
1947{
1948 const QV4::ExecutableCompilationUnit *unit
1949 = engine->currentStackFrame->v4Function->executableCompilationUnit();
1950 const QV4::CompiledData::Class *cls = unit->unitData()->classAt(idx: classIndex);
1951
1952 Scope scope(engine);
1953 ScopedObject protoParent(scope, engine->objectPrototype());
1954 ScopedObject constructorParent(scope, engine->functionPrototype());
1955 if (!superClass.isEmpty()) {
1956 if (superClass.isNull()) {
1957 protoParent = Encode::null();
1958 } else {
1959 const FunctionObject *superFunction = superClass.as<FunctionObject>();
1960 // ### check that the heritage object is a constructor
1961 if (!superFunction || !superFunction->isConstructor())
1962 return engine->throwTypeError(QStringLiteral("The superclass is not a function object."));
1963 const FunctionObject *s = static_cast<const FunctionObject *>(&superClass);
1964 ScopedValue result(scope, s->get(name: scope.engine->id_prototype()));
1965 if (!result->isObject() && !result->isNull())
1966 return engine->throwTypeError(QStringLiteral("The value of the superclass's prototype property is not an object."));
1967 protoParent = *result;
1968 constructorParent = superClass;
1969 }
1970 }
1971
1972 ScopedObject proto(scope, engine->newObject());
1973 proto->setPrototypeUnchecked(protoParent);
1974 ExecutionContext *current = engine->currentContext();
1975
1976 ScopedFunctionObject constructor(scope);
1977 QV4::Function *f = cls->constructorFunction != UINT_MAX ? unit->runtimeFunctions[cls->constructorFunction] : nullptr;
1978 constructor = FunctionObject::createConstructorFunction(scope: current, function: f, homeObject: proto, isDerivedConstructor: !superClass.isEmpty())->asReturnedValue();
1979 constructor->setPrototypeUnchecked(constructorParent);
1980 Value argCount = Value::fromInt32(i: f ? f->nFormals : 0);
1981 constructor->defineReadonlyConfigurableProperty(name: scope.engine->id_length(), value: argCount);
1982 constructor->defineReadonlyConfigurableProperty(name: engine->id_prototype(), value: proto);
1983 proto->defineDefaultProperty(name: engine->id_constructor(), value: constructor);
1984
1985 ScopedString name(scope);
1986 if (cls->nameIndex != UINT_MAX) {
1987 name = unit->runtimeStrings[cls->nameIndex];
1988 constructor->defineReadonlyConfigurableProperty(name: engine->id_name(), value: name);
1989 }
1990
1991 ScopedObject receiver(scope, *constructor);
1992 ScopedPropertyKey propertyName(scope);
1993 ScopedFunctionObject function(scope);
1994 ScopedProperty property(scope);
1995 const CompiledData::Method *methods = cls->methodTable();
1996 for (uint i = 0; i < cls->nStaticMethods + cls->nMethods; ++i) {
1997 if (i == cls->nStaticMethods)
1998 receiver = proto;
1999 if (methods[i].name == UINT_MAX) {
2000 propertyName = computedNames->toPropertyKey(e: engine);
2001 if (propertyName == scope.engine->id_prototype()->propertyKey() && receiver->d() == constructor->d())
2002 return engine->throwTypeError(QStringLiteral("Cannot declare a static method named 'prototype'."));
2003 if (engine->hasException)
2004 return Encode::undefined();
2005 ++computedNames;
2006 } else {
2007 name = unit->runtimeStrings[methods[i].name];
2008 propertyName = name->toPropertyKey();
2009 }
2010 QV4::Function *f = unit->runtimeFunctions[methods[i].function];
2011 Q_ASSERT(f);
2012 PropertyKey::FunctionNamePrefix prefix = PropertyKey::None;
2013 if (methods[i].type == CompiledData::Method::Getter)
2014 prefix = PropertyKey::Getter;
2015 else if (methods[i].type == CompiledData::Method::Setter)
2016 prefix = PropertyKey::Setter;
2017
2018 name = propertyName->asFunctionName(e: engine, prefix);
2019
2020 if (f->isGenerator())
2021 function = MemberGeneratorFunction::create(scope: current, function: f, homeObject: receiver, name);
2022 else
2023 function = FunctionObject::createMemberFunction(scope: current, function: f, homeObject: receiver, name);
2024 Q_ASSERT(function);
2025 PropertyAttributes attributes;
2026 switch (methods[i].type) {
2027 case CompiledData::Method::Getter:
2028 property->setGetter(function);
2029 property->set = Value::emptyValue();
2030 attributes = Attr_Accessor|Attr_NotEnumerable;
2031 break;
2032 case CompiledData::Method::Setter:
2033 property->value = Value::emptyValue();
2034 property->setSetter(function);
2035 attributes = Attr_Accessor|Attr_NotEnumerable;
2036 break;
2037 default: // Regular
2038 property->value = function;
2039 property->set = Value::emptyValue();
2040 attributes = Attr_Data|Attr_NotEnumerable;
2041 break;
2042 }
2043 receiver->defineOwnProperty(id: propertyName, p: property, attrs: attributes);
2044 }
2045
2046 return constructor->asReturnedValue();
2047}
2048
2049QV4::ReturnedValue Runtime::CreateMappedArgumentsObject::call(ExecutionEngine *engine)
2050{
2051 Q_ASSERT(engine->currentContext()->d()->type == Heap::ExecutionContext::Type_CallContext);
2052 Heap::InternalClass *ic = engine->internalClasses(icType: EngineBase::Class_ArgumentsObject);
2053 return engine->memoryManager->allocObject<ArgumentsObject>(ic, args&: engine->currentStackFrame)->asReturnedValue();
2054}
2055
2056QV4::ReturnedValue Runtime::CreateUnmappedArgumentsObject::call(ExecutionEngine *engine)
2057{
2058 Q_ASSERT(engine->currentStackFrame->isJSTypesFrame());
2059 Heap::InternalClass *ic = engine->internalClasses(icType: EngineBase::Class_StrictArgumentsObject);
2060 return engine->memoryManager->allocObject<StrictArgumentsObject>(
2061 ic, args: static_cast<JSTypesStackFrame *>(engine->currentStackFrame))->asReturnedValue();
2062}
2063
2064QV4::ReturnedValue Runtime::CreateRestParameter::call(ExecutionEngine *engine, int argIndex)
2065{
2066 Q_ASSERT(engine->currentStackFrame->isJSTypesFrame());
2067 JSTypesStackFrame *frame = static_cast<JSTypesStackFrame *>(engine->currentStackFrame);
2068 const Value *values = frame->argv() + argIndex;
2069 int nValues = frame->argc() - argIndex;
2070 if (nValues <= 0)
2071 return engine->newArrayObject(count: 0)->asReturnedValue();
2072 return engine->newArrayObject(values, length: nValues)->asReturnedValue();
2073}
2074
2075ReturnedValue Runtime::RegexpLiteral::call(ExecutionEngine *engine, int id)
2076{
2077 const auto val
2078 = engine->currentStackFrame->v4Function->compilationUnit->runtimeRegularExpressions[id];
2079 Heap::RegExpObject *ro = engine->newRegExpObject(re: Value::fromStaticValue(staticValue: val).as<RegExp>());
2080 return ro->asReturnedValue();
2081}
2082
2083ReturnedValue Runtime::ToObject::call(ExecutionEngine *engine, const Value &obj)
2084{
2085 if (obj.isObject())
2086 return obj.asReturnedValue();
2087
2088 return obj.toObject(e: engine)->asReturnedValue();
2089}
2090
2091Bool Runtime::ToBoolean::call(const Value &obj)
2092{
2093 return obj.toBoolean();
2094}
2095
2096ReturnedValue Runtime::ToNumber::call(ExecutionEngine *, const Value &v)
2097{
2098 return Encode(v.toNumber());
2099}
2100
2101ReturnedValue Runtime::UMinus::call(const Value &value)
2102{
2103 TRACE1(value);
2104
2105 // +0 != -0, so we need to convert to double when negating 0
2106 if (value.isInteger() && value.integerValue() &&
2107 value.integerValue() != std::numeric_limits<int>::min())
2108 return Encode(-value.integerValue());
2109 else {
2110 double n = RuntimeHelpers::toNumber(value);
2111 return Encode(-n);
2112 }
2113}
2114
2115// binary operators
2116
2117ReturnedValue Runtime::Add::call(ExecutionEngine *engine, const Value &left, const Value &right)
2118{
2119 TRACE2(left, right);
2120
2121 if (Q_LIKELY(left.integerCompatible() && right.integerCompatible()))
2122 return add_int32(a: left.integerValue(), b: right.integerValue());
2123 if (left.isNumber() && right.isNumber())
2124 return Value::fromDouble(d: left.asDouble() + right.asDouble()).asReturnedValue();
2125
2126 return RuntimeHelpers::addHelper(engine, left, right);
2127}
2128
2129ReturnedValue Runtime::Sub::call(const Value &left, const Value &right)
2130{
2131 TRACE2(left, right);
2132
2133 if (Q_LIKELY(left.integerCompatible() && right.integerCompatible()))
2134 return sub_int32(a: left.integerValue(), b: right.integerValue());
2135
2136 double lval = left.isNumber() ? left.asDouble() : left.toNumberImpl();
2137 double rval = right.isNumber() ? right.asDouble() : right.toNumberImpl();
2138
2139 return Value::fromDouble(d: lval - rval).asReturnedValue();
2140}
2141
2142ReturnedValue Runtime::Mul::call(const Value &left, const Value &right)
2143{
2144 TRACE2(left, right);
2145
2146 if (Q_LIKELY(left.integerCompatible() && right.integerCompatible()))
2147 return mul_int32(a: left.integerValue(), b: right.integerValue());
2148
2149 double lval = left.isNumber() ? left.asDouble() : left.toNumberImpl();
2150 double rval = right.isNumber() ? right.asDouble() : right.toNumberImpl();
2151
2152 return Value::fromDouble(d: lval * rval).asReturnedValue();
2153}
2154
2155ReturnedValue Runtime::Div::call(const Value &left, const Value &right)
2156{
2157 TRACE2(left, right);
2158
2159 if (Value::integerCompatible(a: left, b: right)) {
2160 int lval = left.integerValue();
2161 int rval = right.integerValue();
2162 if (rval != 0 // division by zero should result in a NaN
2163 && !(lval == std::numeric_limits<int>::min() && rval == -1) // doesn't fit in int
2164 && (lval % rval == 0) // fractions can't be stored in an int
2165 && !(lval == 0 && rval < 0)) // 0 / -something results in -0.0
2166 return Encode(int(lval / rval));
2167 else
2168 return Encode(double(lval) / rval);
2169 }
2170
2171 double lval = left.toNumber();
2172 double rval = right.toNumber();
2173 return Value::fromDouble(d: lval / rval).asReturnedValue();
2174}
2175
2176ReturnedValue Runtime::Mod::call(const Value &left, const Value &right)
2177{
2178 TRACE2(left, right);
2179
2180 if (Value::integerCompatible(a: left, b: right) && left.integerValue() >= 0 && right.integerValue() > 0) {
2181 // special cases are handled by fmod, among them:
2182 // - arithmic execeptions for ints in c++, eg: INT_MIN % -1
2183 // - undefined behavior in c++, e.g.: anything % 0
2184 // - uncommon cases which would complicate the condition, e.g.: negative integers
2185 // (this makes sure that -1 % 1 == -0 by passing it to fmod)
2186 return Encode(left.integerValue() % right.integerValue());
2187 }
2188
2189 double lval = RuntimeHelpers::toNumber(value: left);
2190 double rval = RuntimeHelpers::toNumber(value: right);
2191#ifdef fmod
2192# undef fmod
2193#endif
2194 return Value::fromDouble(d: std::fmod(x: lval, y: rval)).asReturnedValue();
2195}
2196
2197ReturnedValue Runtime::Exp::call(const Value &base, const Value &exp)
2198{
2199 double b = base.toNumber();
2200 double e = exp.toNumber();
2201 return Encode(QQmlPrivate::jsExponentiate(base: b, exponent: e));
2202}
2203
2204ReturnedValue Runtime::BitAnd::call(const Value &left, const Value &right)
2205{
2206 TRACE2(left, right);
2207
2208 int lval = left.toInt32();
2209 int rval = right.toInt32();
2210 return Encode((int)(lval & rval));
2211}
2212
2213ReturnedValue Runtime::BitOr::call(const Value &left, const Value &right)
2214{
2215 TRACE2(left, right);
2216
2217 int lval = left.toInt32();
2218 int rval = right.toInt32();
2219 return Encode((int)(lval | rval));
2220}
2221
2222ReturnedValue Runtime::BitXor::call(const Value &left, const Value &right)
2223{
2224 TRACE2(left, right);
2225
2226 int lval = left.toInt32();
2227 int rval = right.toInt32();
2228 return Encode((int)(lval ^ rval));
2229}
2230
2231ReturnedValue Runtime::Shl::call(const Value &left, const Value &right)
2232{
2233 TRACE2(left, right);
2234
2235 int lval = left.toInt32();
2236 int rval = right.toInt32() & 0x1f;
2237 return Encode((int)(lval << rval));
2238}
2239
2240ReturnedValue Runtime::Shr::call(const Value &left, const Value &right)
2241{
2242 TRACE2(left, right);
2243
2244 int lval = left.toInt32();
2245 unsigned rval = right.toUInt32() & 0x1f;
2246 return Encode((int)(lval >> rval));
2247}
2248
2249ReturnedValue Runtime::UShr::call(const Value &left, const Value &right)
2250{
2251 TRACE2(left, right);
2252
2253 unsigned lval = left.toUInt32();
2254 unsigned rval = right.toUInt32() & 0x1f;
2255 uint res = lval >> rval;
2256
2257 return Encode(res);
2258}
2259
2260ReturnedValue Runtime::GreaterThan::call(const Value &left, const Value &right)
2261{
2262 TRACE2(left, right);
2263
2264 bool r = CompareGreaterThan::call(l: left, r: right);
2265 return Encode(r);
2266}
2267
2268ReturnedValue Runtime::LessThan::call(const Value &left, const Value &right)
2269{
2270 TRACE2(left, right);
2271
2272 bool r = CompareLessThan::call(l: left, r: right);
2273 return Encode(r);
2274}
2275
2276ReturnedValue Runtime::GreaterEqual::call(const Value &left, const Value &right)
2277{
2278 TRACE2(left, right);
2279
2280 bool r = CompareGreaterEqual::call(l: left, r: right);
2281 return Encode(r);
2282}
2283
2284ReturnedValue Runtime::LessEqual::call(const Value &left, const Value &right)
2285{
2286 TRACE2(left, right);
2287
2288 bool r = CompareLessEqual::call(l: left, r: right);
2289 return Encode(r);
2290}
2291
2292struct LazyScope
2293{
2294 ExecutionEngine *engine = nullptr;
2295 Value *stackMark = nullptr;
2296 ~LazyScope() {
2297 if (engine)
2298 engine->jsStackTop = stackMark;
2299 }
2300 template <typename T>
2301 void set(Value **scopedValue, T value, ExecutionEngine *e) {
2302 if (!engine) {
2303 engine = e;
2304 stackMark = engine->jsStackTop;
2305 }
2306 if (!*scopedValue)
2307 *scopedValue = e->jsAlloca(nValues: 1);
2308 **scopedValue = value;
2309 }
2310};
2311
2312Bool Runtime::CompareEqual::call(const Value &left, const Value &right)
2313{
2314 TRACE2(left, right);
2315
2316 Value lhs = left;
2317 Value rhs = right;
2318
2319 LazyScope scope;
2320 Value *lhsGuard = nullptr;
2321 Value *rhsGuard = nullptr;
2322
2323 redo:
2324 if (lhs.asReturnedValue() == rhs.asReturnedValue())
2325 return !lhs.isNaN();
2326
2327 quint32 lt = lhs.quickType();
2328 quint32 rt = rhs.quickType();
2329
2330 // LHS: Check if managed
2331 if ((lt & (Value::ManagedMask >> Value::Tag_Shift)) == 0) {
2332 if (lhs.isUndefined())
2333 return rhs.isNullOrUndefined();
2334
2335 // RHS: Check if managed
2336 if ((rt & (Value::ManagedMask >> Value::Tag_Shift)) == 0) {
2337 if (rhs.isUndefined())
2338 return false;
2339
2340 Heap::Base *l = lhs.m();
2341 Heap::Base *r = rhs.m();
2342 Q_ASSERT(l);
2343 Q_ASSERT(r);
2344 if (l->internalClass->vtable->isStringOrSymbol == r->internalClass->vtable->isStringOrSymbol)
2345 return static_cast<QV4::Managed &>(lhs).isEqualTo(other: &static_cast<QV4::Managed &>(rhs));
2346 if (l->internalClass->vtable->isStringOrSymbol) {
2347 scope.set(scopedValue: &rhsGuard, value: RuntimeHelpers::objectDefaultValue(object: &static_cast<QV4::Object &>(rhs), typeHint: PREFERREDTYPE_HINT), e: r->internalClass->engine);
2348 rhs = rhsGuard->asReturnedValue();
2349 goto redo;
2350 } else {
2351 Q_ASSERT(r->internalClass->vtable->isStringOrSymbol);
2352 scope.set(scopedValue: &lhsGuard, value: RuntimeHelpers::objectDefaultValue(object: &static_cast<QV4::Object &>(lhs), typeHint: PREFERREDTYPE_HINT), e: l->internalClass->engine);
2353 lhs = lhsGuard->asReturnedValue();
2354 goto redo;
2355 }
2356 return false;
2357 }
2358
2359lhs_managed_and_rhs_not:
2360 switch (rt) {
2361 case QV4::Value::QT_Empty:
2362 Q_UNREACHABLE();
2363 case QV4::Value::QT_Null:
2364 return false;
2365 case QV4::Value::QT_Bool:
2366 case QV4::Value::QT_Int:
2367 rhs = Value::fromDouble(d: rhs.int_32());
2368 Q_FALLTHROUGH();
2369 default: // double
2370 if (lhs.m()->internalClass->vtable->isStringOrSymbol) {
2371 return lhs.m()->internalClass->vtable->isString ? (RuntimeHelpers::toNumber(value: lhs) == rhs.doubleValue()) : false;
2372 } else {
2373 scope.set(scopedValue: &lhsGuard, value: RuntimeHelpers::objectDefaultValue(object: &static_cast<QV4::Object &>(lhs), typeHint: PREFERREDTYPE_HINT), e: lhs.m()->internalClass->engine);
2374 lhs = lhsGuard->asReturnedValue();
2375 }
2376 }
2377 goto redo;
2378 } else if ((rt & (Value::ManagedMask >> Value::Tag_Shift)) == 0) {
2379 if (rhs.isUndefined())
2380 return lhs.isNull(); // Can't be undefined
2381 qSwap(value1&: lhs, value2&: rhs);
2382 qSwap(value1&: lt, value2&: rt);
2383 goto lhs_managed_and_rhs_not;
2384 }
2385
2386 switch (lt) {
2387 case QV4::Value::QT_Empty:
2388 Q_UNREACHABLE();
2389 case QV4::Value::QT_Null:
2390 return rhs.isNull();
2391 case QV4::Value::QT_Bool:
2392 case QV4::Value::QT_Int:
2393 switch (rt) {
2394 case QV4::Value::QT_Empty:
2395 Q_UNREACHABLE();
2396 case QV4::Value::QT_Null:
2397 return false;
2398 case QV4::Value::QT_Bool:
2399 case QV4::Value::QT_Int:
2400 return lhs.int_32() == rhs.int_32();
2401 default: // double
2402 return lhs.int_32() == rhs.doubleValue();
2403 }
2404 default: // double
2405 switch (rt) {
2406 case QV4::Value::QT_Empty:
2407 Q_UNREACHABLE();
2408 case QV4::Value::QT_Null:
2409 return false;
2410 case QV4::Value::QT_Bool:
2411 case QV4::Value::QT_Int:
2412 return lhs.doubleValue() == rhs.int_32();
2413 default: // double
2414 return lhs.doubleValue() == rhs.doubleValue();
2415 }
2416 }
2417}
2418
2419ReturnedValue Runtime::Equal::call(const Value &left, const Value &right)
2420{
2421 TRACE2(left, right);
2422
2423 bool r = CompareEqual::call(left, right);
2424 return Encode(r);
2425}
2426
2427ReturnedValue Runtime::NotEqual::call(const Value &left, const Value &right)
2428{
2429 TRACE2(left, right);
2430
2431 bool r = !CompareEqual::call(left, right);
2432 return Encode(r);
2433}
2434
2435ReturnedValue Runtime::StrictEqual::call(const Value &left, const Value &right)
2436{
2437 TRACE2(left, right);
2438
2439 bool r = RuntimeHelpers::strictEqual(x: left, y: right);
2440 return Encode(r);
2441}
2442
2443ReturnedValue Runtime::StrictNotEqual::call(const Value &left, const Value &right)
2444{
2445 TRACE2(left, right);
2446
2447 bool r = ! RuntimeHelpers::strictEqual(x: left, y: right);
2448 return Encode(r);
2449}
2450
2451Bool Runtime::CompareNotEqual::call(const Value &left, const Value &right)
2452{
2453 TRACE2(left, right);
2454
2455 return !Runtime::CompareEqual::call(left, right);
2456}
2457
2458Bool Runtime::CompareStrictEqual::call(const Value &left, const Value &right)
2459{
2460 TRACE2(left, right);
2461
2462 return RuntimeHelpers::strictEqual(x: left, y: right);
2463}
2464
2465Bool Runtime::CompareStrictNotEqual::call(const Value &left, const Value &right)
2466{
2467 TRACE2(left, right);
2468
2469 return ! RuntimeHelpers::strictEqual(x: left, y: right);
2470}
2471
2472template<typename Operation>
2473static inline const void *symbol()
2474{
2475 return reinterpret_cast<void *>(&Operation::call);
2476}
2477
2478QHash<const void *, const char *> Runtime::symbolTable()
2479{
2480 static const QHash<const void *, const char *> symbols({
2481#ifndef V4_BOOTSTRAP
2482 {symbol<CallGlobalLookup>(), "CallGlobalLookup" },
2483 {symbol<CallQmlContextPropertyLookup>(), "CallQmlContextPropertyLookup" },
2484 {symbol<CallName>(), "CallName" },
2485 {symbol<CallProperty>(), "CallProperty" },
2486 {symbol<CallPropertyLookup>(), "CallPropertyLookup" },
2487 {symbol<CallValue>(), "CallValue" },
2488 {symbol<CallWithReceiver>(), "CallWithReceiver" },
2489 {symbol<CallPossiblyDirectEval>(), "CallPossiblyDirectEval" },
2490 {symbol<CallWithSpread>(), "CallWithSpread" },
2491 {symbol<TailCall>(), "TailCall" },
2492
2493 {symbol<Construct>(), "Construct" },
2494 {symbol<ConstructWithSpread>(), "ConstructWithSpread" },
2495
2496 {symbol<StoreNameStrict>(), "StoreNameStrict" },
2497 {symbol<StoreNameSloppy>(), "StoreNameSloppy" },
2498 {symbol<StoreProperty>(), "StoreProperty" },
2499 {symbol<StoreElement>(), "StoreElement" },
2500 {symbol<LoadProperty>(), "LoadProperty" },
2501 {symbol<LoadName>(), "LoadName" },
2502 {symbol<LoadElement>(), "LoadElement" },
2503 {symbol<LoadSuperProperty>(), "LoadSuperProperty" },
2504 {symbol<StoreSuperProperty>(), "StoreSuperProperty" },
2505 {symbol<LoadSuperConstructor>(), "LoadSuperConstructor" },
2506 {symbol<LoadGlobalLookup>(), "LoadGlobalLookup" },
2507 {symbol<LoadQmlContextPropertyLookup>(), "LoadQmlContextPropertyLookup" },
2508 {symbol<GetLookup>(), "GetLookup" },
2509 {symbol<SetLookupStrict>(), "SetLookupStrict" },
2510 {symbol<SetLookupSloppy>(), "SetLookupSloppy" },
2511
2512 {symbol<TypeofValue>(), "TypeofValue" },
2513 {symbol<TypeofName>(), "TypeofName" },
2514
2515 {symbol<DeleteProperty_NoThrow>(), "DeleteProperty_NoThrow" },
2516 {symbol<DeleteProperty>(), "DeleteProperty" },
2517 {symbol<DeleteName_NoThrow>(), "DeleteName_NoThrow" },
2518 {symbol<DeleteName>(), "DeleteName" },
2519
2520 {symbol<ThrowException>(), "ThrowException" },
2521 {symbol<PushCallContext>(), "PushCallContext" },
2522 {symbol<PushWithContext>(), "PushWithContext" },
2523 {symbol<PushCatchContext>(), "PushCatchContext" },
2524 {symbol<PushBlockContext>(), "PushBlockContext" },
2525 {symbol<CloneBlockContext>(), "CloneBlockContext" },
2526 {symbol<PushScriptContext>(), "PushScriptContext" },
2527 {symbol<PopScriptContext>(), "PopScriptContext" },
2528 {symbol<ThrowReferenceError>(), "ThrowReferenceError" },
2529 {symbol<ThrowOnNullOrUndefined>(), "ThrowOnNullOrUndefined" },
2530
2531 {symbol<Closure>(), "Closure" },
2532
2533 {symbol<MarkCustom>(), "MarkCustom"},
2534
2535 {symbol<ConvertThisToObject>(), "ConvertThisToObject" },
2536 {symbol<DeclareVar>(), "DeclareVar" },
2537 {symbol<CreateMappedArgumentsObject>(), "CreateMappedArgumentsObject" },
2538 {symbol<CreateUnmappedArgumentsObject>(), "CreateUnmappedArgumentsObject" },
2539 {symbol<CreateRestParameter>(), "CreateRestParameter" },
2540
2541 {symbol<ArrayLiteral>(), "ArrayLiteral" },
2542 {symbol<ObjectLiteral>(), "ObjectLiteral" },
2543 {symbol<CreateClass>(), "CreateClass" },
2544
2545 {symbol<GetIterator>(), "GetIterator" },
2546 {symbol<IteratorNext>(), "IteratorNext" },
2547 {symbol<IteratorNextForYieldStar>(), "IteratorNextForYieldStar" },
2548 {symbol<IteratorClose>(), "IteratorClose" },
2549 {symbol<DestructureRestElement>(), "DestructureRestElement" },
2550
2551 {symbol<ToObject>(), "ToObject" },
2552 {symbol<ToBoolean>(), "ToBoolean" },
2553 {symbol<ToNumber>(), "ToNumber" },
2554
2555 {symbol<UMinus>(), "UMinus" },
2556
2557 {symbol<Instanceof>(), "Instanceof" },
2558 {symbol<As>(), "As" },
2559 {symbol<In>(), "In" },
2560 {symbol<Add>(), "Add" },
2561 {symbol<Sub>(), "Sub" },
2562 {symbol<Mul>(), "Mul" },
2563 {symbol<Div>(), "Div" },
2564 {symbol<Mod>(), "Mod" },
2565 {symbol<Exp>(), "Exp" },
2566 {symbol<BitAnd>(), "BitAnd" },
2567 {symbol<BitOr>(), "BitOr" },
2568 {symbol<BitXor>(), "BitXor" },
2569 {symbol<Shl>(), "Shl" },
2570 {symbol<Shr>(), "Shr" },
2571 {symbol<UShr>(), "UShr" },
2572 {symbol<GreaterThan>(), "GreaterThan" },
2573 {symbol<LessThan>(), "LessThan" },
2574 {symbol<GreaterEqual>(), "GreaterEqual" },
2575 {symbol<LessEqual>(), "LessEqual" },
2576 {symbol<Equal>(), "Equal" },
2577 {symbol<NotEqual>(), "NotEqual" },
2578 {symbol<StrictEqual>(), "StrictEqual" },
2579 {symbol<StrictNotEqual>(), "StrictNotEqual" },
2580
2581 {symbol<CompareGreaterThan>(), "CompareGreaterThan" },
2582 {symbol<CompareLessThan>(), "CompareLessThan" },
2583 {symbol<CompareGreaterEqual>(), "CompareGreaterEqual" },
2584 {symbol<CompareLessEqual>(), "CompareLessEqual" },
2585 {symbol<CompareEqual>(), "CompareEqual" },
2586 {symbol<CompareNotEqual>(), "CompareNotEqual" },
2587 {symbol<CompareStrictEqual>(), "CompareStrictEqual" },
2588 {symbol<CompareStrictNotEqual>(), "CompareStrictNotEqual" },
2589
2590 {symbol<CompareInstanceof>(), "CompareInstanceOf" },
2591 {symbol<CompareIn>(), "CompareIn" },
2592
2593 {symbol<RegexpLiteral>(), "RegexpLiteral" },
2594 {symbol<GetTemplateObject>(), "GetTemplateObject" }
2595#endif
2596 });
2597
2598 return symbols;
2599}
2600
2601} // namespace QV4
2602
2603QT_END_NAMESPACE
2604

source code of qtdeclarative/src/qml/jsruntime/qv4runtime.cpp