1// Copyright (C) 2016 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
4#include "qv4vme_moth_p.h"
5
6#include <QtCore/qjsondocument.h>
7#include <QtCore/qjsonobject.h>
8
9#include <private/qv4alloca_p.h>
10#include <private/qv4instr_moth_p.h>
11#include <private/qv4value_p.h>
12#include <private/qv4debugging_p.h>
13#include <private/qv4function_p.h>
14#include <private/qv4functionobject_p.h>
15#include <private/qv4math_p.h>
16#include <private/qv4scopedvalue_p.h>
17#include <private/qv4lookup_p.h>
18#include <private/qv4regexp_p.h>
19#include <private/qv4regexpobject_p.h>
20#include <private/qv4string_p.h>
21#include <private/qv4profiling_p.h>
22#include <private/qv4jscall_p.h>
23#include <private/qv4generatorobject_p.h>
24#include <private/qv4alloca_p.h>
25#include <private/qqmljavascriptexpression_p.h>
26#include <private/qv4qmlcontext_p.h>
27#include <QtQml/private/qv4runtime_p.h>
28#include <iostream>
29
30#if QT_CONFIG(qml_jit)
31#include <private/qv4baselinejit_p.h>
32#endif
33
34#include <qtqml_tracepoints_p.h>
35
36#undef COUNT_INSTRUCTIONS
37
38Q_TRACE_POINT(qtqml, QQmlV4_function_call_entry, const QV4::ExecutionEngine *engine, const QString &function, const QString &fileName, int line, int column)
39Q_TRACE_POINT(qtqml, QQmlV4_function_call_exit)
40
41enum { ShowWhenDeoptimiationHappens = 0 };
42
43extern "C" {
44
45// This is the interface to Qt Creator's (new) QML debugger.
46
47/*! \internal
48 \since 5.5
49
50 This function is called uncondionally from VME::run().
51
52 An attached debugger can set a breakpoint here to
53 intercept calls to VME::run().
54 */
55
56Q_QML_EXPORT void qt_v4ResolvePendingBreakpointsHook()
57{
58}
59
60/*! \internal
61 \since 5.5
62
63 This function is called when a QML interpreter breakpoint
64 is hit.
65
66 An attached debugger can set a breakpoint here.
67*/
68Q_QML_EXPORT void qt_v4TriggeredBreakpointHook()
69{
70}
71
72/*! \internal
73 \since 5.5
74
75 The main entry point into "Native Mixed" Debugging.
76
77 Commands are passed as UTF-8 encoded JSON data.
78 The data has two compulsory fields:
79 \list
80 \li \c version: Version of the protocol (currently 1)
81 \li \c command: Name of the command
82 \endlist
83
84 Depending on \c command, more fields can be present.
85
86 Error is indicated by negative return values,
87 success by non-negative return values.
88
89 \c protocolVersion:
90 Returns version of implemented protocol.
91
92 \c insertBreakpoint:
93 Sets a breakpoint on a given file and line.
94 \list
95 \li \c fullName: Name of the QML/JS file
96 \li \c lineNumber: Line number in the file
97 \li \c condition: Breakpoint condition
98 \endlist
99 Returns a unique positive number as handle.
100
101 \c removeBreakpoint:
102 Removes a breakpoint from a given file and line.
103 \list
104 \li \c fullName: Name of the QML/JS file
105 \li \c lineNumber: Line number in the file
106 \li \c condition: Breakpoint condition
107 \endlist
108 Returns zero on success, a negative number on failure.
109
110 \c prepareStep:
111 Puts the interpreter in stepping mode.
112 Returns zero.
113
114*/
115Q_QML_EXPORT int qt_v4DebuggerHook(const char *json);
116
117
118} // extern "C"
119
120#if QT_CONFIG(qml_debug)
121static int qt_v4BreakpointCount = 0;
122static bool qt_v4IsDebugging = false;
123static bool qt_v4IsStepping = false;
124
125class Breakpoint
126{
127public:
128 Breakpoint() : bpNumber(0), lineNumber(-1) {}
129
130 bool matches(const QString &file, int line) const
131 {
132 return fullName == file && lineNumber == line;
133 }
134
135 int bpNumber;
136 int lineNumber;
137 QString fullName; // e.g. /opt/project/main.qml
138 QString engineName; // e.g. qrc:/main.qml
139 QString condition; // optional
140};
141
142static QVector<Breakpoint> qt_v4Breakpoints;
143static Breakpoint qt_v4LastStop;
144
145static void qt_v4TriggerBreakpoint(const Breakpoint &bp, QV4::Function *function)
146{
147 qt_v4LastStop = bp;
148
149 // Set up some auxiliary data for informational purpose.
150 // This is not part of the protocol.
151 QV4::Heap::String *functionName = function->name();
152 QByteArray functionNameUtf8;
153 if (functionName)
154 functionNameUtf8 = functionName->toQString().toUtf8();
155
156 qt_v4TriggeredBreakpointHook(); // Trigger Breakpoint.
157}
158
159int qt_v4DebuggerHook(const char *json)
160{
161 const int ProtocolVersion = 1;
162
163 enum {
164 Success = 0,
165 WrongProtocol,
166 NoSuchCommand,
167 NoSuchBreakpoint
168 };
169
170 QJsonDocument doc = QJsonDocument::fromJson(json);
171 QJsonObject ob = doc.object();
172 QByteArray command = ob.value(key: QLatin1String("command")).toString().toUtf8();
173
174 if (command == "protocolVersion") {
175 return ProtocolVersion; // Version number.
176 }
177
178 int version = ob.value(key: QLatin1String("version")).toString().toInt();
179 if (version != ProtocolVersion) {
180 return -WrongProtocol;
181 }
182
183 if (command == "insertBreakpoint") {
184 Breakpoint bp;
185 bp.bpNumber = ++qt_v4BreakpointCount;
186 bp.lineNumber = ob.value(key: QLatin1String("lineNumber")).toString().toInt();
187 bp.engineName = ob.value(key: QLatin1String("engineName")).toString();
188 bp.fullName = ob.value(key: QLatin1String("fullName")).toString();
189 bp.condition = ob.value(key: QLatin1String("condition")).toString();
190 qt_v4Breakpoints.append(t: bp);
191 qt_v4IsDebugging = true;
192 return bp.bpNumber;
193 }
194
195 if (command == "removeBreakpoint") {
196 int lineNumber = ob.value(key: QLatin1String("lineNumber")).toString().toInt();
197 QString fullName = ob.value(key: QLatin1String("fullName")).toString();
198 if (qt_v4Breakpoints.last().matches(file: fullName, line: lineNumber)) {
199 qt_v4Breakpoints.removeLast();
200 qt_v4IsDebugging = !qt_v4Breakpoints.isEmpty();
201 return Success;
202 }
203 for (int i = 0; i + 1 < qt_v4Breakpoints.size(); ++i) {
204 if (qt_v4Breakpoints.at(i).matches(file: fullName, line: lineNumber)) {
205 qt_v4Breakpoints[i] = qt_v4Breakpoints.takeLast();
206 return Success; // Ok.
207 }
208 }
209 return -NoSuchBreakpoint; // Failure
210 }
211
212 if (command == "prepareStep") {
213 qt_v4IsStepping = true;
214 return Success; // Ok.
215 }
216
217
218 return -NoSuchCommand; // Failure.
219}
220
221Q_NEVER_INLINE static void qt_v4CheckForBreak(QV4::CppStackFrame *frame)
222{
223 if (!qt_v4IsStepping && !qt_v4Breakpoints.size())
224 return;
225
226 const int lineNumber = frame->lineNumber();
227 QV4::Function *function = frame->v4Function;
228 QString engineName = function->sourceFile();
229
230 if (engineName.isEmpty())
231 return;
232
233 if (qt_v4IsStepping) {
234 if (qt_v4LastStop.lineNumber != lineNumber
235 || qt_v4LastStop.engineName != engineName) {
236 qt_v4IsStepping = false;
237 Breakpoint bp;
238 bp.bpNumber = 0;
239 bp.lineNumber = lineNumber;
240 bp.engineName = engineName;
241 qt_v4TriggerBreakpoint(bp, function);
242 return;
243 }
244 }
245
246 for (int i = qt_v4Breakpoints.size(); --i >= 0; ) {
247 const Breakpoint &bp = qt_v4Breakpoints.at(i);
248 if (bp.lineNumber != lineNumber)
249 continue;
250 if (bp.engineName != engineName)
251 continue;
252
253 qt_v4TriggerBreakpoint(bp, function);
254 }
255}
256
257Q_NEVER_INLINE static void debug_slowPath(QV4::ExecutionEngine *engine)
258{
259 QV4::Debugging::Debugger *debugger = engine->debugger();
260 if (debugger && debugger->pauseAtNextOpportunity())
261 debugger->maybeBreakAtInstruction();
262 if (qt_v4IsDebugging)
263 qt_v4CheckForBreak(frame: engine->currentStackFrame);
264}
265
266#endif // QT_CONFIG(qml_debug)
267// End of debugger interface
268
269using namespace QV4;
270using namespace QV4::Moth;
271
272#ifdef COUNT_INSTRUCTIONS
273static struct InstrCount {
274 InstrCount() {
275 fprintf(stderr, "Counting instructions...\n");
276 for (int i = 0; i < MOTH_NUM_INSTRUCTIONS(); ++i)
277 hits[i] = 0;
278 }
279 ~InstrCount() {
280 fprintf(stderr, "Instruction count:\n");
281#define BLAH(I) \
282 fprintf(stderr, "%llu : %s\n", hits[int(Instr::Type::I)], #I);
283 FOR_EACH_MOTH_INSTR(BLAH)
284 #undef BLAH
285 }
286 quint64 hits[MOTH_NUM_INSTRUCTIONS()];
287 void hit(Instr::Type i) { hits[int(i)]++; }
288} instrCount;
289#endif // COUNT_INSTRUCTIONS
290
291#define MOTH_BEGIN_INSTR_COMMON(instr) \
292 { \
293 INSTR_##instr(MOTH_DECODE)
294
295#ifdef COUNT_INSTRUCTIONS
296# define MOTH_BEGIN_INSTR(instr) \
297 MOTH_BEGIN_INSTR_COMMON(instr) \
298 instrCount.hit(Instr::Type::instr);
299#else // !COUNT_INSTRUCTIONS
300# define MOTH_BEGIN_INSTR(instr) \
301 MOTH_BEGIN_INSTR_COMMON(instr)
302#endif // COUNT_INSTRUCTIONS
303
304#ifdef MOTH_COMPUTED_GOTO
305#define MOTH_END_INSTR(instr) \
306 MOTH_DISPATCH_SINGLE() \
307 }
308#else // !MOTH_COMPUTED_GOTO
309#define MOTH_END_INSTR(instr) \
310 continue; \
311 }
312#endif
313
314static inline QV4::Value &stackValue(QV4::Value *stack, size_t slot, const JSTypesStackFrame *frame)
315{
316 Q_ASSERT(slot < CallData::HeaderSize() / sizeof(QV4::StaticValue)
317 + frame->jsFrame->argc()
318 + frame->v4Function->compiledFunction->nRegisters);
319 Q_UNUSED(frame);
320
321 return stack[slot];
322}
323
324#define STACK_VALUE(temp) stackValue(stack, temp, frame)
325
326// qv4scopedvalue_p.h also defines a CHECK_EXCEPTION macro
327#ifdef CHECK_EXCEPTION
328#undef CHECK_EXCEPTION
329#endif
330#define CHECK_EXCEPTION \
331 if (engine->hasException || engine->isInterrupted.loadRelaxed()) \
332 goto handleUnwind
333
334static inline Heap::CallContext *getScope(QV4::Value *stack, int level)
335{
336 Heap::ExecutionContext *scope = static_cast<ExecutionContext &>(stack[CallData::Context]).d();
337 while (level > 0) {
338 --level;
339 scope = scope->outer;
340 }
341 Q_ASSERT(scope);
342 return static_cast<Heap::CallContext *>(scope);
343}
344
345static inline const QV4::Value &constant(Function *function, int index)
346{
347 return function->compilationUnit->constants[index].asValue<QV4::Value>();
348}
349
350static bool compareEqualInt(QV4::Value &accumulator, QV4::Value lhs, int rhs)
351{
352 redo:
353 if (lhs.isUndefined())
354 return false;
355 if (lhs.isManagedOrUndefined()) {
356 // LHS: Managed
357 if (lhs.m()->internalClass->vtable->isString)
358 return RuntimeHelpers::stringToNumber(s: static_cast<String &>(lhs).toQString()) == rhs;
359 accumulator = lhs;
360 lhs = QV4::Value::fromReturnedValue(val: RuntimeHelpers::objectDefaultValue(object: &static_cast<QV4::Object &>(accumulator), typeHint: PREFERREDTYPE_HINT));
361 goto redo;
362 }
363
364 switch (lhs.quickType()) {
365 case QV4::Value::QT_Empty:
366 Q_UNREACHABLE();
367 case QV4::Value::QT_Null:
368 return false;
369 case QV4::Value::QT_Bool:
370 case QV4::Value::QT_Int:
371 return lhs.int_32() == rhs;
372 default: // double
373 return lhs.doubleValue() == rhs;
374 }
375}
376
377#define STORE_IP() frame->instructionPointer = int(code - function->codeData);
378#define STORE_ACC() accumulator = acc;
379#define ACC Value::fromReturnedValue(acc)
380#define VALUE_TO_INT(i, val) \
381 int i; \
382 do { \
383 if (Q_LIKELY(val.integerCompatible())) { \
384 i = val.int_32(); \
385 } else { \
386 double d; \
387 if (val.isDouble()) \
388 d = val.doubleValue(); \
389 else { \
390 STORE_ACC(); \
391 d = val.toNumberImpl(); \
392 CHECK_EXCEPTION; \
393 } \
394 i = QJSNumberCoercion::toInteger(d); \
395 } \
396 } while (false)
397
398void VME::exec(MetaTypesStackFrame *frame, ExecutionEngine *engine)
399{
400 qt_v4ResolvePendingBreakpointsHook();
401 if (engine->checkStackLimits()) {
402 frame->setReturnValueUndefined();
403 return;
404 }
405 ExecutionEngineCallDepthRecorder executionEngineCallDepthRecorder(engine);
406
407 Function *function = frame->v4Function;
408 Q_ASSERT(function->aotCompiledFunction);
409 Q_TRACE_SCOPE(QQmlV4_function_call, engine, function->name()->toQString(),
410 function->executableCompilationUnit()->fileName(),
411 function->compiledFunction->location.line(),
412 function->compiledFunction->location.column());
413 Profiling::FunctionCallProfiler profiler(engine, function); // start execution profiling
414
415 const qsizetype numFunctionArguments = function->aotCompiledFunction->argumentTypes.size();
416
417 Q_ALLOCA_DECLARE(void *, transformedArguments);
418 for (qsizetype i = 0; i < numFunctionArguments; ++i) {
419 const bool isValid = frame->argc() > i;
420 const QMetaType frameType = isValid ? frame->argTypes()[i] : QMetaType();
421
422 const QMetaType argumentType = function->aotCompiledFunction->argumentTypes[i];
423 if (isValid && argumentType == frameType)
424 continue;
425
426 if (transformedArguments == nullptr) {
427 Q_ALLOCA_ASSIGN(void *, transformedArguments, numFunctionArguments * sizeof(void *));
428 memcpy(dest: transformedArguments, src: frame->argv(), n: frame->argc() * sizeof(void *));
429 }
430
431 if (argumentType.sizeOf() == 0) {
432 transformedArguments[i] = nullptr;
433 continue;
434 }
435
436 void *frameVal = isValid ? frame->argv()[i] : nullptr;
437 if (isValid && frameType == QMetaType::fromType<QVariant>()) {
438 QVariant *variant = static_cast<QVariant *>(frameVal);
439
440 const QMetaType variantType = variant->metaType();
441 if (variantType == argumentType) {
442 // Slightly nasty, but we're allowed to do this.
443 // We don't want to destruct() the QVariant's data() below.
444 transformedArguments[i] = frame->argv()[i] = variant->data();
445 } else {
446 Q_ALLOCA_VAR(void, arg, argumentType.sizeOf());
447 argumentType.construct(where: arg);
448 QMetaType::convert(fromType: variantType, from: variant->data(), toType: argumentType, to: arg);
449 transformedArguments[i] = arg;
450 }
451 continue;
452 }
453
454 Q_ALLOCA_VAR(void, arg, argumentType.sizeOf());
455
456 if (argumentType == QMetaType::fromType<QVariant>()) {
457 if (isValid)
458 new (arg) QVariant(frameType, frameVal);
459 else
460 new (arg) QVariant();
461 } else if (argumentType == QMetaType::fromType<QJSPrimitiveValue>()) {
462 if (isValid)
463 new (arg) QJSPrimitiveValue(frameType, frameVal);
464 else
465 new (arg) QJSPrimitiveValue();
466 } else {
467
468 argumentType.construct(where: arg);
469 if (isValid)
470 QMetaType::convert(fromType: frameType, from: frameVal, toType: argumentType, to: arg);
471 }
472
473 transformedArguments[i] = arg;
474 }
475
476 const QMetaType returnType = function->aotCompiledFunction->returnType;
477 const QMetaType frameReturn = frame->returnType();
478 bool returnsQVariantWrapper = false;
479 Q_ALLOCA_DECLARE(void, transformedResult);
480 if (frame->returnValue() && returnType != frameReturn) {
481 if (frameReturn == QMetaType::fromType<QVariant>()) {
482 void *returnValue = frame->returnValue();
483 new (returnValue) QVariant(returnType);
484 transformedResult = static_cast<QVariant *>(returnValue)->data();
485 returnsQVariantWrapper = true;
486 } else if (returnType.sizeOf() > 0) {
487 Q_ALLOCA_ASSIGN(void, transformedResult, returnType.sizeOf());
488 } else {
489 transformedResult = frame; // Some non-null marker value
490 }
491 }
492
493 QQmlPrivate::AOTCompiledContext aotContext;
494 if (auto context = QV4::ExecutionEngine::qmlContext(ctx: frame->context()->d())) {
495 QV4::Heap::QQmlContextWrapper *wrapper = static_cast<Heap::QmlContext *>(context)->qml();
496 aotContext.qmlScopeObject = wrapper->scopeObject;
497 aotContext.qmlContext = wrapper->context;
498 }
499
500 aotContext.engine = engine->jsEngine();
501 aotContext.compilationUnit = function->executableCompilationUnit();
502
503 function->aotCompiledFunction->functionPtr(
504 &aotContext, transformedResult ? transformedResult : frame->returnValue(),
505 transformedArguments ? transformedArguments : frame->argv());
506
507 if (transformedResult && !returnsQVariantWrapper) {
508 // Shortcut the common case of the AOT function returning a more generic QObject pointer
509 // that we need to QObject-cast. No need to construct or destruct anything in that case.
510 if ((frameReturn.flags() & QMetaType::PointerToQObject)
511 && (returnType.flags() & QMetaType::PointerToQObject)) {
512 QObject *resultObj = *static_cast<QObject **>(transformedResult);
513 *static_cast<QObject **>(frame->returnValue())
514 = (resultObj && resultObj->metaObject()->inherits(metaObject: frameReturn.metaObject()))
515 ? resultObj
516 : nullptr;
517 } else if (returnType == QMetaType::fromType<QVariant>()) {
518 const QVariant *resultVariant = static_cast<QVariant *>(transformedResult);
519 if (resultVariant->metaType() == frameReturn) {
520 frameReturn.construct(where: frame->returnValue(), copy: resultVariant->data());
521 } else {
522 // Convert needs a pre-constructed target.
523 frameReturn.construct(where: frame->returnValue());
524 QMetaType::convert(fromType: resultVariant->metaType(), from: resultVariant->data(),
525 toType: frameReturn, to: frame->returnValue());
526 }
527 resultVariant->~QVariant();
528 } else {
529 // Convert needs a pre-constructed target.
530 frameReturn.construct(where: frame->returnValue());
531 QMetaType::convert(fromType: returnType, from: transformedResult, toType: frameReturn, to: frame->returnValue());
532 returnType.destruct(data: transformedResult);
533 }
534 }
535
536 if (transformedArguments) {
537 for (int i = 0; i < numFunctionArguments; ++i) {
538 void *arg = transformedArguments[i];
539 if (arg == nullptr)
540 continue;
541 if (i >= frame->argc() || arg != frame->argv()[i])
542 function->aotCompiledFunction->argumentTypes[i].destruct(data: arg);
543 }
544 }
545}
546
547ReturnedValue VME::exec(JSTypesStackFrame *frame, ExecutionEngine *engine)
548{
549 qt_v4ResolvePendingBreakpointsHook();
550 CHECK_STACK_LIMITS(engine);
551
552 Function *function = frame->v4Function;
553 Q_TRACE_SCOPE(QQmlV4_function_call, engine, function->name()->toQString(),
554 function->executableCompilationUnit()->fileName(),
555 function->compiledFunction->location.line(),
556 function->compiledFunction->location.column());
557 Profiling::FunctionCallProfiler profiler(engine, function); // start execution profiling
558 QV4::Debugging::Debugger *debugger = engine->debugger();
559
560#if QT_CONFIG(qml_jit)
561 if (debugger == nullptr) {
562 // Check for codeRef here. In rare cases the JIT compilation may fail, which leaves us
563 // with a (useless) codeRef, but no jittedCode. In that case, don't try to JIT again every
564 // time we execute the function, but just interpret instead.
565 if (function->codeRef == nullptr) {
566 if (engine->canJIT(f: function))
567 QV4::JIT::BaselineJIT(function).generate();
568 else
569 ++function->interpreterCallCount;
570 }
571 }
572#endif // QT_CONFIG(qml_jit)
573
574 // interpreter
575 if (debugger)
576 debugger->enteringFunction();
577
578 ReturnedValue result;
579 Q_ASSERT(function->kind != Function::AotCompiled);
580 if (function->jittedCode != nullptr && debugger == nullptr) {
581 result = function->jittedCode(frame, engine);
582 } else {
583 // interpreter
584 result = interpret(frame, engine, codeEntry: function->codeData);
585 }
586
587 if (debugger)
588 debugger->leavingFunction(retVal: result);
589
590 return result;
591}
592
593QV4::ReturnedValue VME::interpret(JSTypesStackFrame *frame, ExecutionEngine *engine, const char *code)
594{
595 QV4::Function *function = frame->v4Function;
596 QV4::Value &accumulator = frame->jsFrame->accumulator.asValue<Value>();
597 QV4::ReturnedValue acc = accumulator.asReturnedValue();
598 Value *stack = reinterpret_cast<Value *>(frame->jsFrame);
599
600 MOTH_JUMP_TABLE;
601
602 for (;;) {
603 MOTH_DISPATCH()
604 Q_UNREACHABLE(); // only reached when the dispatch doesn't jump somewhere
605
606 MOTH_BEGIN_INSTR(LoadConst)
607 acc = constant(function, index).asReturnedValue();
608 MOTH_END_INSTR(LoadConst)
609
610 MOTH_BEGIN_INSTR(LoadNull)
611 acc = Encode::null();
612 MOTH_END_INSTR(LoadNull)
613
614 MOTH_BEGIN_INSTR(LoadZero)
615 acc = Encode(static_cast<int>(0));
616 MOTH_END_INSTR(LoadZero)
617
618 MOTH_BEGIN_INSTR(LoadTrue)
619 acc = Encode(true);
620 MOTH_END_INSTR(LoadTrue)
621
622 MOTH_BEGIN_INSTR(LoadFalse)
623 acc = Encode(false);
624 MOTH_END_INSTR(LoadFalse)
625
626 MOTH_BEGIN_INSTR(LoadUndefined)
627 acc = Encode::undefined();
628 MOTH_END_INSTR(LoadUndefined)
629
630 MOTH_BEGIN_INSTR(LoadInt)
631 acc = Encode(value);
632 MOTH_END_INSTR(LoadInt)
633
634 MOTH_BEGIN_INSTR(MoveConst)
635 STACK_VALUE(destTemp) = constant(function, index: constIndex);
636 MOTH_END_INSTR(MoveConst)
637
638 MOTH_BEGIN_INSTR(LoadReg)
639 acc = STACK_VALUE(reg).asReturnedValue();
640 MOTH_END_INSTR(LoadReg)
641
642 MOTH_BEGIN_INSTR(StoreReg)
643 STACK_VALUE(reg) = acc;
644 MOTH_END_INSTR(StoreReg)
645
646 MOTH_BEGIN_INSTR(MoveReg)
647 STACK_VALUE(destReg) = STACK_VALUE(srcReg);
648 MOTH_END_INSTR(MoveReg)
649
650 MOTH_BEGIN_INSTR(LoadImport)
651 acc = function->compilationUnit->imports[index]->asReturnedValue();
652 MOTH_END_INSTR(LoadImport)
653
654 MOTH_BEGIN_INSTR(LoadLocal)
655 auto cc = static_cast<Heap::CallContext *>(STACK_VALUE(CallData::Context).m());
656 Q_ASSERT(cc->type != QV4::Heap::CallContext::Type_GlobalContext);
657 acc = cc->locals[index].asReturnedValue();
658 MOTH_END_INSTR(LoadLocal)
659
660 MOTH_BEGIN_INSTR(StoreLocal)
661 CHECK_EXCEPTION;
662 auto cc = static_cast<Heap::CallContext *>(STACK_VALUE(CallData::Context).m());
663 Q_ASSERT(cc->type != QV4::Heap::CallContext::Type_GlobalContext);
664 QV4::WriteBarrier::write(engine, base: cc, slot: cc->locals.values[index].data_ptr(), value: acc);
665 MOTH_END_INSTR(StoreLocal)
666
667 MOTH_BEGIN_INSTR(LoadScopedLocal)
668 auto cc = getScope(stack, level: scope);
669 acc = cc->locals[index].asReturnedValue();
670 MOTH_END_INSTR(LoadScopedLocal)
671
672 MOTH_BEGIN_INSTR(StoreScopedLocal)
673 CHECK_EXCEPTION;
674 auto cc = getScope(stack, level: scope);
675 QV4::WriteBarrier::write(engine, base: cc, slot: cc->locals.values[index].data_ptr(), value: acc);
676 MOTH_END_INSTR(StoreScopedLocal)
677
678 MOTH_BEGIN_INSTR(LoadRuntimeString)
679 acc = function->compilationUnit->runtimeStrings[stringId]->asReturnedValue();
680 MOTH_END_INSTR(LoadRuntimeString)
681
682 MOTH_BEGIN_INSTR(MoveRegExp)
683 STACK_VALUE(destReg) = Runtime::RegexpLiteral::call(engine, regExpId);
684 MOTH_END_INSTR(MoveRegExp)
685
686 MOTH_BEGIN_INSTR(LoadClosure)
687 acc = Runtime::Closure::call(engine, value);
688 MOTH_END_INSTR(LoadClosure)
689
690 MOTH_BEGIN_INSTR(LoadName)
691 STORE_IP();
692 acc = Runtime::LoadName::call(engine, name);
693 CHECK_EXCEPTION;
694 MOTH_END_INSTR(LoadName)
695
696 MOTH_BEGIN_INSTR(LoadGlobalLookup)
697 STORE_IP();
698 QV4::Lookup *l = function->executableCompilationUnit()->runtimeLookups + index;
699 acc = l->globalGetter(l, engine);
700 CHECK_EXCEPTION;
701 MOTH_END_INSTR(LoadGlobalLookup)
702
703 MOTH_BEGIN_INSTR(LoadQmlContextPropertyLookup)
704 STORE_IP();
705 QV4::Lookup *l = function->executableCompilationUnit()->runtimeLookups + index;
706 acc = l->qmlContextPropertyGetter(l, engine, nullptr);
707 CHECK_EXCEPTION;
708 MOTH_END_INSTR(LoadQmlContextPropertyLookup)
709
710 MOTH_BEGIN_INSTR(StoreNameStrict)
711 STORE_IP();
712 STORE_ACC();
713 Runtime::StoreNameStrict::call(engine, name, accumulator);
714 CHECK_EXCEPTION;
715 MOTH_END_INSTR(StoreNameStrict)
716
717 MOTH_BEGIN_INSTR(StoreNameSloppy)
718 STORE_IP();
719 STORE_ACC();
720 Runtime::StoreNameSloppy::call(engine, name, accumulator);
721 CHECK_EXCEPTION;
722 MOTH_END_INSTR(StoreNameSloppy)
723
724 MOTH_BEGIN_INSTR(LoadElement)
725 STORE_IP();
726 STORE_ACC();
727 acc = Runtime::LoadElement::call(engine, STACK_VALUE(base), accumulator);
728 CHECK_EXCEPTION;
729 MOTH_END_INSTR(LoadElement)
730
731 MOTH_BEGIN_INSTR(StoreElement)
732 STORE_IP();
733 STORE_ACC();
734 Runtime::StoreElement::call(engine, STACK_VALUE(base), STACK_VALUE(index), accumulator);
735 CHECK_EXCEPTION;
736 MOTH_END_INSTR(StoreElement)
737
738 MOTH_BEGIN_INSTR(LoadProperty)
739 STORE_IP();
740 STORE_ACC();
741 acc = Runtime::LoadProperty::call(engine, accumulator, name);
742 CHECK_EXCEPTION;
743 MOTH_END_INSTR(LoadProperty)
744
745 MOTH_BEGIN_INSTR(LoadOptionalProperty)
746 STORE_IP();
747 STORE_ACC();
748 if (accumulator.isNullOrUndefined()) {
749 acc = Encode::undefined();
750 code += offset;
751 } else {
752 acc = Runtime::LoadProperty::call(engine, accumulator, name);
753 }
754 CHECK_EXCEPTION;
755 MOTH_END_INSTR(LoadOptionalProperty)
756
757 MOTH_BEGIN_INSTR(GetLookup)
758 STORE_IP();
759 STORE_ACC();
760
761 QV4::Lookup *l = function->executableCompilationUnit()->runtimeLookups + index;
762
763 if (accumulator.isNullOrUndefined()) {
764 QString message = QStringLiteral("Cannot read property '%1' of %2")
765 .arg(a: engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[l->nameIndex]->toQString())
766 .arg(a: accumulator.toQStringNoThrow());
767 acc = engine->throwTypeError(message);
768 goto handleUnwind;
769 }
770
771 acc = l->getter(l, engine, accumulator);
772 CHECK_EXCEPTION;
773 MOTH_END_INSTR(GetLookup)
774
775 MOTH_BEGIN_INSTR(GetOptionalLookup)
776 STORE_IP();
777 STORE_ACC();
778
779 QV4::Lookup *l = function->executableCompilationUnit()->runtimeLookups + index;
780
781 if (accumulator.isNullOrUndefined()) {
782 acc = Encode::undefined();
783 code += offset;
784 } else {
785 acc = l->getter(l, engine, accumulator);
786 }
787 CHECK_EXCEPTION;
788 MOTH_END_INSTR(GetOptionalLookup)
789
790 MOTH_BEGIN_INSTR(StoreProperty)
791 STORE_IP();
792 STORE_ACC();
793 Runtime::StoreProperty::call(engine, STACK_VALUE(base), name, accumulator);
794 CHECK_EXCEPTION;
795 MOTH_END_INSTR(StoreProperty)
796
797 MOTH_BEGIN_INSTR(SetLookup)
798 STORE_IP();
799 STORE_ACC();
800 QV4::Lookup *l = function->executableCompilationUnit()->runtimeLookups + index;
801 if (!l->setter(l, engine, STACK_VALUE(base), accumulator) && function->isStrict())
802 engine->throwTypeError();
803 CHECK_EXCEPTION;
804 MOTH_END_INSTR(SetLookup)
805
806 MOTH_BEGIN_INSTR(LoadSuperProperty)
807 STORE_IP();
808 acc = Runtime::LoadSuperProperty::call(engine, STACK_VALUE(property));
809 CHECK_EXCEPTION;
810 MOTH_END_INSTR(LoadSuperProperty)
811
812 MOTH_BEGIN_INSTR(StoreSuperProperty)
813 STORE_IP();
814 STORE_ACC();
815 Runtime::StoreSuperProperty::call(engine, STACK_VALUE(property), accumulator);
816 CHECK_EXCEPTION;
817 MOTH_END_INSTR(StoreSuperProperty)
818
819 MOTH_BEGIN_INSTR(Yield)
820 frame->setYield(code);
821 frame->setYieldIsIterator(false);
822 return acc;
823 MOTH_END_INSTR(Yield)
824
825 MOTH_BEGIN_INSTR(YieldStar)
826 frame->setYield(code);
827 frame->setYieldIsIterator(true);
828 return acc;
829 MOTH_END_INSTR(YieldStar)
830
831 MOTH_BEGIN_INSTR(Resume)
832 // check exception, in case the generator was called with throw() or return()
833 if (engine->hasException) {
834 // an empty value indicates that the generator was called with return()
835 if (engine->exceptionValue->asReturnedValue() != Value::emptyValue().asReturnedValue())
836 goto handleUnwind;
837 engine->hasException = false;
838 *engine->exceptionValue = Value::undefinedValue();
839 } else {
840 code += offset;
841 }
842 MOTH_END_INSTR(Resume)
843
844 MOTH_BEGIN_INSTR(IteratorNextForYieldStar)
845 STORE_ACC();
846 acc = Runtime::IteratorNextForYieldStar::call(engine, accumulator, STACK_VALUE(iterator), &STACK_VALUE(object));
847 CHECK_EXCEPTION;
848 MOTH_END_INSTR(IteratorNextForYieldStar)
849
850 MOTH_BEGIN_INSTR(CallValue)
851 STORE_IP();
852 Value func = STACK_VALUE(name);
853 if (Q_UNLIKELY(!func.isFunctionObject())) {
854 acc = engine->throwTypeError(QStringLiteral("%1 is not a function").arg(a: func.toQStringNoThrow()));
855 goto handleUnwind;
856 }
857 Value undef = Value::undefinedValue();
858 acc = static_cast<const FunctionObject &>(func).call(thisObject: &undef, argv: stack + argv, argc);
859 CHECK_EXCEPTION;
860 MOTH_END_INSTR(CallValue)
861
862 MOTH_BEGIN_INSTR(CallWithReceiver)
863 STORE_IP();
864 Value func = STACK_VALUE(name);
865 if (Q_UNLIKELY(!func.isFunctionObject())) {
866 acc = engine->throwTypeError(QStringLiteral("%1 is not a function").arg(a: func.toQStringNoThrow()));
867 goto handleUnwind;
868 }
869 acc = static_cast<const FunctionObject &>(func).call(thisObject: stack + thisObject, argv: stack + argv, argc);
870 CHECK_EXCEPTION;
871 MOTH_END_INSTR(CallWithReceiver)
872
873 MOTH_BEGIN_INSTR(CallProperty)
874 STORE_IP();
875 acc = Runtime::CallProperty::call(engine, STACK_VALUE(base), name, stack + argv, argc);
876 CHECK_EXCEPTION;
877 MOTH_END_INSTR(CallProperty)
878
879 MOTH_BEGIN_INSTR(CallPropertyLookup)
880 STORE_IP();
881 Lookup *l = function->executableCompilationUnit()->runtimeLookups + lookupIndex;
882
883 if (STACK_VALUE(base).isNullOrUndefined()) {
884 QString message = QStringLiteral("Cannot call method '%1' of %2")
885 .arg(a: engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[l->nameIndex]->toQString())
886 .arg(STACK_VALUE(base).toQStringNoThrow());
887 acc = engine->throwTypeError(message);
888 goto handleUnwind;
889 }
890
891 // ok to have the value on the stack here
892 Value f = Value::fromReturnedValue(val: l->getter(l, engine, STACK_VALUE(base)));
893
894 if (Q_UNLIKELY(!f.isFunctionObject())) {
895 QString message = QStringLiteral("Property '%1' of object %2 is not a function")
896 .arg(a: engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[l->nameIndex]->toQString())
897 .arg(STACK_VALUE(base).toQStringNoThrow());
898 acc = engine->throwTypeError(message);
899 goto handleUnwind;
900 }
901
902 acc = static_cast<FunctionObject &>(f).call(thisObject: stack + base, argv: stack + argv, argc);
903 CHECK_EXCEPTION;
904 MOTH_END_INSTR(CallPropertyLookup)
905
906 MOTH_BEGIN_INSTR(CallName)
907 STORE_IP();
908 acc = Runtime::CallName::call(engine, name, stack + argv, argc);
909 CHECK_EXCEPTION;
910 MOTH_END_INSTR(CallName)
911
912 MOTH_BEGIN_INSTR(CallPossiblyDirectEval)
913 STORE_IP();
914 acc = Runtime::CallPossiblyDirectEval::call(engine, stack + argv, argc);
915 CHECK_EXCEPTION;
916 MOTH_END_INSTR(CallPossiblyDirectEval)
917
918 MOTH_BEGIN_INSTR(CallGlobalLookup)
919 STORE_IP();
920 acc = Runtime::CallGlobalLookup::call(engine, index, stack + argv, argc);
921 CHECK_EXCEPTION;
922 MOTH_END_INSTR(CallGlobalLookup)
923
924 MOTH_BEGIN_INSTR(CallQmlContextPropertyLookup)
925 STORE_IP();
926 acc = Runtime::CallQmlContextPropertyLookup::call(engine, index, stack + argv, argc);
927 CHECK_EXCEPTION;
928 MOTH_END_INSTR(CallQmlContextPropertyLookup)
929
930 MOTH_BEGIN_INSTR(CallWithSpread)
931 STORE_IP();
932 acc = Runtime::CallWithSpread::call(engine, STACK_VALUE(func), STACK_VALUE(thisObject), stack + argv, argc);
933 CHECK_EXCEPTION;
934 MOTH_END_INSTR(CallWithSpread)
935
936 MOTH_BEGIN_INSTR(TailCall)
937 STORE_IP();
938 *engine->jsAlloca(nValues: 1) = Primitive::fromInt32(i: argc);
939 *engine->jsAlloca(nValues: 1) = Primitive::fromInt32(i: argv);
940 *engine->jsAlloca(nValues: 1) = STACK_VALUE(thisObject);
941 *engine->jsAlloca(nValues: 1) = STACK_VALUE(func);
942 return Runtime::TailCall::call(frame, engine);
943 CHECK_EXCEPTION;
944 MOTH_END_INSTR(TailCall)
945
946 MOTH_BEGIN_INSTR(Construct)
947 STORE_IP();
948 STORE_ACC();
949 acc = Runtime::Construct::call(engine, STACK_VALUE(func), ACC, stack + argv, argc);
950 CHECK_EXCEPTION;
951 MOTH_END_INSTR(Construct)
952
953 MOTH_BEGIN_INSTR(ConstructWithSpread)
954 STORE_IP();
955 STORE_ACC();
956 acc = Runtime::ConstructWithSpread::call(engine, STACK_VALUE(func), ACC, stack + argv, argc);
957 CHECK_EXCEPTION;
958 MOTH_END_INSTR(ConstructWithSpread)
959
960 MOTH_BEGIN_INSTR(SetUnwindHandler)
961 frame->unwindHandler = offset ? code + offset : nullptr;
962 MOTH_END_INSTR(SetUnwindHandler)
963
964 MOTH_BEGIN_INSTR(UnwindDispatch)
965 CHECK_EXCEPTION;
966 if (frame->unwindLevel) {
967 --frame->unwindLevel;
968 if (frame->unwindLevel)
969 goto handleUnwind;
970 code = frame->unwindLabel;
971 }
972 MOTH_END_INSTR(UnwindDispatch)
973
974 MOTH_BEGIN_INSTR(UnwindToLabel)
975 frame->unwindLevel = level;
976 frame->unwindLabel = code + offset;
977 goto handleUnwind;
978 MOTH_END_INSTR(UnwindToLabel)
979
980 MOTH_BEGIN_INSTR(DeadTemporalZoneCheck)
981 if (ACC.isEmpty()) {
982 STORE_IP();
983 Runtime::ThrowReferenceError::call(engine, name);
984 goto handleUnwind;
985 }
986 MOTH_END_INSTR(DeadTemporalZoneCheck)
987
988 MOTH_BEGIN_INSTR(ThrowException)
989 STORE_IP();
990 STORE_ACC();
991 Runtime::ThrowException::call(engine, accumulator);
992 goto handleUnwind;
993 MOTH_END_INSTR(ThrowException)
994
995 MOTH_BEGIN_INSTR(GetException)
996 acc = engine->hasException ? engine->exceptionValue->asReturnedValue()
997 : Value::emptyValue().asReturnedValue();
998 engine->hasException = false;
999 MOTH_END_INSTR(HasException)
1000
1001 MOTH_BEGIN_INSTR(SetException)
1002 if (acc != Value::emptyValue().asReturnedValue()) {
1003 *engine->exceptionValue = acc;
1004 engine->hasException = true;
1005 }
1006 MOTH_END_INSTR(SetException)
1007
1008 MOTH_BEGIN_INSTR(PushCatchContext)
1009 Runtime::PushCatchContext::call(engine, index, name);
1010 MOTH_END_INSTR(PushCatchContext)
1011
1012 MOTH_BEGIN_INSTR(CreateCallContext)
1013 Runtime::PushCallContext::call(frame);
1014 MOTH_END_INSTR(CreateCallContext)
1015
1016 MOTH_BEGIN_INSTR(PushWithContext)
1017 STORE_IP();
1018 STORE_ACC();
1019 acc = Runtime::PushWithContext::call(engine, STACK_VALUE(CallData::Accumulator));
1020 CHECK_EXCEPTION;
1021 MOTH_END_INSTR(PushWithContext)
1022
1023 MOTH_BEGIN_INSTR(PushBlockContext)
1024 STORE_ACC();
1025 Runtime::PushBlockContext::call(engine, index);
1026 MOTH_END_INSTR(PushBlockContext)
1027
1028 MOTH_BEGIN_INSTR(CloneBlockContext)
1029 STORE_ACC();
1030 Runtime::CloneBlockContext::call(engine);
1031 MOTH_END_INSTR(CloneBlockContext)
1032
1033 MOTH_BEGIN_INSTR(PushScriptContext)
1034 Runtime::PushScriptContext::call(engine, index);
1035 MOTH_END_INSTR(PushScriptContext)
1036
1037 MOTH_BEGIN_INSTR(PopScriptContext)
1038 Runtime::PopScriptContext::call(engine);
1039 MOTH_END_INSTR(PopScriptContext)
1040
1041 MOTH_BEGIN_INSTR(PopContext)
1042 ExecutionContext *c = static_cast<ExecutionContext *>(stack + CallData::Context);
1043 STACK_VALUE(CallData::Context) = c->d()->outer;
1044 MOTH_END_INSTR(PopContext)
1045
1046 MOTH_BEGIN_INSTR(GetIterator)
1047 STORE_IP();
1048 STORE_ACC();
1049 acc = Runtime::GetIterator::call(engine, accumulator, iterator);
1050 CHECK_EXCEPTION;
1051 MOTH_END_INSTR(GetIterator)
1052
1053 MOTH_BEGIN_INSTR(IteratorNext)
1054 STORE_IP();
1055 STORE_ACC();
1056 acc = Runtime::IteratorNext::call(engine, accumulator, &STACK_VALUE(value));
1057 STACK_VALUE(done) = acc;
1058 CHECK_EXCEPTION;
1059 MOTH_END_INSTR(IteratorNext)
1060
1061 MOTH_BEGIN_INSTR(IteratorClose)
1062 STORE_IP();
1063 STORE_ACC();
1064 acc = Runtime::IteratorClose::call(engine, accumulator, STACK_VALUE(done));
1065 CHECK_EXCEPTION;
1066 MOTH_END_INSTR(IteratorClose)
1067
1068 MOTH_BEGIN_INSTR(DestructureRestElement)
1069 STORE_IP();
1070 STORE_ACC();
1071 acc = Runtime::DestructureRestElement::call(engine, ACC);
1072 CHECK_EXCEPTION;
1073 MOTH_END_INSTR(DestructureRestElement)
1074
1075 MOTH_BEGIN_INSTR(DeleteProperty)
1076 acc = Runtime::DeleteProperty::call(engine, function, STACK_VALUE(base), STACK_VALUE(index));
1077 CHECK_EXCEPTION;
1078 MOTH_END_INSTR(DeleteProperty)
1079
1080 MOTH_BEGIN_INSTR(DeleteName)
1081 acc = Runtime::DeleteName::call(engine, function, name);
1082 CHECK_EXCEPTION;
1083 MOTH_END_INSTR(DeleteName)
1084
1085 MOTH_BEGIN_INSTR(TypeofName)
1086 acc = Runtime::TypeofName::call(engine, name);
1087 MOTH_END_INSTR(TypeofName)
1088
1089 MOTH_BEGIN_INSTR(TypeofValue)
1090 STORE_ACC();
1091 acc = Runtime::TypeofValue::call(engine, accumulator);
1092 MOTH_END_INSTR(TypeofValue)
1093
1094 MOTH_BEGIN_INSTR(DeclareVar)
1095 Runtime::DeclareVar::call(engine, isDeletable, varName);
1096 MOTH_END_INSTR(DeclareVar)
1097
1098 MOTH_BEGIN_INSTR(DefineArray)
1099 QV4::Value *arguments = stack + args;
1100 acc = Runtime::ArrayLiteral::call(engine, arguments, argc);
1101 MOTH_END_INSTR(DefineArray)
1102
1103 MOTH_BEGIN_INSTR(DefineObjectLiteral)
1104 QV4::Value *arguments = stack + args;
1105 acc = Runtime::ObjectLiteral::call(engine, internalClassId, arguments, argc);
1106 MOTH_END_INSTR(DefineObjectLiteral)
1107
1108 MOTH_BEGIN_INSTR(CreateClass)
1109 acc = Runtime::CreateClass::call(engine, classIndex, STACK_VALUE(heritage), stack + computedNames);
1110 MOTH_END_INSTR(CreateClass)
1111
1112 MOTH_BEGIN_INSTR(CreateMappedArgumentsObject)
1113 acc = Runtime::CreateMappedArgumentsObject::call(engine);
1114 MOTH_END_INSTR(CreateMappedArgumentsObject)
1115
1116 MOTH_BEGIN_INSTR(CreateUnmappedArgumentsObject)
1117 acc = Runtime::CreateUnmappedArgumentsObject::call(engine);
1118 MOTH_END_INSTR(CreateUnmappedArgumentsObject)
1119
1120 MOTH_BEGIN_INSTR(CreateRestParameter)
1121 acc = Runtime::CreateRestParameter::call(engine, argIndex);
1122 MOTH_END_INSTR(CreateRestParameter)
1123
1124 MOTH_BEGIN_INSTR(ConvertThisToObject)
1125 STORE_ACC();
1126 stack[CallData::This] = Runtime::ConvertThisToObject::call(
1127 engine, STACK_VALUE(CallData::This));
1128 CHECK_EXCEPTION;
1129 MOTH_END_INSTR(ConvertThisToObject)
1130
1131 MOTH_BEGIN_INSTR(LoadSuperConstructor)
1132 acc = Runtime::LoadSuperConstructor::call(engine, STACK_VALUE(CallData::Function));
1133 CHECK_EXCEPTION;
1134 MOTH_END_INSTR(LoadSuperConstructor)
1135
1136 MOTH_BEGIN_INSTR(ToObject)
1137 STORE_ACC();
1138 acc = ACC.toObject(e: engine)->asReturnedValue();
1139 CHECK_EXCEPTION;
1140 MOTH_END_INSTR(ToObject)
1141
1142 MOTH_BEGIN_INSTR(Jump)
1143 code += offset;
1144 MOTH_END_INSTR(Jump)
1145
1146 MOTH_BEGIN_INSTR(JumpTrue)
1147 bool takeJump;
1148 if (Q_LIKELY(ACC.integerCompatible()))
1149 takeJump = ACC.int_32();
1150 else
1151 takeJump = ACC.toBoolean();
1152 if (takeJump)
1153 code += offset;
1154 MOTH_END_INSTR(JumpTrue)
1155
1156 MOTH_BEGIN_INSTR(JumpFalse)
1157 bool takeJump;
1158 if (Q_LIKELY(ACC.integerCompatible()))
1159 takeJump = !ACC.int_32();
1160 else
1161 takeJump = !ACC.toBoolean();
1162 if (takeJump)
1163 code += offset;
1164 MOTH_END_INSTR(JumpFalse)
1165
1166 MOTH_BEGIN_INSTR(JumpNoException)
1167 if (!engine->hasException)
1168 code += offset;
1169 MOTH_END_INSTR(JumpNoException)
1170
1171 MOTH_BEGIN_INSTR(JumpNotUndefined)
1172 if (Q_LIKELY(acc != QV4::Encode::undefined()))
1173 code += offset;
1174 MOTH_END_INSTR(JumpNotUndefined)
1175
1176 MOTH_BEGIN_INSTR(CheckException)
1177 CHECK_EXCEPTION;
1178 MOTH_END_INSTR(CheckException)
1179
1180 MOTH_BEGIN_INSTR(CmpEqNull)
1181 acc = Encode(ACC.isNullOrUndefined());
1182 MOTH_END_INSTR(CmpEqNull)
1183
1184 MOTH_BEGIN_INSTR(CmpNeNull)
1185 acc = Encode(!ACC.isNullOrUndefined());
1186 MOTH_END_INSTR(CmpNeNull)
1187
1188 MOTH_BEGIN_INSTR(CmpEqInt)
1189 if (ACC.isIntOrBool()) {
1190 acc = Encode(ACC.int_32() == lhs);
1191 } else {
1192 STORE_ACC();
1193 acc = Encode(compareEqualInt(accumulator, ACC, rhs: lhs));
1194 CHECK_EXCEPTION;
1195 }
1196 MOTH_END_INSTR(CmpEqInt)
1197
1198 MOTH_BEGIN_INSTR(CmpNeInt)
1199 if (ACC.isIntOrBool()) {
1200 acc = Encode(bool(ACC.int_32() != lhs));
1201 } else {
1202 STORE_ACC();
1203 acc = Encode(!compareEqualInt(accumulator, ACC, rhs: lhs));
1204 CHECK_EXCEPTION;
1205 }
1206 MOTH_END_INSTR(CmpNeInt)
1207
1208 MOTH_BEGIN_INSTR(CmpEq)
1209 const Value left = STACK_VALUE(lhs);
1210 if (Q_LIKELY(left.asReturnedValue() == ACC.asReturnedValue())) {
1211 acc = Encode(!ACC.isNaN());
1212 } else if (Q_LIKELY(left.isInteger() && ACC.isInteger())) {
1213 acc = Encode(left.int_32() == ACC.int_32());
1214 } else {
1215 STORE_ACC();
1216 acc = Encode(bool(Runtime::CompareEqual::call(left, accumulator)));
1217 CHECK_EXCEPTION;
1218 }
1219 MOTH_END_INSTR(CmpEq)
1220
1221 MOTH_BEGIN_INSTR(CmpNe)
1222 const Value left = STACK_VALUE(lhs);
1223 if (Q_LIKELY(left.isInteger() && ACC.isInteger())) {
1224 acc = Encode(bool(left.int_32() != ACC.int_32()));
1225 } else {
1226 STORE_ACC();
1227 acc = Encode(bool(!Runtime::CompareEqual::call(left, accumulator)));
1228 CHECK_EXCEPTION;
1229 }
1230 MOTH_END_INSTR(CmpNe)
1231
1232 MOTH_BEGIN_INSTR(CmpGt)
1233 const Value left = STACK_VALUE(lhs);
1234 if (Q_LIKELY(left.isInteger() && ACC.isInteger())) {
1235 acc = Encode(left.int_32() > ACC.int_32());
1236 } else if (left.isNumber() && ACC.isNumber()) {
1237 acc = Encode(left.asDouble() > ACC.asDouble());
1238 } else {
1239 STORE_ACC();
1240 acc = Encode(bool(Runtime::CompareGreaterThan::call(left, accumulator)));
1241 CHECK_EXCEPTION;
1242 }
1243 MOTH_END_INSTR(CmpGt)
1244
1245 MOTH_BEGIN_INSTR(CmpGe)
1246 const Value left = STACK_VALUE(lhs);
1247 if (Q_LIKELY(left.isInteger() && ACC.isInteger())) {
1248 acc = Encode(left.int_32() >= ACC.int_32());
1249 } else if (left.isNumber() && ACC.isNumber()) {
1250 acc = Encode(left.asDouble() >= ACC.asDouble());
1251 } else {
1252 STORE_ACC();
1253 acc = Encode(bool(Runtime::CompareGreaterEqual::call(left, accumulator)));
1254 CHECK_EXCEPTION;
1255 }
1256 MOTH_END_INSTR(CmpGe)
1257
1258 MOTH_BEGIN_INSTR(CmpLt)
1259 const Value left = STACK_VALUE(lhs);
1260 if (Q_LIKELY(left.isInteger() && ACC.isInteger())) {
1261 acc = Encode(left.int_32() < ACC.int_32());
1262 } else if (left.isNumber() && ACC.isNumber()) {
1263 acc = Encode(left.asDouble() < ACC.asDouble());
1264 } else {
1265 STORE_ACC();
1266 acc = Encode(bool(Runtime::CompareLessThan::call(left, accumulator)));
1267 CHECK_EXCEPTION;
1268 }
1269 MOTH_END_INSTR(CmpLt)
1270
1271 MOTH_BEGIN_INSTR(CmpLe)
1272 const Value left = STACK_VALUE(lhs);
1273 if (Q_LIKELY(left.isInteger() && ACC.isInteger())) {
1274 acc = Encode(left.int_32() <= ACC.int_32());
1275 } else if (left.isNumber() && ACC.isNumber()) {
1276 acc = Encode(left.asDouble() <= ACC.asDouble());
1277 } else {
1278 STORE_ACC();
1279 acc = Encode(bool(Runtime::CompareLessEqual::call(left, accumulator)));
1280 CHECK_EXCEPTION;
1281 }
1282 MOTH_END_INSTR(CmpLe)
1283
1284 MOTH_BEGIN_INSTR(CmpStrictEqual)
1285 if (STACK_VALUE(lhs).rawValue() == ACC.rawValue() && !ACC.isNaN()) {
1286 acc = Encode(true);
1287 } else {
1288 STORE_ACC();
1289 acc = Runtime::StrictEqual::call(STACK_VALUE(lhs), accumulator);
1290 CHECK_EXCEPTION;
1291 }
1292 MOTH_END_INSTR(CmpStrictEqual)
1293
1294 MOTH_BEGIN_INSTR(CmpStrictNotEqual)
1295 if (STACK_VALUE(lhs).rawValue() != ACC.rawValue() || ACC.isNaN()) {
1296 STORE_ACC();
1297 acc = Runtime::StrictNotEqual::call(STACK_VALUE(lhs), accumulator);
1298 CHECK_EXCEPTION;
1299 } else {
1300 acc = Encode(false);
1301 }
1302 MOTH_END_INSTR(CmpStrictNotEqual)
1303
1304 MOTH_BEGIN_INSTR(CmpIn)
1305 STORE_IP();
1306 STORE_ACC();
1307 acc = Runtime::In::call(engine, STACK_VALUE(lhs), accumulator);
1308 CHECK_EXCEPTION;
1309 MOTH_END_INSTR(CmpIn)
1310
1311 MOTH_BEGIN_INSTR(CmpInstanceOf)
1312 STORE_ACC();
1313 acc = Runtime::Instanceof::call(engine, STACK_VALUE(lhs), ACC);
1314 CHECK_EXCEPTION;
1315 MOTH_END_INSTR(CmpInstanceOf)
1316
1317 MOTH_BEGIN_INSTR(UNot)
1318 if (ACC.integerCompatible()) {
1319 acc = Encode(!static_cast<bool>(ACC.int_32()));
1320 } else {
1321 acc = Encode(!Value::toBooleanImpl(ACC));
1322 }
1323 MOTH_END_INSTR(UNot)
1324
1325 MOTH_BEGIN_INSTR(UPlus)
1326 if (Q_UNLIKELY(!ACC.isNumber())) {
1327 acc = Encode(ACC.toNumberImpl());
1328 CHECK_EXCEPTION;
1329 }
1330 MOTH_END_INSTR(UPlus)
1331
1332 MOTH_BEGIN_INSTR(UMinus)
1333 if (Q_LIKELY(ACC.integerCompatible())) {
1334 int a = ACC.int_32();
1335 if (a == 0 || a == std::numeric_limits<int>::min()) {
1336 acc = Encode(-static_cast<double>(a));
1337 } else {
1338 acc = sub_int32(a: 0, ACC.int_32());
1339 }
1340 } else if (ACC.isDouble()) {
1341 acc ^= (1ull << 63); // simply flip sign bit
1342 } else {
1343 acc = Encode(-ACC.toNumberImpl());
1344 CHECK_EXCEPTION;
1345 }
1346 MOTH_END_INSTR(UMinus)
1347
1348 MOTH_BEGIN_INSTR(UCompl)
1349 VALUE_TO_INT(a, ACC);
1350 acc = Encode(~a);
1351 MOTH_END_INSTR(UCompl)
1352
1353 MOTH_BEGIN_INSTR(Increment)
1354 if (Q_LIKELY(ACC.integerCompatible())) {
1355 acc = add_int32(ACC.int_32(), b: 1);
1356 } else if (ACC.isDouble()) {
1357 acc = QV4::Encode(ACC.doubleValue() + 1.);
1358 } else {
1359 acc = Encode(ACC.toNumberImpl() + 1.);
1360 CHECK_EXCEPTION;
1361 }
1362 MOTH_END_INSTR(Increment)
1363
1364 MOTH_BEGIN_INSTR(Decrement)
1365 if (Q_LIKELY(ACC.integerCompatible())) {
1366 acc = sub_int32(ACC.int_32(), b: 1);
1367 } else if (ACC.isDouble()) {
1368 acc = QV4::Encode(ACC.doubleValue() - 1.);
1369 } else {
1370 acc = Encode(ACC.toNumberImpl() - 1.);
1371 CHECK_EXCEPTION;
1372 }
1373 MOTH_END_INSTR(Decrement)
1374
1375 MOTH_BEGIN_INSTR(Add)
1376 const Value left = STACK_VALUE(lhs);
1377 if (Q_LIKELY(Value::integerCompatible(left, ACC))) {
1378 acc = add_int32(a: left.int_32(), ACC.int_32());
1379 } else if (left.isNumber() && ACC.isNumber()) {
1380 acc = Encode(left.asDouble() + ACC.asDouble());
1381 } else {
1382 STORE_ACC();
1383 acc = Runtime::Add::call(engine, left, accumulator);
1384 CHECK_EXCEPTION;
1385 }
1386 MOTH_END_INSTR(Add)
1387
1388 MOTH_BEGIN_INSTR(Sub)
1389 const Value left = STACK_VALUE(lhs);
1390 if (Q_LIKELY(Value::integerCompatible(left, ACC))) {
1391 acc = sub_int32(a: left.int_32(), ACC.int_32());
1392 } else if (left.isNumber() && ACC.isNumber()) {
1393 acc = Encode(left.asDouble() - ACC.asDouble());
1394 } else {
1395 STORE_ACC();
1396 acc = Runtime::Sub::call(left, accumulator);
1397 CHECK_EXCEPTION;
1398 }
1399 MOTH_END_INSTR(Sub)
1400
1401 MOTH_BEGIN_INSTR(As)
1402 const Value left = STACK_VALUE(lhs);
1403 STORE_ACC();
1404 acc = Runtime::As::call(engine, left, accumulator);
1405 MOTH_END_INSTR(As)
1406
1407 MOTH_BEGIN_INSTR(Exp)
1408 const Value left = STACK_VALUE(lhs);
1409 double base = left.toNumber();
1410 double exp = ACC.toNumber();
1411 acc = Encode(QQmlPrivate::jsExponentiate(base, exponent: exp));
1412 MOTH_END_INSTR(Exp)
1413
1414 MOTH_BEGIN_INSTR(Mul)
1415 const Value left = STACK_VALUE(lhs);
1416 if (Q_LIKELY(Value::integerCompatible(left, ACC))) {
1417 acc = mul_int32(a: left.int_32(), ACC.int_32());
1418 } else if (left.isNumber() && ACC.isNumber()) {
1419 acc = Encode(left.asDouble() * ACC.asDouble());
1420 } else {
1421 STORE_ACC();
1422 acc = Runtime::Mul::call(left, accumulator);
1423 CHECK_EXCEPTION;
1424 }
1425 MOTH_END_INSTR(Mul)
1426
1427 MOTH_BEGIN_INSTR(Div)
1428 STORE_ACC();
1429 acc = Runtime::Div::call(STACK_VALUE(lhs), accumulator);
1430 CHECK_EXCEPTION;
1431 MOTH_END_INSTR(Div)
1432
1433 MOTH_BEGIN_INSTR(Mod)
1434 STORE_ACC();
1435 acc = Runtime::Mod::call(STACK_VALUE(lhs), accumulator);
1436 CHECK_EXCEPTION;
1437 MOTH_END_INSTR(Mod)
1438
1439 MOTH_BEGIN_INSTR(BitAnd)
1440 VALUE_TO_INT(l, STACK_VALUE(lhs));
1441 VALUE_TO_INT(a, ACC);
1442 acc = Encode(l & a);
1443 MOTH_END_INSTR(BitAnd)
1444
1445 MOTH_BEGIN_INSTR(BitOr)
1446 VALUE_TO_INT(l, STACK_VALUE(lhs));
1447 VALUE_TO_INT(a, ACC);
1448 acc = Encode(l | a);
1449 MOTH_END_INSTR(BitOr)
1450
1451 MOTH_BEGIN_INSTR(BitXor)
1452 VALUE_TO_INT(l, STACK_VALUE(lhs));
1453 VALUE_TO_INT(a, ACC);
1454 acc = Encode(l ^ a);
1455 MOTH_END_INSTR(BitXor)
1456
1457 MOTH_BEGIN_INSTR(UShr)
1458 VALUE_TO_INT(l, STACK_VALUE(lhs));
1459 VALUE_TO_INT(a, ACC);
1460 acc = Encode(static_cast<uint>(l) >> uint(a & 0x1f));
1461 MOTH_END_INSTR(UShr)
1462
1463 MOTH_BEGIN_INSTR(Shr)
1464 VALUE_TO_INT(l, STACK_VALUE(lhs));
1465 VALUE_TO_INT(a, ACC);
1466 acc = Encode(l >> (a & 0x1f));
1467 MOTH_END_INSTR(Shr)
1468
1469 MOTH_BEGIN_INSTR(Shl)
1470 VALUE_TO_INT(l, STACK_VALUE(lhs));
1471 VALUE_TO_INT(a, ACC);
1472 acc = Encode(l << (a & 0x1f));
1473 MOTH_END_INSTR(Shl)
1474
1475 MOTH_BEGIN_INSTR(BitAndConst)
1476 VALUE_TO_INT(a, ACC);
1477 acc = Encode(a & rhs);
1478 CHECK_EXCEPTION;
1479 MOTH_END_INSTR(BitAndConst)
1480
1481 MOTH_BEGIN_INSTR(BitOrConst)
1482 VALUE_TO_INT(a, ACC);
1483 acc = Encode(a | rhs);
1484 MOTH_END_INSTR(BitOrConst)
1485
1486 MOTH_BEGIN_INSTR(BitXorConst)
1487 VALUE_TO_INT(a, ACC);
1488 acc = Encode(a ^ rhs);
1489 MOTH_END_INSTR(BitXorConst)
1490
1491 MOTH_BEGIN_INSTR(UShrConst)
1492 acc = Encode(ACC.toUInt32() >> uint(rhs));
1493 MOTH_END_INSTR(UShrConst)
1494
1495 MOTH_BEGIN_INSTR(ShrConst)
1496 VALUE_TO_INT(a, ACC);
1497 acc = Encode(a >> rhs);
1498 MOTH_END_INSTR(ShrConst)
1499
1500 MOTH_BEGIN_INSTR(ShlConst)
1501 VALUE_TO_INT(a, ACC);
1502 acc = Encode(a << rhs);
1503 MOTH_END_INSTR(ShlConst)
1504
1505 MOTH_BEGIN_INSTR(Ret)
1506 return acc;
1507 MOTH_END_INSTR(Ret)
1508
1509 MOTH_BEGIN_INSTR(InitializeBlockDeadTemporalZone)
1510 acc = Encode(Value::emptyValue());
1511 for (int i = firstReg, end = firstReg + count; i < end; ++i)
1512 STACK_VALUE(i) = acc;
1513 MOTH_END_INSTR(InitializeBlockDeadTemporalZone)
1514
1515 MOTH_BEGIN_INSTR(ThrowOnNullOrUndefined)
1516 if (Value::fromReturnedValue(val: acc).isNullOrUndefined()) {
1517 engine->throwTypeError();
1518 goto handleUnwind;
1519 }
1520 MOTH_END_INSTR(ThrowOnNullOrUndefined)
1521
1522 MOTH_BEGIN_INSTR(GetTemplateObject)
1523 acc = Runtime::GetTemplateObject::call(function, index);
1524 MOTH_END_INSTR(GetTemplateObject)
1525
1526 MOTH_BEGIN_INSTR(Debug)
1527#if QT_CONFIG(qml_debug)
1528 STORE_IP();
1529 debug_slowPath(engine);
1530#endif // QT_CONFIG(qml_debug)
1531 MOTH_END_INSTR(Debug)
1532
1533 handleUnwind:
1534 // We do start the exception handler in case of isInterrupted. The exception handler will
1535 // immediately abort, due to the same isInterrupted. We don't skip the exception handler
1536 // because the current behavior is easier to implement in the JIT.
1537 Q_ASSERT(engine->hasException || engine->isInterrupted.loadRelaxed() || frame->unwindLevel);
1538 if (!frame->unwindHandler) {
1539 acc = Encode::undefined();
1540 return acc;
1541 }
1542 code = frame->unwindHandler;
1543 }
1544}
1545

source code of qtdeclarative/src/qml/jsruntime/qv4vme_moth.cpp