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

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