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
1215
1217{
1220 l->setter(engine, const_cast<Value &>(base), value);
1221}
1222
1224{
1227 if (!l->setter(engine, const_cast<Value &>(base), value))
1229}
1230
1232{
1236 // ### TODO: fix line number
1238 QStringLiteral("super() already called."), QString(), 0, 0);
1239 }
1240 } else {
1243 if (frame->thisObject() != nullptr) {
1244 // ### TODO: fix line number
1246 QStringLiteral("super() already called."), QString(), 0, 0);
1247 }
1248 }
1249
1250 const FunctionObject *f = t.as<FunctionObject>();
1251 if (!f)
1252 return engine->throwTypeError();
1253 Heap::Object *c = static_cast<const Object &>(t).getPrototypeOf();
1254 if (!c->vtable()->callAsConstructor)
1255 return engine->throwTypeError();
1256 return c->asReturnedValue();
1257}
1258
1260{
1261 Q_ASSERT(x.type() != y.type() || (x.isManaged() && (x.isString() != y.isString())));
1262
1263 if (x.isNumber() && y.isNumber())
1264 return x.asDouble() == y.asDouble();
1265 if (x.isNull() && y.isUndefined()) {
1266 return true;
1267 } else if (x.isUndefined() && y.isNull()) {
1268 return true;
1269 } else if (x.isNumber() && y.isString()) {
1270 double dy = RuntimeHelpers::toNumber(y);
1271 return x.asDouble() == dy;
1272 } else if (x.isString() && y.isNumber()) {
1273 double dx = RuntimeHelpers::toNumber(x);
1274 return dx == y.asDouble();
1275 } else if (x.isBoolean()) {
1276 return Runtime::CompareEqual::call(Value::fromDouble((double) x.booleanValue()), y);
1277 } else if (y.isBoolean()) {
1278 return Runtime::CompareEqual::call(x, Value::fromDouble((double) y.booleanValue()));
1279 } else {
1280 Object *xo = x.objectValue();
1281 Object *yo = y.objectValue();
1282 if (yo && (x.isNumber() || x.isString())) {
1283 Scope scope(yo->engine());
1285 return Runtime::CompareEqual::call(x, py);
1286 } else if (xo && (y.isNumber() || y.isString())) {
1287 Scope scope(xo->engine());
1289 return Runtime::CompareEqual::call(px, y);
1290 }
1291 }
1292
1293 return false;
1294}
1295
1297{
1298 TRACE2(x, y);
1299
1300 if (x.rawValue() == y.rawValue())
1301 // NaN != NaN
1302 return !x.isNaN();
1303
1304 if (x.isNumber())
1305 return y.isNumber() && x.asDouble() == y.asDouble();
1306 if (x.isManaged()) {
1307 return y.isManaged() && x.cast<Managed>()->isEqualTo(y.cast<Managed>());
1308 }
1309 return false;
1310}
1311
1313{
1314 TRACE2(l, r);
1315 if (l.isInteger() && r.isInteger())
1316 return l.integerValue() > r.integerValue();
1317 if (l.isNumber() && r.isNumber())
1318 return l.asDouble() > r.asDouble();
1319 String *sl = l.stringValue();
1320 String *sr = r.stringValue();
1321 if (sl && sr) {
1322 return sr->lessThan(sl);
1323 }
1324
1325 Object *ro = r.objectValue();
1326 Object *lo = l.objectValue();
1327 if (ro || lo) {
1328 QV4::ExecutionEngine *e = (lo ? lo : ro)->engine();
1329 QV4::Scope scope(e);
1332 return Runtime::CompareGreaterThan::call(pl, pr);
1333 }
1334
1335 double dl = RuntimeHelpers::toNumber(l);
1336 double dr = RuntimeHelpers::toNumber(r);
1337 return dl > dr;
1338}
1339
1341{
1342 TRACE2(l, r);
1343 if (l.isInteger() && r.isInteger())
1344 return l.integerValue() < r.integerValue();
1345 if (l.isNumber() && r.isNumber())
1346 return l.asDouble() < r.asDouble();
1347 String *sl = l.stringValue();
1348 String *sr = r.stringValue();
1349 if (sl && sr) {
1350 return sl->lessThan(sr);
1351 }
1352
1353 Object *ro = r.objectValue();
1354 Object *lo = l.objectValue();
1355 if (ro || lo) {
1356 QV4::ExecutionEngine *e = (lo ? lo : ro)->engine();
1357 QV4::Scope scope(e);
1360 return Runtime::CompareLessThan::call(pl, pr);
1361 }
1362
1363 double dl = RuntimeHelpers::toNumber(l);
1364 double dr = RuntimeHelpers::toNumber(r);
1365 return dl < dr;
1366}
1367
1369{
1370 TRACE2(l, r);
1371 if (l.isInteger() && r.isInteger())
1372 return l.integerValue() >= r.integerValue();
1373 if (l.isNumber() && r.isNumber())
1374 return l.asDouble() >= r.asDouble();
1375 String *sl = l.stringValue();
1376 String *sr = r.stringValue();
1377 if (sl && sr) {
1378 return !sl->lessThan(sr);
1379 }
1380
1381 Object *ro = r.objectValue();
1382 Object *lo = l.objectValue();
1383 if (ro || lo) {
1384 QV4::ExecutionEngine *e = (lo ? lo : ro)->engine();
1385 QV4::Scope scope(e);
1389 }
1390
1391 double dl = RuntimeHelpers::toNumber(l);
1392 double dr = RuntimeHelpers::toNumber(r);
1393 return dl >= dr;
1394}
1395
1397{
1398 TRACE2(l, r);
1399 if (l.isInteger() && r.isInteger())
1400 return l.integerValue() <= r.integerValue();
1401 if (l.isNumber() && r.isNumber())
1402 return l.asDouble() <= r.asDouble();
1403 String *sl = l.stringValue();
1404 String *sr = r.stringValue();
1405 if (sl && sr) {
1406 return !sr->lessThan(sl);
1407 }
1408
1409 Object *ro = r.objectValue();
1410 Object *lo = l.objectValue();
1411 if (ro || lo) {
1412 QV4::ExecutionEngine *e = (lo ? lo : ro)->engine();
1413 QV4::Scope scope(e);
1416 return Runtime::CompareLessEqual::call(pl, pr);
1417 }
1418
1419 double dl = RuntimeHelpers::toNumber(l);
1420 double dr = RuntimeHelpers::toNumber(r);
1421 return dl <= dr;
1422}
1423
1425{
1426 TRACE2(left, right);
1427
1430 return v->booleanValue();
1431}
1432
1434{
1435 TRACE2(left, right);
1436
1439 return v->booleanValue();
1440}
1441
1442static ReturnedValue throwPropertyIsNotAFunctionTypeError(ExecutionEngine *engine, Value *thisObject, const QString &propertyName)
1443{
1444 QString objectAsString = QStringLiteral("[null]");
1445 if (!thisObject->isUndefined())
1446 objectAsString = thisObject->toQStringNoThrow();
1447 QString msg = QStringLiteral("Property '%1' of object %2 is not a function")
1448 .arg(propertyName, objectAsString);
1449 return engine->throwTypeError(msg);
1450}
1451
1466
1482
1501
1520
1522{
1523 const Value *base = &baseRef;
1526 scope,
1529
1530 if (!lookupObject) {
1531 Q_ASSERT(!base->isEmpty());
1532 if (base->isNullOrUndefined()) {
1533 QString message = QStringLiteral("Cannot call method '%1' of %2")
1535 return engine->throwTypeError(message);
1536 }
1537
1538 if (base->isManaged()) {
1539 const Managed *m = static_cast<const Managed *>(base);
1542 } else {
1544 if (engine->hasException) // type error
1545 return Encode::undefined();
1548 }
1549 }
1550
1552
1553 if (!f) {
1554 QString error = QStringLiteral("Property '%1' of object %2 is not a function")
1555 .arg(name->toQString(),
1557 return engine->throwTypeError(error);
1558 }
1559
1560 return checkedResult(engine, f->call(base, argv, argc));
1561}
1562
1564{
1566 // ok to have the value on the stack here
1568
1570 return checkedResult(engine, static_cast<FunctionObject &>(f).call(&base, argv, argc));
1571
1574
1575 const QString message = QStringLiteral("Property '%1' of object %2 is not a function")
1578 ->toQString(),
1580 return engine->throwTypeError(message);
1581}
1582
1584{
1585 if (!func.isFunctionObject())
1586 return engine->throwTypeError(QStringLiteral("%1 is not a function").arg(func.toQStringNoThrow()));
1588 return checkedResult(engine, static_cast<const FunctionObject &>(func).call(
1589 &undef, argv, argc));
1590}
1591
1593 const Value &thisObject, Value argv[], int argc)
1594{
1595 if (!func.isFunctionObject())
1596 return engine->throwTypeError(QStringLiteral("%1 is not a function").arg(func.toQStringNoThrow()));
1597 return checkedResult(engine, static_cast<const FunctionObject &>(func).call(
1598 &thisObject, argv, argc));
1599}
1600
1601struct CallArgs {
1602 Value *argv;
1603 int argc;
1604};
1605
1606static CallArgs createSpreadArguments(Scope &scope, Value *argv, int argc)
1607{
1608 ScopedValue it(scope);
1609 ScopedValue done(scope);
1610
1611 int argCount = 0;
1612
1613 Value *v = scope.constructUndefined(1);
1614 Value *arguments = v;
1615 for (int i = 0; i < argc; ++i) {
1616 if (!argv[i].isEmpty()) {
1617 *v = argv[i];
1618 ++argCount;
1619 v = scope.constructUndefined(1);
1620 continue;
1621 }
1622 // spread element
1623 ++i;
1624 it = Runtime::GetIterator::call(scope.engine, argv[i], /* ForInIterator */ 1);
1625 if (scope.hasException())
1626 return { nullptr, 0 };
1627 while (1) {
1628 done = Runtime::IteratorNext::call(scope.engine, it, v);
1629 if (scope.hasException())
1630 return { nullptr, 0 };
1631 Q_ASSERT(done->isBoolean());
1632 if (done->booleanValue())
1633 break;
1634 ++argCount;
1635 constexpr auto safetyMargin = 100; // leave some space on the stack for actual work with the elements
1636 if (qint64(scope.engine->jsStackLimit - scope.engine->jsStackTop) < safetyMargin) {
1637 scope.engine->throwRangeError(QLatin1String("Too many elements in array to use it with the spread operator"));
1638 return { nullptr, 0 };
1639 }
1640 v = scope.constructUndefined(1);
1641 }
1642 }
1643 return { arguments, argCount };
1644}
1645
1660
1662{
1664 return engine->throwTypeError();
1665
1666 return static_cast<const FunctionObject &>(function).callAsConstructor(argv, argc, &newTarget);
1667}
1668
1681
1683{
1684 // IMPORTANT! The JIT assumes that this method has the same amount (or less) arguments than
1685 // the jitted function, so it can safely do a tail call.
1686
1690 Value *argv = reinterpret_cast<Value *>(frame->jsFrame) + tos[StackOffsets::tailCall_argv].int_32();
1692 Q_ASSERT(argc >= 0);
1693
1695 if (!jsfo) {
1696 if (const FunctionObject *fo = function.as<FunctionObject>())
1698 return engine->throwTypeError();
1699 }
1700
1702 || unsigned(argc) > jsfo->formalParameterCount()) {
1703 // Cannot tailcall, do a normal call:
1705 }
1706
1707 memmove(frame->jsFrame->args, argv, argc * sizeof(Value));
1714 return Encode::undefined();
1715}
1716
1722
1724{
1727 switch (value.type()) {
1728 case Value::Undefined_Type:
1729 res = engine->id_undefined();
1730 break;
1731 case Value::Null_Type:
1732 res = engine->id_object();
1733 break;
1734 case Value::Boolean_Type:
1735 res = engine->id_boolean();
1736 break;
1737 case Value::Managed_Type:
1738 if (value.isString())
1739 res = engine->id_string();
1740 else if (value.isSymbol())
1741 res = engine->id_symbol();
1742 else if (value.objectValue()->as<FunctionObject>())
1743 res = engine->id_function();
1744 else
1745 res = engine->id_object(); // ### implementation-defined
1746 break;
1747 default:
1748 res = engine->id_number();
1749 break;
1750 }
1751 return res.asReturnedValue();
1752}
1753
1763
1768
1784
1792
1799
1809
1819
1827
1834
1840
1842{
1843 auto *h = toBeMarked.heapObject();
1844 if (!h)
1845 return;
1847 auto engine = h->internalClass->engine;
1849 // runtime function is only meant to be called while gc is ongoing
1852 h->mark(ms);
1853 });
1854}
1855
1857{
1858 if (!t.isObject()) {
1859 if (t.isNullOrUndefined()) {
1861 } else {
1862 return t.toObject(engine)->asReturnedValue();
1863 }
1864 }
1865 return t.asReturnedValue();
1866}
1867
1874
1879
1881{
1885
1886 Q_ASSERT(uint(argc) >= klass->d()->size);
1887
1888 for (uint i = 0; i < klass->d()->size; ++i)
1889 o->setProperty(i, *args++);
1890
1891 Q_ASSERT((argc - klass->d()->size) % 3 == 0);
1892 int additionalArgs = (argc - int(klass->d()->size))/3;
1893
1894 if (!additionalArgs)
1895 return o->asReturnedValue();
1896
1902 for (int i = 0; i < additionalArgs; ++i) {
1906 value = args[2];
1907 if (engine->hasException)
1908 return Encode::undefined();
1910 Q_ASSERT(args[2].isInteger());
1911 int functionId = args[2].integerValue();
1914 Q_ASSERT(clos);
1915
1919 else if (arg == ObjectLiteralArgument::Setter)
1921 else
1924
1926 if (clos->isGenerator())
1928 else
1930 } else if (args[2].isFunctionObject()) {
1931 fn = static_cast<const FunctionObject &>(args[2]);
1932
1934 fn->setName(fnName);
1935 }
1939 pd->value = value;
1940 pd->set = Value::emptyValue();
1941 } else {
1942 pd->value = Value::emptyValue();
1943 pd->set = value;
1944 }
1946 if (!ok)
1947 return engine->throwTypeError();
1948
1949 args += 3;
1950 }
1951 return o.asReturnedValue();
1952}
1953
1956{
1960
1964 if (!superClass.isEmpty()) {
1965 if (superClass.isNull()) {
1966 protoParent = Encode::null();
1967 } else {
1969 // ### check that the heritage object is a constructor
1971 return engine->throwTypeError(QStringLiteral("The superclass is not a function object."));
1972 const FunctionObject *s = static_cast<const FunctionObject *>(&superClass);
1974 if (!result->isObject() && !result->isNull())
1975 return engine->throwTypeError(QStringLiteral("The value of the superclass's prototype property is not an object."));
1978 }
1979 }
1980
1984
1993
1995 if (cls->nameIndex != UINT_MAX) {
1998 }
1999
2005 for (uint i = 0; i < cls->nStaticMethods + cls->nMethods; ++i) {
2006 if (i == cls->nStaticMethods)
2007 receiver = proto;
2008 if (methods[i].name == UINT_MAX) {
2011 return engine->throwTypeError(QStringLiteral("Cannot declare a static method named 'prototype'."));
2012 if (engine->hasException)
2013 return Encode::undefined();
2014 ++computedNames;
2015 } else {
2018 }
2020 Q_ASSERT(f);
2024 else if (methods[i].type == CompiledData::Method::Setter)
2026
2028
2029 if (f->isGenerator())
2031 else
2035 switch (methods[i].type) {
2036 case CompiledData::Method::Getter:
2040 break;
2041 case CompiledData::Method::Setter:
2045 break;
2046 default: // Regular
2050 break;
2051 }
2053 }
2054
2055 return constructor->asReturnedValue();
2056}
2057
2064
2072
2083
2091
2093{
2094 if (obj.isObject())
2095 return obj.asReturnedValue();
2096
2098}
2099
2101{
2102 return obj.toBoolean();
2103}
2104
2106{
2107 return Encode(v.toNumber());
2108}
2109
2111{
2112 TRACE1(value);
2113
2114 // +0 != -0, so we need to convert to double when negating 0
2115 if (value.isInteger() && value.integerValue() &&
2116 value.integerValue() != std::numeric_limits<int>::min())
2117 return Encode(-value.integerValue());
2118 else {
2119 double n = RuntimeHelpers::toNumber(value);
2120 return Encode(-n);
2121 }
2122}
2123
2124// binary operators
2125
2137
2139{
2140 TRACE2(left, right);
2141
2144
2145 double lval = left.isNumber() ? left.asDouble() : left.toNumberImpl();
2146 double rval = right.isNumber() ? right.asDouble() : right.toNumberImpl();
2147
2149}
2150
2152{
2153 TRACE2(left, right);
2154
2157
2158 double lval = left.isNumber() ? left.asDouble() : left.toNumberImpl();
2159 double rval = right.isNumber() ? right.asDouble() : right.toNumberImpl();
2160
2162}
2163
2165{
2166 TRACE2(left, right);
2167
2169 int lval = left.integerValue();
2170 int rval = right.integerValue();
2171 if (rval != 0 // division by zero should result in a NaN
2172 && !(lval == std::numeric_limits<int>::min() && rval == -1) // doesn't fit in int
2173 && (lval % rval == 0) // fractions can't be stored in an int
2174 && !(lval == 0 && rval < 0)) // 0 / -something results in -0.0
2175 return Encode(int(lval / rval));
2176 else
2177 return Encode(double(lval) / rval);
2178 }
2179
2180 double lval = left.toNumber();
2181 double rval = right.toNumber();
2183}
2184
2186{
2187 TRACE2(left, right);
2188
2190 // special cases are handled by fmod, among them:
2191 // - arithmic execeptions for ints in c++, eg: INT_MIN % -1
2192 // - undefined behavior in c++, e.g.: anything % 0
2193 // - uncommon cases which would complicate the condition, e.g.: negative integers
2194 // (this makes sure that -1 % 1 == -0 by passing it to fmod)
2196 }
2197
2198 double lval = RuntimeHelpers::toNumber(left);
2199 double rval = RuntimeHelpers::toNumber(right);
2200#ifdef fmod
2201# undef fmod
2202#endif
2204}
2205
2207{
2208 double b = base.toNumber();
2209 double e = exp.toNumber();
2210 return Encode(QQmlPrivate::jsExponentiate(b, e));
2211}
2212
2214{
2215 TRACE2(left, right);
2216
2217 int lval = left.toInt32();
2218 int rval = right.toInt32();
2219 return Encode((int)(lval & rval));
2220}
2221
2223{
2224 TRACE2(left, right);
2225
2226 int lval = left.toInt32();
2227 int rval = right.toInt32();
2228 return Encode((int)(lval | rval));
2229}
2230
2232{
2233 TRACE2(left, right);
2234
2235 int lval = left.toInt32();
2236 int rval = right.toInt32();
2237 return Encode((int)(lval ^ rval));
2238}
2239
2241{
2242 TRACE2(left, right);
2243
2244 int lval = left.toInt32();
2245 int rval = right.toInt32() & 0x1f;
2246 return Encode((int)(lval << rval));
2247}
2248
2250{
2251 TRACE2(left, right);
2252
2253 int lval = left.toInt32();
2254 unsigned rval = right.toUInt32() & 0x1f;
2255 return Encode((int)(lval >> rval));
2256}
2257
2259{
2260 TRACE2(left, right);
2261
2262 unsigned lval = left.toUInt32();
2263 unsigned rval = right.toUInt32() & 0x1f;
2264 uint res = lval >> rval;
2265
2266 return Encode(res);
2267}
2268
2270{
2271 TRACE2(left, right);
2272
2274 return Encode(r);
2275}
2276
2278{
2279 TRACE2(left, right);
2280
2281 bool r = CompareLessThan::call(left, right);
2282 return Encode(r);
2283}
2284
2286{
2287 TRACE2(left, right);
2288
2290 return Encode(r);
2291}
2292
2294{
2295 TRACE2(left, right);
2296
2297 bool r = CompareLessEqual::call(left, right);
2298 return Encode(r);
2299}
2300
2302{
2303 ExecutionEngine *engine = nullptr;
2304 Value *stackMark = nullptr;
2306 if (engine)
2307 engine->jsStackTop = stackMark;
2308 }
2309 template <typename T>
2310 void set(Value **scopedValue, T value, ExecutionEngine *e) {
2311 if (!engine) {
2312 engine = e;
2313 stackMark = engine->jsStackTop;
2314 }
2315 if (!*scopedValue)
2316 *scopedValue = e->jsAlloca(1);
2317 **scopedValue = value;
2318 }
2319};
2320
2322{
2323 TRACE2(left, right);
2324
2325 Value lhs = left;
2326 Value rhs = right;
2327
2329 Value *lhsGuard = nullptr;
2330 Value *rhsGuard = nullptr;
2331
2332 redo:
2334 return !lhs.isNaN();
2335
2336 quint32 lt = lhs.quickType();
2337 quint32 rt = rhs.quickType();
2338
2339 // LHS: Check if managed
2340 if ((lt & (Value::ManagedMask >> Value::Tag_Shift)) == 0) {
2341 if (lhs.isUndefined())
2342 return rhs.isNullOrUndefined();
2343
2344 // RHS: Check if managed
2345 if ((rt & (Value::ManagedMask >> Value::Tag_Shift)) == 0) {
2346 if (rhs.isUndefined())
2347 return false;
2348
2349 Heap::Base *l = lhs.m();
2350 Heap::Base *r = rhs.m();
2351 Q_ASSERT(l);
2352 Q_ASSERT(r);
2354 return static_cast<QV4::Managed &>(lhs).isEqualTo(&static_cast<QV4::Managed &>(rhs));
2358 goto redo;
2359 } else {
2363 goto redo;
2364 }
2365 return false;
2366 }
2367
2369 switch (rt) {
2370 case QV4::Value::QT_Empty:
2371 Q_UNREACHABLE();
2372 case QV4::Value::QT_Null:
2373 return false;
2374 case QV4::Value::QT_Bool:
2375 case QV4::Value::QT_Int:
2377 Q_FALLTHROUGH();
2378 default: // double
2380 return lhs.m()->internalClass->vtable->isString ? (RuntimeHelpers::toNumber(lhs) == rhs.doubleValue()) : false;
2381 } else {
2384 }
2385 }
2386 goto redo;
2387 } else if ((rt & (Value::ManagedMask >> Value::Tag_Shift)) == 0) {
2388 if (rhs.isUndefined())
2389 return lhs.isNull(); // Can't be undefined
2390 qSwap(lhs, rhs);
2391 qSwap(lt, rt);
2393 }
2394
2395 switch (lt) {
2396 case QV4::Value::QT_Empty:
2397 Q_UNREACHABLE();
2398 case QV4::Value::QT_Null:
2399 return rhs.isNull();
2400 case QV4::Value::QT_Bool:
2401 case QV4::Value::QT_Int:
2402 switch (rt) {
2403 case QV4::Value::QT_Empty:
2404 Q_UNREACHABLE();
2405 case QV4::Value::QT_Null:
2406 return false;
2407 case QV4::Value::QT_Bool:
2408 case QV4::Value::QT_Int:
2409 return lhs.int_32() == rhs.int_32();
2410 default: // double
2411 return lhs.int_32() == rhs.doubleValue();
2412 }
2413 default: // double
2414 switch (rt) {
2415 case QV4::Value::QT_Empty:
2416 Q_UNREACHABLE();
2417 case QV4::Value::QT_Null:
2418 return false;
2419 case QV4::Value::QT_Bool:
2420 case QV4::Value::QT_Int:
2421 return lhs.doubleValue() == rhs.int_32();
2422 default: // double
2423 return lhs.doubleValue() == rhs.doubleValue();
2424 }
2425 }
2426}
2427
2429{
2430 TRACE2(left, right);
2431
2432 bool r = CompareEqual::call(left, right);
2433 return Encode(r);
2434}
2435
2437{
2438 TRACE2(left, right);
2439
2440 bool r = !CompareEqual::call(left, right);
2441 return Encode(r);
2442}
2443
2445{
2446 TRACE2(left, right);
2447
2449 return Encode(r);
2450}
2451
2453{
2454 TRACE2(left, right);
2455
2457 return Encode(r);
2458}
2459
2461{
2462 TRACE2(left, right);
2463
2464 return !Runtime::CompareEqual::call(left, right);
2465}
2466
2468{
2469 TRACE2(left, right);
2470
2472}
2473
2475{
2476 TRACE2(left, right);
2477
2479}
2480
2481template<typename Operation>
2482static inline const void *symbol()
2483{
2484 return reinterpret_cast<void *>(&Operation::call);
2485}
2486
2487QHash<const void *, const char *> Runtime::symbolTable()
2488{
2489 static const QHash<const void *, const char *> symbols({
2490#ifndef V4_BOOTSTRAP
2491 {symbol<CallGlobalLookup>(), "CallGlobalLookup" },
2492 {symbol<CallQmlContextPropertyLookup>(), "CallQmlContextPropertyLookup" },
2493 {symbol<CallName>(), "CallName" },
2494 {symbol<CallProperty>(), "CallProperty" },
2495 {symbol<CallPropertyLookup>(), "CallPropertyLookup" },
2496 {symbol<CallValue>(), "CallValue" },
2497 {symbol<CallWithReceiver>(), "CallWithReceiver" },
2498 {symbol<CallPossiblyDirectEval>(), "CallPossiblyDirectEval" },
2499 {symbol<CallWithSpread>(), "CallWithSpread" },
2500 {symbol<TailCall>(), "TailCall" },
2501
2502 {symbol<Construct>(), "Construct" },
2503 {symbol<ConstructWithSpread>(), "ConstructWithSpread" },
2504
2505 {symbol<StoreNameStrict>(), "StoreNameStrict" },
2506 {symbol<StoreNameSloppy>(), "StoreNameSloppy" },
2507 {symbol<StoreProperty>(), "StoreProperty" },
2508 {symbol<StoreElement>(), "StoreElement" },
2509 {symbol<LoadProperty>(), "LoadProperty" },
2510 {symbol<LoadName>(), "LoadName" },
2511 {symbol<LoadElement>(), "LoadElement" },
2512 {symbol<LoadSuperProperty>(), "LoadSuperProperty" },
2513 {symbol<StoreSuperProperty>(), "StoreSuperProperty" },
2514 {symbol<LoadSuperConstructor>(), "LoadSuperConstructor" },
2515 {symbol<LoadGlobalLookup>(), "LoadGlobalLookup" },
2516 {symbol<LoadQmlContextPropertyLookup>(), "LoadQmlContextPropertyLookup" },
2517 {symbol<GetLookup>(), "GetLookup" },
2518 {symbol<SetLookupStrict>(), "SetLookupStrict" },
2519 {symbol<SetLookupSloppy>(), "SetLookupSloppy" },
2520
2521 {symbol<TypeofValue>(), "TypeofValue" },
2522 {symbol<TypeofName>(), "TypeofName" },
2523
2524 {symbol<DeleteProperty_NoThrow>(), "DeleteProperty_NoThrow" },
2525 {symbol<DeleteProperty>(), "DeleteProperty" },
2526 {symbol<DeleteName_NoThrow>(), "DeleteName_NoThrow" },
2527 {symbol<DeleteName>(), "DeleteName" },
2528
2529 {symbol<ThrowException>(), "ThrowException" },
2530 {symbol<PushCallContext>(), "PushCallContext" },
2531 {symbol<PushWithContext>(), "PushWithContext" },
2532 {symbol<PushCatchContext>(), "PushCatchContext" },
2533 {symbol<PushBlockContext>(), "PushBlockContext" },
2534 {symbol<CloneBlockContext>(), "CloneBlockContext" },
2535 {symbol<PushScriptContext>(), "PushScriptContext" },
2536 {symbol<PopScriptContext>(), "PopScriptContext" },
2537 {symbol<ThrowReferenceError>(), "ThrowReferenceError" },
2538 {symbol<ThrowOnNullOrUndefined>(), "ThrowOnNullOrUndefined" },
2539
2540 {symbol<Closure>(), "Closure" },
2541
2542 {symbol<MarkCustom>(), "MarkCustom"},
2543
2544 {symbol<ConvertThisToObject>(), "ConvertThisToObject" },
2545 {symbol<DeclareVar>(), "DeclareVar" },
2546 {symbol<CreateMappedArgumentsObject>(), "CreateMappedArgumentsObject" },
2547 {symbol<CreateUnmappedArgumentsObject>(), "CreateUnmappedArgumentsObject" },
2548 {symbol<CreateRestParameter>(), "CreateRestParameter" },
2549
2550 {symbol<ArrayLiteral>(), "ArrayLiteral" },
2551 {symbol<ObjectLiteral>(), "ObjectLiteral" },
2552 {symbol<CreateClass>(), "CreateClass" },
2553
2554 {symbol<GetIterator>(), "GetIterator" },
2555 {symbol<IteratorNext>(), "IteratorNext" },
2556 {symbol<IteratorNextForYieldStar>(), "IteratorNextForYieldStar" },
2557 {symbol<IteratorClose>(), "IteratorClose" },
2558 {symbol<DestructureRestElement>(), "DestructureRestElement" },
2559
2560 {symbol<ToObject>(), "ToObject" },
2561 {symbol<ToBoolean>(), "ToBoolean" },
2562 {symbol<ToNumber>(), "ToNumber" },
2563
2564 {symbol<UMinus>(), "UMinus" },
2565
2566 {symbol<Instanceof>(), "Instanceof" },
2567 {symbol<As>(), "As" },
2568 {symbol<In>(), "In" },
2569 {symbol<Add>(), "Add" },
2570 {symbol<Sub>(), "Sub" },
2571 {symbol<Mul>(), "Mul" },
2572 {symbol<Div>(), "Div" },
2573 {symbol<Mod>(), "Mod" },
2574 {symbol<Exp>(), "Exp" },
2575 {symbol<BitAnd>(), "BitAnd" },
2576 {symbol<BitOr>(), "BitOr" },
2577 {symbol<BitXor>(), "BitXor" },
2578 {symbol<Shl>(), "Shl" },
2579 {symbol<Shr>(), "Shr" },
2580 {symbol<UShr>(), "UShr" },
2581 {symbol<GreaterThan>(), "GreaterThan" },
2582 {symbol<LessThan>(), "LessThan" },
2583 {symbol<GreaterEqual>(), "GreaterEqual" },
2584 {symbol<LessEqual>(), "LessEqual" },
2585 {symbol<Equal>(), "Equal" },
2586 {symbol<NotEqual>(), "NotEqual" },
2587 {symbol<StrictEqual>(), "StrictEqual" },
2588 {symbol<StrictNotEqual>(), "StrictNotEqual" },
2589
2590 {symbol<CompareGreaterThan>(), "CompareGreaterThan" },
2591 {symbol<CompareLessThan>(), "CompareLessThan" },
2592 {symbol<CompareGreaterEqual>(), "CompareGreaterEqual" },
2593 {symbol<CompareLessEqual>(), "CompareLessEqual" },
2594 {symbol<CompareEqual>(), "CompareEqual" },
2595 {symbol<CompareNotEqual>(), "CompareNotEqual" },
2596 {symbol<CompareStrictEqual>(), "CompareStrictEqual" },
2597 {symbol<CompareStrictNotEqual>(), "CompareStrictNotEqual" },
2598
2599 {symbol<CompareInstanceof>(), "CompareInstanceOf" },
2600 {symbol<CompareIn>(), "CompareIn" },
2601
2602 {symbol<RegexpLiteral>(), "RegexpLiteral" },
2603 {symbol<GetTemplateObject>(), "GetTemplateObject" }
2604#endif
2605 });
2606
2607 return symbols;
2608}
2609
2610} // namespace QV4
2611
2612QT_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)