Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qv4runtime.cpp
Go to the documentation of this file.
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// Qt-Security score:significant
4
5#include "qv4runtime_p.h"
6
7#include <private/qqmlengine_p.h>
8#include <private/qqmljavascriptexpression_p.h>
9#include <private/qqmljsast_p.h>
10#include <private/qqmltypewrapper_p.h>
11#include <private/qqmlvaluetypewrapper_p.h>
12#include <private/qv4argumentsobject_p.h>
13#include <private/qv4engine_p.h>
14#include <private/qv4function_p.h>
15#include <private/qv4generatorobject_p.h>
16#include <private/qv4global_p.h>
17#include <private/qv4globalobject_p.h>
18#include <private/qv4jscall_p.h>
19#include <private/qv4lookup_p.h>
20#include <private/qv4math_p.h>
21#include <private/qv4object_p.h>
22#include <private/qv4qmlcontext_p.h>
23#include <private/qv4qobjectwrapper_p.h>
24#include <private/qv4regexp_p.h>
25#include <private/qv4regexpobject_p.h>
26#include <private/qv4scopedvalue_p.h>
27#include <private/qv4stackframe_p.h>
28#include <private/qv4symbol_p.h>
29
30#include <wtf/MathExtras.h>
31
32#include <QtCore/private/qlocale_tools_p.h>
33#include <QtCore/qdebug.h>
34
35#ifdef QV4_COUNT_RUNTIME_FUNCTIONS
36# include <QtCore/qbuffer.h>
37#endif // QV4_COUNT_RUNTIME_FUNCTIONS
38
39#include <cassert>
40#include <cstdio>
41#include <stdlib.h>
42
44
45Q_STATIC_LOGGING_CATEGORY(lcCoercingTypeAssertion, "qt.qml.coercingTypeAssertion");
46
47namespace QV4 {
48
49#ifdef QV4_COUNT_RUNTIME_FUNCTIONS
50struct RuntimeCounters::Data {
51 enum Type {
52 None = 0,
53 Undefined = 1,
54 Null = 2,
55 Boolean = 3,
56 Integer = 4,
57 Managed = 5,
58 Double = 7
59 };
60
61 static const char *pretty(Type t) {
62 switch (t) {
63 case None: return "";
64 case Undefined: return "Undefined";
65 case Null: return "Null";
66 case Boolean: return "Boolean";
67 case Integer: return "Integer";
68 case Managed: return "Managed";
69 case Double: return "Double";
70 default: return "Unknown";
71 }
72 }
73
74 static unsigned mangle(unsigned tag) {
75 switch (tag) {
76 case Value::Undefined_Type: return Undefined;
77 case Value::Null_Type: return Null;
78 case Value::Boolean_Type: return Boolean;
79 case Value::Integer_Type: return Integer;
80 case Value::Managed_Type: return Managed;
81 default: return Double;
82 }
83 }
84
85 static unsigned mangle(unsigned tag1, unsigned tag2) {
86 return (mangle(tag1) << 3) | mangle(tag2);
87 }
88
89 static void unmangle(unsigned signature, Type &tag1, Type &tag2) {
90 tag1 = Type((signature >> 3) & 7);
91 tag2 = Type(signature & 7);
92 }
93
94 typedef QList<quint64> Counters;
95 QHash<const char *, Counters> counters;
96
97 inline void count(const char *func) {
99 if (cnt.isEmpty())
100 cnt.resize(64);
101 cnt[0] += 1;
102 }
103
104 inline void count(const char *func, unsigned tag) {
106 if (cnt.isEmpty())
107 cnt.resize(64);
108 cnt[mangle(tag)] += 1;
109 }
110
111 inline void count(const char *func, unsigned tag1, unsigned tag2) {
113 if (cnt.isEmpty())
114 cnt.resize(64);
115 cnt[mangle(tag1, tag2)] += 1;
116 }
117
118 struct Line {
119 const char *func;
120 Type tag1, tag2;
122
123 static bool less(const Line &line1, const Line &line2) {
124 return line1.count > line2.count;
125 }
126 };
127
128 void dump() const {
129 QBuffer buf;
133 for (auto it = counters.cbegin(), end = counters.cend(); it != end; ++it) {
134 const Counters &fCount = it.value();
135 for (int i = 0, ei = fCount.size(); i != ei; ++i) {
137 if (!count)
138 continue;
139 Line line;
140 line.func = it.key();
142 line.count = count;
144 }
145 }
146 std::sort(lines.begin(), lines.end(), Line::less);
147 outs << lines.size() << " counters:" << endl;
148 for (const Line &line : std::as_const(lines))
150 << " | " << line.func
151 << " | " << pretty(line.tag1)
152 << " | " << pretty(line.tag2)
153 << endl;
154 qDebug("%s", buf.data().constData());
155 }
156};
157
161 : d(new Data)
162{
163 if (!instance)
164 instance = this;
165}
166
168{
169 d->dump();
170 delete d;
171}
172
173void RuntimeCounters::count(const char *func)
174{
175 d->count(func);
176}
177
178void RuntimeCounters::count(const char *func, uint tag)
179{
180 d->count(func, tag);
181}
182
183void RuntimeCounters::count(const char *func, uint tag1, uint tag2)
184{
185 d->count(func, tag1, tag2);
186}
187
188#endif // QV4_COUNT_RUNTIME_FUNCTIONS
189
190static QV4::Lookup *runtimeLookup(Function *f, uint i)
191{
192 return f->executableCompilationUnit()->runtimeLookups + i;
193}
194
196{
198
199 if (std::isnan(num)) {
200 *result = QStringLiteral("NaN");
201 return;
202 } else if (qt_is_inf(num)) {
203 *result = num < 0 ? QStringLiteral("-Infinity") : QStringLiteral("Infinity");
204 return;
205 }
206
207 if (radix == 10) {
208 // We cannot use our usual locale->toString(...) here, because EcmaScript has special rules
209 // about the longest permissible number, depending on if it's <0 or >0.
210 const int ecma_shortest_low = -6;
211 const int ecma_shortest_high = 21;
212
213 const QLatin1Char zero('0');
214 const QLatin1Char dot('.');
215
216 int decpt = 0;
217 int sign = 0;
218 *result = qdtoa(num, &decpt, &sign);
219
221 if (result->size() > 1)
222 result->insert(1, dot);
224 if (decpt > 0)
227 } else if (decpt <= 0) {
229 } else if (decpt < result->size()) {
231 } else {
233 }
234
235 if (sign && num)
237
238 return;
239 }
240
241 result->clear();
242 bool negative = false;
243
244 if (num < 0) {
245 negative = true;
246 num = -num;
247 }
248
249 double frac = num - ::floor(num);
251
252 do {
253 char c = (char)::fmod(num, radix);
254 c = (c < 10) ? (c + '0') : (c - 10 + 'a');
256 num = ::floor(num / radix);
257 } while (num != 0);
258
259 if (frac != 0) {
261 double magnitude = 1;
262 double next = frac;
263 do {
264 next *= radix;
265 const int floored = ::floor(next);
266 char c = char(floored);
267 c = (c < 10) ? (c + '0') : (c - 10 + 'a');
269 magnitude /= radix;
270 frac -= double(floored) * magnitude;
271 next -= double(floored);
272
273 // The next digit still makes a difference
274 // if a value of "radix" for it would change frac.
275 // Otherwise we've reached the limit of numerical precision.
276 } while (frac > 0 && frac - magnitude != frac);
277 }
278
279 if (negative)
281}
282
284{
287 Q_ASSERT(clos);
289 Scope s(engine);
291
292 if (clos->isGenerator())
294 else
296 // ### TODO: only keep reference to scripts if actually needed; see QTBUG-130795
298 if (callingQmlContext) {
299 // ### TODO: It would be more efficient to use custom Function prototypes instead of setting a property
300 // ==> QTBUG-130798
304 /* we can use fromReturnedValue here, as isNullOrUndefined doesn't allocate.
305 fetching importedScripts twvice is less overhead than unconditionally creating a scope,
306 as script imports are rather rare*/
308 QV4::ScopedString name(s, engine->newString(QLatin1StringView("$importedScripts")));
311 }
312 }
313 return closure.asReturnedValue();
314}
315
329
331{
333 if (function->isStrict())
335 return Encode(false);
336 } else {
337 return Encode(true);
338 }
339}
340
347
349{
351 if (function->isStrict())
353 return Encode(false);
354 } else {
355 return Encode(true);
356 }
357}
358
359static QV4::ReturnedValue doInstanceof(ExecutionEngine *engine, const Value &lval, const Value &rval)
360{
361 // 11.8.6, 5: rval must be an Object
362 const Object *rhs = rval.as<Object>();
363 if (!rhs)
364 return engine->throwTypeError();
365
366 const FunctionObject *f = rhs->as<FunctionObject>();
367 // shortcut hasInstance evaluation. In this case we know that we are calling the regular hasInstance()
368 // method of the FunctionPrototype
369 if (f && f->d()->prototype() == engine->functionPrototype()->d() && !f->hasHasInstanceProperty())
370 return Object::checkedInstanceOf(engine, f, lval);
371
372 Scope scope(engine);
373 ScopedValue hasInstance(scope, rhs->get(engine->symbol_hasInstance()));
374 if (hasInstance->isUndefined())
375 return Encode(rhs->instanceOf(lval));
376
377 FunctionObject *fHasInstance = hasInstance->as<FunctionObject>();
378 if (!fHasInstance)
379 return engine->throwTypeError();
380
381 return Encode(fHasInstance->call(&rval, &lval, 1));
382}
383
390
392{
395
396 if (scope.hasException()) {
397 // "foo instanceof valueType" must not throw an exception.
398 // So this can only be an object type.
400 return Encode::null();
401 }
402
403 if (result->toBoolean())
404 return lval.asReturnedValue();
405 else if (result->isBoolean())
406 return Encode::null();
407
409 return Encode::undefined();
410
411 // Try to convert the value type
413 if (!typeWrapper)
414 return Encode::undefined();
415
416 const auto *stackFrame = engine->currentStackFrame;
419 << stackFrame->source() << ':' << stackFrame->lineNumber() << ':'
420 << " Coercing between incompatible value types mistakenly yields null rather than"
421 << " undefined. Add 'pragma ValueTypeBehavior: Assertable' to prevent this.";
422 return Encode::null();
423 }
424
425 if (lval.as<QV4::QObjectWrapper>()) {
427 << stackFrame->source() << ':' << stackFrame->lineNumber() << ':'
428 << " Coercing from instances of object types to value types mistakenly yields null"
429 << " rather than undefined. Add 'pragma ValueTypeBehavior: Assertable' to prevent"
430 << " this.";
431 return Encode::null();
432 }
433
434 result = coerce(engine, lval, typeWrapper->d()->type(), false);
435 if (result->isUndefined())
436 return Encode::undefined();
437
439 << stackFrame->source() << ':' << stackFrame->lineNumber() << ':'
440 << " Coercing a value to " << typeWrapper->toQStringNoThrow()
441 << " using a type assertion. This behavior is deprecated."
442 << " Add 'pragma ValueTypeBehavior: Assertable' to prevent it.";
443 return result->asReturnedValue();
444}
445
447{
449 if (!ro)
450 return engine->throwTypeError();
453 if (scope.hasException())
454 return Encode::undefined();
455 bool r = ro->hasProperty(s);
456 return Encode(r);
457}
458
460{
461 // The actual maximum valid length is certainly shorter, but due to the sheer number of
462 // different number formatting variants, we rather err on the side of caution here.
463 // For example, you can have up to 772 valid decimal digits left of the dot, as stated in the
464 // libdoubleconversion sources. The same maximum value would be represented by roughly 3.5 times
465 // as many binary digits.
466 const int excessiveLength = 16 * 1024;
468 return qQNaN();
469
471 if (s.startsWith(QLatin1Char('0'))) {
472 int base = -1;
473 if (s.startsWith(QLatin1String("0x")) || s.startsWith(QLatin1String("0X")))
474 base = 16;
475 else if (s.startsWith(QLatin1String("0o")) || s.startsWith(QLatin1String("0O")))
476 base = 8;
477 else if (s.startsWith(QLatin1String("0b")) || s.startsWith(QLatin1String("0B")))
478 base = 2;
479 if (base > 0) {
480 bool ok = true;
482 num = s.mid(2).toLongLong(&ok, base);
483 if (!ok)
484 return qQNaN();
485 return num;
486 }
487 }
488 bool ok = false;
490 const char *begin = ba.constData();
491 const char *end = nullptr;
492 double d = qstrtod(begin, &end, &ok);
493 if (end - begin != ba.size()) {
494 if (ba == "Infinity" || ba == "+Infinity")
495 d = Q_INFINITY;
496 else if (ba == "-Infinity")
497 d = -Q_INFINITY;
498 else
499 d = std::numeric_limits<double>::quiet_NaN();
500 }
501 return d;
502}
503
510
512{
514 if (engine->hasException)
515 return Encode::undefined();
516
517 String *hint;
518 switch (typeHint) {
519 case STRING_HINT:
520 hint = engine->id_string();
521 break;
522 case NUMBER_HINT:
523 hint = engine->id_number();
524 break;
525 default:
527 break;
528 }
529
533 engine->throwTypeError(QStringLiteral("Value for symbol toPrimitive is not a function"));
534 return Encode::undefined();
535 }
536
538 if (toPrimitive) {
540 if (engine->hasException)
541 return Encode::undefined();
542 if (!result->isPrimitive())
543 return engine->throwTypeError();
544 return result->asReturnedValue();
545 }
546
547 if (hint == engine->id_default())
548 hint = engine->id_number();
550}
551
552
554{
556
559
561 qSwap(meth1, meth2);
562 } else {
564 }
565
568
571 result = o->call(object, nullptr, 0);
572 if (engine->hasException)
573 return Encode::undefined();
574 if (result->isPrimitive())
575 return result->asReturnedValue();
576 }
577
578 if (engine->hasException)
579 return Encode::undefined();
580
581 conv = object->get(meth2);
583 result = o->call(object, nullptr, 0);
584 if (engine->hasException)
585 return Encode::undefined();
586 if (result->isPrimitive())
587 return result->asReturnedValue();
588 }
589
590 return engine->throwTypeError();
591}
592
593
595{
597 switch (value.type()) {
598 case Value::Undefined_Type:
599 engine->throwTypeError(QLatin1String("Value is undefined and could not be converted to an object"));
600 return nullptr;
601 case Value::Null_Type:
602 engine->throwTypeError(QLatin1String("Value is null and could not be converted to an object"));
603 return nullptr;
604 case Value::Boolean_Type:
606 case Value::Managed_Type:
608 if (!value.isString())
611 case Value::Integer_Type:
612 default: // double
614 }
615}
616
618{
619 redo:
620 switch (value.type()) {
621 case Value::Empty_Type:
622 Q_ASSERT(!"empty Value encountered");
624 case Value::Undefined_Type:
625 return engine->id_undefined()->d();
626 case Value::Null_Type:
627 return engine->id_null()->d();
628 case Value::Boolean_Type:
629 if (value.booleanValue())
630 return engine->id_true()->d();
631 else
632 return engine->id_false()->d();
633 case Value::Managed_Type: {
634 if (value.isString())
635 return static_cast<const String &>(value).d();
636 if (value.isSymbol()) {
637 engine->throwTypeError(QLatin1String("Cannot convert a symbol to a string."));
638 return nullptr;
639 }
642 if (value.isString())
643 return static_cast<const String &>(value).d();
644 goto redo;
645 }
646 case Value::Integer_Type:
648 default: // double
650 } // switch
651}
652
653// This is slightly different from the method above, as
654// the + operator requires a slightly different conversion
655static Heap::String *convert_to_string_add(ExecutionEngine *engine, Value value)
656{
657 return RuntimeHelpers::convertToString(engine, value, PREFERREDTYPE_HINT);
658}
659
661{
663
668 if (sleft || sright) {
669 if (!sleft) {
671 sleft = static_cast<String *>(pleft.ptr);
672 }
673 if (!sright) {
675 sright = static_cast<String *>(pright.ptr);
676 }
677 if (engine->hasException)
678 return Encode::undefined();
679 if (!sleft->d()->length())
680 return sright->asReturnedValue();
681 if (!sright->d()->length())
682 return sleft->asReturnedValue();
684 return (mm->alloc<ComplexString>(sleft->d(), sright->d()))->asReturnedValue();
685 }
686 double x = RuntimeHelpers::toNumber(pleft);
687 double y = RuntimeHelpers::toNumber(pright);
688 return Encode(x + y);
689}
690
695
712
713static Q_NEVER_INLINE ReturnedValue getElementIntFallback(ExecutionEngine *engine, const Value &object, uint idx)
714{
717
719 if (!o) {
720 if (const String *str = object.as<String>()) {
721 if (idx >= (uint)str->toQString().size()) {
722 return Encode::undefined();
723 }
724 const QString s = str->toQString().mid(idx, 1);
726 }
727
729 QString message = QStringLiteral("Cannot read property '%1' of %2").arg(idx).arg(object.toQStringNoThrow());
731 }
732
734 Q_ASSERT(!!o); // can't fail as null/undefined is covered above
735 }
736
737 if (o->arrayData() && !o->arrayData()->attrs) {
739 if (!v->isEmpty())
740 return v->asReturnedValue();
741 }
742
743 return o->get(idx);
744}
745
746static Q_NEVER_INLINE ReturnedValue getElementFallback(ExecutionEngine *engine, const Value &object, const Value &index)
747{
749
751
753 if (!o) {
755 const QString message =
756 QStringLiteral("Cannot read property '%1' of %2")
759 }
760
762 Q_ASSERT(!!o); // can't fail as null/undefined is covered above
763 }
764
766 if (scope.hasException())
767 return Encode::undefined();
768 return o->get(name);
769}
770
772{
773 if (index.isPositiveInt()) {
774 uint idx = static_cast<uint>(index.int_32());
775 if (Heap::Base *b = object.heapObject()) {
777 Heap::Object *o = static_cast<Heap::Object *>(b);
778 if (o->arrayData && o->arrayData->type == Heap::ArrayData::Simple) {
780 if (idx < s->values.size)
781 if (!s->data(idx).isEmpty())
782 return s->data(idx).asReturnedValue();
783 }
784 }
785 }
787 }
788
790}
791
792static Q_NEVER_INLINE bool setElementFallback(ExecutionEngine *engine, const Value &object, const Value &index, const Value &value)
793{
794 Scope scope(engine);
795 ScopedObject o(scope, object);
796 if (!o) {
797 if (engine->currentStackFrame->v4Function->isStrict()) {
798 engine->throwTypeError();
799 return false;
800 }
801
802 o = object.toObject(engine);
803 }
804 if (engine->hasException)
805 return false;
806
807 if (index.isPositiveInt()) {
808 uint idx = static_cast<uint>(index.int_32());
809 if (o->d()->arrayData && o->d()->arrayData->type == Heap::ArrayData::Simple) {
810 Heap::SimpleArrayData *s = o->d()->arrayData.cast<Heap::SimpleArrayData>();
811 if (idx < s->values.size) {
812 s->setData(engine, idx, value);
813 return true;
814 }
815 }
816 return o->put(idx, value);
817 }
818
819 ScopedPropertyKey name(scope, index.toPropertyKey(engine));
820 if (engine->hasException)
821 return false;
822 return o->put(name, value);
823}
824
826{
827 if (index.isPositiveInt()) {
828 uint idx = static_cast<uint>(index.int_32());
829 if (Heap::Base *b = object.heapObject()) {
831 Heap::Object *o = static_cast<Heap::Object *>(b);
832 if (o->arrayData && o->arrayData->type == Heap::ArrayData::Simple) {
834 if (idx < s->values.size) {
836 return;
837 }
838 }
839 }
840 }
841 }
842
845}
846
848{
850 ScopedObject o(scope, (Object *)nullptr);
851 if (!in.isNullOrUndefined())
852 o = in.toObject(engine);
853 if (engine->hasException)
854 return Encode::undefined();
855 if (iterator == static_cast<int>(QQmlJS::AST::ForEachType::Of)) {
856 if (!o)
857 return engine->throwTypeError();
859 if (!f)
860 return engine->throwTypeError();
861 JSCallData cData(o, nullptr, 0);
863 if (engine->hasException)
864 return Encode::undefined();
865 if (!it)
866 return engine->throwTypeError();
867 return it->asReturnedValue();
868 }
870}
871
873{
874 // if we throw an exception from here, return true, not undefined. This is to ensure iteratorDone is set to true
875 // and the stack unwinding won't close the iterator
877
879 ScopedFunctionObject f(scope, static_cast<const Object &>(iterator).get(engine->id_next()));
880 if (!f) {
882 return Encode(true);
883 }
884 JSCallData cData(&iterator, nullptr, 0);
886 if (scope.hasException())
887 return Encode(true);
888 if (!o) {
890 return Encode(true);
891 }
892
894 if (scope.hasException())
895 return Encode(true);
896 bool done = d->toBoolean();
897 if (done) {
898 *value = Encode::undefined();
899 return Encode(true);
900 }
901
902 *value = o->get(engine->id_value());
903 if (scope.hasException())
904 return Encode(true);
905 return Encode(false);
906}
907
909{
910 // the return value encodes how to continue the yield* iteration.
911 // true implies iteration is done, false for iteration to continue
912 // a return value of undefines is a special marker, that the iterator has been invoked with return()
913
916
917 const Value *arg = &received;
918 bool returnCalled = false;
919 FunctionObject *f = nullptr;
920 if (engine->hasException) {
921 if (engine->exceptionValue->isEmpty()) {
922 // generator called with return()
924 engine->hasException = false;
925
926 ScopedValue ret(scope, static_cast<const Object &>(iterator).get(engine->id_return()));
927 if (engine->hasException)
928 return Encode(true);
929 if (ret->isUndefined()) {
930 // propagate return()
931 return Encode::undefined();
932 }
933 returnCalled = true;
934 f = ret->as<FunctionObject>();
935 } else {
936 // generator called with throw
939 engine->hasException = false;
940
941 ScopedValue t(scope, static_cast<const Object &>(iterator).get(engine->id_throw()));
942 if (engine->hasException)
943 return Encode(true);
944 if (t->isUndefined()) {
945 // no throw method on the iterator
947 if (!engine->hasException)
949 return Encode(true);
950 }
951 f = t->as<FunctionObject>();
953 }
954 } else {
955 // generator called with next()
956 ScopedFunctionObject next(scope, static_cast<const Object &>(iterator).get(engine->id_next()));
957 f = next->as<FunctionObject>();
958 }
959
960 if (!f) {
962 return Encode(true);
963 }
964
966 if (scope.hasException())
967 return Encode(true);
968 if (!o) {
970 return Encode(true);
971 }
972
974 if (scope.hasException())
975 return Encode(true);
976 bool done = d->toBoolean();
977 if (done) {
978 *object = o->get(engine->id_value());
979 return (returnCalled && !engine->hasException) ? Encode::undefined() : Encode(true);
980 }
981 *object = o;
982 return Encode(false);
983}
984
986{
988
992 if (hadException) {
994 engine->hasException = false;
995 }
996
997 auto originalCompletion = [=]() {
998 if (hadException) {
1001 }
1002 return Encode::undefined();
1003 };
1004
1005 ScopedValue ret(scope, static_cast<const Object &>(iterator).get(engine->id_return()));
1007 if (!ret->isUndefined()) {
1009 o = f->call(&iterator, nullptr, 0);
1011 return Encode::undefined();
1012 }
1013 if (hadException || ret->isUndefined())
1014 return originalCompletion();
1015
1016 if (!o)
1017 return engine->throwTypeError();
1018
1019 return originalCompletion();
1020}
1021
1023{
1025
1028 array->arrayCreate();
1029 uint index = 0;
1030 while (1) {
1033 if (engine->hasException)
1034 return Encode::undefined();
1036 if (done->booleanValue())
1037 break;
1038 array->arraySet(index, n);
1039 ++index;
1040 }
1041 return array->asReturnedValue();
1042}
1043
1053
1064
1066{
1069
1071 if (o)
1072 return o->get(name);
1073
1074 if (object.isNullOrUndefined()) {
1075 const QString message = QStringLiteral("Cannot read property '%1' of %2")
1077 return engine->throwTypeError(message);
1078 }
1079
1081 if (!o) // type error
1082 return Encode::undefined();
1083 return o->get(name);
1084}
1085
1092
1093static Object *getSuperBase(Scope &scope)
1094{
1095 Scoped<JavaScriptFunctionObject> f(scope);
1096 ScopedObject homeObject(scope);
1097 if (scope.engine->currentStackFrame->isJSTypesFrame()) {
1098 JSTypesStackFrame *frame = static_cast<JSTypesStackFrame *>(
1099 scope.engine->currentStackFrame);
1100
1101 if (frame->jsFrame->thisObject.isEmpty()) {
1102 scope.engine->throwReferenceError(
1103 QStringLiteral("Missing call to super()."), QString(), 0, 0);
1104 return nullptr;
1105 }
1106
1107 f = Value::fromStaticValue(frame->jsFrame->function);
1108 homeObject = f->getHomeObject();
1109 } else {
1110 Q_ASSERT(scope.engine->currentStackFrame->isMetaTypesFrame());
1111 MetaTypesStackFrame *frame = static_cast<MetaTypesStackFrame *>(
1112 scope.engine->currentStackFrame);
1113 if (frame->thisObject() == nullptr) {
1114 scope.engine->throwReferenceError(
1115 QStringLiteral("Missing call to super()."), QString(), 0, 0);
1116 return nullptr;
1117 }
1118 }
1119
1120 if (!homeObject) {
1121 ScopedContext ctx(scope, scope.engine->currentContext());
1122 Q_ASSERT(ctx);
1123 while (ctx) {
1124 if (CallContext *c = ctx->asCallContext()) {
1125 f = c->d()->function;
1126 QV4::Function *fn = f->function();
1127 if (fn && !fn->isArrowFunction() && fn->kind != Function::Eval)
1128 break;
1129 }
1130 ctx = ctx->d()->outer;
1131 }
1132 if (f)
1133 homeObject = f->getHomeObject();
1134 }
1135 if (!homeObject) {
1136 scope.engine->throwTypeError();
1137 return nullptr;
1138 }
1139 Q_ASSERT(homeObject);
1140 ScopedObject proto(scope, homeObject->getPrototypeOf());
1141 if (!proto) {
1142 scope.engine->throwTypeError();
1143 return nullptr;
1144 }
1145 return proto;
1146}
1147
1170
1197
1203
1209
1211{
1213
1214 if (base.isNullOrUndefined()) {
1215 const QString message = QStringLiteral("Cannot read property '%1' of %2")
1218 return engine->throwTypeError(message);
1219 }
1220
1221 return l->getter(engine, base);
1222}
1223
1225{
1228 l->setter(engine, const_cast<Value &>(base), value);
1229}
1230
1232{
1235 if (!l->setter(engine, const_cast<Value &>(base), value))
1237}
1238
1240{
1244 // ### TODO: fix line number
1246 QStringLiteral("super() already called."), QString(), 0, 0);
1247 }
1248 } else {
1251 if (frame->thisObject() != nullptr) {
1252 // ### TODO: fix line number
1254 QStringLiteral("super() already called."), QString(), 0, 0);
1255 }
1256 }
1257
1258 const FunctionObject *f = t.as<FunctionObject>();
1259 if (!f)
1260 return engine->throwTypeError();
1261 Heap::Object *c = static_cast<const Object &>(t).getPrototypeOf();
1262 if (!c->vtable()->callAsConstructor)
1263 return engine->throwTypeError();
1264 return c->asReturnedValue();
1265}
1266
1268{
1269 Q_ASSERT(x.type() != y.type() || (x.isManaged() && (x.isString() != y.isString())));
1270
1271 if (x.isNumber() && y.isNumber())
1272 return x.asDouble() == y.asDouble();
1273 if (x.isNull() && y.isUndefined()) {
1274 return true;
1275 } else if (x.isUndefined() && y.isNull()) {
1276 return true;
1277 } else if (x.isNumber() && y.isString()) {
1278 double dy = RuntimeHelpers::toNumber(y);
1279 return x.asDouble() == dy;
1280 } else if (x.isString() && y.isNumber()) {
1281 double dx = RuntimeHelpers::toNumber(x);
1282 return dx == y.asDouble();
1283 } else if (x.isBoolean()) {
1284 return Runtime::CompareEqual::call(Value::fromDouble((double) x.booleanValue()), y);
1285 } else if (y.isBoolean()) {
1286 return Runtime::CompareEqual::call(x, Value::fromDouble((double) y.booleanValue()));
1287 } else {
1288 Object *xo = x.objectValue();
1289 Object *yo = y.objectValue();
1290 if (yo && (x.isNumber() || x.isString())) {
1291 Scope scope(yo->engine());
1293 return Runtime::CompareEqual::call(x, py);
1294 } else if (xo && (y.isNumber() || y.isString())) {
1295 Scope scope(xo->engine());
1297 return Runtime::CompareEqual::call(px, y);
1298 }
1299 }
1300
1301 return false;
1302}
1303
1305{
1306 TRACE2(x, y);
1307
1308 if (x.rawValue() == y.rawValue())
1309 // NaN != NaN
1310 return !x.isNaN();
1311
1312 if (x.isNumber())
1313 return y.isNumber() && x.asDouble() == y.asDouble();
1314 if (x.isManaged()) {
1315 return y.isManaged() && x.cast<Managed>()->isEqualTo(y.cast<Managed>());
1316 }
1317 return false;
1318}
1319
1321{
1322 TRACE2(l, r);
1323 if (l.isInteger() && r.isInteger())
1324 return l.integerValue() > r.integerValue();
1325 if (l.isNumber() && r.isNumber())
1326 return l.asDouble() > r.asDouble();
1327 String *sl = l.stringValue();
1328 String *sr = r.stringValue();
1329 if (sl && sr) {
1330 return sr->lessThan(sl);
1331 }
1332
1333 Object *ro = r.objectValue();
1334 Object *lo = l.objectValue();
1335 if (ro || lo) {
1336 QV4::ExecutionEngine *e = (lo ? lo : ro)->engine();
1337 QV4::Scope scope(e);
1340 return Runtime::CompareGreaterThan::call(pl, pr);
1341 }
1342
1343 double dl = RuntimeHelpers::toNumber(l);
1344 double dr = RuntimeHelpers::toNumber(r);
1345 return dl > dr;
1346}
1347
1349{
1350 TRACE2(l, r);
1351 if (l.isInteger() && r.isInteger())
1352 return l.integerValue() < r.integerValue();
1353 if (l.isNumber() && r.isNumber())
1354 return l.asDouble() < r.asDouble();
1355 String *sl = l.stringValue();
1356 String *sr = r.stringValue();
1357 if (sl && sr) {
1358 return sl->lessThan(sr);
1359 }
1360
1361 Object *ro = r.objectValue();
1362 Object *lo = l.objectValue();
1363 if (ro || lo) {
1364 QV4::ExecutionEngine *e = (lo ? lo : ro)->engine();
1365 QV4::Scope scope(e);
1368 return Runtime::CompareLessThan::call(pl, pr);
1369 }
1370
1371 double dl = RuntimeHelpers::toNumber(l);
1372 double dr = RuntimeHelpers::toNumber(r);
1373 return dl < dr;
1374}
1375
1377{
1378 TRACE2(l, r);
1379 if (l.isInteger() && r.isInteger())
1380 return l.integerValue() >= r.integerValue();
1381 if (l.isNumber() && r.isNumber())
1382 return l.asDouble() >= r.asDouble();
1383 String *sl = l.stringValue();
1384 String *sr = r.stringValue();
1385 if (sl && sr) {
1386 return !sl->lessThan(sr);
1387 }
1388
1389 Object *ro = r.objectValue();
1390 Object *lo = l.objectValue();
1391 if (ro || lo) {
1392 QV4::ExecutionEngine *e = (lo ? lo : ro)->engine();
1393 QV4::Scope scope(e);
1397 }
1398
1399 double dl = RuntimeHelpers::toNumber(l);
1400 double dr = RuntimeHelpers::toNumber(r);
1401 return dl >= dr;
1402}
1403
1405{
1406 TRACE2(l, r);
1407 if (l.isInteger() && r.isInteger())
1408 return l.integerValue() <= r.integerValue();
1409 if (l.isNumber() && r.isNumber())
1410 return l.asDouble() <= r.asDouble();
1411 String *sl = l.stringValue();
1412 String *sr = r.stringValue();
1413 if (sl && sr) {
1414 return !sr->lessThan(sl);
1415 }
1416
1417 Object *ro = r.objectValue();
1418 Object *lo = l.objectValue();
1419 if (ro || lo) {
1420 QV4::ExecutionEngine *e = (lo ? lo : ro)->engine();
1421 QV4::Scope scope(e);
1424 return Runtime::CompareLessEqual::call(pl, pr);
1425 }
1426
1427 double dl = RuntimeHelpers::toNumber(l);
1428 double dr = RuntimeHelpers::toNumber(r);
1429 return dl <= dr;
1430}
1431
1433{
1434 TRACE2(left, right);
1435
1438 return v->booleanValue();
1439}
1440
1442{
1443 TRACE2(left, right);
1444
1447 return v->booleanValue();
1448}
1449
1450static ReturnedValue throwPropertyIsNotAFunctionTypeError(ExecutionEngine *engine, Value *thisObject, const QString &propertyName)
1451{
1452 QString objectAsString = QStringLiteral("[null]");
1453 if (!thisObject->isUndefined())
1454 objectAsString = thisObject->toQStringNoThrow();
1455 QString msg = QStringLiteral("Property '%1' of object %2 is not a function")
1456 .arg(propertyName, objectAsString);
1457 return engine->throwTypeError(msg);
1458}
1459
1474
1490
1509
1528
1530{
1531 const Value *base = &baseRef;
1534 scope,
1537
1538 if (!lookupObject) {
1539 Q_ASSERT(!base->isEmpty());
1540 if (base->isNullOrUndefined()) {
1541 QString message = QStringLiteral("Cannot call method '%1' of %2")
1543 return engine->throwTypeError(message);
1544 }
1545
1546 if (base->isManaged()) {
1547 const Managed *m = static_cast<const Managed *>(base);
1550 } else {
1552 if (engine->hasException) // type error
1553 return Encode::undefined();
1556 }
1557 }
1558
1560
1561 if (!f) {
1562 QString error = QStringLiteral("Property '%1' of object %2 is not a function")
1563 .arg(name->toQString(),
1565 return engine->throwTypeError(error);
1566 }
1567
1568 return checkedResult(engine, f->call(base, argv, argc));
1569}
1570
1572{
1574
1575 if (base.isNullOrUndefined()) {
1576 const QString message = QStringLiteral("Cannot call method '%1' of %2")
1580 return engine->throwTypeError(message);
1581 }
1582
1583 // ok to have the value on the stack here
1585
1587 return checkedResult(engine, static_cast<FunctionObject &>(f).call(&base, argv, argc));
1588
1591
1592 const QString message = QStringLiteral("Property '%1' of object %2 is not a function")
1595 ->toQString(),
1597 return engine->throwTypeError(message);
1598}
1599
1601{
1602 if (!func.isFunctionObject())
1603 return engine->throwTypeError(QStringLiteral("%1 is not a function").arg(func.toQStringNoThrow()));
1605 return checkedResult(engine, static_cast<const FunctionObject &>(func).call(
1606 &undef, argv, argc));
1607}
1608
1610 const Value &thisObject, Value argv[], int argc)
1611{
1612 if (!func.isFunctionObject())
1613 return engine->throwTypeError(QStringLiteral("%1 is not a function").arg(func.toQStringNoThrow()));
1614 return checkedResult(engine, static_cast<const FunctionObject &>(func).call(
1615 &thisObject, argv, argc));
1616}
1617
1618struct CallArgs {
1619 Value *argv;
1620 int argc;
1621};
1622
1623static CallArgs createSpreadArguments(Scope &scope, Value *argv, int argc)
1624{
1625 ScopedValue it(scope);
1626 ScopedValue done(scope);
1627
1628 int argCount = 0;
1629
1630 Value *v = scope.constructUndefined(1);
1631 Value *arguments = v;
1632 for (int i = 0; i < argc; ++i) {
1633 if (!argv[i].isEmpty()) {
1634 *v = argv[i];
1635 ++argCount;
1636 v = scope.constructUndefined(1);
1637 continue;
1638 }
1639 // spread element
1640 ++i;
1641 it = Runtime::GetIterator::call(scope.engine, argv[i], /* ForInIterator */ 1);
1642 if (scope.hasException())
1643 return { nullptr, 0 };
1644 while (1) {
1645 done = Runtime::IteratorNext::call(scope.engine, it, v);
1646 if (scope.hasException())
1647 return { nullptr, 0 };
1648 Q_ASSERT(done->isBoolean());
1649 if (done->booleanValue())
1650 break;
1651 ++argCount;
1652 constexpr auto safetyMargin = 100; // leave some space on the stack for actual work with the elements
1653 if (qint64(scope.engine->jsStackLimit - scope.engine->jsStackTop) < safetyMargin) {
1654 scope.engine->throwRangeError(QLatin1String("Too many elements in array to use it with the spread operator"));
1655 return { nullptr, 0 };
1656 }
1657 v = scope.constructUndefined(1);
1658 }
1659 }
1660 return { arguments, argCount };
1661}
1662
1677
1679{
1681 return engine->throwTypeError();
1682
1683 return static_cast<const FunctionObject &>(function).callAsConstructor(argv, argc, &newTarget);
1684}
1685
1698
1700{
1701 // IMPORTANT! The JIT assumes that this method has the same amount (or less) arguments than
1702 // the jitted function, so it can safely do a tail call.
1703
1707 Value *argv = reinterpret_cast<Value *>(frame->jsFrame) + tos[StackOffsets::tailCall_argv].int_32();
1709 Q_ASSERT(argc >= 0);
1710
1712 if (!jsfo) {
1713 if (const FunctionObject *fo = function.as<FunctionObject>())
1715 return engine->throwTypeError();
1716 }
1717
1719 || unsigned(argc) > jsfo->formalParameterCount()) {
1720 // Cannot tailcall, do a normal call:
1722 }
1723
1724 memmove(frame->jsFrame->args, argv, argc * sizeof(Value));
1731 return Encode::undefined();
1732}
1733
1739
1741{
1744 switch (value.type()) {
1745 case Value::Undefined_Type:
1746 res = engine->id_undefined();
1747 break;
1748 case Value::Null_Type:
1749 res = engine->id_object();
1750 break;
1751 case Value::Boolean_Type:
1752 res = engine->id_boolean();
1753 break;
1754 case Value::Managed_Type:
1755 if (value.isString())
1756 res = engine->id_string();
1757 else if (value.isSymbol())
1758 res = engine->id_symbol();
1759 else if (value.objectValue()->as<FunctionObject>())
1760 res = engine->id_function();
1761 else
1762 res = engine->id_object(); // ### implementation-defined
1763 break;
1764 default:
1765 res = engine->id_number();
1766 break;
1767 }
1768 return res.asReturnedValue();
1769}
1770
1780
1785
1801
1809
1816
1826
1836
1844
1851
1857
1859{
1860 auto *h = toBeMarked.heapObject();
1861 if (!h)
1862 return;
1864 auto engine = h->internalClass->engine;
1866 // runtime function is only meant to be called while gc is ongoing
1869 h->mark(ms);
1870 });
1871}
1872
1874{
1875 if (!t.isObject()) {
1876 if (t.isNullOrUndefined()) {
1878 } else {
1879 return t.toObject(engine)->asReturnedValue();
1880 }
1881 }
1882 return t.asReturnedValue();
1883}
1884
1891
1896
1898{
1902
1903 Q_ASSERT(uint(argc) >= klass->d()->size);
1904
1905 for (uint i = 0; i < klass->d()->size; ++i)
1906 o->setProperty(i, *args++);
1907
1908 Q_ASSERT((argc - klass->d()->size) % 3 == 0);
1909 int additionalArgs = (argc - int(klass->d()->size))/3;
1910
1911 if (!additionalArgs)
1912 return o->asReturnedValue();
1913
1919 for (int i = 0; i < additionalArgs; ++i) {
1923 value = args[2];
1924 if (engine->hasException)
1925 return Encode::undefined();
1927 Q_ASSERT(args[2].isInteger());
1928 int functionId = args[2].integerValue();
1931 Q_ASSERT(clos);
1932
1936 else if (arg == ObjectLiteralArgument::Setter)
1938 else
1941
1943 if (clos->isGenerator())
1945 else
1947 } else if (args[2].isFunctionObject()) {
1948 fn = static_cast<const FunctionObject &>(args[2]);
1949
1951 fn->setName(fnName);
1952 }
1956 pd->value = value;
1957 pd->set = Value::emptyValue();
1958 } else {
1959 pd->value = Value::emptyValue();
1960 pd->set = value;
1961 }
1963 if (!ok)
1964 return engine->throwTypeError();
1965
1966 args += 3;
1967 }
1968 return o.asReturnedValue();
1969}
1970
1973{
1977
1981 if (!superClass.isEmpty()) {
1982 if (superClass.isNull()) {
1983 protoParent = Encode::null();
1984 } else {
1986 // ### check that the heritage object is a constructor
1988 return engine->throwTypeError(QStringLiteral("The superclass is not a function object."));
1989 const FunctionObject *s = static_cast<const FunctionObject *>(&superClass);
1991 if (!result->isObject() && !result->isNull())
1992 return engine->throwTypeError(QStringLiteral("The value of the superclass's prototype property is not an object."));
1995 }
1996 }
1997
2001
2010
2012 if (cls->nameIndex != UINT_MAX) {
2015 }
2016
2022 for (uint i = 0; i < cls->nStaticMethods + cls->nMethods; ++i) {
2023 if (i == cls->nStaticMethods)
2024 receiver = proto;
2025 if (methods[i].name == UINT_MAX) {
2028 return engine->throwTypeError(QStringLiteral("Cannot declare a static method named 'prototype'."));
2029 if (engine->hasException)
2030 return Encode::undefined();
2031 ++computedNames;
2032 } else {
2035 }
2037 Q_ASSERT(f);
2041 else if (methods[i].type == CompiledData::Method::Setter)
2043
2045
2046 if (f->isGenerator())
2048 else
2052 switch (methods[i].type) {
2053 case CompiledData::Method::Getter:
2057 break;
2058 case CompiledData::Method::Setter:
2062 break;
2063 default: // Regular
2067 break;
2068 }
2070 }
2071
2072 return constructor->asReturnedValue();
2073}
2074
2081
2089
2100
2108
2110{
2111 if (obj.isObject())
2112 return obj.asReturnedValue();
2113
2115}
2116
2118{
2119 return obj.toBoolean();
2120}
2121
2123{
2124 return Encode(v.toNumber());
2125}
2126
2128{
2129 TRACE1(value);
2130
2131 // +0 != -0, so we need to convert to double when negating 0
2132 if (value.isInteger() && value.integerValue() &&
2133 value.integerValue() != std::numeric_limits<int>::min())
2134 return Encode(-value.integerValue());
2135 else {
2136 double n = RuntimeHelpers::toNumber(value);
2137 return Encode(-n);
2138 }
2139}
2140
2141// binary operators
2142
2154
2156{
2157 TRACE2(left, right);
2158
2161
2162 double lval = left.isNumber() ? left.asDouble() : left.toNumberImpl();
2163 double rval = right.isNumber() ? right.asDouble() : right.toNumberImpl();
2164
2166}
2167
2169{
2170 TRACE2(left, right);
2171
2174
2175 double lval = left.isNumber() ? left.asDouble() : left.toNumberImpl();
2176 double rval = right.isNumber() ? right.asDouble() : right.toNumberImpl();
2177
2179}
2180
2182{
2183 TRACE2(left, right);
2184
2186 int lval = left.integerValue();
2187 int rval = right.integerValue();
2188 if (rval != 0 // division by zero should result in a NaN
2189 && !(lval == std::numeric_limits<int>::min() && rval == -1) // doesn't fit in int
2190 && (lval % rval == 0) // fractions can't be stored in an int
2191 && !(lval == 0 && rval < 0)) // 0 / -something results in -0.0
2192 return Encode(int(lval / rval));
2193 else
2194 return Encode(double(lval) / rval);
2195 }
2196
2197 double lval = left.toNumber();
2198 double rval = right.toNumber();
2200}
2201
2203{
2204 TRACE2(left, right);
2205
2207 // special cases are handled by fmod, among them:
2208 // - arithmic execeptions for ints in c++, eg: INT_MIN % -1
2209 // - undefined behavior in c++, e.g.: anything % 0
2210 // - uncommon cases which would complicate the condition, e.g.: negative integers
2211 // (this makes sure that -1 % 1 == -0 by passing it to fmod)
2213 }
2214
2215 double lval = RuntimeHelpers::toNumber(left);
2216 double rval = RuntimeHelpers::toNumber(right);
2217#ifdef fmod
2218# undef fmod
2219#endif
2221}
2222
2224{
2225 double b = base.toNumber();
2226 double e = exp.toNumber();
2227 return Encode(QQmlPrivate::jsExponentiate(b, e));
2228}
2229
2231{
2232 TRACE2(left, right);
2233
2234 int lval = left.toInt32();
2235 int rval = right.toInt32();
2236 return Encode((int)(lval & rval));
2237}
2238
2240{
2241 TRACE2(left, right);
2242
2243 int lval = left.toInt32();
2244 int rval = right.toInt32();
2245 return Encode((int)(lval | rval));
2246}
2247
2249{
2250 TRACE2(left, right);
2251
2252 int lval = left.toInt32();
2253 int rval = right.toInt32();
2254 return Encode((int)(lval ^ rval));
2255}
2256
2258{
2259 TRACE2(left, right);
2260
2261 int lval = left.toInt32();
2262 int rval = right.toInt32() & 0x1f;
2263 return Encode((int)(lval << rval));
2264}
2265
2267{
2268 TRACE2(left, right);
2269
2270 int lval = left.toInt32();
2271 unsigned rval = right.toUInt32() & 0x1f;
2272 return Encode((int)(lval >> rval));
2273}
2274
2276{
2277 TRACE2(left, right);
2278
2279 unsigned lval = left.toUInt32();
2280 unsigned rval = right.toUInt32() & 0x1f;
2281 uint res = lval >> rval;
2282
2283 return Encode(res);
2284}
2285
2287{
2288 TRACE2(left, right);
2289
2291 return Encode(r);
2292}
2293
2295{
2296 TRACE2(left, right);
2297
2298 bool r = CompareLessThan::call(left, right);
2299 return Encode(r);
2300}
2301
2303{
2304 TRACE2(left, right);
2305
2307 return Encode(r);
2308}
2309
2311{
2312 TRACE2(left, right);
2313
2314 bool r = CompareLessEqual::call(left, right);
2315 return Encode(r);
2316}
2317
2319{
2320 ExecutionEngine *engine = nullptr;
2321 Value *stackMark = nullptr;
2323 if (engine)
2324 engine->jsStackTop = stackMark;
2325 }
2326 template <typename T>
2327 void set(Value **scopedValue, T value, ExecutionEngine *e) {
2328 if (!engine) {
2329 engine = e;
2330 stackMark = engine->jsStackTop;
2331 }
2332 if (!*scopedValue)
2333 *scopedValue = e->jsAlloca(1);
2334 **scopedValue = value;
2335 }
2336};
2337
2339{
2340 TRACE2(left, right);
2341
2342 Value lhs = left;
2343 Value rhs = right;
2344
2346 Value *lhsGuard = nullptr;
2347 Value *rhsGuard = nullptr;
2348
2349 redo:
2351 return !lhs.isNaN();
2352
2353 quint32 lt = lhs.quickType();
2354 quint32 rt = rhs.quickType();
2355
2356 // LHS: Check if managed
2357 if ((lt & (Value::ManagedMask >> Value::Tag_Shift)) == 0) {
2358 if (lhs.isUndefined())
2359 return rhs.isNullOrUndefined();
2360
2361 // RHS: Check if managed
2362 if ((rt & (Value::ManagedMask >> Value::Tag_Shift)) == 0) {
2363 if (rhs.isUndefined())
2364 return false;
2365
2366 Heap::Base *l = lhs.m();
2367 Heap::Base *r = rhs.m();
2368 Q_ASSERT(l);
2369 Q_ASSERT(r);
2371 return static_cast<QV4::Managed &>(lhs).isEqualTo(&static_cast<QV4::Managed &>(rhs));
2375 goto redo;
2376 } else {
2380 goto redo;
2381 }
2382 return false;
2383 }
2384
2386 switch (rt) {
2387 case QV4::Value::QT_Empty:
2388 Q_UNREACHABLE();
2389 case QV4::Value::QT_Null:
2390 return false;
2391 case QV4::Value::QT_Bool:
2392 case QV4::Value::QT_Int:
2394 Q_FALLTHROUGH();
2395 default: // double
2397 return lhs.m()->internalClass->vtable->isString ? (RuntimeHelpers::toNumber(lhs) == rhs.doubleValue()) : false;
2398 } else {
2401 }
2402 }
2403 goto redo;
2404 } else if ((rt & (Value::ManagedMask >> Value::Tag_Shift)) == 0) {
2405 if (rhs.isUndefined())
2406 return lhs.isNull(); // Can't be undefined
2407 qSwap(lhs, rhs);
2408 qSwap(lt, rt);
2410 }
2411
2412 switch (lt) {
2413 case QV4::Value::QT_Empty:
2414 Q_UNREACHABLE();
2415 case QV4::Value::QT_Null:
2416 return rhs.isNull();
2417 case QV4::Value::QT_Bool:
2418 case QV4::Value::QT_Int:
2419 switch (rt) {
2420 case QV4::Value::QT_Empty:
2421 Q_UNREACHABLE();
2422 case QV4::Value::QT_Null:
2423 return false;
2424 case QV4::Value::QT_Bool:
2425 case QV4::Value::QT_Int:
2426 return lhs.int_32() == rhs.int_32();
2427 default: // double
2428 return lhs.int_32() == rhs.doubleValue();
2429 }
2430 default: // double
2431 switch (rt) {
2432 case QV4::Value::QT_Empty:
2433 Q_UNREACHABLE();
2434 case QV4::Value::QT_Null:
2435 return false;
2436 case QV4::Value::QT_Bool:
2437 case QV4::Value::QT_Int:
2438 return lhs.doubleValue() == rhs.int_32();
2439 default: // double
2440 return lhs.doubleValue() == rhs.doubleValue();
2441 }
2442 }
2443}
2444
2446{
2447 TRACE2(left, right);
2448
2449 bool r = CompareEqual::call(left, right);
2450 return Encode(r);
2451}
2452
2454{
2455 TRACE2(left, right);
2456
2457 bool r = !CompareEqual::call(left, right);
2458 return Encode(r);
2459}
2460
2462{
2463 TRACE2(left, right);
2464
2466 return Encode(r);
2467}
2468
2470{
2471 TRACE2(left, right);
2472
2474 return Encode(r);
2475}
2476
2478{
2479 TRACE2(left, right);
2480
2481 return !Runtime::CompareEqual::call(left, right);
2482}
2483
2485{
2486 TRACE2(left, right);
2487
2489}
2490
2492{
2493 TRACE2(left, right);
2494
2496}
2497
2498template<typename Operation>
2499static inline const void *symbol()
2500{
2501 return reinterpret_cast<void *>(&Operation::call);
2502}
2503
2504QHash<const void *, const char *> Runtime::symbolTable()
2505{
2506 static const QHash<const void *, const char *> symbols({
2507#ifndef V4_BOOTSTRAP
2508 {symbol<CallGlobalLookup>(), "CallGlobalLookup" },
2509 {symbol<CallQmlContextPropertyLookup>(), "CallQmlContextPropertyLookup" },
2510 {symbol<CallName>(), "CallName" },
2511 {symbol<CallProperty>(), "CallProperty" },
2512 {symbol<CallPropertyLookup>(), "CallPropertyLookup" },
2513 {symbol<CallValue>(), "CallValue" },
2514 {symbol<CallWithReceiver>(), "CallWithReceiver" },
2515 {symbol<CallPossiblyDirectEval>(), "CallPossiblyDirectEval" },
2516 {symbol<CallWithSpread>(), "CallWithSpread" },
2517 {symbol<TailCall>(), "TailCall" },
2518
2519 {symbol<Construct>(), "Construct" },
2520 {symbol<ConstructWithSpread>(), "ConstructWithSpread" },
2521
2522 {symbol<StoreNameStrict>(), "StoreNameStrict" },
2523 {symbol<StoreNameSloppy>(), "StoreNameSloppy" },
2524 {symbol<StoreProperty>(), "StoreProperty" },
2525 {symbol<StoreElement>(), "StoreElement" },
2526 {symbol<LoadProperty>(), "LoadProperty" },
2527 {symbol<LoadName>(), "LoadName" },
2528 {symbol<LoadElement>(), "LoadElement" },
2529 {symbol<LoadSuperProperty>(), "LoadSuperProperty" },
2530 {symbol<StoreSuperProperty>(), "StoreSuperProperty" },
2531 {symbol<LoadSuperConstructor>(), "LoadSuperConstructor" },
2532 {symbol<LoadGlobalLookup>(), "LoadGlobalLookup" },
2533 {symbol<LoadQmlContextPropertyLookup>(), "LoadQmlContextPropertyLookup" },
2534 {symbol<GetLookup>(), "GetLookup" },
2535 {symbol<SetLookupStrict>(), "SetLookupStrict" },
2536 {symbol<SetLookupSloppy>(), "SetLookupSloppy" },
2537
2538 {symbol<TypeofValue>(), "TypeofValue" },
2539 {symbol<TypeofName>(), "TypeofName" },
2540
2541 {symbol<DeleteProperty_NoThrow>(), "DeleteProperty_NoThrow" },
2542 {symbol<DeleteProperty>(), "DeleteProperty" },
2543 {symbol<DeleteName_NoThrow>(), "DeleteName_NoThrow" },
2544 {symbol<DeleteName>(), "DeleteName" },
2545
2546 {symbol<ThrowException>(), "ThrowException" },
2547 {symbol<PushCallContext>(), "PushCallContext" },
2548 {symbol<PushWithContext>(), "PushWithContext" },
2549 {symbol<PushCatchContext>(), "PushCatchContext" },
2550 {symbol<PushBlockContext>(), "PushBlockContext" },
2551 {symbol<CloneBlockContext>(), "CloneBlockContext" },
2552 {symbol<PushScriptContext>(), "PushScriptContext" },
2553 {symbol<PopScriptContext>(), "PopScriptContext" },
2554 {symbol<ThrowReferenceError>(), "ThrowReferenceError" },
2555 {symbol<ThrowOnNullOrUndefined>(), "ThrowOnNullOrUndefined" },
2556
2557 {symbol<Closure>(), "Closure" },
2558
2559 {symbol<MarkCustom>(), "MarkCustom"},
2560
2561 {symbol<ConvertThisToObject>(), "ConvertThisToObject" },
2562 {symbol<DeclareVar>(), "DeclareVar" },
2563 {symbol<CreateMappedArgumentsObject>(), "CreateMappedArgumentsObject" },
2564 {symbol<CreateUnmappedArgumentsObject>(), "CreateUnmappedArgumentsObject" },
2565 {symbol<CreateRestParameter>(), "CreateRestParameter" },
2566
2567 {symbol<ArrayLiteral>(), "ArrayLiteral" },
2568 {symbol<ObjectLiteral>(), "ObjectLiteral" },
2569 {symbol<CreateClass>(), "CreateClass" },
2570
2571 {symbol<GetIterator>(), "GetIterator" },
2572 {symbol<IteratorNext>(), "IteratorNext" },
2573 {symbol<IteratorNextForYieldStar>(), "IteratorNextForYieldStar" },
2574 {symbol<IteratorClose>(), "IteratorClose" },
2575 {symbol<DestructureRestElement>(), "DestructureRestElement" },
2576
2577 {symbol<ToObject>(), "ToObject" },
2578 {symbol<ToBoolean>(), "ToBoolean" },
2579 {symbol<ToNumber>(), "ToNumber" },
2580
2581 {symbol<UMinus>(), "UMinus" },
2582
2583 {symbol<Instanceof>(), "Instanceof" },
2584 {symbol<As>(), "As" },
2585 {symbol<In>(), "In" },
2586 {symbol<Add>(), "Add" },
2587 {symbol<Sub>(), "Sub" },
2588 {symbol<Mul>(), "Mul" },
2589 {symbol<Div>(), "Div" },
2590 {symbol<Mod>(), "Mod" },
2591 {symbol<Exp>(), "Exp" },
2592 {symbol<BitAnd>(), "BitAnd" },
2593 {symbol<BitOr>(), "BitOr" },
2594 {symbol<BitXor>(), "BitXor" },
2595 {symbol<Shl>(), "Shl" },
2596 {symbol<Shr>(), "Shr" },
2597 {symbol<UShr>(), "UShr" },
2598 {symbol<GreaterThan>(), "GreaterThan" },
2599 {symbol<LessThan>(), "LessThan" },
2600 {symbol<GreaterEqual>(), "GreaterEqual" },
2601 {symbol<LessEqual>(), "LessEqual" },
2602 {symbol<Equal>(), "Equal" },
2603 {symbol<NotEqual>(), "NotEqual" },
2604 {symbol<StrictEqual>(), "StrictEqual" },
2605 {symbol<StrictNotEqual>(), "StrictNotEqual" },
2606
2607 {symbol<CompareGreaterThan>(), "CompareGreaterThan" },
2608 {symbol<CompareLessThan>(), "CompareLessThan" },
2609 {symbol<CompareGreaterEqual>(), "CompareGreaterEqual" },
2610 {symbol<CompareLessEqual>(), "CompareLessEqual" },
2611 {symbol<CompareEqual>(), "CompareEqual" },
2612 {symbol<CompareNotEqual>(), "CompareNotEqual" },
2613 {symbol<CompareStrictEqual>(), "CompareStrictEqual" },
2614 {symbol<CompareStrictNotEqual>(), "CompareStrictNotEqual" },
2615
2616 {symbol<CompareInstanceof>(), "CompareInstanceOf" },
2617 {symbol<CompareIn>(), "CompareIn" },
2618
2619 {symbol<RegexpLiteral>(), "RegexpLiteral" },
2620 {symbol<GetTemplateObject>(), "GetTemplateObject" }
2621#endif
2622 });
2623
2624 return symbols;
2625}
2626
2627} // namespace QV4
2628
2629QT_END_NAMESPACE
Definition qjsvalue.h:24
static QV4::ReturnedValue doInstanceof(ExecutionEngine *engine, const Value &lval, const Value &rval)
static Heap::String * convert_to_string_add(ExecutionEngine *engine, Value value)
Scoped< Object > ScopedObject
static ReturnedValue throwPropertyIsNotAFunctionTypeError(ExecutionEngine *engine, Value *thisObject, const QString &propertyName)
static QV4::Lookup * runtimeLookup(Function *f, uint i)
static Q_NEVER_INLINE bool setElementFallback(ExecutionEngine *engine, const Value &object, const Value &index, const Value &value)
static Object * getSuperBase(Scope &scope)
static CallArgs createSpreadArguments(Scope &scope, Value *argv, int argc)
static const void * symbol()
Scoped< ExecutionContext > ScopedContext
QT_BEGIN_NAMESPACE Q_STATIC_LOGGING_CATEGORY(lcSynthesizedIterableAccess, "qt.iterable.synthesized", QtWarningMsg)
#define TRACE1(x)
#define TRACE2(x, y)
ExecutionEngine * engine
void set(Value **scopedValue, T value, ExecutionEngine *e)