blob: fb103d492dcb5262fb014b1a0362a4acce778b29 [file] [log] [blame]
/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the QtQml module of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 3 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL3 included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 3 requirements
** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 2.0 or (at your option) the GNU General
** Public license version 3 or any later version approved by the KDE Free
** Qt Foundation. The licenses are as published by the Free Software
** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-2.0.html and
** https://www.gnu.org/licenses/gpl-3.0.html.
**
** $QT_END_LICENSE$
**
****************************************************************************/
#include "qv4vme_moth_p.h"
#include <QtCore/qjsondocument.h>
#include <QtCore/qjsonobject.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 <iostream>
#if QT_CONFIG(qml_jit)
#include <private/qv4baselinejit_p.h>
#endif
#include <qtqml_tracepoints_p.h>
#undef COUNT_INSTRUCTIONS
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;
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
};
static QVector<Breakpoint> qt_v4Breakpoints;
static Breakpoint qt_v4LastStop;
static 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 CppStackFrame *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.loadAcquire()) \
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:
switch (lhs.quickType()) {
case QV4::Value::QT_ManagedOrUndefined:
if (lhs.isUndefined())
return false;
Q_FALLTHROUGH();
case QV4::Value::QT_ManagedOrUndefined1:
case QV4::Value::QT_ManagedOrUndefined2:
case QV4::Value::QT_ManagedOrUndefined3:
// 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;
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 = Double::toInt32(d); \
} \
} while (false)
ReturnedValue VME::exec(CppStackFrame *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) {
if (function->jittedCode == 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;
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(CppStackFrame *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(l, engine);
CHECK_EXCEPTION;
MOTH_END_INSTR(LoadGlobalLookup)
MOTH_BEGIN_INSTR(LoadQmlContextPropertyLookup)
STORE_IP();
QV4::Lookup *l = function->executableCompilationUnit()->runtimeLookups + index;
acc = l->qmlContextPropertyGetter(l, 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(GetLookup)
STORE_IP();
STORE_ACC();
QV4::Lookup *l = function->executableCompilationUnit()->runtimeLookups + index;
if (accumulator.isNullOrUndefined()) {
QString message = QStringLiteral("Cannot read property '%1' of %2")
.arg(engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[l->nameIndex]->toQString())
.arg(accumulator.toQStringNoThrow());
acc = engine->throwTypeError(message);
goto handleUnwind;
}
acc = l->getter(l, engine, accumulator);
CHECK_EXCEPTION;
MOTH_END_INSTR(GetLookup)
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(l, 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->yield = code;
frame->yieldIsIterator = false;
return acc;
MOTH_END_INSTR(Yield)
MOTH_BEGIN_INSTR(YieldStar)
frame->yield = code;
frame->yieldIsIterator = 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));
CHECK_EXCEPTION;
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()) {
QString message = QStringLiteral("Cannot call method '%1' of %2")
.arg(engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[l->nameIndex]->toQString())
.arg(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(l, engine, STACK_VALUE(base)));
if (Q_UNLIKELY(!f.isFunctionObject())) {
QString message = QStringLiteral("Property '%1' of object %2 is not a function")
.arg(engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[l->nameIndex]->toQString())
.arg(STACK_VALUE(base).toQStringNoThrow());
acc = engine->throwTypeError(message);
goto handleUnwind;
}
acc = static_cast<FunctionObject &>(f).call(stack + base, stack + argv, argc);
CHECK_EXCEPTION;
MOTH_END_INSTR(CallPropertyLookup)
MOTH_BEGIN_INSTR(CallElement)
STORE_IP();
acc = Runtime::CallElement::call(engine, STACK_VALUE(base), STACK_VALUE(index), stack + argv, argc);
CHECK_EXCEPTION;
MOTH_END_INSTR(CallElement)
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(HasException)
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));
STACK_VALUE(done) = acc;
CHECK_EXCEPTION;
MOTH_END_INSTR(IteratorNext)
MOTH_BEGIN_INSTR(IteratorClose)
STORE_IP();
STORE_ACC();
acc = Runtime::IteratorClose::call(engine, accumulator, STACK_VALUE(done));
CHECK_EXCEPTION;
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_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(Exp)
const Value left = STACK_VALUE(lhs);
double base = left.toNumber();
double exp = ACC.toNumber();
if (qIsInf(exp) && (base == 1 || base == -1))
acc = Encode(qQNaN());
else
acc = Encode(pow(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.loadAcquire() || frame->unwindLevel);
if (!frame->unwindHandler) {
acc = Encode::undefined();
return acc;
}
code = frame->unwindHandler;
}
}