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 QVector<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 QString message = QStringLiteral("Cannot read property '%1' of %2").arg(index.toQStringNoThrow()).arg(object.toQStringNoThrow());
753 }
754
756 Q_ASSERT(!!o); // can't fail as null/undefined is covered above
757 }
758
760 if (scope.hasException())
761 return Encode::undefined();
762 return o->get(name);
763}
764
766{
767 if (index.isPositiveInt()) {
768 uint idx = static_cast<uint>(index.int_32());
769 if (Heap::Base *b = object.heapObject()) {
771 Heap::Object *o = static_cast<Heap::Object *>(b);
772 if (o->arrayData && o->arrayData->type == Heap::ArrayData::Simple) {
774 if (idx < s->values.size)
775 if (!s->data(idx).isEmpty())
776 return s->data(idx).asReturnedValue();
777 }
778 }
779 }
781 }
782
784}
785
786static Q_NEVER_INLINE bool setElementFallback(ExecutionEngine *engine, const Value &object, const Value &index, const Value &value)
787{
788 Scope scope(engine);
789 ScopedObject o(scope, object);
790 if (!o) {
791 if (engine->currentStackFrame->v4Function->isStrict()) {
792 engine->throwTypeError();
793 return false;
794 }
795
796 o = object.toObject(engine);
797 }
798 if (engine->hasException)
799 return false;
800
801 if (index.isPositiveInt()) {
802 uint idx = static_cast<uint>(index.int_32());
803 if (o->d()->arrayData && o->d()->arrayData->type == Heap::ArrayData::Simple) {
804 Heap::SimpleArrayData *s = o->d()->arrayData.cast<Heap::SimpleArrayData>();
805 if (idx < s->values.size) {
806 s->setData(engine, idx, value);
807 return true;
808 }
809 }
810 return o->put(idx, value);
811 }
812
813 ScopedPropertyKey name(scope, index.toPropertyKey(engine));
814 if (engine->hasException)
815 return false;
816 return o->put(name, value);
817}
818
820{
821 if (index.isPositiveInt()) {
822 uint idx = static_cast<uint>(index.int_32());
823 if (Heap::Base *b = object.heapObject()) {
825 Heap::Object *o = static_cast<Heap::Object *>(b);
826 if (o->arrayData && o->arrayData->type == Heap::ArrayData::Simple) {
828 if (idx < s->values.size) {
830 return;
831 }
832 }
833 }
834 }
835 }
836
839}
840
842{
844 ScopedObject o(scope, (Object *)nullptr);
845 if (!in.isNullOrUndefined())
846 o = in.toObject(engine);
847 if (engine->hasException)
848 return Encode::undefined();
849 if (iterator == static_cast<int>(QQmlJS::AST::ForEachType::Of)) {
850 if (!o)
851 return engine->throwTypeError();
853 if (!f)
854 return engine->throwTypeError();
855 JSCallData cData(o, nullptr, 0);
857 if (engine->hasException)
858 return Encode::undefined();
859 if (!it)
860 return engine->throwTypeError();
861 return it->asReturnedValue();
862 }
864}
865
867{
868 // if we throw an exception from here, return true, not undefined. This is to ensure iteratorDone is set to true
869 // and the stack unwinding won't close the iterator
871
873 ScopedFunctionObject f(scope, static_cast<const Object &>(iterator).get(engine->id_next()));
874 if (!f) {
876 return Encode(true);
877 }
878 JSCallData cData(&iterator, nullptr, 0);
880 if (scope.hasException())
881 return Encode(true);
882 if (!o) {
884 return Encode(true);
885 }
886
888 if (scope.hasException())
889 return Encode(true);
890 bool done = d->toBoolean();
891 if (done) {
892 *value = Encode::undefined();
893 return Encode(true);
894 }
895
896 *value = o->get(engine->id_value());
897 if (scope.hasException())
898 return Encode(true);
899 return Encode(false);
900}
901
903{
904 // the return value encodes how to continue the yield* iteration.
905 // true implies iteration is done, false for iteration to continue
906 // a return value of undefines is a special marker, that the iterator has been invoked with return()
907
910
911 const Value *arg = &received;
912 bool returnCalled = false;
913 FunctionObject *f = nullptr;
914 if (engine->hasException) {
915 if (engine->exceptionValue->isEmpty()) {
916 // generator called with return()
918 engine->hasException = false;
919
920 ScopedValue ret(scope, static_cast<const Object &>(iterator).get(engine->id_return()));
921 if (engine->hasException)
922 return Encode(true);
923 if (ret->isUndefined()) {
924 // propagate return()
925 return Encode::undefined();
926 }
927 returnCalled = true;
928 f = ret->as<FunctionObject>();
929 } else {
930 // generator called with throw
933 engine->hasException = false;
934
935 ScopedValue t(scope, static_cast<const Object &>(iterator).get(engine->id_throw()));
936 if (engine->hasException)
937 return Encode(true);
938 if (t->isUndefined()) {
939 // no throw method on the iterator
941 if (!engine->hasException)
943 return Encode(true);
944 }
945 f = t->as<FunctionObject>();
947 }
948 } else {
949 // generator called with next()
950 ScopedFunctionObject next(scope, static_cast<const Object &>(iterator).get(engine->id_next()));
951 f = next->as<FunctionObject>();
952 }
953
954 if (!f) {
956 return Encode(true);
957 }
958
960 if (scope.hasException())
961 return Encode(true);
962 if (!o) {
964 return Encode(true);
965 }
966
968 if (scope.hasException())
969 return Encode(true);
970 bool done = d->toBoolean();
971 if (done) {
972 *object = o->get(engine->id_value());
973 return (returnCalled && !engine->hasException) ? Encode::undefined() : Encode(true);
974 }
975 *object = o;
976 return Encode(false);
977}
978
980{
982
986 if (hadException) {
988 engine->hasException = false;
989 }
990
991 auto originalCompletion = [=]() {
992 if (hadException) {
995 }
996 return Encode::undefined();
997 };
998
999 ScopedValue ret(scope, static_cast<const Object &>(iterator).get(engine->id_return()));
1001 if (!ret->isUndefined()) {
1003 o = f->call(&iterator, nullptr, 0);
1005 return Encode::undefined();
1006 }
1007 if (hadException || ret->isUndefined())
1008 return originalCompletion();
1009
1010 if (!o)
1011 return engine->throwTypeError();
1012
1013 return originalCompletion();
1014}
1015
1017{
1019
1022 array->arrayCreate();
1023 uint index = 0;
1024 while (1) {
1027 if (engine->hasException)
1028 return Encode::undefined();
1030 if (done->booleanValue())
1031 break;
1032 array->arraySet(index, n);
1033 ++index;
1034 }
1035 return array->asReturnedValue();
1036}
1037
1047
1058
1060{
1063
1065 if (o)
1066 return o->get(name);
1067
1068 if (object.isNullOrUndefined()) {
1069 QString message = QStringLiteral("Cannot read property '%1' of %2").arg(name->toQString()).arg(object.toQStringNoThrow());
1070 return engine->throwTypeError(message);
1071 }
1072
1074 if (!o) // type error
1075 return Encode::undefined();
1076 return o->get(name);
1077}
1078
1085
1086static Object *getSuperBase(Scope &scope)
1087{
1088 Scoped<JavaScriptFunctionObject> f(scope);
1089 ScopedObject homeObject(scope);
1090 if (scope.engine->currentStackFrame->isJSTypesFrame()) {
1091 JSTypesStackFrame *frame = static_cast<JSTypesStackFrame *>(
1092 scope.engine->currentStackFrame);
1093
1094 if (frame->jsFrame->thisObject.isEmpty()) {
1095 scope.engine->throwReferenceError(
1096 QStringLiteral("Missing call to super()."), QString(), 0, 0);
1097 return nullptr;
1098 }
1099
1100 f = Value::fromStaticValue(frame->jsFrame->function);
1101 homeObject = f->getHomeObject();
1102 } else {
1103 Q_ASSERT(scope.engine->currentStackFrame->isMetaTypesFrame());
1104 MetaTypesStackFrame *frame = static_cast<MetaTypesStackFrame *>(
1105 scope.engine->currentStackFrame);
1106 if (frame->thisObject() == nullptr) {
1107 scope.engine->throwReferenceError(
1108 QStringLiteral("Missing call to super()."), QString(), 0, 0);
1109 return nullptr;
1110 }
1111 }
1112
1113 if (!homeObject) {
1114 ScopedContext ctx(scope, scope.engine->currentContext());
1115 Q_ASSERT(ctx);
1116 while (ctx) {
1117 if (CallContext *c = ctx->asCallContext()) {
1118 f = c->d()->function;
1119 QV4::Function *fn = f->function();
1120 if (fn && !fn->isArrowFunction() && fn->kind != Function::Eval)
1121 break;
1122 }
1123 ctx = ctx->d()->outer;
1124 }
1125 if (f)
1126 homeObject = f->getHomeObject();
1127 }
1128 if (!homeObject) {
1129 scope.engine->throwTypeError();
1130 return nullptr;
1131 }
1132 Q_ASSERT(homeObject);
1133 ScopedObject proto(scope, homeObject->getPrototypeOf());
1134 if (!proto) {
1135 scope.engine->throwTypeError();
1136 return nullptr;
1137 }
1138 return proto;
1139}
1140
1163
1190
1196
1202
1208
1210{
1213 l->setter(engine, const_cast<Value &>(base), value);
1214}
1215
1217{
1220 if (!l->setter(engine, const_cast<Value &>(base), value))
1222}
1223
1225{
1229 // ### TODO: fix line number
1231 QStringLiteral("super() already called."), QString(), 0, 0);
1232 }
1233 } else {
1236 if (frame->thisObject() != nullptr) {
1237 // ### TODO: fix line number
1239 QStringLiteral("super() already called."), QString(), 0, 0);
1240 }
1241 }
1242
1243 const FunctionObject *f = t.as<FunctionObject>();
1244 if (!f)
1245 return engine->throwTypeError();
1246 Heap::Object *c = static_cast<const Object &>(t).getPrototypeOf();
1247 if (!c->vtable()->callAsConstructor)
1248 return engine->throwTypeError();
1249 return c->asReturnedValue();
1250}
1251
1253{
1254 Q_ASSERT(x.type() != y.type() || (x.isManaged() && (x.isString() != y.isString())));
1255
1256 if (x.isNumber() && y.isNumber())
1257 return x.asDouble() == y.asDouble();
1258 if (x.isNull() && y.isUndefined()) {
1259 return true;
1260 } else if (x.isUndefined() && y.isNull()) {
1261 return true;
1262 } else if (x.isNumber() && y.isString()) {
1263 double dy = RuntimeHelpers::toNumber(y);
1264 return x.asDouble() == dy;
1265 } else if (x.isString() && y.isNumber()) {
1266 double dx = RuntimeHelpers::toNumber(x);
1267 return dx == y.asDouble();
1268 } else if (x.isBoolean()) {
1269 return Runtime::CompareEqual::call(Value::fromDouble((double) x.booleanValue()), y);
1270 } else if (y.isBoolean()) {
1271 return Runtime::CompareEqual::call(x, Value::fromDouble((double) y.booleanValue()));
1272 } else {
1273 Object *xo = x.objectValue();
1274 Object *yo = y.objectValue();
1275 if (yo && (x.isNumber() || x.isString())) {
1276 Scope scope(yo->engine());
1278 return Runtime::CompareEqual::call(x, py);
1279 } else if (xo && (y.isNumber() || y.isString())) {
1280 Scope scope(xo->engine());
1282 return Runtime::CompareEqual::call(px, y);
1283 }
1284 }
1285
1286 return false;
1287}
1288
1290{
1291 TRACE2(x, y);
1292
1293 if (x.rawValue() == y.rawValue())
1294 // NaN != NaN
1295 return !x.isNaN();
1296
1297 if (x.isNumber())
1298 return y.isNumber() && x.asDouble() == y.asDouble();
1299 if (x.isManaged()) {
1300 return y.isManaged() && x.cast<Managed>()->isEqualTo(y.cast<Managed>());
1301 }
1302 return false;
1303}
1304
1306{
1307 TRACE2(l, r);
1308 if (l.isInteger() && r.isInteger())
1309 return l.integerValue() > r.integerValue();
1310 if (l.isNumber() && r.isNumber())
1311 return l.asDouble() > r.asDouble();
1312 String *sl = l.stringValue();
1313 String *sr = r.stringValue();
1314 if (sl && sr) {
1315 return sr->lessThan(sl);
1316 }
1317
1318 Object *ro = r.objectValue();
1319 Object *lo = l.objectValue();
1320 if (ro || lo) {
1321 QV4::ExecutionEngine *e = (lo ? lo : ro)->engine();
1322 QV4::Scope scope(e);
1325 return Runtime::CompareGreaterThan::call(pl, pr);
1326 }
1327
1328 double dl = RuntimeHelpers::toNumber(l);
1329 double dr = RuntimeHelpers::toNumber(r);
1330 return dl > dr;
1331}
1332
1334{
1335 TRACE2(l, r);
1336 if (l.isInteger() && r.isInteger())
1337 return l.integerValue() < r.integerValue();
1338 if (l.isNumber() && r.isNumber())
1339 return l.asDouble() < r.asDouble();
1340 String *sl = l.stringValue();
1341 String *sr = r.stringValue();
1342 if (sl && sr) {
1343 return sl->lessThan(sr);
1344 }
1345
1346 Object *ro = r.objectValue();
1347 Object *lo = l.objectValue();
1348 if (ro || lo) {
1349 QV4::ExecutionEngine *e = (lo ? lo : ro)->engine();
1350 QV4::Scope scope(e);
1353 return Runtime::CompareLessThan::call(pl, pr);
1354 }
1355
1356 double dl = RuntimeHelpers::toNumber(l);
1357 double dr = RuntimeHelpers::toNumber(r);
1358 return dl < dr;
1359}
1360
1362{
1363 TRACE2(l, r);
1364 if (l.isInteger() && r.isInteger())
1365 return l.integerValue() >= r.integerValue();
1366 if (l.isNumber() && r.isNumber())
1367 return l.asDouble() >= r.asDouble();
1368 String *sl = l.stringValue();
1369 String *sr = r.stringValue();
1370 if (sl && sr) {
1371 return !sl->lessThan(sr);
1372 }
1373
1374 Object *ro = r.objectValue();
1375 Object *lo = l.objectValue();
1376 if (ro || lo) {
1377 QV4::ExecutionEngine *e = (lo ? lo : ro)->engine();
1378 QV4::Scope scope(e);
1382 }
1383
1384 double dl = RuntimeHelpers::toNumber(l);
1385 double dr = RuntimeHelpers::toNumber(r);
1386 return dl >= dr;
1387}
1388
1390{
1391 TRACE2(l, r);
1392 if (l.isInteger() && r.isInteger())
1393 return l.integerValue() <= r.integerValue();
1394 if (l.isNumber() && r.isNumber())
1395 return l.asDouble() <= r.asDouble();
1396 String *sl = l.stringValue();
1397 String *sr = r.stringValue();
1398 if (sl && sr) {
1399 return !sr->lessThan(sl);
1400 }
1401
1402 Object *ro = r.objectValue();
1403 Object *lo = l.objectValue();
1404 if (ro || lo) {
1405 QV4::ExecutionEngine *e = (lo ? lo : ro)->engine();
1406 QV4::Scope scope(e);
1409 return Runtime::CompareLessEqual::call(pl, pr);
1410 }
1411
1412 double dl = RuntimeHelpers::toNumber(l);
1413 double dr = RuntimeHelpers::toNumber(r);
1414 return dl <= dr;
1415}
1416
1418{
1419 TRACE2(left, right);
1420
1423 return v->booleanValue();
1424}
1425
1427{
1428 TRACE2(left, right);
1429
1432 return v->booleanValue();
1433}
1434
1435static ReturnedValue throwPropertyIsNotAFunctionTypeError(ExecutionEngine *engine, Value *thisObject, const QString &propertyName)
1436{
1437 QString objectAsString = QStringLiteral("[null]");
1438 if (!thisObject->isUndefined())
1439 objectAsString = thisObject->toQStringNoThrow();
1440 QString msg = QStringLiteral("Property '%1' of object %2 is not a function")
1441 .arg(propertyName, objectAsString);
1442 return engine->throwTypeError(msg);
1443}
1444
1459
1475
1494
1513
1515{
1516 const Value *base = &baseRef;
1519 scope,
1522
1523 if (!lookupObject) {
1524 Q_ASSERT(!base->isEmpty());
1525 if (base->isNullOrUndefined()) {
1526 QString message = QStringLiteral("Cannot call method '%1' of %2")
1528 return engine->throwTypeError(message);
1529 }
1530
1531 if (base->isManaged()) {
1532 const Managed *m = static_cast<const Managed *>(base);
1535 } else {
1537 if (engine->hasException) // type error
1538 return Encode::undefined();
1541 }
1542 }
1543
1545
1546 if (!f) {
1547 QString error = QStringLiteral("Property '%1' of object %2 is not a function")
1548 .arg(name->toQString(),
1550 return engine->throwTypeError(error);
1551 }
1552
1553 return checkedResult(engine, f->call(base, argv, argc));
1554}
1555
1557{
1559 // ok to have the value on the stack here
1561
1563 return checkedResult(engine, static_cast<FunctionObject &>(f).call(&base, argv, argc));
1564
1567
1568 const QString message = QStringLiteral("Property '%1' of object %2 is not a function")
1572 return engine->throwTypeError(message);
1573}
1574
1576{
1577 if (!func.isFunctionObject())
1578 return engine->throwTypeError(QStringLiteral("%1 is not a function").arg(func.toQStringNoThrow()));
1580 return checkedResult(engine, static_cast<const FunctionObject &>(func).call(
1581 &undef, argv, argc));
1582}
1583
1585 const Value &thisObject, Value argv[], int argc)
1586{
1587 if (!func.isFunctionObject())
1588 return engine->throwTypeError(QStringLiteral("%1 is not a function").arg(func.toQStringNoThrow()));
1589 return checkedResult(engine, static_cast<const FunctionObject &>(func).call(
1590 &thisObject, argv, argc));
1591}
1592
1593struct CallArgs {
1594 Value *argv;
1595 int argc;
1596};
1597
1598static CallArgs createSpreadArguments(Scope &scope, Value *argv, int argc)
1599{
1600 ScopedValue it(scope);
1601 ScopedValue done(scope);
1602
1603 int argCount = 0;
1604
1605 Value *v = scope.constructUndefined(1);
1606 Value *arguments = v;
1607 for (int i = 0; i < argc; ++i) {
1608 if (!argv[i].isEmpty()) {
1609 *v = argv[i];
1610 ++argCount;
1611 v = scope.constructUndefined(1);
1612 continue;
1613 }
1614 // spread element
1615 ++i;
1616 it = Runtime::GetIterator::call(scope.engine, argv[i], /* ForInIterator */ 1);
1617 if (scope.hasException())
1618 return { nullptr, 0 };
1619 while (1) {
1620 done = Runtime::IteratorNext::call(scope.engine, it, v);
1621 if (scope.hasException())
1622 return { nullptr, 0 };
1623 Q_ASSERT(done->isBoolean());
1624 if (done->booleanValue())
1625 break;
1626 ++argCount;
1627 constexpr auto safetyMargin = 100; // leave some space on the stack for actual work with the elements
1628 if (qint64(scope.engine->jsStackLimit - scope.engine->jsStackTop) < safetyMargin) {
1629 scope.engine->throwRangeError(QLatin1String("Too many elements in array to use it with the spread operator"));
1630 return { nullptr, 0 };
1631 }
1632 v = scope.constructUndefined(1);
1633 }
1634 }
1635 return { arguments, argCount };
1636}
1637
1652
1654{
1656 return engine->throwTypeError();
1657
1658 return static_cast<const FunctionObject &>(function).callAsConstructor(argv, argc, &newTarget);
1659}
1660
1673
1675{
1676 // IMPORTANT! The JIT assumes that this method has the same amount (or less) arguments than
1677 // the jitted function, so it can safely do a tail call.
1678
1682 Value *argv = reinterpret_cast<Value *>(frame->jsFrame) + tos[StackOffsets::tailCall_argv].int_32();
1684 Q_ASSERT(argc >= 0);
1685
1687 if (!jsfo) {
1688 if (const FunctionObject *fo = function.as<FunctionObject>())
1690 return engine->throwTypeError();
1691 }
1692
1694 || unsigned(argc) > jsfo->formalParameterCount()) {
1695 // Cannot tailcall, do a normal call:
1697 }
1698
1699 memmove(frame->jsFrame->args, argv, argc * sizeof(Value));
1706 return Encode::undefined();
1707}
1708
1714
1716{
1719 switch (value.type()) {
1720 case Value::Undefined_Type:
1721 res = engine->id_undefined();
1722 break;
1723 case Value::Null_Type:
1724 res = engine->id_object();
1725 break;
1726 case Value::Boolean_Type:
1727 res = engine->id_boolean();
1728 break;
1729 case Value::Managed_Type:
1730 if (value.isString())
1731 res = engine->id_string();
1732 else if (value.isSymbol())
1733 res = engine->id_symbol();
1734 else if (value.objectValue()->as<FunctionObject>())
1735 res = engine->id_function();
1736 else
1737 res = engine->id_object(); // ### implementation-defined
1738 break;
1739 default:
1740 res = engine->id_number();
1741 break;
1742 }
1743 return res.asReturnedValue();
1744}
1745
1755
1760
1776
1784
1791
1801
1811
1819
1826
1832
1834{
1835 auto *h = toBeMarked.heapObject();
1836 if (!h)
1837 return;
1839 auto engine = h->internalClass->engine;
1841 // runtime function is only meant to be called while gc is ongoing
1844 h->mark(ms);
1845 });
1846}
1847
1849{
1850 if (!t.isObject()) {
1851 if (t.isNullOrUndefined()) {
1853 } else {
1854 return t.toObject(engine)->asReturnedValue();
1855 }
1856 }
1857 return t.asReturnedValue();
1858}
1859
1866
1871
1873{
1877
1878 Q_ASSERT(uint(argc) >= klass->d()->size);
1879
1880 for (uint i = 0; i < klass->d()->size; ++i)
1881 o->setProperty(i, *args++);
1882
1883 Q_ASSERT((argc - klass->d()->size) % 3 == 0);
1884 int additionalArgs = (argc - int(klass->d()->size))/3;
1885
1886 if (!additionalArgs)
1887 return o->asReturnedValue();
1888
1894 for (int i = 0; i < additionalArgs; ++i) {
1898 value = args[2];
1899 if (engine->hasException)
1900 return Encode::undefined();
1902 Q_ASSERT(args[2].isInteger());
1903 int functionId = args[2].integerValue();
1906 Q_ASSERT(clos);
1907
1911 else if (arg == ObjectLiteralArgument::Setter)
1913 else
1916
1918 if (clos->isGenerator())
1920 else
1922 } else if (args[2].isFunctionObject()) {
1923 fn = static_cast<const FunctionObject &>(args[2]);
1924
1926 fn->setName(fnName);
1927 }
1931 pd->value = value;
1932 pd->set = Value::emptyValue();
1933 } else {
1934 pd->value = Value::emptyValue();
1935 pd->set = value;
1936 }
1938 if (!ok)
1939 return engine->throwTypeError();
1940
1941 args += 3;
1942 }
1943 return o.asReturnedValue();
1944}
1945
1948{
1952
1956 if (!superClass.isEmpty()) {
1957 if (superClass.isNull()) {
1958 protoParent = Encode::null();
1959 } else {
1961 // ### check that the heritage object is a constructor
1963 return engine->throwTypeError(QStringLiteral("The superclass is not a function object."));
1964 const FunctionObject *s = static_cast<const FunctionObject *>(&superClass);
1966 if (!result->isObject() && !result->isNull())
1967 return engine->throwTypeError(QStringLiteral("The value of the superclass's prototype property is not an object."));
1970 }
1971 }
1972
1976
1985
1987 if (cls->nameIndex != UINT_MAX) {
1990 }
1991
1997 for (uint i = 0; i < cls->nStaticMethods + cls->nMethods; ++i) {
1998 if (i == cls->nStaticMethods)
1999 receiver = proto;
2000 if (methods[i].name == UINT_MAX) {
2003 return engine->throwTypeError(QStringLiteral("Cannot declare a static method named 'prototype'."));
2004 if (engine->hasException)
2005 return Encode::undefined();
2006 ++computedNames;
2007 } else {
2010 }
2012 Q_ASSERT(f);
2016 else if (methods[i].type == CompiledData::Method::Setter)
2018
2020
2021 if (f->isGenerator())
2023 else
2027 switch (methods[i].type) {
2028 case CompiledData::Method::Getter:
2032 break;
2033 case CompiledData::Method::Setter:
2037 break;
2038 default: // Regular
2042 break;
2043 }
2045 }
2046
2047 return constructor->asReturnedValue();
2048}
2049
2056
2064
2075
2083
2085{
2086 if (obj.isObject())
2087 return obj.asReturnedValue();
2088
2090}
2091
2093{
2094 return obj.toBoolean();
2095}
2096
2098{
2099 return Encode(v.toNumber());
2100}
2101
2103{
2104 TRACE1(value);
2105
2106 // +0 != -0, so we need to convert to double when negating 0
2107 if (value.isInteger() && value.integerValue() &&
2108 value.integerValue() != std::numeric_limits<int>::min())
2109 return Encode(-value.integerValue());
2110 else {
2111 double n = RuntimeHelpers::toNumber(value);
2112 return Encode(-n);
2113 }
2114}
2115
2116// binary operators
2117
2129
2131{
2132 TRACE2(left, right);
2133
2136
2137 double lval = left.isNumber() ? left.asDouble() : left.toNumberImpl();
2138 double rval = right.isNumber() ? right.asDouble() : right.toNumberImpl();
2139
2141}
2142
2144{
2145 TRACE2(left, right);
2146
2149
2150 double lval = left.isNumber() ? left.asDouble() : left.toNumberImpl();
2151 double rval = right.isNumber() ? right.asDouble() : right.toNumberImpl();
2152
2154}
2155
2157{
2158 TRACE2(left, right);
2159
2161 int lval = left.integerValue();
2162 int rval = right.integerValue();
2163 if (rval != 0 // division by zero should result in a NaN
2164 && !(lval == std::numeric_limits<int>::min() && rval == -1) // doesn't fit in int
2165 && (lval % rval == 0) // fractions can't be stored in an int
2166 && !(lval == 0 && rval < 0)) // 0 / -something results in -0.0
2167 return Encode(int(lval / rval));
2168 else
2169 return Encode(double(lval) / rval);
2170 }
2171
2172 double lval = left.toNumber();
2173 double rval = right.toNumber();
2175}
2176
2178{
2179 TRACE2(left, right);
2180
2182 // special cases are handled by fmod, among them:
2183 // - arithmic execeptions for ints in c++, eg: INT_MIN % -1
2184 // - undefined behavior in c++, e.g.: anything % 0
2185 // - uncommon cases which would complicate the condition, e.g.: negative integers
2186 // (this makes sure that -1 % 1 == -0 by passing it to fmod)
2188 }
2189
2190 double lval = RuntimeHelpers::toNumber(left);
2191 double rval = RuntimeHelpers::toNumber(right);
2192#ifdef fmod
2193# undef fmod
2194#endif
2196}
2197
2199{
2200 double b = base.toNumber();
2201 double e = exp.toNumber();
2202 return Encode(QQmlPrivate::jsExponentiate(b, e));
2203}
2204
2206{
2207 TRACE2(left, right);
2208
2209 int lval = left.toInt32();
2210 int rval = right.toInt32();
2211 return Encode((int)(lval & rval));
2212}
2213
2215{
2216 TRACE2(left, right);
2217
2218 int lval = left.toInt32();
2219 int rval = right.toInt32();
2220 return Encode((int)(lval | rval));
2221}
2222
2224{
2225 TRACE2(left, right);
2226
2227 int lval = left.toInt32();
2228 int rval = right.toInt32();
2229 return Encode((int)(lval ^ rval));
2230}
2231
2233{
2234 TRACE2(left, right);
2235
2236 int lval = left.toInt32();
2237 int rval = right.toInt32() & 0x1f;
2238 return Encode((int)(lval << rval));
2239}
2240
2242{
2243 TRACE2(left, right);
2244
2245 int lval = left.toInt32();
2246 unsigned rval = right.toUInt32() & 0x1f;
2247 return Encode((int)(lval >> rval));
2248}
2249
2251{
2252 TRACE2(left, right);
2253
2254 unsigned lval = left.toUInt32();
2255 unsigned rval = right.toUInt32() & 0x1f;
2256 uint res = lval >> rval;
2257
2258 return Encode(res);
2259}
2260
2262{
2263 TRACE2(left, right);
2264
2266 return Encode(r);
2267}
2268
2270{
2271 TRACE2(left, right);
2272
2273 bool r = CompareLessThan::call(left, right);
2274 return Encode(r);
2275}
2276
2278{
2279 TRACE2(left, right);
2280
2282 return Encode(r);
2283}
2284
2286{
2287 TRACE2(left, right);
2288
2289 bool r = CompareLessEqual::call(left, right);
2290 return Encode(r);
2291}
2292
2294{
2295 ExecutionEngine *engine = nullptr;
2296 Value *stackMark = nullptr;
2298 if (engine)
2299 engine->jsStackTop = stackMark;
2300 }
2301 template <typename T>
2302 void set(Value **scopedValue, T value, ExecutionEngine *e) {
2303 if (!engine) {
2304 engine = e;
2305 stackMark = engine->jsStackTop;
2306 }
2307 if (!*scopedValue)
2308 *scopedValue = e->jsAlloca(1);
2309 **scopedValue = value;
2310 }
2311};
2312
2314{
2315 TRACE2(left, right);
2316
2317 Value lhs = left;
2318 Value rhs = right;
2319
2321 Value *lhsGuard = nullptr;
2322 Value *rhsGuard = nullptr;
2323
2324 redo:
2326 return !lhs.isNaN();
2327
2328 quint32 lt = lhs.quickType();
2329 quint32 rt = rhs.quickType();
2330
2331 // LHS: Check if managed
2332 if ((lt & (Value::ManagedMask >> Value::Tag_Shift)) == 0) {
2333 if (lhs.isUndefined())
2334 return rhs.isNullOrUndefined();
2335
2336 // RHS: Check if managed
2337 if ((rt & (Value::ManagedMask >> Value::Tag_Shift)) == 0) {
2338 if (rhs.isUndefined())
2339 return false;
2340
2341 Heap::Base *l = lhs.m();
2342 Heap::Base *r = rhs.m();
2343 Q_ASSERT(l);
2344 Q_ASSERT(r);
2346 return static_cast<QV4::Managed &>(lhs).isEqualTo(&static_cast<QV4::Managed &>(rhs));
2350 goto redo;
2351 } else {
2355 goto redo;
2356 }
2357 return false;
2358 }
2359
2361 switch (rt) {
2362 case QV4::Value::QT_Empty:
2363 Q_UNREACHABLE();
2364 case QV4::Value::QT_Null:
2365 return false;
2366 case QV4::Value::QT_Bool:
2367 case QV4::Value::QT_Int:
2369 Q_FALLTHROUGH();
2370 default: // double
2372 return lhs.m()->internalClass->vtable->isString ? (RuntimeHelpers::toNumber(lhs) == rhs.doubleValue()) : false;
2373 } else {
2376 }
2377 }
2378 goto redo;
2379 } else if ((rt & (Value::ManagedMask >> Value::Tag_Shift)) == 0) {
2380 if (rhs.isUndefined())
2381 return lhs.isNull(); // Can't be undefined
2382 qSwap(lhs, rhs);
2383 qSwap(lt, rt);
2385 }
2386
2387 switch (lt) {
2388 case QV4::Value::QT_Empty:
2389 Q_UNREACHABLE();
2390 case QV4::Value::QT_Null:
2391 return rhs.isNull();
2392 case QV4::Value::QT_Bool:
2393 case QV4::Value::QT_Int:
2394 switch (rt) {
2395 case QV4::Value::QT_Empty:
2396 Q_UNREACHABLE();
2397 case QV4::Value::QT_Null:
2398 return false;
2399 case QV4::Value::QT_Bool:
2400 case QV4::Value::QT_Int:
2401 return lhs.int_32() == rhs.int_32();
2402 default: // double
2403 return lhs.int_32() == rhs.doubleValue();
2404 }
2405 default: // double
2406 switch (rt) {
2407 case QV4::Value::QT_Empty:
2408 Q_UNREACHABLE();
2409 case QV4::Value::QT_Null:
2410 return false;
2411 case QV4::Value::QT_Bool:
2412 case QV4::Value::QT_Int:
2413 return lhs.doubleValue() == rhs.int_32();
2414 default: // double
2415 return lhs.doubleValue() == rhs.doubleValue();
2416 }
2417 }
2418}
2419
2421{
2422 TRACE2(left, right);
2423
2424 bool r = CompareEqual::call(left, right);
2425 return Encode(r);
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
2441 return Encode(r);
2442}
2443
2445{
2446 TRACE2(left, right);
2447
2449 return Encode(r);
2450}
2451
2453{
2454 TRACE2(left, right);
2455
2456 return !Runtime::CompareEqual::call(left, right);
2457}
2458
2460{
2461 TRACE2(left, right);
2462
2464}
2465
2467{
2468 TRACE2(left, right);
2469
2471}
2472
2473template<typename Operation>
2474static inline const void *symbol()
2475{
2476 return reinterpret_cast<void *>(&Operation::call);
2477}
2478
2479QHash<const void *, const char *> Runtime::symbolTable()
2480{
2481 static const QHash<const void *, const char *> symbols({
2482#ifndef V4_BOOTSTRAP
2483 {symbol<CallGlobalLookup>(), "CallGlobalLookup" },
2484 {symbol<CallQmlContextPropertyLookup>(), "CallQmlContextPropertyLookup" },
2485 {symbol<CallName>(), "CallName" },
2486 {symbol<CallProperty>(), "CallProperty" },
2487 {symbol<CallPropertyLookup>(), "CallPropertyLookup" },
2488 {symbol<CallValue>(), "CallValue" },
2489 {symbol<CallWithReceiver>(), "CallWithReceiver" },
2490 {symbol<CallPossiblyDirectEval>(), "CallPossiblyDirectEval" },
2491 {symbol<CallWithSpread>(), "CallWithSpread" },
2492 {symbol<TailCall>(), "TailCall" },
2493
2494 {symbol<Construct>(), "Construct" },
2495 {symbol<ConstructWithSpread>(), "ConstructWithSpread" },
2496
2497 {symbol<StoreNameStrict>(), "StoreNameStrict" },
2498 {symbol<StoreNameSloppy>(), "StoreNameSloppy" },
2499 {symbol<StoreProperty>(), "StoreProperty" },
2500 {symbol<StoreElement>(), "StoreElement" },
2501 {symbol<LoadProperty>(), "LoadProperty" },
2502 {symbol<LoadName>(), "LoadName" },
2503 {symbol<LoadElement>(), "LoadElement" },
2504 {symbol<LoadSuperProperty>(), "LoadSuperProperty" },
2505 {symbol<StoreSuperProperty>(), "StoreSuperProperty" },
2506 {symbol<LoadSuperConstructor>(), "LoadSuperConstructor" },
2507 {symbol<LoadGlobalLookup>(), "LoadGlobalLookup" },
2508 {symbol<LoadQmlContextPropertyLookup>(), "LoadQmlContextPropertyLookup" },
2509 {symbol<GetLookup>(), "GetLookup" },
2510 {symbol<SetLookupStrict>(), "SetLookupStrict" },
2511 {symbol<SetLookupSloppy>(), "SetLookupSloppy" },
2512
2513 {symbol<TypeofValue>(), "TypeofValue" },
2514 {symbol<TypeofName>(), "TypeofName" },
2515
2516 {symbol<DeleteProperty_NoThrow>(), "DeleteProperty_NoThrow" },
2517 {symbol<DeleteProperty>(), "DeleteProperty" },
2518 {symbol<DeleteName_NoThrow>(), "DeleteName_NoThrow" },
2519 {symbol<DeleteName>(), "DeleteName" },
2520
2521 {symbol<ThrowException>(), "ThrowException" },
2522 {symbol<PushCallContext>(), "PushCallContext" },
2523 {symbol<PushWithContext>(), "PushWithContext" },
2524 {symbol<PushCatchContext>(), "PushCatchContext" },
2525 {symbol<PushBlockContext>(), "PushBlockContext" },
2526 {symbol<CloneBlockContext>(), "CloneBlockContext" },
2527 {symbol<PushScriptContext>(), "PushScriptContext" },
2528 {symbol<PopScriptContext>(), "PopScriptContext" },
2529 {symbol<ThrowReferenceError>(), "ThrowReferenceError" },
2530 {symbol<ThrowOnNullOrUndefined>(), "ThrowOnNullOrUndefined" },
2531
2532 {symbol<Closure>(), "Closure" },
2533
2534 {symbol<MarkCustom>(), "MarkCustom"},
2535
2536 {symbol<ConvertThisToObject>(), "ConvertThisToObject" },
2537 {symbol<DeclareVar>(), "DeclareVar" },
2538 {symbol<CreateMappedArgumentsObject>(), "CreateMappedArgumentsObject" },
2539 {symbol<CreateUnmappedArgumentsObject>(), "CreateUnmappedArgumentsObject" },
2540 {symbol<CreateRestParameter>(), "CreateRestParameter" },
2541
2542 {symbol<ArrayLiteral>(), "ArrayLiteral" },
2543 {symbol<ObjectLiteral>(), "ObjectLiteral" },
2544 {symbol<CreateClass>(), "CreateClass" },
2545
2546 {symbol<GetIterator>(), "GetIterator" },
2547 {symbol<IteratorNext>(), "IteratorNext" },
2548 {symbol<IteratorNextForYieldStar>(), "IteratorNextForYieldStar" },
2549 {symbol<IteratorClose>(), "IteratorClose" },
2550 {symbol<DestructureRestElement>(), "DestructureRestElement" },
2551
2552 {symbol<ToObject>(), "ToObject" },
2553 {symbol<ToBoolean>(), "ToBoolean" },
2554 {symbol<ToNumber>(), "ToNumber" },
2555
2556 {symbol<UMinus>(), "UMinus" },
2557
2558 {symbol<Instanceof>(), "Instanceof" },
2559 {symbol<As>(), "As" },
2560 {symbol<In>(), "In" },
2561 {symbol<Add>(), "Add" },
2562 {symbol<Sub>(), "Sub" },
2563 {symbol<Mul>(), "Mul" },
2564 {symbol<Div>(), "Div" },
2565 {symbol<Mod>(), "Mod" },
2566 {symbol<Exp>(), "Exp" },
2567 {symbol<BitAnd>(), "BitAnd" },
2568 {symbol<BitOr>(), "BitOr" },
2569 {symbol<BitXor>(), "BitXor" },
2570 {symbol<Shl>(), "Shl" },
2571 {symbol<Shr>(), "Shr" },
2572 {symbol<UShr>(), "UShr" },
2573 {symbol<GreaterThan>(), "GreaterThan" },
2574 {symbol<LessThan>(), "LessThan" },
2575 {symbol<GreaterEqual>(), "GreaterEqual" },
2576 {symbol<LessEqual>(), "LessEqual" },
2577 {symbol<Equal>(), "Equal" },
2578 {symbol<NotEqual>(), "NotEqual" },
2579 {symbol<StrictEqual>(), "StrictEqual" },
2580 {symbol<StrictNotEqual>(), "StrictNotEqual" },
2581
2582 {symbol<CompareGreaterThan>(), "CompareGreaterThan" },
2583 {symbol<CompareLessThan>(), "CompareLessThan" },
2584 {symbol<CompareGreaterEqual>(), "CompareGreaterEqual" },
2585 {symbol<CompareLessEqual>(), "CompareLessEqual" },
2586 {symbol<CompareEqual>(), "CompareEqual" },
2587 {symbol<CompareNotEqual>(), "CompareNotEqual" },
2588 {symbol<CompareStrictEqual>(), "CompareStrictEqual" },
2589 {symbol<CompareStrictNotEqual>(), "CompareStrictNotEqual" },
2590
2591 {symbol<CompareInstanceof>(), "CompareInstanceOf" },
2592 {symbol<CompareIn>(), "CompareIn" },
2593
2594 {symbol<RegexpLiteral>(), "RegexpLiteral" },
2595 {symbol<GetTemplateObject>(), "GetTemplateObject" }
2596#endif
2597 });
2598
2599 return symbols;
2600}
2601
2602} // namespace QV4
2603
2604QT_END_NAMESPACE
Definition qjsvalue.h:23
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
Q_STATIC_LOGGING_CATEGORY(lcAccessibilityCore, "qt.accessibility.core")
#define TRACE1(x)
#define TRACE2(x, y)
ExecutionEngine * engine
void set(Value **scopedValue, T value, ExecutionEngine *e)