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