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
532 if (engine->hasException)
533 return Encode::undefined();
534 if (toPrimitive) {
536 if (engine->hasException)
537 return Encode::undefined();
538 if (!result->isPrimitive())
539 return engine->throwTypeError();
540 return result->asReturnedValue();
541 }
542
543 if (hint == engine->id_default())
544 hint = engine->id_number();
546}
547
548
550{
552
555
557 qSwap(meth1, meth2);
558 } else {
560 }
561
564
567 result = o->call(object, nullptr, 0);
568 if (engine->hasException)
569 return Encode::undefined();
570 if (result->isPrimitive())
571 return result->asReturnedValue();
572 }
573
574 if (engine->hasException)
575 return Encode::undefined();
576
577 conv = object->get(meth2);
579 result = o->call(object, nullptr, 0);
580 if (engine->hasException)
581 return Encode::undefined();
582 if (result->isPrimitive())
583 return result->asReturnedValue();
584 }
585
586 return engine->throwTypeError();
587}
588
589
591{
593 switch (value.type()) {
594 case Value::Undefined_Type:
595 engine->throwTypeError(QLatin1String("Value is undefined and could not be converted to an object"));
596 return nullptr;
597 case Value::Null_Type:
598 engine->throwTypeError(QLatin1String("Value is null and could not be converted to an object"));
599 return nullptr;
600 case Value::Boolean_Type:
602 case Value::Managed_Type:
604 if (!value.isString())
607 case Value::Integer_Type:
608 default: // double
610 }
611}
612
614{
615 redo:
616 switch (value.type()) {
617 case Value::Empty_Type:
618 Q_ASSERT(!"empty Value encountered");
620 case Value::Undefined_Type:
621 return engine->id_undefined()->d();
622 case Value::Null_Type:
623 return engine->id_null()->d();
624 case Value::Boolean_Type:
625 if (value.booleanValue())
626 return engine->id_true()->d();
627 else
628 return engine->id_false()->d();
629 case Value::Managed_Type: {
630 if (value.isString())
631 return static_cast<const String &>(value).d();
632 if (value.isSymbol()) {
633 engine->throwTypeError(QLatin1String("Cannot convert a symbol to a string."));
634 return nullptr;
635 }
638 if (value.isString())
639 return static_cast<const String &>(value).d();
640 goto redo;
641 }
642 case Value::Integer_Type:
644 default: // double
646 } // switch
647}
648
649// This is slightly different from the method above, as
650// the + operator requires a slightly different conversion
651static Heap::String *convert_to_string_add(ExecutionEngine *engine, Value value)
652{
653 return RuntimeHelpers::convertToString(engine, value, PREFERREDTYPE_HINT);
654}
655
657{
659
664 if (sleft || sright) {
665 if (!sleft) {
667 sleft = static_cast<String *>(pleft.ptr);
668 }
669 if (!sright) {
671 sright = static_cast<String *>(pright.ptr);
672 }
673 if (engine->hasException)
674 return Encode::undefined();
675 if (!sleft->d()->length())
676 return sright->asReturnedValue();
677 if (!sright->d()->length())
678 return sleft->asReturnedValue();
680 return (mm->alloc<ComplexString>(sleft->d(), sright->d()))->asReturnedValue();
681 }
682 double x = RuntimeHelpers::toNumber(pleft);
683 double y = RuntimeHelpers::toNumber(pright);
684 return Encode(x + y);
685}
686
691
708
709static Q_NEVER_INLINE ReturnedValue getElementIntFallback(ExecutionEngine *engine, const Value &object, uint idx)
710{
713
715 if (!o) {
716 if (const String *str = object.as<String>()) {
717 if (idx >= (uint)str->toQString().size()) {
718 return Encode::undefined();
719 }
720 const QString s = str->toQString().mid(idx, 1);
722 }
723
725 QString message = QStringLiteral("Cannot read property '%1' of %2").arg(idx).arg(object.toQStringNoThrow());
727 }
728
730 Q_ASSERT(!!o); // can't fail as null/undefined is covered above
731 }
732
733 if (o->arrayData() && !o->arrayData()->attrs) {
735 if (!v->isEmpty())
736 return v->asReturnedValue();
737 }
738
739 return o->get(idx);
740}
741
742static Q_NEVER_INLINE ReturnedValue getElementFallback(ExecutionEngine *engine, const Value &object, const Value &index)
743{
745
747
749 if (!o) {
751 const QString message =
752 QStringLiteral("Cannot read property '%1' of %2")
755 }
756
758 Q_ASSERT(!!o); // can't fail as null/undefined is covered above
759 }
760
762 if (scope.hasException())
763 return Encode::undefined();
764 return o->get(name);
765}
766
768{
769 if (index.isPositiveInt()) {
770 uint idx = static_cast<uint>(index.int_32());
771 if (Heap::Base *b = object.heapObject()) {
773 Heap::Object *o = static_cast<Heap::Object *>(b);
774 if (o->arrayData && o->arrayData->type == Heap::ArrayData::Simple) {
776 if (idx < s->values.size)
777 if (!s->data(idx).isEmpty())
778 return s->data(idx).asReturnedValue();
779 }
780 }
781 }
783 }
784
786}
787
788static Q_NEVER_INLINE bool setElementFallback(ExecutionEngine *engine, const Value &object, const Value &index, const Value &value)
789{
790 Scope scope(engine);
791 ScopedObject o(scope, object);
792 if (!o) {
793 if (engine->currentStackFrame->v4Function->isStrict()) {
794 engine->throwTypeError();
795 return false;
796 }
797
798 o = object.toObject(engine);
799 }
800 if (engine->hasException)
801 return false;
802
803 if (index.isPositiveInt()) {
804 uint idx = static_cast<uint>(index.int_32());
805 if (o->d()->arrayData && o->d()->arrayData->type == Heap::ArrayData::Simple) {
806 Heap::SimpleArrayData *s = o->d()->arrayData.cast<Heap::SimpleArrayData>();
807 if (idx < s->values.size) {
808 s->setData(engine, idx, value);
809 return true;
810 }
811 }
812 return o->put(idx, value);
813 }
814
815 ScopedPropertyKey name(scope, index.toPropertyKey(engine));
816 if (engine->hasException)
817 return false;
818 return o->put(name, value);
819}
820
822{
823 if (index.isPositiveInt()) {
824 uint idx = static_cast<uint>(index.int_32());
825 if (Heap::Base *b = object.heapObject()) {
827 Heap::Object *o = static_cast<Heap::Object *>(b);
828 if (o->arrayData && o->arrayData->type == Heap::ArrayData::Simple) {
830 if (idx < s->values.size) {
832 return;
833 }
834 }
835 }
836 }
837 }
838
841}
842
844{
846 ScopedObject o(scope, (Object *)nullptr);
847 if (!in.isNullOrUndefined())
848 o = in.toObject(engine);
849 if (engine->hasException)
850 return Encode::undefined();
851 if (iterator == static_cast<int>(QQmlJS::AST::ForEachType::Of)) {
852 if (!o)
853 return engine->throwTypeError();
855 if (!f)
856 return engine->throwTypeError();
857 JSCallData cData(o, nullptr, 0);
859 if (engine->hasException)
860 return Encode::undefined();
861 if (!it)
862 return engine->throwTypeError();
863 return it->asReturnedValue();
864 }
866}
867
869{
870 // if we throw an exception from here, return true, not undefined. This is to ensure iteratorDone is set to true
871 // and the stack unwinding won't close the iterator
873
875 ScopedFunctionObject f(scope, static_cast<const Object &>(iterator).get(engine->id_next()));
876 if (!f) {
878 return Encode(true);
879 }
880 JSCallData cData(&iterator, nullptr, 0);
882 if (scope.hasException())
883 return Encode(true);
884 if (!o) {
886 return Encode(true);
887 }
888
890 if (scope.hasException())
891 return Encode(true);
892 bool done = d->toBoolean();
893 if (done) {
894 *value = Encode::undefined();
895 return Encode(true);
896 }
897
898 *value = o->get(engine->id_value());
899 if (scope.hasException())
900 return Encode(true);
901 return Encode(false);
902}
903
905{
906 // the return value encodes how to continue the yield* iteration.
907 // true implies iteration is done, false for iteration to continue
908 // a return value of undefines is a special marker, that the iterator has been invoked with return()
909
912
913 const Value *arg = &received;
914 bool returnCalled = false;
915 FunctionObject *f = nullptr;
916 if (engine->hasException) {
917 if (engine->exceptionValue->isEmpty()) {
918 // generator called with return()
920 engine->hasException = false;
921
922 ScopedValue ret(scope, static_cast<const Object &>(iterator).get(engine->id_return()));
923 if (engine->hasException)
924 return Encode(true);
925 if (ret->isUndefined()) {
926 // propagate return()
927 return Encode::undefined();
928 }
929 returnCalled = true;
930 f = ret->as<FunctionObject>();
931 } else {
932 // generator called with throw
935 engine->hasException = false;
936
937 ScopedValue t(scope, static_cast<const Object &>(iterator).get(engine->id_throw()));
938 if (engine->hasException)
939 return Encode(true);
940 if (t->isUndefined()) {
941 // no throw method on the iterator
943 if (!engine->hasException)
945 return Encode(true);
946 }
947 f = t->as<FunctionObject>();
949 }
950 } else {
951 // generator called with next()
952 ScopedFunctionObject next(scope, static_cast<const Object &>(iterator).get(engine->id_next()));
953 f = next->as<FunctionObject>();
954 }
955
956 if (!f) {
958 return Encode(true);
959 }
960
962 if (scope.hasException())
963 return Encode(true);
964 if (!o) {
966 return Encode(true);
967 }
968
970 if (scope.hasException())
971 return Encode(true);
972 bool done = d->toBoolean();
973 if (done) {
974 *object = o->get(engine->id_value());
975 return (returnCalled && !engine->hasException) ? Encode::undefined() : Encode(true);
976 }
977 *object = o;
978 return Encode(false);
979}
980
982{
984
988 if (hadException) {
990 engine->hasException = false;
991 }
992
993 auto originalCompletion = [=]() {
994 if (hadException) {
997 }
998 return Encode::undefined();
999 };
1000
1001 ScopedValue ret(scope, static_cast<const Object &>(iterator).get(engine->id_return()));
1003 if (!ret->isUndefined()) {
1005 o = f->call(&iterator, nullptr, 0);
1007 return Encode::undefined();
1008 }
1009 if (hadException || ret->isUndefined())
1010 return originalCompletion();
1011
1012 if (!o)
1013 return engine->throwTypeError();
1014
1015 return originalCompletion();
1016}
1017
1019{
1021
1024 array->arrayCreate();
1025 uint index = 0;
1026 while (1) {
1029 if (engine->hasException)
1030 return Encode::undefined();
1032 if (done->booleanValue())
1033 break;
1034 array->arraySet(index, n);
1035 ++index;
1036 }
1037 return array->asReturnedValue();
1038}
1039
1049
1060
1062{
1065
1067 if (o)
1068 return o->get(name);
1069
1070 if (object.isNullOrUndefined()) {
1071 const QString message = QStringLiteral("Cannot read property '%1' of %2")
1073 return engine->throwTypeError(message);
1074 }
1075
1077 if (!o) // type error
1078 return Encode::undefined();
1079 return o->get(name);
1080}
1081
1088
1089static Object *getSuperBase(Scope &scope)
1090{
1091 Scoped<JavaScriptFunctionObject> f(scope);
1092 ScopedObject homeObject(scope);
1093 if (scope.engine->currentStackFrame->isJSTypesFrame()) {
1094 JSTypesStackFrame *frame = static_cast<JSTypesStackFrame *>(
1095 scope.engine->currentStackFrame);
1096
1097 if (frame->jsFrame->thisObject.isEmpty()) {
1098 scope.engine->throwReferenceError(
1099 QStringLiteral("Missing call to super()."), QString(), 0, 0);
1100 return nullptr;
1101 }
1102
1103 f = Value::fromStaticValue(frame->jsFrame->function);
1104 homeObject = f->getHomeObject();
1105 } else {
1106 Q_ASSERT(scope.engine->currentStackFrame->isMetaTypesFrame());
1107 MetaTypesStackFrame *frame = static_cast<MetaTypesStackFrame *>(
1108 scope.engine->currentStackFrame);
1109 if (frame->thisObject() == nullptr) {
1110 scope.engine->throwReferenceError(
1111 QStringLiteral("Missing call to super()."), QString(), 0, 0);
1112 return nullptr;
1113 }
1114 }
1115
1116 if (!homeObject) {
1117 ScopedContext ctx(scope, scope.engine->currentContext());
1118 Q_ASSERT(ctx);
1119 while (ctx) {
1120 if (CallContext *c = ctx->asCallContext()) {
1121 f = c->d()->function;
1122 QV4::Function *fn = f->function();
1123 if (fn && !fn->isArrowFunction() && fn->kind != Function::Eval)
1124 break;
1125 }
1126 ctx = ctx->d()->outer;
1127 }
1128 if (f)
1129 homeObject = f->getHomeObject();
1130 }
1131 if (!homeObject) {
1132 scope.engine->throwTypeError();
1133 return nullptr;
1134 }
1135 Q_ASSERT(homeObject);
1136 ScopedObject proto(scope, homeObject->getPrototypeOf());
1137 if (!proto) {
1138 scope.engine->throwTypeError();
1139 return nullptr;
1140 }
1141 return proto;
1142}
1143
1166
1193
1199
1205
1211
1213{
1216 l->setter(engine, const_cast<Value &>(base), value);
1217}
1218
1220{
1223 if (!l->setter(engine, const_cast<Value &>(base), value))
1225}
1226
1228{
1232 // ### TODO: fix line number
1234 QStringLiteral("super() already called."), QString(), 0, 0);
1235 }
1236 } else {
1239 if (frame->thisObject() != nullptr) {
1240 // ### TODO: fix line number
1242 QStringLiteral("super() already called."), QString(), 0, 0);
1243 }
1244 }
1245
1246 const FunctionObject *f = t.as<FunctionObject>();
1247 if (!f)
1248 return engine->throwTypeError();
1249 Heap::Object *c = static_cast<const Object &>(t).getPrototypeOf();
1250 if (!c->vtable()->callAsConstructor)
1251 return engine->throwTypeError();
1252 return c->asReturnedValue();
1253}
1254
1256{
1257 Q_ASSERT(x.type() != y.type() || (x.isManaged() && (x.isString() != y.isString())));
1258
1259 if (x.isNumber() && y.isNumber())
1260 return x.asDouble() == y.asDouble();
1261 if (x.isNull() && y.isUndefined()) {
1262 return true;
1263 } else if (x.isUndefined() && y.isNull()) {
1264 return true;
1265 } else if (x.isNumber() && y.isString()) {
1266 double dy = RuntimeHelpers::toNumber(y);
1267 return x.asDouble() == dy;
1268 } else if (x.isString() && y.isNumber()) {
1269 double dx = RuntimeHelpers::toNumber(x);
1270 return dx == y.asDouble();
1271 } else if (x.isBoolean()) {
1272 return Runtime::CompareEqual::call(Value::fromDouble((double) x.booleanValue()), y);
1273 } else if (y.isBoolean()) {
1274 return Runtime::CompareEqual::call(x, Value::fromDouble((double) y.booleanValue()));
1275 } else {
1276 Object *xo = x.objectValue();
1277 Object *yo = y.objectValue();
1278 if (yo && (x.isNumber() || x.isString())) {
1279 Scope scope(yo->engine());
1281 return Runtime::CompareEqual::call(x, py);
1282 } else if (xo && (y.isNumber() || y.isString())) {
1283 Scope scope(xo->engine());
1285 return Runtime::CompareEqual::call(px, y);
1286 }
1287 }
1288
1289 return false;
1290}
1291
1293{
1294 TRACE2(x, y);
1295
1296 if (x.rawValue() == y.rawValue())
1297 // NaN != NaN
1298 return !x.isNaN();
1299
1300 if (x.isNumber())
1301 return y.isNumber() && x.asDouble() == y.asDouble();
1302 if (x.isManaged()) {
1303 return y.isManaged() && x.cast<Managed>()->isEqualTo(y.cast<Managed>());
1304 }
1305 return false;
1306}
1307
1309{
1310 TRACE2(l, r);
1311 if (l.isInteger() && r.isInteger())
1312 return l.integerValue() > r.integerValue();
1313 if (l.isNumber() && r.isNumber())
1314 return l.asDouble() > r.asDouble();
1315 String *sl = l.stringValue();
1316 String *sr = r.stringValue();
1317 if (sl && sr) {
1318 return sr->lessThan(sl);
1319 }
1320
1321 Object *ro = r.objectValue();
1322 Object *lo = l.objectValue();
1323 if (ro || lo) {
1324 QV4::ExecutionEngine *e = (lo ? lo : ro)->engine();
1325 QV4::Scope scope(e);
1328 return Runtime::CompareGreaterThan::call(pl, pr);
1329 }
1330
1331 double dl = RuntimeHelpers::toNumber(l);
1332 double dr = RuntimeHelpers::toNumber(r);
1333 return dl > dr;
1334}
1335
1337{
1338 TRACE2(l, r);
1339 if (l.isInteger() && r.isInteger())
1340 return l.integerValue() < r.integerValue();
1341 if (l.isNumber() && r.isNumber())
1342 return l.asDouble() < r.asDouble();
1343 String *sl = l.stringValue();
1344 String *sr = r.stringValue();
1345 if (sl && sr) {
1346 return sl->lessThan(sr);
1347 }
1348
1349 Object *ro = r.objectValue();
1350 Object *lo = l.objectValue();
1351 if (ro || lo) {
1352 QV4::ExecutionEngine *e = (lo ? lo : ro)->engine();
1353 QV4::Scope scope(e);
1356 return Runtime::CompareLessThan::call(pl, pr);
1357 }
1358
1359 double dl = RuntimeHelpers::toNumber(l);
1360 double dr = RuntimeHelpers::toNumber(r);
1361 return dl < dr;
1362}
1363
1365{
1366 TRACE2(l, r);
1367 if (l.isInteger() && r.isInteger())
1368 return l.integerValue() >= r.integerValue();
1369 if (l.isNumber() && r.isNumber())
1370 return l.asDouble() >= r.asDouble();
1371 String *sl = l.stringValue();
1372 String *sr = r.stringValue();
1373 if (sl && sr) {
1374 return !sl->lessThan(sr);
1375 }
1376
1377 Object *ro = r.objectValue();
1378 Object *lo = l.objectValue();
1379 if (ro || lo) {
1380 QV4::ExecutionEngine *e = (lo ? lo : ro)->engine();
1381 QV4::Scope scope(e);
1385 }
1386
1387 double dl = RuntimeHelpers::toNumber(l);
1388 double dr = RuntimeHelpers::toNumber(r);
1389 return dl >= dr;
1390}
1391
1393{
1394 TRACE2(l, r);
1395 if (l.isInteger() && r.isInteger())
1396 return l.integerValue() <= r.integerValue();
1397 if (l.isNumber() && r.isNumber())
1398 return l.asDouble() <= r.asDouble();
1399 String *sl = l.stringValue();
1400 String *sr = r.stringValue();
1401 if (sl && sr) {
1402 return !sr->lessThan(sl);
1403 }
1404
1405 Object *ro = r.objectValue();
1406 Object *lo = l.objectValue();
1407 if (ro || lo) {
1408 QV4::ExecutionEngine *e = (lo ? lo : ro)->engine();
1409 QV4::Scope scope(e);
1412 return Runtime::CompareLessEqual::call(pl, pr);
1413 }
1414
1415 double dl = RuntimeHelpers::toNumber(l);
1416 double dr = RuntimeHelpers::toNumber(r);
1417 return dl <= dr;
1418}
1419
1421{
1422 TRACE2(left, right);
1423
1426 return v->booleanValue();
1427}
1428
1430{
1431 TRACE2(left, right);
1432
1435 return v->booleanValue();
1436}
1437
1438static ReturnedValue throwPropertyIsNotAFunctionTypeError(ExecutionEngine *engine, Value *thisObject, const QString &propertyName)
1439{
1440 QString objectAsString = QStringLiteral("[null]");
1441 if (!thisObject->isUndefined())
1442 objectAsString = thisObject->toQStringNoThrow();
1443 QString msg = QStringLiteral("Property '%1' of object %2 is not a function")
1444 .arg(propertyName, objectAsString);
1445 return engine->throwTypeError(msg);
1446}
1447
1462
1478
1497
1516
1518{
1519 const Value *base = &baseRef;
1522 scope,
1525
1526 if (!lookupObject) {
1527 Q_ASSERT(!base->isEmpty());
1528 if (base->isNullOrUndefined()) {
1529 QString message = QStringLiteral("Cannot call method '%1' of %2")
1531 return engine->throwTypeError(message);
1532 }
1533
1534 if (base->isManaged()) {
1535 const Managed *m = static_cast<const Managed *>(base);
1538 } else {
1540 if (engine->hasException) // type error
1541 return Encode::undefined();
1544 }
1545 }
1546
1548
1549 if (!f) {
1550 QString error = QStringLiteral("Property '%1' of object %2 is not a function")
1551 .arg(name->toQString(),
1553 return engine->throwTypeError(error);
1554 }
1555
1556 return checkedResult(engine, f->call(base, argv, argc));
1557}
1558
1560{
1562 // ok to have the value on the stack here
1564
1566 return checkedResult(engine, static_cast<FunctionObject &>(f).call(&base, argv, argc));
1567
1570
1571 const QString message = QStringLiteral("Property '%1' of object %2 is not a function")
1574 ->toQString(),
1576 return engine->throwTypeError(message);
1577}
1578
1580{
1581 if (!func.isFunctionObject())
1582 return engine->throwTypeError(QStringLiteral("%1 is not a function").arg(func.toQStringNoThrow()));
1584 return checkedResult(engine, static_cast<const FunctionObject &>(func).call(
1585 &undef, argv, argc));
1586}
1587
1589 const Value &thisObject, Value argv[], int argc)
1590{
1591 if (!func.isFunctionObject())
1592 return engine->throwTypeError(QStringLiteral("%1 is not a function").arg(func.toQStringNoThrow()));
1593 return checkedResult(engine, static_cast<const FunctionObject &>(func).call(
1594 &thisObject, argv, argc));
1595}
1596
1597struct CallArgs {
1598 Value *argv;
1599 int argc;
1600};
1601
1602static CallArgs createSpreadArguments(Scope &scope, Value *argv, int argc)
1603{
1604 ScopedValue it(scope);
1605 ScopedValue done(scope);
1606
1607 int argCount = 0;
1608
1609 Value *v = scope.constructUndefined(1);
1610 Value *arguments = v;
1611 for (int i = 0; i < argc; ++i) {
1612 if (!argv[i].isEmpty()) {
1613 *v = argv[i];
1614 ++argCount;
1615 v = scope.constructUndefined(1);
1616 continue;
1617 }
1618 // spread element
1619 ++i;
1620 it = Runtime::GetIterator::call(scope.engine, argv[i], /* ForInIterator */ 1);
1621 if (scope.hasException())
1622 return { nullptr, 0 };
1623 while (1) {
1624 done = Runtime::IteratorNext::call(scope.engine, it, v);
1625 if (scope.hasException())
1626 return { nullptr, 0 };
1627 Q_ASSERT(done->isBoolean());
1628 if (done->booleanValue())
1629 break;
1630 ++argCount;
1631 constexpr auto safetyMargin = 100; // leave some space on the stack for actual work with the elements
1632 if (qint64(scope.engine->jsStackLimit - scope.engine->jsStackTop) < safetyMargin) {
1633 scope.engine->throwRangeError(QLatin1String("Too many elements in array to use it with the spread operator"));
1634 return { nullptr, 0 };
1635 }
1636 v = scope.constructUndefined(1);
1637 }
1638 }
1639 return { arguments, argCount };
1640}
1641
1656
1658{
1660 return engine->throwTypeError();
1661
1662 return static_cast<const FunctionObject &>(function).callAsConstructor(argv, argc, &newTarget);
1663}
1664
1677
1679{
1680 // IMPORTANT! The JIT assumes that this method has the same amount (or less) arguments than
1681 // the jitted function, so it can safely do a tail call.
1682
1686 Value *argv = reinterpret_cast<Value *>(frame->jsFrame) + tos[StackOffsets::tailCall_argv].int_32();
1688 Q_ASSERT(argc >= 0);
1689
1691 if (!jsfo) {
1692 if (const FunctionObject *fo = function.as<FunctionObject>())
1694 return engine->throwTypeError();
1695 }
1696
1698 || unsigned(argc) > jsfo->formalParameterCount()) {
1699 // Cannot tailcall, do a normal call:
1701 }
1702
1703 memmove(frame->jsFrame->args, argv, argc * sizeof(Value));
1710 return Encode::undefined();
1711}
1712
1718
1720{
1723 switch (value.type()) {
1724 case Value::Undefined_Type:
1725 res = engine->id_undefined();
1726 break;
1727 case Value::Null_Type:
1728 res = engine->id_object();
1729 break;
1730 case Value::Boolean_Type:
1731 res = engine->id_boolean();
1732 break;
1733 case Value::Managed_Type:
1734 if (value.isString())
1735 res = engine->id_string();
1736 else if (value.isSymbol())
1737 res = engine->id_symbol();
1738 else if (value.objectValue()->as<FunctionObject>())
1739 res = engine->id_function();
1740 else
1741 res = engine->id_object(); // ### implementation-defined
1742 break;
1743 default:
1744 res = engine->id_number();
1745 break;
1746 }
1747 return res.asReturnedValue();
1748}
1749
1759
1764
1780
1788
1795
1805
1815
1823
1830
1836
1838{
1839 auto *h = toBeMarked.heapObject();
1840 if (!h)
1841 return;
1843 auto engine = h->internalClass->engine;
1845 // runtime function is only meant to be called while gc is ongoing
1848 h->mark(ms);
1849 });
1850}
1851
1853{
1854 if (!t.isObject()) {
1855 if (t.isNullOrUndefined()) {
1857 } else {
1858 return t.toObject(engine)->asReturnedValue();
1859 }
1860 }
1861 return t.asReturnedValue();
1862}
1863
1870
1875
1877{
1881
1882 Q_ASSERT(uint(argc) >= klass->d()->size);
1883
1884 for (uint i = 0; i < klass->d()->size; ++i)
1885 o->setProperty(i, *args++);
1886
1887 Q_ASSERT((argc - klass->d()->size) % 3 == 0);
1888 int additionalArgs = (argc - int(klass->d()->size))/3;
1889
1890 if (!additionalArgs)
1891 return o->asReturnedValue();
1892
1898 for (int i = 0; i < additionalArgs; ++i) {
1902 value = args[2];
1903 if (engine->hasException)
1904 return Encode::undefined();
1906 Q_ASSERT(args[2].isInteger());
1907 int functionId = args[2].integerValue();
1910 Q_ASSERT(clos);
1911
1915 else if (arg == ObjectLiteralArgument::Setter)
1917 else
1920
1922 if (clos->isGenerator())
1924 else
1926 } else if (args[2].isFunctionObject()) {
1927 fn = static_cast<const FunctionObject &>(args[2]);
1928
1930 fn->setName(fnName);
1931 }
1935 pd->value = value;
1936 pd->set = Value::emptyValue();
1937 } else {
1938 pd->value = Value::emptyValue();
1939 pd->set = value;
1940 }
1942 if (!ok)
1943 return engine->throwTypeError();
1944
1945 args += 3;
1946 }
1947 return o.asReturnedValue();
1948}
1949
1952{
1956
1960 if (!superClass.isEmpty()) {
1961 if (superClass.isNull()) {
1962 protoParent = Encode::null();
1963 } else {
1965 // ### check that the heritage object is a constructor
1967 return engine->throwTypeError(QStringLiteral("The superclass is not a function object."));
1968 const FunctionObject *s = static_cast<const FunctionObject *>(&superClass);
1970 if (!result->isObject() && !result->isNull())
1971 return engine->throwTypeError(QStringLiteral("The value of the superclass's prototype property is not an object."));
1974 }
1975 }
1976
1980
1989
1991 if (cls->nameIndex != UINT_MAX) {
1994 }
1995
2001 for (uint i = 0; i < cls->nStaticMethods + cls->nMethods; ++i) {
2002 if (i == cls->nStaticMethods)
2003 receiver = proto;
2004 if (methods[i].name == UINT_MAX) {
2007 return engine->throwTypeError(QStringLiteral("Cannot declare a static method named 'prototype'."));
2008 if (engine->hasException)
2009 return Encode::undefined();
2010 ++computedNames;
2011 } else {
2014 }
2016 Q_ASSERT(f);
2020 else if (methods[i].type == CompiledData::Method::Setter)
2022
2024
2025 if (f->isGenerator())
2027 else
2031 switch (methods[i].type) {
2032 case CompiledData::Method::Getter:
2036 break;
2037 case CompiledData::Method::Setter:
2041 break;
2042 default: // Regular
2046 break;
2047 }
2049 }
2050
2051 return constructor->asReturnedValue();
2052}
2053
2060
2068
2079
2087
2089{
2090 if (obj.isObject())
2091 return obj.asReturnedValue();
2092
2094}
2095
2097{
2098 return obj.toBoolean();
2099}
2100
2102{
2103 return Encode(v.toNumber());
2104}
2105
2107{
2108 TRACE1(value);
2109
2110 // +0 != -0, so we need to convert to double when negating 0
2111 if (value.isInteger() && value.integerValue() &&
2112 value.integerValue() != std::numeric_limits<int>::min())
2113 return Encode(-value.integerValue());
2114 else {
2115 double n = RuntimeHelpers::toNumber(value);
2116 return Encode(-n);
2117 }
2118}
2119
2120// binary operators
2121
2133
2135{
2136 TRACE2(left, right);
2137
2140
2141 double lval = left.isNumber() ? left.asDouble() : left.toNumberImpl();
2142 double rval = right.isNumber() ? right.asDouble() : right.toNumberImpl();
2143
2145}
2146
2148{
2149 TRACE2(left, right);
2150
2153
2154 double lval = left.isNumber() ? left.asDouble() : left.toNumberImpl();
2155 double rval = right.isNumber() ? right.asDouble() : right.toNumberImpl();
2156
2158}
2159
2161{
2162 TRACE2(left, right);
2163
2165 int lval = left.integerValue();
2166 int rval = right.integerValue();
2167 if (rval != 0 // division by zero should result in a NaN
2168 && !(lval == std::numeric_limits<int>::min() && rval == -1) // doesn't fit in int
2169 && (lval % rval == 0) // fractions can't be stored in an int
2170 && !(lval == 0 && rval < 0)) // 0 / -something results in -0.0
2171 return Encode(int(lval / rval));
2172 else
2173 return Encode(double(lval) / rval);
2174 }
2175
2176 double lval = left.toNumber();
2177 double rval = right.toNumber();
2179}
2180
2182{
2183 TRACE2(left, right);
2184
2186 // special cases are handled by fmod, among them:
2187 // - arithmic execeptions for ints in c++, eg: INT_MIN % -1
2188 // - undefined behavior in c++, e.g.: anything % 0
2189 // - uncommon cases which would complicate the condition, e.g.: negative integers
2190 // (this makes sure that -1 % 1 == -0 by passing it to fmod)
2192 }
2193
2194 double lval = RuntimeHelpers::toNumber(left);
2195 double rval = RuntimeHelpers::toNumber(right);
2196#ifdef fmod
2197# undef fmod
2198#endif
2200}
2201
2203{
2204 double b = base.toNumber();
2205 double e = exp.toNumber();
2206 return Encode(QQmlPrivate::jsExponentiate(b, e));
2207}
2208
2210{
2211 TRACE2(left, right);
2212
2213 int lval = left.toInt32();
2214 int rval = right.toInt32();
2215 return Encode((int)(lval & rval));
2216}
2217
2219{
2220 TRACE2(left, right);
2221
2222 int lval = left.toInt32();
2223 int rval = right.toInt32();
2224 return Encode((int)(lval | rval));
2225}
2226
2228{
2229 TRACE2(left, right);
2230
2231 int lval = left.toInt32();
2232 int rval = right.toInt32();
2233 return Encode((int)(lval ^ rval));
2234}
2235
2237{
2238 TRACE2(left, right);
2239
2240 int lval = left.toInt32();
2241 int rval = right.toInt32() & 0x1f;
2242 return Encode((int)(lval << rval));
2243}
2244
2246{
2247 TRACE2(left, right);
2248
2249 int lval = left.toInt32();
2250 unsigned rval = right.toUInt32() & 0x1f;
2251 return Encode((int)(lval >> rval));
2252}
2253
2255{
2256 TRACE2(left, right);
2257
2258 unsigned lval = left.toUInt32();
2259 unsigned rval = right.toUInt32() & 0x1f;
2260 uint res = lval >> rval;
2261
2262 return Encode(res);
2263}
2264
2266{
2267 TRACE2(left, right);
2268
2270 return Encode(r);
2271}
2272
2274{
2275 TRACE2(left, right);
2276
2277 bool r = CompareLessThan::call(left, right);
2278 return Encode(r);
2279}
2280
2282{
2283 TRACE2(left, right);
2284
2286 return Encode(r);
2287}
2288
2290{
2291 TRACE2(left, right);
2292
2293 bool r = CompareLessEqual::call(left, right);
2294 return Encode(r);
2295}
2296
2298{
2299 ExecutionEngine *engine = nullptr;
2300 Value *stackMark = nullptr;
2302 if (engine)
2303 engine->jsStackTop = stackMark;
2304 }
2305 template <typename T>
2306 void set(Value **scopedValue, T value, ExecutionEngine *e) {
2307 if (!engine) {
2308 engine = e;
2309 stackMark = engine->jsStackTop;
2310 }
2311 if (!*scopedValue)
2312 *scopedValue = e->jsAlloca(1);
2313 **scopedValue = value;
2314 }
2315};
2316
2318{
2319 TRACE2(left, right);
2320
2321 Value lhs = left;
2322 Value rhs = right;
2323
2325 Value *lhsGuard = nullptr;
2326 Value *rhsGuard = nullptr;
2327
2328 redo:
2330 return !lhs.isNaN();
2331
2332 quint32 lt = lhs.quickType();
2333 quint32 rt = rhs.quickType();
2334
2335 // LHS: Check if managed
2336 if ((lt & (Value::ManagedMask >> Value::Tag_Shift)) == 0) {
2337 if (lhs.isUndefined())
2338 return rhs.isNullOrUndefined();
2339
2340 // RHS: Check if managed
2341 if ((rt & (Value::ManagedMask >> Value::Tag_Shift)) == 0) {
2342 if (rhs.isUndefined())
2343 return false;
2344
2345 Heap::Base *l = lhs.m();
2346 Heap::Base *r = rhs.m();
2347 Q_ASSERT(l);
2348 Q_ASSERT(r);
2350 return static_cast<QV4::Managed &>(lhs).isEqualTo(&static_cast<QV4::Managed &>(rhs));
2354 goto redo;
2355 } else {
2359 goto redo;
2360 }
2361 return false;
2362 }
2363
2365 switch (rt) {
2366 case QV4::Value::QT_Empty:
2367 Q_UNREACHABLE();
2368 case QV4::Value::QT_Null:
2369 return false;
2370 case QV4::Value::QT_Bool:
2371 case QV4::Value::QT_Int:
2373 Q_FALLTHROUGH();
2374 default: // double
2376 return lhs.m()->internalClass->vtable->isString ? (RuntimeHelpers::toNumber(lhs) == rhs.doubleValue()) : false;
2377 } else {
2380 }
2381 }
2382 goto redo;
2383 } else if ((rt & (Value::ManagedMask >> Value::Tag_Shift)) == 0) {
2384 if (rhs.isUndefined())
2385 return lhs.isNull(); // Can't be undefined
2386 qSwap(lhs, rhs);
2387 qSwap(lt, rt);
2389 }
2390
2391 switch (lt) {
2392 case QV4::Value::QT_Empty:
2393 Q_UNREACHABLE();
2394 case QV4::Value::QT_Null:
2395 return rhs.isNull();
2396 case QV4::Value::QT_Bool:
2397 case QV4::Value::QT_Int:
2398 switch (rt) {
2399 case QV4::Value::QT_Empty:
2400 Q_UNREACHABLE();
2401 case QV4::Value::QT_Null:
2402 return false;
2403 case QV4::Value::QT_Bool:
2404 case QV4::Value::QT_Int:
2405 return lhs.int_32() == rhs.int_32();
2406 default: // double
2407 return lhs.int_32() == rhs.doubleValue();
2408 }
2409 default: // double
2410 switch (rt) {
2411 case QV4::Value::QT_Empty:
2412 Q_UNREACHABLE();
2413 case QV4::Value::QT_Null:
2414 return false;
2415 case QV4::Value::QT_Bool:
2416 case QV4::Value::QT_Int:
2417 return lhs.doubleValue() == rhs.int_32();
2418 default: // double
2419 return lhs.doubleValue() == rhs.doubleValue();
2420 }
2421 }
2422}
2423
2425{
2426 TRACE2(left, right);
2427
2428 bool r = CompareEqual::call(left, right);
2429 return Encode(r);
2430}
2431
2433{
2434 TRACE2(left, right);
2435
2436 bool r = !CompareEqual::call(left, right);
2437 return Encode(r);
2438}
2439
2441{
2442 TRACE2(left, right);
2443
2445 return Encode(r);
2446}
2447
2449{
2450 TRACE2(left, right);
2451
2453 return Encode(r);
2454}
2455
2457{
2458 TRACE2(left, right);
2459
2460 return !Runtime::CompareEqual::call(left, right);
2461}
2462
2464{
2465 TRACE2(left, right);
2466
2468}
2469
2471{
2472 TRACE2(left, right);
2473
2475}
2476
2477template<typename Operation>
2478static inline const void *symbol()
2479{
2480 return reinterpret_cast<void *>(&Operation::call);
2481}
2482
2483QHash<const void *, const char *> Runtime::symbolTable()
2484{
2485 static const QHash<const void *, const char *> symbols({
2486#ifndef V4_BOOTSTRAP
2487 {symbol<CallGlobalLookup>(), "CallGlobalLookup" },
2488 {symbol<CallQmlContextPropertyLookup>(), "CallQmlContextPropertyLookup" },
2489 {symbol<CallName>(), "CallName" },
2490 {symbol<CallProperty>(), "CallProperty" },
2491 {symbol<CallPropertyLookup>(), "CallPropertyLookup" },
2492 {symbol<CallValue>(), "CallValue" },
2493 {symbol<CallWithReceiver>(), "CallWithReceiver" },
2494 {symbol<CallPossiblyDirectEval>(), "CallPossiblyDirectEval" },
2495 {symbol<CallWithSpread>(), "CallWithSpread" },
2496 {symbol<TailCall>(), "TailCall" },
2497
2498 {symbol<Construct>(), "Construct" },
2499 {symbol<ConstructWithSpread>(), "ConstructWithSpread" },
2500
2501 {symbol<StoreNameStrict>(), "StoreNameStrict" },
2502 {symbol<StoreNameSloppy>(), "StoreNameSloppy" },
2503 {symbol<StoreProperty>(), "StoreProperty" },
2504 {symbol<StoreElement>(), "StoreElement" },
2505 {symbol<LoadProperty>(), "LoadProperty" },
2506 {symbol<LoadName>(), "LoadName" },
2507 {symbol<LoadElement>(), "LoadElement" },
2508 {symbol<LoadSuperProperty>(), "LoadSuperProperty" },
2509 {symbol<StoreSuperProperty>(), "StoreSuperProperty" },
2510 {symbol<LoadSuperConstructor>(), "LoadSuperConstructor" },
2511 {symbol<LoadGlobalLookup>(), "LoadGlobalLookup" },
2512 {symbol<LoadQmlContextPropertyLookup>(), "LoadQmlContextPropertyLookup" },
2513 {symbol<GetLookup>(), "GetLookup" },
2514 {symbol<SetLookupStrict>(), "SetLookupStrict" },
2515 {symbol<SetLookupSloppy>(), "SetLookupSloppy" },
2516
2517 {symbol<TypeofValue>(), "TypeofValue" },
2518 {symbol<TypeofName>(), "TypeofName" },
2519
2520 {symbol<DeleteProperty_NoThrow>(), "DeleteProperty_NoThrow" },
2521 {symbol<DeleteProperty>(), "DeleteProperty" },
2522 {symbol<DeleteName_NoThrow>(), "DeleteName_NoThrow" },
2523 {symbol<DeleteName>(), "DeleteName" },
2524
2525 {symbol<ThrowException>(), "ThrowException" },
2526 {symbol<PushCallContext>(), "PushCallContext" },
2527 {symbol<PushWithContext>(), "PushWithContext" },
2528 {symbol<PushCatchContext>(), "PushCatchContext" },
2529 {symbol<PushBlockContext>(), "PushBlockContext" },
2530 {symbol<CloneBlockContext>(), "CloneBlockContext" },
2531 {symbol<PushScriptContext>(), "PushScriptContext" },
2532 {symbol<PopScriptContext>(), "PopScriptContext" },
2533 {symbol<ThrowReferenceError>(), "ThrowReferenceError" },
2534 {symbol<ThrowOnNullOrUndefined>(), "ThrowOnNullOrUndefined" },
2535
2536 {symbol<Closure>(), "Closure" },
2537
2538 {symbol<MarkCustom>(), "MarkCustom"},
2539
2540 {symbol<ConvertThisToObject>(), "ConvertThisToObject" },
2541 {symbol<DeclareVar>(), "DeclareVar" },
2542 {symbol<CreateMappedArgumentsObject>(), "CreateMappedArgumentsObject" },
2543 {symbol<CreateUnmappedArgumentsObject>(), "CreateUnmappedArgumentsObject" },
2544 {symbol<CreateRestParameter>(), "CreateRestParameter" },
2545
2546 {symbol<ArrayLiteral>(), "ArrayLiteral" },
2547 {symbol<ObjectLiteral>(), "ObjectLiteral" },
2548 {symbol<CreateClass>(), "CreateClass" },
2549
2550 {symbol<GetIterator>(), "GetIterator" },
2551 {symbol<IteratorNext>(), "IteratorNext" },
2552 {symbol<IteratorNextForYieldStar>(), "IteratorNextForYieldStar" },
2553 {symbol<IteratorClose>(), "IteratorClose" },
2554 {symbol<DestructureRestElement>(), "DestructureRestElement" },
2555
2556 {symbol<ToObject>(), "ToObject" },
2557 {symbol<ToBoolean>(), "ToBoolean" },
2558 {symbol<ToNumber>(), "ToNumber" },
2559
2560 {symbol<UMinus>(), "UMinus" },
2561
2562 {symbol<Instanceof>(), "Instanceof" },
2563 {symbol<As>(), "As" },
2564 {symbol<In>(), "In" },
2565 {symbol<Add>(), "Add" },
2566 {symbol<Sub>(), "Sub" },
2567 {symbol<Mul>(), "Mul" },
2568 {symbol<Div>(), "Div" },
2569 {symbol<Mod>(), "Mod" },
2570 {symbol<Exp>(), "Exp" },
2571 {symbol<BitAnd>(), "BitAnd" },
2572 {symbol<BitOr>(), "BitOr" },
2573 {symbol<BitXor>(), "BitXor" },
2574 {symbol<Shl>(), "Shl" },
2575 {symbol<Shr>(), "Shr" },
2576 {symbol<UShr>(), "UShr" },
2577 {symbol<GreaterThan>(), "GreaterThan" },
2578 {symbol<LessThan>(), "LessThan" },
2579 {symbol<GreaterEqual>(), "GreaterEqual" },
2580 {symbol<LessEqual>(), "LessEqual" },
2581 {symbol<Equal>(), "Equal" },
2582 {symbol<NotEqual>(), "NotEqual" },
2583 {symbol<StrictEqual>(), "StrictEqual" },
2584 {symbol<StrictNotEqual>(), "StrictNotEqual" },
2585
2586 {symbol<CompareGreaterThan>(), "CompareGreaterThan" },
2587 {symbol<CompareLessThan>(), "CompareLessThan" },
2588 {symbol<CompareGreaterEqual>(), "CompareGreaterEqual" },
2589 {symbol<CompareLessEqual>(), "CompareLessEqual" },
2590 {symbol<CompareEqual>(), "CompareEqual" },
2591 {symbol<CompareNotEqual>(), "CompareNotEqual" },
2592 {symbol<CompareStrictEqual>(), "CompareStrictEqual" },
2593 {symbol<CompareStrictNotEqual>(), "CompareStrictNotEqual" },
2594
2595 {symbol<CompareInstanceof>(), "CompareInstanceOf" },
2596 {symbol<CompareIn>(), "CompareIn" },
2597
2598 {symbol<RegexpLiteral>(), "RegexpLiteral" },
2599 {symbol<GetTemplateObject>(), "GetTemplateObject" }
2600#endif
2601 });
2602
2603 return symbols;
2604}
2605
2606} // namespace QV4
2607
2608QT_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)