Revision control

++ // Copyright (C) 2016 The Qt Company Ltd.
++ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
++ // Qt-Security score:significant
++
++ #include "qv4vme_moth_p.h"
++
++ #include <QtCore/qjsondocument.h>
++ #include <QtCore/qjsonobject.h>
++
++ #include <private/qv4alloca_p.h>
++ #include <private/qv4instr_moth_p.h>
++ #include <private/qv4value_p.h>
++ #include <private/qv4debugging_p.h>
++ #include <private/qv4function_p.h>
++ #include <private/qv4functionobject_p.h>
++ #include <private/qv4math_p.h>
++ #include <private/qv4scopedvalue_p.h>
++ #include <private/qv4lookup_p.h>
++ #include <private/qv4regexp_p.h>
++ #include <private/qv4regexpobject_p.h>
++ #include <private/qv4string_p.h>
++ #include <private/qv4profiling_p.h>
++ #include <private/qv4jscall_p.h>
++ #include <private/qv4generatorobject_p.h>
++ #include <private/qv4alloca_p.h>
++ #include <private/qqmljavascriptexpression_p.h>
++ #include <private/qv4qmlcontext_p.h>
++ #include <QtQml/private/qv4runtime_p.h>
++ #include <iostream>
++
++ #if QT_CONFIG(qml_jit)
++ #include <private/qv4baselinejit_p.h>
++ #endif
++
++ #include <qtqml_tracepoints_p.h>
++
++ #undef COUNT_INSTRUCTIONS
++
++ Q_TRACE_POINT(qtqml, QQmlV4_function_call_entry, const QV4::ExecutionEngine *engine, const QString &function, const QString &fileName, int line, int column)
++ Q_TRACE_POINT(qtqml, QQmlV4_function_call_exit)
++
++ enum { ShowWhenDeoptimiationHappens = 0 };
++
++ extern "C" {
++
++ // This is the interface to Qt Creator's (new) QML debugger.
++
++ /*! \internal
++ \since 5.5
++
++ This function is called uncondionally from VME::run().
++
++ An attached debugger can set a breakpoint here to
++ intercept calls to VME::run().
++ */
++
++ Q_QML_EXPORT void qt_v4ResolvePendingBreakpointsHook()
++ {
++ }
++
++ /*! \internal
++ \since 5.5
++
++ This function is called when a QML interpreter breakpoint
++ is hit.
++
++ An attached debugger can set a breakpoint here.
++ */
++ Q_QML_EXPORT void qt_v4TriggeredBreakpointHook()
++ {
++ }
++
++ /*! \internal
++ \since 5.5
++
++ The main entry point into "Native Mixed" Debugging.
++
++ Commands are passed as UTF-8 encoded JSON data.
++ The data has two compulsory fields:
++ \list
++ \li \c version: Version of the protocol (currently 1)
++ \li \c command: Name of the command
++ \endlist
++
++ Depending on \c command, more fields can be present.
++
++ Error is indicated by negative return values,
++ success by non-negative return values.
++
++ \c protocolVersion:
++ Returns version of implemented protocol.
++
++ \c insertBreakpoint:
++ Sets a breakpoint on a given file and line.
++ \list
++ \li \c fullName: Name of the QML/JS file
++ \li \c lineNumber: Line number in the file
++ \li \c condition: Breakpoint condition
++ \endlist
++ Returns a unique positive number as handle.
++
++ \c removeBreakpoint:
++ Removes a breakpoint from a given file and line.
++ \list
++ \li \c fullName: Name of the QML/JS file
++ \li \c lineNumber: Line number in the file
++ \li \c condition: Breakpoint condition
++ \endlist
++ Returns zero on success, a negative number on failure.
++
++ \c prepareStep:
++ Puts the interpreter in stepping mode.
++ Returns zero.
++
++ */
++ Q_QML_EXPORT int qt_v4DebuggerHook(const char *json);
++
++
++ } // extern "C"
++
++ #if QT_CONFIG(qml_debug)
++ static int qt_v4BreakpointCount = 0;
++ static bool qt_v4IsDebugging = false;
++ static bool qt_v4IsStepping = false;
++
++ namespace {
++ class Breakpoint
++ {
++ public:
++ Breakpoint() : bpNumber(0), lineNumber(-1) {}
++
++ bool matches(const QString &file, int line) const
++ {
++ return fullName == file && lineNumber == line;
++ }
++
++ int bpNumber;
++ int lineNumber;
++ QString fullName; // e.g. /opt/project/main.qml
++ QString engineName; // e.g. qrc:/main.qml
++ QString condition; // optional
++ };
++
++ QList<Breakpoint> qt_v4Breakpoints;
++ Breakpoint qt_v4LastStop;
++
++ void qt_v4TriggerBreakpoint(const Breakpoint &bp, QV4::Function *function)
++ {
++ qt_v4LastStop = bp;
++
++ // Set up some auxiliary data for informational purpose.
++ // This is not part of the protocol.
++ QV4::Heap::String *functionName = function->name();
++ QByteArray functionNameUtf8;
++ if (functionName)
++ functionNameUtf8 = functionName->toQString().toUtf8();
++
++ qt_v4TriggeredBreakpointHook(); // Trigger Breakpoint.
++ }
++
++ }
++
++ int qt_v4DebuggerHook(const char *json)
++ {
++ const int ProtocolVersion = 1;
++
++ enum {
++ Success = 0,
++ WrongProtocol,
++ NoSuchCommand,
++ NoSuchBreakpoint
++ };
++
++ QJsonDocument doc = QJsonDocument::fromJson(json);
++ QJsonObject ob = doc.object();
++ QByteArray command = ob.value(QLatin1String("command")).toString().toUtf8();
++
++ if (command == "protocolVersion") {
++ return ProtocolVersion; // Version number.
++ }
++
++ int version = ob.value(QLatin1String("version")).toString().toInt();
++ if (version != ProtocolVersion) {
++ return -WrongProtocol;
++ }
++
++ if (command == "insertBreakpoint") {
++ Breakpoint bp;
++ bp.bpNumber = ++qt_v4BreakpointCount;
++ bp.lineNumber = ob.value(QLatin1String("lineNumber")).toString().toInt();
++ bp.engineName = ob.value(QLatin1String("engineName")).toString();
++ bp.fullName = ob.value(QLatin1String("fullName")).toString();
++ bp.condition = ob.value(QLatin1String("condition")).toString();
++ qt_v4Breakpoints.append(bp);
++ qt_v4IsDebugging = true;
++ return bp.bpNumber;
++ }
++
++ if (command == "removeBreakpoint") {
++ int lineNumber = ob.value(QLatin1String("lineNumber")).toString().toInt();
++ QString fullName = ob.value(QLatin1String("fullName")).toString();
++ if (qt_v4Breakpoints.last().matches(fullName, lineNumber)) {
++ qt_v4Breakpoints.removeLast();
++ qt_v4IsDebugging = !qt_v4Breakpoints.isEmpty();
++ return Success;
++ }
++ for (int i = 0; i + 1 < qt_v4Breakpoints.size(); ++i) {
++ if (qt_v4Breakpoints.at(i).matches(fullName, lineNumber)) {
++ qt_v4Breakpoints[i] = qt_v4Breakpoints.takeLast();
++ return Success; // Ok.
++ }
++ }
++ return -NoSuchBreakpoint; // Failure
++ }
++
++ if (command == "prepareStep") {
++ qt_v4IsStepping = true;
++ return Success; // Ok.
++ }
++
++
++ return -NoSuchCommand; // Failure.
++ }
++
++ Q_NEVER_INLINE static void qt_v4CheckForBreak(QV4::CppStackFrame *frame)
++ {
++ if (!qt_v4IsStepping && !qt_v4Breakpoints.size())
++ return;
++
++ const int lineNumber = frame->lineNumber();
++ QV4::Function *function = frame->v4Function;
++ QString engineName = function->sourceFile();
++
++ if (engineName.isEmpty())
++ return;
++
++ if (qt_v4IsStepping) {
++ if (qt_v4LastStop.lineNumber != lineNumber
++ || qt_v4LastStop.engineName != engineName) {
++ qt_v4IsStepping = false;
++ Breakpoint bp;
++ bp.bpNumber = 0;
++ bp.lineNumber = lineNumber;
++ bp.engineName = engineName;
++ qt_v4TriggerBreakpoint(bp, function);
++ return;
++ }
++ }
++
++ for (int i = qt_v4Breakpoints.size(); --i >= 0; ) {
++ const Breakpoint &bp = qt_v4Breakpoints.at(i);
++ if (bp.lineNumber != lineNumber)
++ continue;
++ if (bp.engineName != engineName)
++ continue;
++
++ qt_v4TriggerBreakpoint(bp, function);
++ }
++ }
++
++ Q_NEVER_INLINE static void debug_slowPath(QV4::ExecutionEngine *engine)
++ {
++ QV4::Debugging::Debugger *debugger = engine->debugger();
++ if (debugger && debugger->pauseAtNextOpportunity())
++ debugger->maybeBreakAtInstruction();
++ if (qt_v4IsDebugging)
++ qt_v4CheckForBreak(engine->currentStackFrame);
++ }
++
++ #endif // QT_CONFIG(qml_debug)
++ // End of debugger interface
++
++ using namespace QV4;
++ using namespace QV4::Moth;
++
++ #ifdef COUNT_INSTRUCTIONS
++ static struct InstrCount {
++ InstrCount() {
++ fprintf(stderr, "Counting instructions...\n");
++ for (int i = 0; i < MOTH_NUM_INSTRUCTIONS(); ++i)
++ hits[i] = 0;
++ }
++ ~InstrCount() {
++ fprintf(stderr, "Instruction count:\n");
++ #define BLAH(I) \
++ fprintf(stderr, "%llu : %s\n", hits[int(Instr::Type::I)], #I);
++ FOR_EACH_MOTH_INSTR(BLAH)
++ #undef BLAH
++ }
++ quint64 hits[MOTH_NUM_INSTRUCTIONS()];
++ void hit(Instr::Type i) { hits[int(i)]++; }
++ } instrCount;
++ #endif // COUNT_INSTRUCTIONS
++
++ #define MOTH_BEGIN_INSTR_COMMON(instr) \
++ { \
++ INSTR_##instr(MOTH_DECODE)
++
++ #ifdef COUNT_INSTRUCTIONS
++ # define MOTH_BEGIN_INSTR(instr) \
++ MOTH_BEGIN_INSTR_COMMON(instr) \
++ instrCount.hit(Instr::Type::instr);
++ #else // !COUNT_INSTRUCTIONS
++ # define MOTH_BEGIN_INSTR(instr) \
++ MOTH_BEGIN_INSTR_COMMON(instr)
++ #endif // COUNT_INSTRUCTIONS
++
++ #ifdef MOTH_COMPUTED_GOTO
++ #define MOTH_END_INSTR(instr) \
++ MOTH_DISPATCH_SINGLE() \
++ }
++ #else // !MOTH_COMPUTED_GOTO
++ #define MOTH_END_INSTR(instr) \
++ continue; \
++ }
++ #endif
++
++ static inline QV4::Value &stackValue(QV4::Value *stack, size_t slot, const JSTypesStackFrame *frame)
++ {
++ Q_ASSERT(slot < CallData::HeaderSize() / sizeof(QV4::StaticValue)
++ + frame->jsFrame->argc()
++ + frame->v4Function->compiledFunction->nRegisters);
++ Q_UNUSED(frame);
++
++ return stack[slot];
++ }
++
++ #define STACK_VALUE(temp) stackValue(stack, temp, frame)
++
++ // qv4scopedvalue_p.h also defines a CHECK_EXCEPTION macro
++ #ifdef CHECK_EXCEPTION
++ #undef CHECK_EXCEPTION
++ #endif
++ #define CHECK_EXCEPTION \
++ if (engine->hasException || engine->isInterrupted.loadRelaxed()) \
++ goto handleUnwind
++
++ static inline Heap::CallContext *getScope(QV4::Value *stack, int level)
++ {
++ Heap::ExecutionContext *scope = static_cast<ExecutionContext &>(stack[CallData::Context]).d();
++ while (level > 0) {
++ --level;
++ scope = scope->outer;
++ }
++ Q_ASSERT(scope);
++ return static_cast<Heap::CallContext *>(scope);
++ }
++
++ static inline const QV4::Value &constant(Function *function, int index)
++ {
++ return function->compilationUnit->constants[index].asValue<QV4::Value>();
++ }
++
++ static bool compareEqualInt(QV4::Value &accumulator, QV4::Value lhs, int rhs)
++ {
++ redo:
++ if (lhs.isUndefined())
++ return false;
++ if (lhs.isManagedOrUndefined()) {
++ // LHS: Managed
++ if (lhs.m()->internalClass->vtable->isString)
++ return RuntimeHelpers::stringToNumber(static_cast<String &>(lhs).toQString()) == rhs;
++ accumulator = lhs;
++ lhs = QV4::Value::fromReturnedValue(RuntimeHelpers::objectDefaultValue(&static_cast<QV4::Object &>(accumulator), PREFERREDTYPE_HINT));
++ goto redo;
++ }
++
++ switch (lhs.quickType()) {
++ case QV4::Value::QT_Empty:
++ Q_UNREACHABLE();
++ case QV4::Value::QT_Null:
++ return false;
++ case QV4::Value::QT_Bool:
++ case QV4::Value::QT_Int:
++ return lhs.int_32() == rhs;
++ default: // double
++ return lhs.doubleValue() == rhs;
++ }
++ }
++
++ #define STORE_IP() frame->instructionPointer = int(code - function->codeData);
++ #define STORE_ACC() accumulator = acc;
++ #define ACC Value::fromReturnedValue(acc)
++ #define VALUE_TO_INT(i, val) \
++ int i; \
++ do { \
++ if (Q_LIKELY(val.integerCompatible())) { \
++ i = val.int_32(); \
++ } else { \
++ double d; \
++ if (val.isDouble()) \
++ d = val.doubleValue(); \
++ else { \
++ STORE_ACC(); \
++ d = val.toNumberImpl(); \
++ CHECK_EXCEPTION; \
++ } \
++ i = QJSNumberCoercion::toInteger(d); \
++ } \
++ } while (false)
++
++ namespace {
++ struct AOTCompiledMetaMethod
++ {
++ public:
++ AOTCompiledMetaMethod(const Function::AOTCompiledFunction *aotCompiledFunction)
++ : aotCompiledFunction(aotCompiledFunction)
++ {}
++
++ int parameterCount() const { return aotCompiledFunction->types.size() - 1; }
++ QMetaType returnMetaType() const { return aotCompiledFunction->types[0]; }
++ QMetaType parameterMetaType(int i) const { return aotCompiledFunction->types[i + 1]; }
++
++ private:
++ const Function::AOTCompiledFunction *aotCompiledFunction = nullptr;
++ };
++ }
++
++ void VME::exec(MetaTypesStackFrame *frame, ExecutionEngine *engine)
++ {
++ qt_v4ResolvePendingBreakpointsHook();
++ if (engine->checkStackLimits()) {
++ frame->setReturnValueUndefined();
++ return;
++ }
++ ExecutionEngineCallDepthRecorder executionEngineCallDepthRecorder(engine);
++
++ Function *function = frame->v4Function;
++ Q_ASSERT(function->aotCompiledCode);
++ Q_TRACE_SCOPE(QQmlV4_function_call, engine, function->name()->toQString(),
++ function->executableCompilationUnit()->fileName(),
++ function->compiledFunction->location.line(),
++ function->compiledFunction->location.column());
++ Profiling::FunctionCallProfiler profiler(engine, function); // start execution profiling
++
++ const AOTCompiledMetaMethod method(&function->aotCompiledFunction);
++ QV4::coerceAndCall(
++ engine, &method, frame->returnAndArgValues(),
++ frame->returnAndArgTypes(), frame->argc(),
++ [frame, engine, function](void **argv, int argc) {
++ Q_UNUSED(argc);
++
++ QQmlPrivate::AOTCompiledContext aotContext;
++ if (auto context = QV4::ExecutionEngine::qmlContext(frame->context()->d())) {
++ QV4::Heap::QQmlContextWrapper *wrapper = static_cast<Heap::QmlContext *>(context)->qml();
++ aotContext.qmlScopeObject = wrapper->scopeObject;
++ aotContext.qmlContext = wrapper->context;
++ }
++
++ aotContext.engine = engine->jsEngine();
++ aotContext.compilationUnit = function->executableCompilationUnit();
++ function->aotCompiledCode(&aotContext, argv);
++
++ // The tracked-locals storage is a local variable of the AOT-compiled
++ // function. We should not be able to use it between here and the
++ // popping of the stack frame. However, nulling it is a cheap defense
++ // in depth that will make mistaken code crash with a clean null pointer
++ // dereference or skip rather than corrupt random stack values.
++ frame->setLocals(nullptr);
++ });
++ }
++
++ ReturnedValue VME::exec(JSTypesStackFrame *frame, ExecutionEngine *engine)
++ {
++ qt_v4ResolvePendingBreakpointsHook();
++ CHECK_STACK_LIMITS(engine);
++
++ Function *function = frame->v4Function;
++ Q_TRACE_SCOPE(QQmlV4_function_call, engine, function->name()->toQString(),
++ function->executableCompilationUnit()->fileName(),
++ function->compiledFunction->location.line(),
++ function->compiledFunction->location.column());
++ Profiling::FunctionCallProfiler profiler(engine, function); // start execution profiling
++ QV4::Debugging::Debugger *debugger = engine->debugger();
++
++ #if QT_CONFIG(qml_jit)
++ if (debugger == nullptr) {
++ // Check for codeRef here. In rare cases the JIT compilation may fail, which leaves us
++ // with a (useless) codeRef, but no jittedCode. In that case, don't try to JIT again every
++ // time we execute the function, but just interpret instead.
++ if (function->codeRef == nullptr) {
++ if (engine->canJIT(function))
++ QV4::JIT::BaselineJIT(function).generate();
++ else
++ ++function->interpreterCallCount;
++ }
++ }
++ #endif // QT_CONFIG(qml_jit)
++
++ // interpreter
++ if (debugger)
++ debugger->enteringFunction();
++
++ ReturnedValue result;
++ Q_ASSERT(function->kind != Function::AotCompiled);
++ if (function->jittedCode != nullptr && debugger == nullptr) {
++ result = function->jittedCode(frame, engine);
++ } else {
++ // interpreter
++ result = interpret(frame, engine, function->codeData);
++ }
++
++ if (debugger)
++ debugger->leavingFunction(result);
++
++ return result;
++ }
++
++ QV4::ReturnedValue VME::interpret(JSTypesStackFrame *frame, ExecutionEngine *engine, const char *code)
++ {
++ QV4::Function *function = frame->v4Function;
++ QV4::Value &accumulator = frame->jsFrame->accumulator.asValue<Value>();
++ QV4::ReturnedValue acc = accumulator.asReturnedValue();
++ Value *stack = reinterpret_cast<Value *>(frame->jsFrame);
++
++ MOTH_JUMP_TABLE;
++
++ for (;;) {
++ MOTH_DISPATCH()
++ Q_UNREACHABLE(); // only reached when the dispatch doesn't jump somewhere
++
++ MOTH_BEGIN_INSTR(LoadConst)
++ acc = constant(function, index).asReturnedValue();
++ MOTH_END_INSTR(LoadConst)
++
++ MOTH_BEGIN_INSTR(LoadNull)
++ acc = Encode::null();
++ MOTH_END_INSTR(LoadNull)
++
++ MOTH_BEGIN_INSTR(LoadZero)
++ acc = Encode(static_cast<int>(0));
++ MOTH_END_INSTR(LoadZero)
++
++ MOTH_BEGIN_INSTR(LoadTrue)
++ acc = Encode(true);
++ MOTH_END_INSTR(LoadTrue)
++
++ MOTH_BEGIN_INSTR(LoadFalse)
++ acc = Encode(false);
++ MOTH_END_INSTR(LoadFalse)
++
++ MOTH_BEGIN_INSTR(LoadUndefined)
++ acc = Encode::undefined();
++ MOTH_END_INSTR(LoadUndefined)
++
++ MOTH_BEGIN_INSTR(LoadInt)
++ acc = Encode(value);
++ MOTH_END_INSTR(LoadInt)
++
++ MOTH_BEGIN_INSTR(MoveConst)
++ STACK_VALUE(destTemp) = constant(function, constIndex);
++ MOTH_END_INSTR(MoveConst)
++
++ MOTH_BEGIN_INSTR(LoadReg)
++ acc = STACK_VALUE(reg).asReturnedValue();
++ MOTH_END_INSTR(LoadReg)
++
++ MOTH_BEGIN_INSTR(StoreReg)
++ STACK_VALUE(reg) = acc;
++ MOTH_END_INSTR(StoreReg)
++
++ MOTH_BEGIN_INSTR(MoveReg)
++ STACK_VALUE(destReg) = STACK_VALUE(srcReg);
++ MOTH_END_INSTR(MoveReg)
++
++ MOTH_BEGIN_INSTR(LoadImport)
++ acc = function->compilationUnit->imports[index]->asReturnedValue();
++ MOTH_END_INSTR(LoadImport)
++
++ MOTH_BEGIN_INSTR(LoadLocal)
++ auto cc = static_cast<Heap::CallContext *>(STACK_VALUE(CallData::Context).m());
++ Q_ASSERT(cc->type != QV4::Heap::CallContext::Type_GlobalContext);
++ acc = cc->locals[index].asReturnedValue();
++ MOTH_END_INSTR(LoadLocal)
++
++ MOTH_BEGIN_INSTR(StoreLocal)
++ CHECK_EXCEPTION;
++ auto cc = static_cast<Heap::CallContext *>(STACK_VALUE(CallData::Context).m());
++ Q_ASSERT(cc->type != QV4::Heap::CallContext::Type_GlobalContext);
++ QV4::WriteBarrier::write(engine, cc, cc->locals.values[index].data_ptr(), acc);
++ MOTH_END_INSTR(StoreLocal)
++
++ MOTH_BEGIN_INSTR(LoadScopedLocal)
++ auto cc = getScope(stack, scope);
++ acc = cc->locals[index].asReturnedValue();
++ MOTH_END_INSTR(LoadScopedLocal)
++
++ MOTH_BEGIN_INSTR(StoreScopedLocal)
++ CHECK_EXCEPTION;
++ auto cc = getScope(stack, scope);
++ QV4::WriteBarrier::write(engine, cc, cc->locals.values[index].data_ptr(), acc);
++ MOTH_END_INSTR(StoreScopedLocal)
++
++ MOTH_BEGIN_INSTR(LoadRuntimeString)
++ acc = function->compilationUnit->runtimeStrings[stringId]->asReturnedValue();
++ MOTH_END_INSTR(LoadRuntimeString)
++
++ MOTH_BEGIN_INSTR(MoveRegExp)
++ STACK_VALUE(destReg) = Runtime::RegexpLiteral::call(engine, regExpId);
++ MOTH_END_INSTR(MoveRegExp)
++
++ MOTH_BEGIN_INSTR(LoadClosure)
++ acc = Runtime::Closure::call(engine, value);
++ MOTH_END_INSTR(LoadClosure)
++
++ MOTH_BEGIN_INSTR(LoadName)
++ STORE_IP();
++ acc = Runtime::LoadName::call(engine, name);
++ CHECK_EXCEPTION;
++ MOTH_END_INSTR(LoadName)
++
++ MOTH_BEGIN_INSTR(LoadGlobalLookup)
++ STORE_IP();
++ QV4::Lookup *l = function->executableCompilationUnit()->runtimeLookups + index;
++ acc = l->globalGetter(engine);
++ CHECK_EXCEPTION;
++ MOTH_END_INSTR(LoadGlobalLookup)
++
++ MOTH_BEGIN_INSTR(LoadQmlContextPropertyLookup)
++ STORE_IP();
++ QV4::Lookup *l = function->executableCompilationUnit()->runtimeLookups + index;
++ acc = l->contextGetter(engine, nullptr);
++ CHECK_EXCEPTION;
++ MOTH_END_INSTR(LoadQmlContextPropertyLookup)
++
++ MOTH_BEGIN_INSTR(StoreNameStrict)
++ STORE_IP();
++ STORE_ACC();
++ Runtime::StoreNameStrict::call(engine, name, accumulator);
++ CHECK_EXCEPTION;
++ MOTH_END_INSTR(StoreNameStrict)
++
++ MOTH_BEGIN_INSTR(StoreNameSloppy)
++ STORE_IP();
++ STORE_ACC();
++ Runtime::StoreNameSloppy::call(engine, name, accumulator);
++ CHECK_EXCEPTION;
++ MOTH_END_INSTR(StoreNameSloppy)
++
++ MOTH_BEGIN_INSTR(LoadElement)
++ STORE_IP();
++ STORE_ACC();
++ acc = Runtime::LoadElement::call(engine, STACK_VALUE(base), accumulator);
++ CHECK_EXCEPTION;
++ MOTH_END_INSTR(LoadElement)
++
++ MOTH_BEGIN_INSTR(StoreElement)
++ STORE_IP();
++ STORE_ACC();
++ Runtime::StoreElement::call(engine, STACK_VALUE(base), STACK_VALUE(index), accumulator);
++ CHECK_EXCEPTION;
++ MOTH_END_INSTR(StoreElement)
++
++ MOTH_BEGIN_INSTR(LoadProperty)
++ STORE_IP();
++ STORE_ACC();
++ acc = Runtime::LoadProperty::call(engine, accumulator, name);
++ CHECK_EXCEPTION;
++ MOTH_END_INSTR(LoadProperty)
++
++ MOTH_BEGIN_INSTR(LoadOptionalProperty)
++ STORE_IP();
++ STORE_ACC();
++ if (accumulator.isNullOrUndefined()) {
++ acc = Encode::undefined();
++ code += offset;
++ } else {
++ acc = Runtime::LoadProperty::call(engine, accumulator, name);
++ }
++ CHECK_EXCEPTION;
++ MOTH_END_INSTR(LoadOptionalProperty)
++
++ MOTH_BEGIN_INSTR(GetLookup)
++ STORE_IP();
++ STORE_ACC();
++
++ QV4::Lookup *l = function->executableCompilationUnit()->runtimeLookups + index;
++
++ if (accumulator.isNullOrUndefined()) {
++ const QString message =
++ QStringLiteral("Cannot read property '%1' of %2")
++ .arg(engine->currentStackFrame->v4Function->compilationUnit
++ ->runtimeStrings[l->nameIndex]
++ ->toQString(),
++ accumulator.toQStringNoThrow());
++ acc = engine->throwTypeError(message);
++ goto handleUnwind;
++ }
++
++ acc = l->getter(engine, accumulator);
++ CHECK_EXCEPTION;
++ MOTH_END_INSTR(GetLookup)
++
++ MOTH_BEGIN_INSTR(GetOptionalLookup)
++ STORE_IP();
++ STORE_ACC();
++
++ QV4::Lookup *l = function->executableCompilationUnit()->runtimeLookups + index;
++
++ if (accumulator.isNullOrUndefined()) {
++ code += offset;
++ } else {
++ acc = l->getter(engine, accumulator);
++ }
++ CHECK_EXCEPTION;
++ MOTH_END_INSTR(GetOptionalLookup)
++
++ MOTH_BEGIN_INSTR(StoreProperty)
++ STORE_IP();
++ STORE_ACC();
++ Runtime::StoreProperty::call(engine, STACK_VALUE(base), name, accumulator);
++ CHECK_EXCEPTION;
++ MOTH_END_INSTR(StoreProperty)
++
++ MOTH_BEGIN_INSTR(SetLookup)
++ STORE_IP();
++ STORE_ACC();
++ QV4::Lookup *l = function->executableCompilationUnit()->runtimeLookups + index;
++ if (!l->setter(engine, STACK_VALUE(base), accumulator) && function->isStrict())
++ engine->throwTypeError();
++ CHECK_EXCEPTION;
++ MOTH_END_INSTR(SetLookup)
++
++ MOTH_BEGIN_INSTR(LoadSuperProperty)
++ STORE_IP();
++ acc = Runtime::LoadSuperProperty::call(engine, STACK_VALUE(property));
++ CHECK_EXCEPTION;
++ MOTH_END_INSTR(LoadSuperProperty)
++
++ MOTH_BEGIN_INSTR(StoreSuperProperty)
++ STORE_IP();
++ STORE_ACC();
++ Runtime::StoreSuperProperty::call(engine, STACK_VALUE(property), accumulator);
++ CHECK_EXCEPTION;
++ MOTH_END_INSTR(StoreSuperProperty)
++
++ MOTH_BEGIN_INSTR(Yield)
++ frame->setYield(code);
++ frame->setYieldIsIterator(false);
++ return acc;
++ MOTH_END_INSTR(Yield)
++
++ MOTH_BEGIN_INSTR(YieldStar)
++ frame->setYield(code);
++ frame->setYieldIsIterator(true);
++ return acc;
++ MOTH_END_INSTR(YieldStar)
++
++ MOTH_BEGIN_INSTR(Resume)
++ // check exception, in case the generator was called with throw() or return()
++ if (engine->hasException) {
++ // an empty value indicates that the generator was called with return()
++ if (engine->exceptionValue->asReturnedValue() != Value::emptyValue().asReturnedValue())
++ goto handleUnwind;
++ engine->hasException = false;
++ *engine->exceptionValue = Value::undefinedValue();
++ } else {
++ code += offset;
++ }
++ MOTH_END_INSTR(Resume)
++
++ MOTH_BEGIN_INSTR(IteratorNextForYieldStar)
++ STORE_ACC();
++ acc = Runtime::IteratorNextForYieldStar::call(engine, accumulator, STACK_VALUE(iterator), &STACK_VALUE(object));
++ if (ACC.toBoolean())
++ code += offset;
++ MOTH_END_INSTR(IteratorNextForYieldStar)
++
++ MOTH_BEGIN_INSTR(CallValue)
++ STORE_IP();
++ Value func = STACK_VALUE(name);
++ if (Q_UNLIKELY(!func.isFunctionObject())) {
++ acc = engine->throwTypeError(QStringLiteral("%1 is not a function").arg(func.toQStringNoThrow()));
++ goto handleUnwind;
++ }
++ Value undef = Value::undefinedValue();
++ acc = static_cast<const FunctionObject &>(func).call(&undef, stack + argv, argc);
++ CHECK_EXCEPTION;
++ MOTH_END_INSTR(CallValue)
++
++ MOTH_BEGIN_INSTR(CallWithReceiver)
++ STORE_IP();
++ Value func = STACK_VALUE(name);
++ if (Q_UNLIKELY(!func.isFunctionObject())) {
++ acc = engine->throwTypeError(QStringLiteral("%1 is not a function").arg(func.toQStringNoThrow()));
++ goto handleUnwind;
++ }
++ acc = static_cast<const FunctionObject &>(func).call(stack + thisObject, stack + argv, argc);
++ CHECK_EXCEPTION;
++ MOTH_END_INSTR(CallWithReceiver)
++
++ MOTH_BEGIN_INSTR(CallProperty)
++ STORE_IP();
++ acc = Runtime::CallProperty::call(engine, STACK_VALUE(base), name, stack + argv, argc);
++ CHECK_EXCEPTION;
++ MOTH_END_INSTR(CallProperty)
++
++ MOTH_BEGIN_INSTR(CallPropertyLookup)
++ STORE_IP();
++ Lookup *l = function->executableCompilationUnit()->runtimeLookups + lookupIndex;
++
++ if (STACK_VALUE(base).isNullOrUndefined()) {
++ const QString message =
++ QStringLiteral("Cannot call method '%1' of %2")
++ .arg(engine->currentStackFrame->v4Function->compilationUnit
++ ->runtimeStrings[l->nameIndex]
++ ->toQString(),
++ STACK_VALUE(base).toQStringNoThrow());
++ acc = engine->throwTypeError(message);
++ goto handleUnwind;
++ }
++
++ // ok to have the value on the stack here
++ Value f = Value::fromReturnedValue(l->getter(engine, STACK_VALUE(base)));
++
++ if (Q_LIKELY(f.isFunctionObject())) {
++ acc = static_cast<FunctionObject &>(f).call(stack + base, stack + argv, argc);
++ } else if (QmlSignalHandler *handler = f.as<QmlSignalHandler>()) {
++ acc = handler->call(stack + base, stack + argv, argc);
++ } else {
++ const QString message =
++ QStringLiteral("Property '%1' of object %2 is not a function")
++ .arg(engine->currentStackFrame->v4Function->compilationUnit
++ ->runtimeStrings[l->nameIndex]
++ ->toQString(),
++ STACK_VALUE(base).toQStringNoThrow());
++ acc = engine->throwTypeError(message);
++ goto handleUnwind;
++ }
++
++ CHECK_EXCEPTION;
++ MOTH_END_INSTR(CallPropertyLookup)
++
++ MOTH_BEGIN_INSTR(CallName)
++ STORE_IP();
++ acc = Runtime::CallName::call(engine, name, stack + argv, argc);
++ CHECK_EXCEPTION;
++ MOTH_END_INSTR(CallName)
++
++ MOTH_BEGIN_INSTR(CallPossiblyDirectEval)
++ STORE_IP();
++ acc = Runtime::CallPossiblyDirectEval::call(engine, stack + argv, argc);
++ CHECK_EXCEPTION;
++ MOTH_END_INSTR(CallPossiblyDirectEval)
++
++ MOTH_BEGIN_INSTR(CallGlobalLookup)
++ STORE_IP();
++ acc = Runtime::CallGlobalLookup::call(engine, index, stack + argv, argc);
++ CHECK_EXCEPTION;
++ MOTH_END_INSTR(CallGlobalLookup)
++
++ MOTH_BEGIN_INSTR(CallQmlContextPropertyLookup)
++ STORE_IP();
++ acc = Runtime::CallQmlContextPropertyLookup::call(engine, index, stack + argv, argc);
++ CHECK_EXCEPTION;
++ MOTH_END_INSTR(CallQmlContextPropertyLookup)
++
++ MOTH_BEGIN_INSTR(CallWithSpread)
++ STORE_IP();
++ acc = Runtime::CallWithSpread::call(engine, STACK_VALUE(func), STACK_VALUE(thisObject), stack + argv, argc);
++ CHECK_EXCEPTION;
++ MOTH_END_INSTR(CallWithSpread)
++
++ MOTH_BEGIN_INSTR(TailCall)
++ STORE_IP();
++ *engine->jsAlloca(1) = Primitive::fromInt32(argc);
++ *engine->jsAlloca(1) = Primitive::fromInt32(argv);
++ *engine->jsAlloca(1) = STACK_VALUE(thisObject);
++ *engine->jsAlloca(1) = STACK_VALUE(func);
++ return Runtime::TailCall::call(frame, engine);
++ CHECK_EXCEPTION;
++ MOTH_END_INSTR(TailCall)
++
++ MOTH_BEGIN_INSTR(Construct)
++ STORE_IP();
++ STORE_ACC();
++ acc = Runtime::Construct::call(engine, STACK_VALUE(func), ACC, stack + argv, argc);
++ CHECK_EXCEPTION;
++ MOTH_END_INSTR(Construct)
++
++ MOTH_BEGIN_INSTR(ConstructWithSpread)
++ STORE_IP();
++ STORE_ACC();
++ acc = Runtime::ConstructWithSpread::call(engine, STACK_VALUE(func), ACC, stack + argv, argc);
++ CHECK_EXCEPTION;
++ MOTH_END_INSTR(ConstructWithSpread)
++
++ MOTH_BEGIN_INSTR(SetUnwindHandler)
++ frame->unwindHandler = offset ? code + offset : nullptr;
++ MOTH_END_INSTR(SetUnwindHandler)
++
++ MOTH_BEGIN_INSTR(UnwindDispatch)
++ CHECK_EXCEPTION;
++ if (frame->unwindLevel) {
++ --frame->unwindLevel;
++ if (frame->unwindLevel)
++ goto handleUnwind;
++ code = frame->unwindLabel;
++ }
++ MOTH_END_INSTR(UnwindDispatch)
++
++ MOTH_BEGIN_INSTR(UnwindToLabel)
++ frame->unwindLevel = level;
++ frame->unwindLabel = code + offset;
++ goto handleUnwind;
++ MOTH_END_INSTR(UnwindToLabel)
++
++ MOTH_BEGIN_INSTR(DeadTemporalZoneCheck)
++ if (ACC.isEmpty()) {
++ STORE_IP();
++ Runtime::ThrowReferenceError::call(engine, name);
++ goto handleUnwind;
++ }
++ MOTH_END_INSTR(DeadTemporalZoneCheck)
++
++ MOTH_BEGIN_INSTR(ThrowException)
++ STORE_IP();
++ STORE_ACC();
++ Runtime::ThrowException::call(engine, accumulator);
++ goto handleUnwind;
++ MOTH_END_INSTR(ThrowException)
++
++ MOTH_BEGIN_INSTR(GetException)
++ acc = engine->hasException ? engine->exceptionValue->asReturnedValue()
++ : Value::emptyValue().asReturnedValue();
++ engine->hasException = false;
++ MOTH_END_INSTR(GetException)
++
++ MOTH_BEGIN_INSTR(SetException)
++ if (acc != Value::emptyValue().asReturnedValue()) {
++ *engine->exceptionValue = acc;
++ engine->hasException = true;
++ }
++ MOTH_END_INSTR(SetException)
++
++ MOTH_BEGIN_INSTR(PushCatchContext)
++ Runtime::PushCatchContext::call(engine, index, name);
++ MOTH_END_INSTR(PushCatchContext)
++
++ MOTH_BEGIN_INSTR(CreateCallContext)
++ Runtime::PushCallContext::call(frame);
++ MOTH_END_INSTR(CreateCallContext)
++
++ MOTH_BEGIN_INSTR(PushWithContext)
++ STORE_IP();
++ STORE_ACC();
++ acc = Runtime::PushWithContext::call(engine, STACK_VALUE(CallData::Accumulator));
++ CHECK_EXCEPTION;
++ MOTH_END_INSTR(PushWithContext)
++
++ MOTH_BEGIN_INSTR(PushBlockContext)
++ STORE_ACC();
++ Runtime::PushBlockContext::call(engine, index);
++ MOTH_END_INSTR(PushBlockContext)
++
++ MOTH_BEGIN_INSTR(CloneBlockContext)
++ STORE_ACC();
++ Runtime::CloneBlockContext::call(engine);
++ MOTH_END_INSTR(CloneBlockContext)
++
++ MOTH_BEGIN_INSTR(PushScriptContext)
++ Runtime::PushScriptContext::call(engine, index);
++ MOTH_END_INSTR(PushScriptContext)
++
++ MOTH_BEGIN_INSTR(PopScriptContext)
++ Runtime::PopScriptContext::call(engine);
++ MOTH_END_INSTR(PopScriptContext)
++
++ MOTH_BEGIN_INSTR(PopContext)
++ ExecutionContext *c = static_cast<ExecutionContext *>(stack + CallData::Context);
++ STACK_VALUE(CallData::Context) = c->d()->outer;
++ MOTH_END_INSTR(PopContext)
++
++ MOTH_BEGIN_INSTR(GetIterator)
++ STORE_IP();
++ STORE_ACC();
++ acc = Runtime::GetIterator::call(engine, accumulator, iterator);
++ CHECK_EXCEPTION;
++ MOTH_END_INSTR(GetIterator)
++
++ MOTH_BEGIN_INSTR(IteratorNext)
++ STORE_IP();
++ STORE_ACC();
++ acc = Runtime::IteratorNext::call(engine, accumulator, &STACK_VALUE(value));
++ if (ACC.toBoolean())
++ code += offset;
++ MOTH_END_INSTR(IteratorNext)
++
++ MOTH_BEGIN_INSTR(IteratorClose)
++ STORE_IP();
++ STORE_ACC();
++ acc = Runtime::IteratorClose::call(engine, accumulator);
++ MOTH_END_INSTR(IteratorClose)
++
++ MOTH_BEGIN_INSTR(DestructureRestElement)
++ STORE_IP();
++ STORE_ACC();
++ acc = Runtime::DestructureRestElement::call(engine, ACC);
++ CHECK_EXCEPTION;
++ MOTH_END_INSTR(DestructureRestElement)
++
++ MOTH_BEGIN_INSTR(DeleteProperty)
++ acc = Runtime::DeleteProperty::call(engine, function, STACK_VALUE(base), STACK_VALUE(index));
++ CHECK_EXCEPTION;
++ MOTH_END_INSTR(DeleteProperty)
++
++ MOTH_BEGIN_INSTR(DeleteName)
++ acc = Runtime::DeleteName::call(engine, function, name);
++ CHECK_EXCEPTION;
++ MOTH_END_INSTR(DeleteName)
++
++ MOTH_BEGIN_INSTR(TypeofName)
++ acc = Runtime::TypeofName::call(engine, name);
++ MOTH_END_INSTR(TypeofName)
++
++ MOTH_BEGIN_INSTR(TypeofValue)
++ STORE_ACC();
++ acc = Runtime::TypeofValue::call(engine, accumulator);
++ MOTH_END_INSTR(TypeofValue)
++
++ MOTH_BEGIN_INSTR(DeclareVar)
++ Runtime::DeclareVar::call(engine, isDeletable, varName);
++ MOTH_END_INSTR(DeclareVar)
++
++ MOTH_BEGIN_INSTR(DefineArray)
++ QV4::Value *arguments = stack + args;
++ acc = Runtime::ArrayLiteral::call(engine, arguments, argc);
++ MOTH_END_INSTR(DefineArray)
++
++ MOTH_BEGIN_INSTR(DefineObjectLiteral)
++ QV4::Value *arguments = stack + args;
++ acc = Runtime::ObjectLiteral::call(engine, internalClassId, arguments, argc);
++ MOTH_END_INSTR(DefineObjectLiteral)
++
++ MOTH_BEGIN_INSTR(CreateClass)
++ acc = Runtime::CreateClass::call(engine, classIndex, STACK_VALUE(heritage), stack + computedNames);
++ MOTH_END_INSTR(CreateClass)
++
++ MOTH_BEGIN_INSTR(CreateMappedArgumentsObject)
++ acc = Runtime::CreateMappedArgumentsObject::call(engine);
++ MOTH_END_INSTR(CreateMappedArgumentsObject)
++
++ MOTH_BEGIN_INSTR(CreateUnmappedArgumentsObject)
++ acc = Runtime::CreateUnmappedArgumentsObject::call(engine);
++ MOTH_END_INSTR(CreateUnmappedArgumentsObject)
++
++ MOTH_BEGIN_INSTR(CreateRestParameter)
++ acc = Runtime::CreateRestParameter::call(engine, argIndex);
++ MOTH_END_INSTR(CreateRestParameter)
++
++ MOTH_BEGIN_INSTR(ConvertThisToObject)
++ STORE_ACC();
++ stack[CallData::This] = Runtime::ConvertThisToObject::call(
++ engine, STACK_VALUE(CallData::This));
++ CHECK_EXCEPTION;
++ MOTH_END_INSTR(ConvertThisToObject)
++
++ MOTH_BEGIN_INSTR(LoadSuperConstructor)
++ acc = Runtime::LoadSuperConstructor::call(engine, STACK_VALUE(CallData::Function));
++ CHECK_EXCEPTION;
++ MOTH_END_INSTR(LoadSuperConstructor)
++
++ MOTH_BEGIN_INSTR(ToObject)
++ STORE_ACC();
++ acc = ACC.toObject(engine)->asReturnedValue();
++ CHECK_EXCEPTION;
++ MOTH_END_INSTR(ToObject)
++
++ MOTH_BEGIN_INSTR(Jump)
++ code += offset;
++ MOTH_END_INSTR(Jump)
++
++ MOTH_BEGIN_INSTR(JumpTrue)
++ bool takeJump;
++ if (Q_LIKELY(ACC.integerCompatible()))
++ takeJump = ACC.int_32();
++ else
++ takeJump = ACC.toBoolean();
++ if (takeJump)
++ code += offset;
++ MOTH_END_INSTR(JumpTrue)
++
++ MOTH_BEGIN_INSTR(JumpFalse)
++ bool takeJump;
++ if (Q_LIKELY(ACC.integerCompatible()))
++ takeJump = !ACC.int_32();
++ else
++ takeJump = !ACC.toBoolean();
++ if (takeJump)
++ code += offset;
++ MOTH_END_INSTR(JumpFalse)
++
++ MOTH_BEGIN_INSTR(JumpNoException)
++ if (!engine->hasException)
++ code += offset;
++ MOTH_END_INSTR(JumpNoException)
++
++ MOTH_BEGIN_INSTR(JumpNotUndefined)
++ if (Q_LIKELY(acc != QV4::Encode::undefined()))
++ code += offset;
++ MOTH_END_INSTR(JumpNotUndefined)
++
++ MOTH_BEGIN_INSTR(CheckException)
++ CHECK_EXCEPTION;
++ MOTH_END_INSTR(CheckException)
++
++ MOTH_BEGIN_INSTR(CmpEqNull)
++ acc = Encode(ACC.isNullOrUndefined());
++ MOTH_END_INSTR(CmpEqNull)
++
++ MOTH_BEGIN_INSTR(CmpNeNull)
++ acc = Encode(!ACC.isNullOrUndefined());
++ MOTH_END_INSTR(CmpNeNull)
++
++ MOTH_BEGIN_INSTR(CmpEqInt)
++ if (ACC.isIntOrBool()) {
++ acc = Encode(ACC.int_32() == lhs);
++ } else {
++ STORE_ACC();
++ acc = Encode(compareEqualInt(accumulator, ACC, lhs));
++ CHECK_EXCEPTION;
++ }
++ MOTH_END_INSTR(CmpEqInt)
++
++ MOTH_BEGIN_INSTR(CmpNeInt)
++ if (ACC.isIntOrBool()) {
++ acc = Encode(bool(ACC.int_32() != lhs));
++ } else {
++ STORE_ACC();
++ acc = Encode(!compareEqualInt(accumulator, ACC, lhs));
++ CHECK_EXCEPTION;
++ }
++ MOTH_END_INSTR(CmpNeInt)
++
++ MOTH_BEGIN_INSTR(CmpEq)
++ const Value left = STACK_VALUE(lhs);
++ if (Q_LIKELY(left.asReturnedValue() == ACC.asReturnedValue())) {
++ acc = Encode(!ACC.isNaN());
++ } else if (Q_LIKELY(left.isInteger() && ACC.isInteger())) {
++ acc = Encode(left.int_32() == ACC.int_32());
++ } else {
++ STORE_ACC();
++ acc = Encode(bool(Runtime::CompareEqual::call(left, accumulator)));
++ CHECK_EXCEPTION;
++ }
++ MOTH_END_INSTR(CmpEq)
++
++ MOTH_BEGIN_INSTR(CmpNe)
++ const Value left = STACK_VALUE(lhs);
++ if (Q_LIKELY(left.isInteger() && ACC.isInteger())) {
++ acc = Encode(bool(left.int_32() != ACC.int_32()));
++ } else {
++ STORE_ACC();
++ acc = Encode(bool(!Runtime::CompareEqual::call(left, accumulator)));
++ CHECK_EXCEPTION;
++ }
++ MOTH_END_INSTR(CmpNe)
++
++ MOTH_BEGIN_INSTR(CmpGt)
++ const Value left = STACK_VALUE(lhs);
++ if (Q_LIKELY(left.isInteger() && ACC.isInteger())) {
++ acc = Encode(left.int_32() > ACC.int_32());
++ } else if (left.isNumber() && ACC.isNumber()) {
++ acc = Encode(left.asDouble() > ACC.asDouble());
++ } else {
++ STORE_ACC();
++ acc = Encode(bool(Runtime::CompareGreaterThan::call(left, accumulator)));
++ CHECK_EXCEPTION;
++ }
++ MOTH_END_INSTR(CmpGt)
++
++ MOTH_BEGIN_INSTR(CmpGe)
++ const Value left = STACK_VALUE(lhs);
++ if (Q_LIKELY(left.isInteger() && ACC.isInteger())) {
++ acc = Encode(left.int_32() >= ACC.int_32());
++ } else if (left.isNumber() && ACC.isNumber()) {
++ acc = Encode(left.asDouble() >= ACC.asDouble());
++ } else {
++ STORE_ACC();
++ acc = Encode(bool(Runtime::CompareGreaterEqual::call(left, accumulator)));
++ CHECK_EXCEPTION;
++ }
++ MOTH_END_INSTR(CmpGe)
++
++ MOTH_BEGIN_INSTR(CmpLt)
++ const Value left = STACK_VALUE(lhs);
++ if (Q_LIKELY(left.isInteger() && ACC.isInteger())) {
++ acc = Encode(left.int_32() < ACC.int_32());
++ } else if (left.isNumber() && ACC.isNumber()) {
++ acc = Encode(left.asDouble() < ACC.asDouble());
++ } else {
++ STORE_ACC();
++ acc = Encode(bool(Runtime::CompareLessThan::call(left, accumulator)));
++ CHECK_EXCEPTION;
++ }
++ MOTH_END_INSTR(CmpLt)
++
++ MOTH_BEGIN_INSTR(CmpLe)
++ const Value left = STACK_VALUE(lhs);
++ if (Q_LIKELY(left.isInteger() && ACC.isInteger())) {
++ acc = Encode(left.int_32() <= ACC.int_32());
++ } else if (left.isNumber() && ACC.isNumber()) {
++ acc = Encode(left.asDouble() <= ACC.asDouble());
++ } else {
++ STORE_ACC();
++ acc = Encode(bool(Runtime::CompareLessEqual::call(left, accumulator)));
++ CHECK_EXCEPTION;
++ }
++ MOTH_END_INSTR(CmpLe)
++
++ MOTH_BEGIN_INSTR(CmpStrictEqual)
++ if (STACK_VALUE(lhs).rawValue() == ACC.rawValue() && !ACC.isNaN()) {
++ acc = Encode(true);
++ } else {
++ STORE_ACC();
++ acc = Runtime::StrictEqual::call(STACK_VALUE(lhs), accumulator);
++ CHECK_EXCEPTION;
++ }
++ MOTH_END_INSTR(CmpStrictEqual)
++
++ MOTH_BEGIN_INSTR(CmpStrictNotEqual)
++ if (STACK_VALUE(lhs).rawValue() != ACC.rawValue() || ACC.isNaN()) {
++ STORE_ACC();
++ acc = Runtime::StrictNotEqual::call(STACK_VALUE(lhs), accumulator);
++ CHECK_EXCEPTION;
++ } else {
++ acc = Encode(false);
++ }
++ MOTH_END_INSTR(CmpStrictNotEqual)
++
++ MOTH_BEGIN_INSTR(CmpIn)
++ STORE_IP();
++ STORE_ACC();
++ acc = Runtime::In::call(engine, STACK_VALUE(lhs), accumulator);
++ CHECK_EXCEPTION;
++ MOTH_END_INSTR(CmpIn)
++
++ MOTH_BEGIN_INSTR(CmpInstanceOf)
++ STORE_ACC();
++ acc = Runtime::Instanceof::call(engine, STACK_VALUE(lhs), ACC);
++ CHECK_EXCEPTION;
++ MOTH_END_INSTR(CmpInstanceOf)
++
++ MOTH_BEGIN_INSTR(UNot)
++ if (ACC.integerCompatible()) {
++ acc = Encode(!static_cast<bool>(ACC.int_32()));
++ } else {
++ acc = Encode(!Value::toBooleanImpl(ACC));
++ }
++ MOTH_END_INSTR(UNot)
++
++ MOTH_BEGIN_INSTR(UPlus)
++ if (Q_UNLIKELY(!ACC.isNumber())) {
++ acc = Encode(ACC.toNumberImpl());
++ CHECK_EXCEPTION;
++ }
++ MOTH_END_INSTR(UPlus)
++
++ MOTH_BEGIN_INSTR(UMinus)
++ if (Q_LIKELY(ACC.integerCompatible())) {
++ int a = ACC.int_32();
++ if (a == 0 || a == std::numeric_limits<int>::min()) {
++ acc = Encode(-static_cast<double>(a));
++ } else {
++ acc = sub_int32(0, ACC.int_32());
++ }
++ } else if (ACC.isDouble()) {
++ acc ^= (1ull << 63); // simply flip sign bit
++ } else {
++ acc = Encode(-ACC.toNumberImpl());
++ CHECK_EXCEPTION;
++ }
++ MOTH_END_INSTR(UMinus)
++
++ MOTH_BEGIN_INSTR(UCompl)
++ VALUE_TO_INT(a, ACC);
++ acc = Encode(~a);
++ MOTH_END_INSTR(UCompl)
++
++ MOTH_BEGIN_INSTR(Increment)
++ if (Q_LIKELY(ACC.integerCompatible())) {
++ acc = add_int32(ACC.int_32(), 1);
++ } else if (ACC.isDouble()) {
++ acc = QV4::Encode(ACC.doubleValue() + 1.);
++ } else {
++ acc = Encode(ACC.toNumberImpl() + 1.);
++ CHECK_EXCEPTION;
++ }
++ MOTH_END_INSTR(Increment)
++
++ MOTH_BEGIN_INSTR(Decrement)
++ if (Q_LIKELY(ACC.integerCompatible())) {
++ acc = sub_int32(ACC.int_32(), 1);
++ } else if (ACC.isDouble()) {
++ acc = QV4::Encode(ACC.doubleValue() - 1.);
++ } else {
++ acc = Encode(ACC.toNumberImpl() - 1.);
++ CHECK_EXCEPTION;
++ }
++ MOTH_END_INSTR(Decrement)
++
++ MOTH_BEGIN_INSTR(Add)
++ const Value left = STACK_VALUE(lhs);
++ if (Q_LIKELY(Value::integerCompatible(left, ACC))) {
++ acc = add_int32(left.int_32(), ACC.int_32());
++ } else if (left.isNumber() && ACC.isNumber()) {
++ acc = Encode(left.asDouble() + ACC.asDouble());
++ } else {
++ STORE_ACC();
++ acc = Runtime::Add::call(engine, left, accumulator);
++ CHECK_EXCEPTION;
++ }
++ MOTH_END_INSTR(Add)
++
++ MOTH_BEGIN_INSTR(Sub)
++ const Value left = STACK_VALUE(lhs);
++ if (Q_LIKELY(Value::integerCompatible(left, ACC))) {
++ acc = sub_int32(left.int_32(), ACC.int_32());
++ } else if (left.isNumber() && ACC.isNumber()) {
++ acc = Encode(left.asDouble() - ACC.asDouble());
++ } else {
++ STORE_ACC();
++ acc = Runtime::Sub::call(left, accumulator);
++ CHECK_EXCEPTION;
++ }
++ MOTH_END_INSTR(Sub)
++
++ MOTH_BEGIN_INSTR(As)
++ const Value left = STACK_VALUE(lhs);
++ STORE_ACC();
++ acc = Runtime::As::call(engine, left, accumulator);
++ MOTH_END_INSTR(As)
++
++ MOTH_BEGIN_INSTR(Exp)
++ const Value left = STACK_VALUE(lhs);
++ double base = left.toNumber();
++ double exp = ACC.toNumber();
++ acc = Encode(QQmlPrivate::jsExponentiate(base, exp));
++ MOTH_END_INSTR(Exp)
++
++ MOTH_BEGIN_INSTR(Mul)
++ const Value left = STACK_VALUE(lhs);
++ if (Q_LIKELY(Value::integerCompatible(left, ACC))) {
++ acc = mul_int32(left.int_32(), ACC.int_32());
++ } else if (left.isNumber() && ACC.isNumber()) {
++ acc = Encode(left.asDouble() * ACC.asDouble());
++ } else {
++ STORE_ACC();
++ acc = Runtime::Mul::call(left, accumulator);
++ CHECK_EXCEPTION;
++ }
++ MOTH_END_INSTR(Mul)
++
++ MOTH_BEGIN_INSTR(Div)
++ STORE_ACC();
++ acc = Runtime::Div::call(STACK_VALUE(lhs), accumulator);
++ CHECK_EXCEPTION;
++ MOTH_END_INSTR(Div)
++
++ MOTH_BEGIN_INSTR(Mod)
++ STORE_ACC();
++ acc = Runtime::Mod::call(STACK_VALUE(lhs), accumulator);
++ CHECK_EXCEPTION;
++ MOTH_END_INSTR(Mod)
++
++ MOTH_BEGIN_INSTR(BitAnd)
++ VALUE_TO_INT(l, STACK_VALUE(lhs));
++ VALUE_TO_INT(a, ACC);
++ acc = Encode(l & a);
++ MOTH_END_INSTR(BitAnd)
++
++ MOTH_BEGIN_INSTR(BitOr)
++ VALUE_TO_INT(l, STACK_VALUE(lhs));
++ VALUE_TO_INT(a, ACC);
++ acc = Encode(l | a);
++ MOTH_END_INSTR(BitOr)
++
++ MOTH_BEGIN_INSTR(BitXor)
++ VALUE_TO_INT(l, STACK_VALUE(lhs));
++ VALUE_TO_INT(a, ACC);
++ acc = Encode(l ^ a);
++ MOTH_END_INSTR(BitXor)
++
++ MOTH_BEGIN_INSTR(UShr)
++ VALUE_TO_INT(l, STACK_VALUE(lhs));
++ VALUE_TO_INT(a, ACC);
++ acc = Encode(static_cast<uint>(l) >> uint(a & 0x1f));
++ MOTH_END_INSTR(UShr)
++
++ MOTH_BEGIN_INSTR(Shr)
++ VALUE_TO_INT(l, STACK_VALUE(lhs));
++ VALUE_TO_INT(a, ACC);
++ acc = Encode(l >> (a & 0x1f));
++ MOTH_END_INSTR(Shr)
++
++ MOTH_BEGIN_INSTR(Shl)
++ VALUE_TO_INT(l, STACK_VALUE(lhs));
++ VALUE_TO_INT(a, ACC);
++ acc = Encode(l << (a & 0x1f));
++ MOTH_END_INSTR(Shl)
++
++ MOTH_BEGIN_INSTR(BitAndConst)
++ VALUE_TO_INT(a, ACC);
++ acc = Encode(a & rhs);
++ CHECK_EXCEPTION;
++ MOTH_END_INSTR(BitAndConst)
++
++ MOTH_BEGIN_INSTR(BitOrConst)
++ VALUE_TO_INT(a, ACC);
++ acc = Encode(a | rhs);
++ MOTH_END_INSTR(BitOrConst)
++
++ MOTH_BEGIN_INSTR(BitXorConst)
++ VALUE_TO_INT(a, ACC);
++ acc = Encode(a ^ rhs);
++ MOTH_END_INSTR(BitXorConst)
++
++ MOTH_BEGIN_INSTR(UShrConst)
++ acc = Encode(ACC.toUInt32() >> uint(rhs));
++ MOTH_END_INSTR(UShrConst)
++
++ MOTH_BEGIN_INSTR(ShrConst)
++ VALUE_TO_INT(a, ACC);
++ acc = Encode(a >> rhs);
++ MOTH_END_INSTR(ShrConst)
++
++ MOTH_BEGIN_INSTR(ShlConst)
++ VALUE_TO_INT(a, ACC);
++ acc = Encode(a << rhs);
++ MOTH_END_INSTR(ShlConst)
++
++ MOTH_BEGIN_INSTR(Ret)
++ return acc;
++ MOTH_END_INSTR(Ret)
++
++ MOTH_BEGIN_INSTR(InitializeBlockDeadTemporalZone)
++ acc = Encode(Value::emptyValue());
++ for (int i = firstReg, end = firstReg + count; i < end; ++i)
++ STACK_VALUE(i) = acc;
++ MOTH_END_INSTR(InitializeBlockDeadTemporalZone)
++
++ MOTH_BEGIN_INSTR(ThrowOnNullOrUndefined)
++ if (Value::fromReturnedValue(acc).isNullOrUndefined()) {
++ engine->throwTypeError();
++ goto handleUnwind;
++ }
++ MOTH_END_INSTR(ThrowOnNullOrUndefined)
++
++ MOTH_BEGIN_INSTR(GetTemplateObject)
++ acc = Runtime::GetTemplateObject::call(function, index);
++ MOTH_END_INSTR(GetTemplateObject)
++
++ MOTH_BEGIN_INSTR(Debug)
++ #if QT_CONFIG(qml_debug)
++ STORE_IP();
++ debug_slowPath(engine);
++ #endif // QT_CONFIG(qml_debug)
++ MOTH_END_INSTR(Debug)
++
++ handleUnwind:
++ // We do start the exception handler in case of isInterrupted. The exception handler will
++ // immediately abort, due to the same isInterrupted. We don't skip the exception handler
++ // because the current behavior is easier to implement in the JIT.
++ Q_ASSERT(engine->hasException || engine->isInterrupted.loadRelaxed() || frame->unwindLevel);
++ if (!frame->unwindHandler) {
++ acc = Encode::undefined();
++ return acc;
++ }
++ code = frame->unwindHandler;
++ }
++ }