aboutsummaryrefslogtreecommitdiffstats
path: root/src/qml/compiler
diff options
context:
space:
mode:
Diffstat (limited to 'src/qml/compiler')
-rw-r--r--src/qml/compiler/compiler.pri6
-rw-r--r--src/qml/compiler/qqmlirbuilder.cpp109
-rw-r--r--src/qml/compiler/qqmlirbuilder_p.h23
-rw-r--r--src/qml/compiler/qqmlpropertycachecreator_p.h28
-rw-r--r--src/qml/compiler/qqmlpropertyvalidator.cpp12
-rw-r--r--src/qml/compiler/qqmlpropertyvalidator_p.h3
-rw-r--r--src/qml/compiler/qqmltypecompiler.cpp25
-rw-r--r--src/qml/compiler/qqmltypecompiler_p.h2
-rw-r--r--src/qml/compiler/qv4bytecodegenerator.cpp48
-rw-r--r--src/qml/compiler/qv4bytecodegenerator_p.h76
-rw-r--r--src/qml/compiler/qv4bytecodehandler.cpp529
-rw-r--r--src/qml/compiler/qv4bytecodehandler_p.h113
-rw-r--r--src/qml/compiler/qv4codegen.cpp1985
-rw-r--r--src/qml/compiler/qv4codegen_p.h106
-rw-r--r--src/qml/compiler/qv4compilationunitmapper_unix.cpp12
-rw-r--r--src/qml/compiler/qv4compilationunitmapper_win.cpp20
-rw-r--r--src/qml/compiler/qv4compileddata.cpp139
-rw-r--r--src/qml/compiler/qv4compileddata_p.h172
-rw-r--r--src/qml/compiler/qv4compiler.cpp173
-rw-r--r--src/qml/compiler/qv4compiler_p.h8
-rw-r--r--src/qml/compiler/qv4compilercontext.cpp254
-rw-r--r--src/qml/compiler/qv4compilercontext_p.h119
-rw-r--r--src/qml/compiler/qv4compilercontrolflow_p.h412
-rw-r--r--src/qml/compiler/qv4compilerscanfunctions.cpp495
-rw-r--r--src/qml/compiler/qv4compilerscanfunctions_p.h51
-rw-r--r--src/qml/compiler/qv4instr_moth.cpp151
-rw-r--r--src/qml/compiler/qv4instr_moth_p.h226
27 files changed, 3717 insertions, 1580 deletions
diff --git a/src/qml/compiler/compiler.pri b/src/qml/compiler/compiler.pri
index 95096db51d..da3c173545 100644
--- a/src/qml/compiler/compiler.pri
+++ b/src/qml/compiler/compiler.pri
@@ -11,7 +11,8 @@ HEADERS += \
$$PWD/qv4codegen_p.h \
$$PWD/qqmlirbuilder_p.h \
$$PWD/qqmltypecompiler_p.h \
- $$PWD/qv4instr_moth_p.h
+ $$PWD/qv4instr_moth_p.h \
+ $$PWD/qv4bytecodehandler_p.h
SOURCES += \
$$PWD/qv4bytecodegenerator.cpp \
@@ -21,7 +22,8 @@ SOURCES += \
$$PWD/qv4compilerscanfunctions.cpp \
$$PWD/qv4codegen.cpp \
$$PWD/qqmlirbuilder.cpp \
- $$PWD/qv4instr_moth.cpp
+ $$PWD/qv4instr_moth.cpp \
+ $$PWD/qv4bytecodehandler.cpp
!qmldevtools_build {
diff --git a/src/qml/compiler/qqmlirbuilder.cpp b/src/qml/compiler/qqmlirbuilder.cpp
index 8a1b3744ee..820f127331 100644
--- a/src/qml/compiler/qqmlirbuilder.cpp
+++ b/src/qml/compiler/qqmlirbuilder.cpp
@@ -96,23 +96,24 @@ void Object::init(QQmlJS::MemoryPool *pool, int typeNameIndex, int idIndex, cons
declarationsOverride = nullptr;
}
-QString Object::sanityCheckFunctionNames(const QSet<QString> &illegalNames, QQmlJS::AST::SourceLocation *errorLocation)
+QString IRBuilder::sanityCheckFunctionNames(Object *obj, const QSet<QString> &illegalNames, QQmlJS::AST::SourceLocation *errorLocation)
{
QSet<int> functionNames;
- for (Function *f = functions->first; f; f = f->next) {
- QQmlJS::AST::FunctionDeclaration *function = f->functionDeclaration;
- Q_ASSERT(function);
- *errorLocation = function->identifierToken;
+ for (auto functionit = obj->functionsBegin(); functionit != obj->functionsEnd(); ++functionit) {
+ Function *f = functionit.ptr;
+ errorLocation->startLine = f->location.line;
+ errorLocation->startColumn = f->location.column;
if (functionNames.contains(f->nameIndex))
return tr("Duplicate method name");
functionNames.insert(f->nameIndex);
- for (QmlIR::Signal *s = qmlSignals->first; s; s = s->next) {
+ for (auto signalit = obj->signalsBegin(); signalit != obj->signalsEnd(); ++signalit) {
+ QmlIR::Signal *s = signalit.ptr;
if (s->nameIndex == f->nameIndex)
return tr("Duplicate method name");
}
- const QString name = function->name.toString();
+ const QString name = stringAt(f->nameIndex);
if (name.at(0).isUpper())
return tr("Method names cannot begin with an upper case letter");
if (illegalNames.contains(name))
@@ -497,7 +498,7 @@ bool IRBuilder::visit(QQmlJS::AST::UiObjectBinding *node)
bool IRBuilder::visit(QQmlJS::AST::UiScriptBinding *node)
{
- appendBinding(node->qualifiedId, node->statement);
+ appendBinding(node->qualifiedId, node->statement, node);
return false;
}
@@ -601,7 +602,7 @@ bool IRBuilder::defineQMLObject(int *objectIndex, QQmlJS::AST::UiQualifiedId *qu
return false;
QQmlJS::AST::SourceLocation loc;
- QString error = obj->sanityCheckFunctionNames(illegalNames, &loc);
+ QString error = sanityCheckFunctionNames(obj, illegalNames, &loc);
if (!error.isEmpty()) {
recordError(loc, error);
return false;
@@ -957,7 +958,7 @@ bool IRBuilder::visit(QQmlJS::AST::UiPublicMember *node)
QQmlJS::AST::Node::accept(node->binding, this);
} else if (node->statement) {
if (!isRedundantNullInitializerForPropertyDeclaration(_propertyDeclaration, node->statement))
- appendBinding(node->identifierToken, node->identifierToken, _propertyDeclaration->nameIndex, node->statement);
+ appendBinding(node->identifierToken, node->identifierToken, _propertyDeclaration->nameIndex, node->statement, node);
}
qSwap(_propertyDeclaration, property);
}
@@ -971,26 +972,27 @@ bool IRBuilder::visit(QQmlJS::AST::UiSourceElement *node)
if (QQmlJS::AST::FunctionDeclaration *funDecl = QQmlJS::AST::cast<QQmlJS::AST::FunctionDeclaration *>(node->sourceElement)) {
CompiledFunctionOrExpression *foe = New<CompiledFunctionOrExpression>();
foe->node = funDecl;
+ foe->parentNode = funDecl;
foe->nameIndex = registerString(funDecl->name.toString());
foe->disableAcceleratedLookups = false;
const int index = _object->functionsAndExpressions->append(foe);
Function *f = New<Function>();
- f->functionDeclaration = funDecl;
QQmlJS::AST::SourceLocation loc = funDecl->identifierToken;
f->location.line = loc.startLine;
f->location.column = loc.startColumn;
f->index = index;
f->nameIndex = registerString(funDecl->name.toString());
- int formalsCount = 0;
- for (QQmlJS::AST::FormalParameterList *it = funDecl->formals; it; it = it->next)
- ++formalsCount;
+ const QStringList formals = funDecl->formals ? funDecl->formals->formals() : QStringList();
+ int formalsCount = formals.size();
f->formals.allocate(pool, formalsCount);
int i = 0;
- for (QQmlJS::AST::FormalParameterList *it = funDecl->formals; it; it = it->next, ++i)
- f->formals[i] = registerString(it->name.toString());
+ for (const QString &arg : formals) {
+ f->formals[i] = registerString(arg);
+ ++i;
+ }
_object->appendFunction(f);
} else {
@@ -1044,7 +1046,7 @@ QStringRef IRBuilder::textRefAt(const QQmlJS::AST::SourceLocation &first, const
return QStringRef(&sourceCode, first.offset, last.offset + last.length - first.offset);
}
-void IRBuilder::setBindingValue(QV4::CompiledData::Binding *binding, QQmlJS::AST::Statement *statement)
+void IRBuilder::setBindingValue(QV4::CompiledData::Binding *binding, QQmlJS::AST::Statement *statement, QQmlJS::AST::Node *parentNode)
{
QQmlJS::AST::SourceLocation loc = statement->firstSourceLocation();
binding->valueLocation.line = loc.startLine;
@@ -1090,6 +1092,7 @@ void IRBuilder::setBindingValue(QV4::CompiledData::Binding *binding, QQmlJS::AST
CompiledFunctionOrExpression *expr = New<CompiledFunctionOrExpression>();
expr->node = statement;
+ expr->parentNode = parentNode;
expr->nameIndex = registerString(QLatin1String("expression for ")
+ stringAt(binding->propertyNameIndex));
expr->disableAcceleratedLookups = false;
@@ -1216,7 +1219,7 @@ void IRBuilder::tryGeneratingTranslationBinding(const QStringRef &base, AST::Arg
}
}
-void IRBuilder::appendBinding(QQmlJS::AST::UiQualifiedId *name, QQmlJS::AST::Statement *value)
+void IRBuilder::appendBinding(QQmlJS::AST::UiQualifiedId *name, QQmlJS::AST::Statement *value, QQmlJS::AST::Node *parentNode)
{
const QQmlJS::AST::SourceLocation qualifiedNameLocation = name->identifierToken;
Object *object = nullptr;
@@ -1227,7 +1230,7 @@ void IRBuilder::appendBinding(QQmlJS::AST::UiQualifiedId *name, QQmlJS::AST::Sta
return;
}
qSwap(_object, object);
- appendBinding(qualifiedNameLocation, name->identifierToken, registerString(name->name.toString()), value);
+ appendBinding(qualifiedNameLocation, name->identifierToken, registerString(name->name.toString()), value, parentNode);
qSwap(_object, object);
}
@@ -1242,7 +1245,8 @@ void IRBuilder::appendBinding(QQmlJS::AST::UiQualifiedId *name, int objectIndex,
qSwap(_object, object);
}
-void IRBuilder::appendBinding(const QQmlJS::AST::SourceLocation &qualifiedNameLocation, const QQmlJS::AST::SourceLocation &nameLocation, quint32 propertyNameIndex, QQmlJS::AST::Statement *value)
+void IRBuilder::appendBinding(const QQmlJS::AST::SourceLocation &qualifiedNameLocation, const QQmlJS::AST::SourceLocation &nameLocation, quint32 propertyNameIndex,
+ QQmlJS::AST::Statement *value, QQmlJS::AST::Node *parentNode)
{
Binding *binding = New<Binding>();
binding->propertyNameIndex = propertyNameIndex;
@@ -1250,7 +1254,7 @@ void IRBuilder::appendBinding(const QQmlJS::AST::SourceLocation &qualifiedNameLo
binding->location.line = nameLocation.startLine;
binding->location.column = nameLocation.startColumn;
binding->flags = 0;
- setBindingValue(binding, value);
+ setBindingValue(binding, value, parentNode);
QString error = bindingsTarget()->appendBinding(binding, /*isListBinding*/false);
if (!error.isEmpty()) {
recordError(qualifiedNameLocation, error);
@@ -1806,24 +1810,36 @@ void JSCodeGen::beginObjectScope(QQmlPropertyCache *scopeObject)
QVector<int> JSCodeGen::generateJSCodeForFunctionsAndBindings(const QList<CompiledFunctionOrExpression> &functions)
{
+ auto qmlName = [&](const CompiledFunctionOrExpression &c) {
+ if (c.nameIndex != 0)
+ return stringPool->stringForIndex(c.nameIndex);
+ else
+ return QStringLiteral("%qml-expression-entry");
+ };
QVector<int> runtimeFunctionIndices(functions.size());
- QV4::Compiler::ScanFunctions scan(this, sourceCode, QV4::Compiler::GlobalCode);
- scan.enterGlobalEnvironment(QV4::Compiler::QmlBinding);
+ QV4::Compiler::ScanFunctions scan(this, sourceCode, QV4::Compiler::ContextType::Global);
+ scan.enterGlobalEnvironment(QV4::Compiler::ContextType::Binding);
for (const CompiledFunctionOrExpression &f : functions) {
Q_ASSERT(f.node != qmlRoot);
+ Q_ASSERT(f.parentNode && f.parentNode != qmlRoot);
QQmlJS::AST::FunctionDeclaration *function = QQmlJS::AST::cast<QQmlJS::AST::FunctionDeclaration*>(f.node);
- if (function)
+ if (function) {
scan.enterQmlFunction(function);
- else
- scan.enterEnvironment(f.node, QV4::Compiler::QmlBinding);
+ } else {
+ Q_ASSERT(f.node != f.parentNode);
+ scan.enterEnvironment(f.parentNode, QV4::Compiler::ContextType::Binding, qmlName(f));
+ }
scan(function ? function->body : f.node);
scan.leaveEnvironment();
}
scan.leaveEnvironment();
+ if (hasError)
+ return QVector<int>();
+
_context = nullptr;
for (int i = 0; i < functions.count(); ++i) {
@@ -1836,15 +1852,13 @@ QVector<int> JSCodeGen::generateJSCodeForFunctionsAndBindings(const QList<Compil
QString name;
if (function)
name = function->name.toString();
- else if (qmlFunction.nameIndex != 0)
- name = stringPool->stringForIndex(qmlFunction.nameIndex);
else
- name = QStringLiteral("%qml-expression-entry");
+ name = qmlName(qmlFunction);
- QQmlJS::AST::SourceElements *body;
- if (function)
- body = function->body ? function->body->elements : nullptr;
- else {
+ QQmlJS::AST::StatementList *body;
+ if (function) {
+ body = function->body;
+ } else {
// Synthesize source elements.
QQmlJS::MemoryPool *pool = jsEngine->pool();
@@ -1854,13 +1868,12 @@ QVector<int> JSCodeGen::generateJSCodeForFunctionsAndBindings(const QList<Compil
QQmlJS::AST::ExpressionNode *expr = node->expressionCast();
stmt = new (pool) QQmlJS::AST::ExpressionStatement(expr);
}
- QQmlJS::AST::SourceElement *element = new (pool) QQmlJS::AST::StatementSourceElement(stmt);
- body = new (pool) QQmlJS::AST::SourceElements(element);
+ body = new (pool) QQmlJS::AST::StatementList(stmt);
body = body->finish();
}
_disableAcceleratedLookups = qmlFunction.disableAcceleratedLookups;
- int idx = defineFunction(name, node,
+ int idx = defineFunction(name, function ? function : qmlFunction.parentNode,
function ? function->formals : nullptr,
body);
runtimeFunctionIndices[i] = idx;
@@ -1869,7 +1882,7 @@ QVector<int> JSCodeGen::generateJSCodeForFunctionsAndBindings(const QList<Compil
return runtimeFunctionIndices;
}
-int JSCodeGen::defineFunction(const QString &name, AST::Node *ast, AST::FormalParameterList *formals, AST::SourceElements *body)
+int JSCodeGen::defineFunction(const QString &name, AST::Node *ast, AST::FormalParameterList *formals, AST::StatementList *body)
{
int qmlContextTemp = -1;
int importedScriptsTemp = -1;
@@ -2190,7 +2203,7 @@ QV4::Compiler::Codegen::Reference JSCodeGen::fallbackNameLookup(const QString &n
// Look for IDs first.
for (const IdMapping &mapping : qAsConst(_idObjects)) {
if (name == mapping.name) {
- if (_context->compilationMode == QV4::Compiler::QmlBinding)
+ if (_context->contextType == QV4::Compiler::ContextType::Binding)
_context->idObjectDependencies.insert(mapping.idIndex);
Instruction::LoadIdObject load;
@@ -2446,8 +2459,6 @@ QmlIR::Object *IRLoader::loadObject(const QV4::CompiledData::Object *serializedO
}
}
- QQmlJS::Engine *jsParserEngine = &output->jsParserEngine;
-
const quint32_le *functionIdx = serializedObject->functionOffsetTable();
for (uint i = 0; i < serializedObject->nFunctions; ++i, ++functionIdx) {
QmlIR::Function *f = pool->New<QmlIR::Function>();
@@ -2458,26 +2469,10 @@ QmlIR::Object *IRLoader::loadObject(const QV4::CompiledData::Object *serializedO
f->location = compiledFunction->location;
f->nameIndex = compiledFunction->nameIndex;
- QQmlJS::AST::FormalParameterList *paramList = nullptr;
- const quint32_le *formalNameIdx = compiledFunction->formalsTable();
- for (uint i = 0; i < compiledFunction->nFormals; ++i, ++formalNameIdx) {
- const QString formal = unit->stringAt(*formalNameIdx);
- QStringRef paramNameRef = jsParserEngine->newStringRef(formal);
-
- if (paramList)
- paramList = new (pool) QQmlJS::AST::FormalParameterList(paramList, paramNameRef);
- else
- paramList = new (pool) QQmlJS::AST::FormalParameterList(paramNameRef);
- }
-
- if (paramList)
- paramList = paramList->finish();
-
const QString name = unit->stringAt(compiledFunction->nameIndex);
- f->functionDeclaration = new(pool) QQmlJS::AST::FunctionDeclaration(jsParserEngine->newStringRef(name), paramList, /*body*/nullptr);
f->formals.allocate(pool, int(compiledFunction->nFormals));
- formalNameIdx = compiledFunction->formalsTable();
+ const quint32_le *formalNameIdx = compiledFunction->formalsTable();
for (uint i = 0; i < compiledFunction->nFormals; ++i, ++formalNameIdx)
f->formals[i] = *formalNameIdx;
diff --git a/src/qml/compiler/qqmlirbuilder_p.h b/src/qml/compiler/qqmlirbuilder_p.h
index 689b232b1c..f8d481e14f 100644
--- a/src/qml/compiler/qqmlirbuilder_p.h
+++ b/src/qml/compiler/qqmlirbuilder_p.h
@@ -57,7 +57,6 @@
#include <private/qqmljsmemorypool_p.h>
#include <private/qv4codegen_p.h>
#include <private/qv4compiler_p.h>
-#include <private/qqmljslexer_p.h>
#include <QTextStream>
#include <QCoreApplication>
@@ -325,7 +324,6 @@ struct Alias : public QV4::CompiledData::Alias
struct Function
{
- QQmlJS::AST::FunctionDeclaration *functionDeclaration;
QV4::CompiledData::Location location;
int nameIndex;
quint32 index; // index in parsedQML::functions
@@ -342,12 +340,9 @@ struct Function
struct Q_QML_PRIVATE_EXPORT CompiledFunctionOrExpression
{
CompiledFunctionOrExpression()
-
{}
- CompiledFunctionOrExpression(QQmlJS::AST::Node *n)
- : node(n)
- {}
+ QQmlJS::AST::Node *parentNode = nullptr; // FunctionDeclaration, Statement or Expression
QQmlJS::AST::Node *node = nullptr; // FunctionDeclaration, Statement or Expression
quint32 nameIndex = 0;
bool disableAcceleratedLookups = false;
@@ -400,8 +395,6 @@ public:
void init(QQmlJS::MemoryPool *pool, int typeNameIndex, int idIndex, const QQmlJS::AST::SourceLocation &location = QQmlJS::AST::SourceLocation());
- QString sanityCheckFunctionNames(const QSet<QString> &illegalNames, QQmlJS::AST::SourceLocation *errorLocation);
-
QString appendEnum(Enum *enumeration);
QString appendSignal(Signal *signal);
QString appendProperty(Property *prop, const QString &propertyName, bool isDefaultProperty, const QQmlJS::AST::SourceLocation &defaultToken, QQmlJS::AST::SourceLocation *errorLocation);
@@ -520,12 +513,12 @@ public:
QStringRef textRefAt(const QQmlJS::AST::SourceLocation &first,
const QQmlJS::AST::SourceLocation &last) const;
- void setBindingValue(QV4::CompiledData::Binding *binding, QQmlJS::AST::Statement *statement);
+ void setBindingValue(QV4::CompiledData::Binding *binding, QQmlJS::AST::Statement *statement, AST::Node *parentNode);
void tryGeneratingTranslationBinding(const QStringRef &base, QQmlJS::AST::ArgumentList *args, QV4::CompiledData::Binding *binding);
- void appendBinding(QQmlJS::AST::UiQualifiedId *name, QQmlJS::AST::Statement *value);
+ void appendBinding(QQmlJS::AST::UiQualifiedId *name, QQmlJS::AST::Statement *value, AST::Node *parentNode);
void appendBinding(QQmlJS::AST::UiQualifiedId *name, int objectIndex, bool isOnAssignment = false);
- void appendBinding(const QQmlJS::AST::SourceLocation &qualifiedNameLocation, const QQmlJS::AST::SourceLocation &nameLocation, quint32 propertyNameIndex, QQmlJS::AST::Statement *value);
+ void appendBinding(const QQmlJS::AST::SourceLocation &qualifiedNameLocation, const QQmlJS::AST::SourceLocation &nameLocation, quint32 propertyNameIndex, QQmlJS::AST::Statement *value, AST::Node *parentNode);
void appendBinding(const QQmlJS::AST::SourceLocation &qualifiedNameLocation, const QQmlJS::AST::SourceLocation &nameLocation, quint32 propertyNameIndex, int objectIndex, bool isListItem = false, bool isOnAssignment = false);
bool appendAlias(QQmlJS::AST::UiPublicMember *node);
@@ -548,6 +541,8 @@ public:
static bool isStatementNodeScript(QQmlJS::AST::Statement *statement);
static bool isRedundantNullInitializerForPropertyDeclaration(Property *property, QQmlJS::AST::Statement *statement);
+ QString sanityCheckFunctionNames(Object *obj, const QSet<QString> &illegalNames, QQmlJS::AST::SourceLocation *errorLocation);
+
QList<QQmlJS::DiagnosticMessage> errors;
QSet<QString> illegalNames;
@@ -578,7 +573,7 @@ private:
#ifndef V4_BOOTSTRAP
struct Q_QML_EXPORT PropertyResolver
{
- PropertyResolver(const QQmlPropertyCache *cache)
+ PropertyResolver(const QQmlRefPointer<QQmlPropertyCache> &cache)
: cache(cache)
{}
@@ -597,7 +592,7 @@ struct Q_QML_EXPORT PropertyResolver
// This code must match the semantics of QQmlPropertyPrivate::findSignalByName
QQmlPropertyData *signal(const QString &name, bool *notInRevision) const;
- const QQmlPropertyCache *cache;
+ QQmlRefPointer<QQmlPropertyCache> cache;
};
#endif
@@ -623,7 +618,7 @@ struct Q_QML_PRIVATE_EXPORT JSCodeGen : public QV4::Compiler::Codegen
int defineFunction(const QString &name, AST::Node *ast,
AST::FormalParameterList *formals,
- AST::SourceElements *body) override;
+ AST::StatementList *body) override;
protected:
void beginFunctionBodyHook() override;
diff --git a/src/qml/compiler/qqmlpropertycachecreator_p.h b/src/qml/compiler/qqmlpropertycachecreator_p.h
index 8bbc8291b4..02517ea6bb 100644
--- a/src/qml/compiler/qqmlpropertycachecreator_p.h
+++ b/src/qml/compiler/qqmlpropertycachecreator_p.h
@@ -99,8 +99,8 @@ public:
protected:
QQmlCompileError buildMetaObjectRecursively(int objectIndex, const QQmlBindingInstantiationContext &context);
- QQmlPropertyCache *propertyCacheForObject(const CompiledObject *obj, const QQmlBindingInstantiationContext &context, QQmlCompileError *error) const;
- QQmlCompileError createMetaObject(int objectIndex, const CompiledObject *obj, QQmlPropertyCache *baseTypeCache);
+ QQmlRefPointer<QQmlPropertyCache> propertyCacheForObject(const CompiledObject *obj, const QQmlBindingInstantiationContext &context, QQmlCompileError *error) const;
+ QQmlCompileError createMetaObject(int objectIndex, const CompiledObject *obj, const QQmlRefPointer<QQmlPropertyCache> &baseTypeCache);
QString stringAt(int index) const { return objectContainer->stringAt(index); }
@@ -152,7 +152,7 @@ inline QQmlCompileError QQmlPropertyCacheCreator<ObjectContainer>::buildMetaObje
const CompiledObject *obj = objectContainer->objectAt(context.referencingObjectIndex);
auto *typeRef = objectContainer->resolvedTypes.value(obj->inheritedTypeNameIndex);
Q_ASSERT(typeRef);
- QQmlPropertyCache *baseTypeCache = typeRef->createPropertyCache(QQmlEnginePrivate::get(enginePrivate));
+ QQmlRefPointer<QQmlPropertyCache> baseTypeCache = typeRef->createPropertyCache(QQmlEnginePrivate::get(enginePrivate));
QQmlCompileError error = createMetaObject(context.referencingObjectIndex, obj, baseTypeCache);
if (error.isSet())
return error;
@@ -166,7 +166,7 @@ inline QQmlCompileError QQmlPropertyCacheCreator<ObjectContainer>::buildMetaObje
}
}
- QQmlPropertyCache *baseTypeCache;
+ QQmlRefPointer<QQmlPropertyCache> baseTypeCache;
{
QQmlCompileError error;
baseTypeCache = propertyCacheForObject(obj, context, &error);
@@ -209,7 +209,7 @@ inline QQmlCompileError QQmlPropertyCacheCreator<ObjectContainer>::buildMetaObje
}
template <typename ObjectContainer>
-inline QQmlPropertyCache *QQmlPropertyCacheCreator<ObjectContainer>::propertyCacheForObject(const CompiledObject *obj, const QQmlBindingInstantiationContext &context, QQmlCompileError *error) const
+inline QQmlRefPointer<QQmlPropertyCache> QQmlPropertyCacheCreator<ObjectContainer>::propertyCacheForObject(const CompiledObject *obj, const QQmlBindingInstantiationContext &context, QQmlCompileError *error) const
{
if (context.instantiatingProperty) {
return context.instantiatingPropertyCache(enginePrivate);
@@ -241,14 +241,12 @@ inline QQmlPropertyCache *QQmlPropertyCacheCreator<ObjectContainer>::propertyCac
QString propertyName = stringAt(context.instantiatingBinding->propertyNameIndex);
if (imports->resolveType(propertyName, &qmltype, nullptr, nullptr, nullptr)) {
if (qmltype.isComposite()) {
- QQmlTypeData *tdata = enginePrivate->typeLoader.getType(qmltype.sourceUrl());
+ QQmlRefPointer<QQmlTypeData> tdata = enginePrivate->typeLoader.getType(qmltype.sourceUrl());
Q_ASSERT(tdata);
Q_ASSERT(tdata->isComplete());
auto compilationUnit = tdata->compilationUnit();
qmltype = QQmlMetaType::qmlType(compilationUnit->metaTypeId);
-
- tdata->release();
}
}
}
@@ -264,7 +262,7 @@ inline QQmlPropertyCache *QQmlPropertyCacheCreator<ObjectContainer>::propertyCac
}
template <typename ObjectContainer>
-inline QQmlCompileError QQmlPropertyCacheCreator<ObjectContainer>::createMetaObject(int objectIndex, const CompiledObject *obj, QQmlPropertyCache *baseTypeCache)
+inline QQmlCompileError QQmlPropertyCacheCreator<ObjectContainer>::createMetaObject(int objectIndex, const CompiledObject *obj, const QQmlRefPointer<QQmlPropertyCache> &baseTypeCache)
{
QQmlRefPointer<QQmlPropertyCache> cache;
cache.adopt(baseTypeCache->copyAndReserve(obj->propertyCount() + obj->aliasCount(),
@@ -314,7 +312,7 @@ inline QQmlCompileError QQmlPropertyCacheCreator<ObjectContainer>::createMetaObj
}
}
if (newClassName.isEmpty()) {
- newClassName = QQmlMetaObject(baseTypeCache).className();
+ newClassName = QQmlMetaObject(baseTypeCache.data()).className();
newClassName.append("_QML_");
newClassName.append(QByteArray::number(classIndexCounter.fetchAndAddRelaxed(1)));
}
@@ -354,7 +352,7 @@ inline QQmlCompileError QQmlPropertyCacheCreator<ObjectContainer>::createMetaObj
// and throw an error if there is a signal/method defined as an override.
QSet<QString> seenSignals;
seenSignals << QStringLiteral("destroyed") << QStringLiteral("parentChanged") << QStringLiteral("objectNameChanged");
- QQmlPropertyCache *parentCache = cache;
+ QQmlPropertyCache *parentCache = cache.data();
while ((parentCache = parentCache->parent())) {
if (int pSigCount = parentCache->signalCount()) {
int pSigOffset = parentCache->signalOffset();
@@ -440,15 +438,13 @@ inline QQmlCompileError QQmlPropertyCacheCreator<ObjectContainer>::createMetaObj
return QQmlCompileError(s->location, QQmlPropertyCacheCreatorBase::tr("Invalid signal parameter type: %1").arg(customTypeName));
if (qmltype.isComposite()) {
- QQmlTypeData *tdata = enginePrivate->typeLoader.getType(qmltype.sourceUrl());
+ QQmlRefPointer<QQmlTypeData> tdata = enginePrivate->typeLoader.getType(qmltype.sourceUrl());
Q_ASSERT(tdata);
Q_ASSERT(tdata->isComplete());
auto compilationUnit = tdata->compilationUnit();
paramTypes[i + 1] = compilationUnit->metaTypeId;
-
- tdata->release();
} else {
paramTypes[i + 1] = qmltype.typeId();
}
@@ -523,7 +519,7 @@ inline QQmlCompileError QQmlPropertyCacheCreator<ObjectContainer>::createMetaObj
Q_ASSERT(qmltype.isValid());
if (qmltype.isComposite()) {
- QQmlTypeData *tdata = enginePrivate->typeLoader.getType(qmltype.sourceUrl());
+ QQmlRefPointer<QQmlTypeData> tdata = enginePrivate->typeLoader.getType(qmltype.sourceUrl());
Q_ASSERT(tdata);
Q_ASSERT(tdata->isComplete());
@@ -534,8 +530,6 @@ inline QQmlCompileError QQmlPropertyCacheCreator<ObjectContainer>::createMetaObj
} else {
propertyType = compilationUnit->listMetaTypeId;
}
-
- tdata->release();
} else {
if (p->type == QV4::CompiledData::Property::Custom) {
propertyType = qmltype.typeId();
diff --git a/src/qml/compiler/qqmlpropertyvalidator.cpp b/src/qml/compiler/qqmlpropertyvalidator.cpp
index ffd3b5975a..afa2e4ad81 100644
--- a/src/qml/compiler/qqmlpropertyvalidator.cpp
+++ b/src/qml/compiler/qqmlpropertyvalidator.cpp
@@ -45,8 +45,9 @@
QT_BEGIN_NAMESPACE
-QQmlPropertyValidator::QQmlPropertyValidator(QQmlEnginePrivate *enginePrivate, const QQmlImports &imports, QV4::CompiledData::CompilationUnit *compilationUnit)
+QQmlPropertyValidator::QQmlPropertyValidator(QQmlEnginePrivate *enginePrivate, const QQmlImports &imports, const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &compilationUnit)
: enginePrivate(enginePrivate)
+ , compilationUnit(compilationUnit)
, imports(imports)
, qmlUnit(compilationUnit->data)
, resolvedTypes(compilationUnit->resolvedTypes)
@@ -629,7 +630,7 @@ QQmlCompileError QQmlPropertyValidator::validateObjectBinding(QQmlPropertyData *
const QV4::CompiledData::Object *targetObject = qmlUnit->objectAt(binding->value.objectIndex);
if (auto *typeRef = resolvedTypes.value(targetObject->inheritedTypeNameIndex)) {
- QQmlPropertyCache *cache = typeRef->createPropertyCache(QQmlEnginePrivate::get(enginePrivate));
+ QQmlRefPointer<QQmlPropertyCache> cache = typeRef->createPropertyCache(QQmlEnginePrivate::get(enginePrivate));
const QMetaObject *mo = cache->firstCppMetaObject();
QQmlType qmlType;
while (mo && !qmlType.isValid()) {
@@ -670,7 +671,7 @@ QQmlCompileError QQmlPropertyValidator::validateObjectBinding(QQmlPropertyData *
} else if (binding->flags & QV4::CompiledData::Binding::IsSignalHandlerObject && property->isFunction()) {
return noError;
} else if (QQmlValueTypeFactory::isValueType(property->propType())) {
- return QQmlCompileError(binding->location, tr("Unexpected object assignment"));
+ return QQmlCompileError(binding->location, tr("Unexpected object assignment for property \"%1\"").arg(propertyName));
} else if (property->propType() == qMetaTypeId<QQmlScriptString>()) {
return QQmlCompileError(binding->valueLocation, tr("Invalid property assignment: script expected"));
} else {
@@ -680,7 +681,7 @@ QQmlCompileError QQmlPropertyValidator::validateObjectBinding(QQmlPropertyData *
// Using -1 for the minor version ensures that we get the raw metaObject.
QQmlPropertyCache *propertyMetaObject = enginePrivate->rawPropertyCacheForType(property->propType(), -1);
- // Will be true if the assgned type inherits propertyMetaObject
+ // Will be true if the assigned type inherits propertyMetaObject
bool isAssignable = false;
// Determine isAssignable value
if (propertyMetaObject) {
@@ -692,7 +693,8 @@ QQmlCompileError QQmlPropertyValidator::validateObjectBinding(QQmlPropertyData *
}
if (!isAssignable) {
- return QQmlCompileError(binding->valueLocation, tr("Cannot assign object to property"));
+ return QQmlCompileError(binding->valueLocation, tr("Cannot assign object of type \"%1\" to property of type \"%2\" as the former is neither the same as the latter nor a sub-class of it.")
+ .arg(stringAt(qmlUnit->objectAt(binding->value.objectIndex)->inheritedTypeNameIndex)).arg(QLatin1String(QMetaType::typeName(property->propType()))));
}
}
return noError;
diff --git a/src/qml/compiler/qqmlpropertyvalidator_p.h b/src/qml/compiler/qqmlpropertyvalidator_p.h
index e37b8141f4..a8f75a0a7e 100644
--- a/src/qml/compiler/qqmlpropertyvalidator_p.h
+++ b/src/qml/compiler/qqmlpropertyvalidator_p.h
@@ -58,7 +58,7 @@ class QQmlPropertyValidator
{
Q_DECLARE_TR_FUNCTIONS(QQmlPropertyValidator)
public:
- QQmlPropertyValidator(QQmlEnginePrivate *enginePrivate, const QQmlImports &imports, QV4::CompiledData::CompilationUnit *compilationUnit);
+ QQmlPropertyValidator(QQmlEnginePrivate *enginePrivate, const QQmlImports &imports, const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &compilationUnit);
QVector<QQmlCompileError> validate();
@@ -74,6 +74,7 @@ private:
QString stringAt(int index) const { return qmlUnit->stringAt(index); }
QQmlEnginePrivate *enginePrivate;
+ QQmlRefPointer<QV4::CompiledData::CompilationUnit> compilationUnit;
const QQmlImports &imports;
const QV4::CompiledData::Unit *qmlUnit;
const QV4::CompiledData::ResolvedTypeReferenceMap &resolvedTypes;
diff --git a/src/qml/compiler/qqmltypecompiler.cpp b/src/qml/compiler/qqmltypecompiler.cpp
index a896745b3f..37271e2c54 100644
--- a/src/qml/compiler/qqmltypecompiler.cpp
+++ b/src/qml/compiler/qqmltypecompiler.cpp
@@ -67,7 +67,7 @@ QQmlTypeCompiler::QQmlTypeCompiler(QQmlEnginePrivate *engine, QQmlTypeData *type
{
}
-QV4::CompiledData::CompilationUnit *QQmlTypeCompiler::compile()
+QQmlRefPointer<QV4::CompiledData::CompilationUnit> QQmlTypeCompiler::compile()
{
// Build property caches and VME meta object data
@@ -147,7 +147,7 @@ QV4::CompiledData::CompilationUnit *QQmlTypeCompiler::compile()
document->jsModule.fileName = typeData->urlString();
document->jsModule.finalUrl = typeData->finalUrlString();
QmlIR::JSCodeGen v4CodeGenerator(document->code, &document->jsGenerator, &document->jsModule, &document->jsParserEngine,
- document->program, typeNameCache, &document->jsGenerator.stringTable, engine->v8engine()->illegalNames());
+ document->program, typeNameCache.data(), &document->jsGenerator.stringTable, engine->v8engine()->illegalNames());
v4CodeGenerator.setUseFastLookups(false);
QQmlJSCodeGenerator jsCodeGen(this, &v4CodeGenerator);
if (!jsCodeGen.generateCodeForComponents())
@@ -165,7 +165,7 @@ QV4::CompiledData::CompilationUnit *QQmlTypeCompiler::compile()
// The js unit owns the data and will free the qml unit.
document->javaScriptCompilationUnit->data = qmlUnit;
- QV4::CompiledData::CompilationUnit *compilationUnit = document->javaScriptCompilationUnit;
+ QQmlRefPointer<QV4::CompiledData::CompilationUnit> compilationUnit = document->javaScriptCompilationUnit;
compilationUnit = document->javaScriptCompilationUnit;
compilationUnit->typeNameCache = typeNameCache;
compilationUnit->resolvedTypes = resolvedTypes;
@@ -339,14 +339,12 @@ bool SignalHandlerConverter::convertSignalHandlerExpressionsToFunctionDeclaratio
if (!type.isValid()) {
if (imports->resolveType(propertyName, &type, nullptr, nullptr, nullptr)) {
if (type.isComposite()) {
- QQmlTypeData *tdata = enginePrivate->typeLoader.getType(type.sourceUrl());
+ QQmlRefPointer<QQmlTypeData> tdata = enginePrivate->typeLoader.getType(type.sourceUrl());
Q_ASSERT(tdata);
Q_ASSERT(tdata->isComplete());
auto compilationUnit = tdata->compilationUnit();
type = QQmlMetaType::qmlType(compilationUnit->metaTypeId);
-
- tdata->release();
}
}
}
@@ -466,14 +464,12 @@ bool SignalHandlerConverter::convertSignalHandlerExpressionsToFunctionDeclaratio
for (const QString &param : qAsConst(parameters)) {
QStringRef paramNameRef = compiler->newStringRef(param);
- if (paramList)
- paramList = new (pool) QQmlJS::AST::FormalParameterList(paramList, paramNameRef);
- else
- paramList = new (pool) QQmlJS::AST::FormalParameterList(paramNameRef);
+ QQmlJS::AST::PatternElement *b = new (pool) QQmlJS::AST::PatternElement(paramNameRef, nullptr);
+ paramList = new (pool) QQmlJS::AST::FormalParameterList(paramList, b);
}
if (paramList)
- paramList = paramList->finish();
+ paramList = paramList->finish(pool);
QmlIR::CompiledFunctionOrExpression *foe = obj->functionsAndExpressions->slowAt(binding->value.compiledScriptIndex);
QQmlJS::AST::FunctionDeclaration *functionDeclaration = nullptr;
@@ -490,11 +486,8 @@ bool SignalHandlerConverter::convertSignalHandlerExpressionsToFunctionDeclaratio
}
if (!functionDeclaration) {
QQmlJS::AST::Statement *statement = static_cast<QQmlJS::AST::Statement*>(foe->node);
- QQmlJS::AST::SourceElement *sourceElement = new (pool) QQmlJS::AST::StatementSourceElement(statement);
- QQmlJS::AST::SourceElements *elements = new (pool) QQmlJS::AST::SourceElements(sourceElement);
- elements = elements->finish();
-
- QQmlJS::AST::FunctionBody *body = new (pool) QQmlJS::AST::FunctionBody(elements);
+ QQmlJS::AST::StatementList *body = new (pool) QQmlJS::AST::StatementList(statement);
+ body = body->finish();
functionDeclaration = new (pool) QQmlJS::AST::FunctionDeclaration(compiler->newStringRef(stringAt(binding->propertyNameIndex)), paramList, body);
functionDeclaration->lbraceToken = functionDeclaration->functionToken
diff --git a/src/qml/compiler/qqmltypecompiler_p.h b/src/qml/compiler/qqmltypecompiler_p.h
index b8eddcb9b2..537f87ab4c 100644
--- a/src/qml/compiler/qqmltypecompiler_p.h
+++ b/src/qml/compiler/qqmltypecompiler_p.h
@@ -92,7 +92,7 @@ public:
QV4::CompiledData::ResolvedTypeReferenceMap resolvedTypes;
// ---
- QV4::CompiledData::CompilationUnit *compile();
+ QQmlRefPointer<QV4::CompiledData::CompilationUnit> compile();
QList<QQmlError> compilationErrors() const { return errors; }
void recordError(QQmlError error);
diff --git a/src/qml/compiler/qv4bytecodegenerator.cpp b/src/qml/compiler/qv4bytecodegenerator.cpp
index 4d50654d27..7e1f49ee86 100644
--- a/src/qml/compiler/qv4bytecodegenerator.cpp
+++ b/src/qml/compiler/qv4bytecodegenerator.cpp
@@ -70,14 +70,14 @@ int BytecodeGenerator::newRegisterArray(int n)
void BytecodeGenerator::packInstruction(I &i)
{
- uchar type = *reinterpret_cast<uchar *>(i.packed);
- Q_ASSERT(type >= MOTH_NUM_INSTRUCTIONS());
- if (type >= MOTH_NUM_INSTRUCTIONS())
- type -= MOTH_NUM_INSTRUCTIONS();
+ Instr::Type type = Instr::unpack(i.packed);
+ Q_ASSERT(int(type) < MOTH_NUM_INSTRUCTIONS());
+ type = Instr::narrowInstructionType(type);
int instructionsAsInts[sizeof(Instr)/sizeof(int)] = {};
int nMembers = Moth::InstrInfo::argumentCount[static_cast<int>(i.type)];
+ uchar *code = i.packed + Instr::encodedLength(type);
for (int j = 0; j < nMembers; ++j) {
- instructionsAsInts[j] = qFromLittleEndian<qint32>(i.packed + 1 + j * sizeof(int));
+ instructionsAsInts[j] = qFromLittleEndian<qint32>(code + j * sizeof(int));
}
enum {
Normal,
@@ -89,11 +89,10 @@ void BytecodeGenerator::packInstruction(I &i)
break;
}
}
- char *code = i.packed;
+ code = i.packed;
switch (width) {
case Normal:
- *reinterpret_cast<uchar *>(code) = type;
- ++code;
+ code = Instr::pack(code, type);
for (int n = 0; n < nMembers; ++n) {
qint8 v = static_cast<qint8>(instructionsAsInts[n]);
memcpy(code, &v, 1);
@@ -122,7 +121,7 @@ void BytecodeGenerator::adjustJumpOffsets()
// qDebug() << "adjusting jump offset for instruction" << index << i.position << i.size << "offsetForJump" << i.offsetForJump << "target"
// << labels.at(i.linkedLabel) << linkedInstruction.position << "jumpOffset" << jumpOffset;
uchar type = *reinterpret_cast<const uchar *>(i.packed);
- if (type >= MOTH_NUM_INSTRUCTIONS()) {
+ if (Instr::isWide(Instr::Type(type))) {
Q_ASSERT(i.offsetForJump == i.size - 4);
qToLittleEndian<qint32>(jumpOffset, c);
} else {
@@ -177,7 +176,7 @@ void BytecodeGenerator::finalize(Compiler::Context *context)
entry.line = currentLine;
lineNumbers.append(entry);
}
- code.append(i.packed, i.size);
+ code.append(reinterpret_cast<const char *>(i.packed), i.size);
}
context->code = code;
@@ -185,6 +184,25 @@ void BytecodeGenerator::finalize(Compiler::Context *context)
}
int BytecodeGenerator::addInstructionHelper(Instr::Type type, const Instr &i, int offsetOfOffset) {
+ if (lastInstrType == int(Instr::Type::StoreReg)) {
+ if (type == Instr::Type::LoadReg) {
+ if (i.LoadReg.reg == lastInstr.StoreReg.reg) {
+ // value is already in the accumulator
+ return -1;
+ }
+ }
+ if (type == Instr::Type::MoveReg) {
+ if (i.MoveReg.srcReg == lastInstr.StoreReg.reg) {
+ Instruction::StoreReg store;
+ store.reg = i.MoveReg.destReg;
+ addInstruction(store);
+ return -1;
+ }
+ }
+ }
+ lastInstrType = int(type);
+ lastInstr = i;
+
#if QT_CONFIG(qml_debug)
if (debugMode && type != Instr::Type::Debug) {
QT_WARNING_PUSH
@@ -207,12 +225,10 @@ QT_WARNING_POP
const int argCount = Moth::InstrInfo::argumentCount[static_cast<int>(type)];
int s = argCount*sizeof(int);
if (offsetOfOffset != -1)
- offsetOfOffset += 1;
- I instr{type, static_cast<short>(s + 1), 0, currentLine, offsetOfOffset, -1, "\0\0" };
- char *code = instr.packed;
- *reinterpret_cast<uchar *>(code) = static_cast<uchar>(MOTH_NUM_INSTRUCTIONS() + static_cast<int>(type));
- ++code;
- Q_ASSERT(MOTH_NUM_INSTRUCTIONS() + static_cast<int>(type) < 256);
+ offsetOfOffset += Instr::encodedLength(type);
+ I instr{type, static_cast<short>(s + Instr::encodedLength(type)), 0, currentLine, offsetOfOffset, -1, "\0\0" };
+ uchar *code = instr.packed;
+ code = Instr::pack(code, Instr::wideInstructionType(type));
for (int j = 0; j < argCount; ++j) {
qToLittleEndian<qint32>(i.argumentsAsInts[j], code);
diff --git a/src/qml/compiler/qv4bytecodegenerator_p.h b/src/qml/compiler/qv4bytecodegenerator_p.h
index e69f2cd310..dca5771356 100644
--- a/src/qml/compiler/qv4bytecodegenerator_p.h
+++ b/src/qml/compiler/qv4bytecodegenerator_p.h
@@ -77,22 +77,18 @@ public:
Label(BytecodeGenerator *generator, LinkMode mode = LinkNow)
: generator(generator),
index(generator->labels.size()) {
- generator->labels.append(mode == LinkNow ? generator->instructions.size() : -1);
- }
- static Label returnLabel() {
- Label l;
- l.index = INT_MAX;
- return l;
- }
- bool isReturn() const {
- return index == INT_MAX;
+ generator->labels.append(-1);
+ if (mode == LinkNow)
+ link();
}
void link() {
Q_ASSERT(index >= 0);
Q_ASSERT(generator->labels[index] == -1);
generator->labels[index] = generator->instructions.size();
+ generator->clearLastInstruction();
}
+ bool isValid() const { return generator != nullptr; }
BytecodeGenerator *generator = nullptr;
int index = -1;
@@ -133,14 +129,16 @@ public:
};
struct ExceptionHandler : public Label {
+ ExceptionHandler() = default;
ExceptionHandler(BytecodeGenerator *generator)
: Label(generator, LinkLater)
{
}
~ExceptionHandler()
{
- Q_ASSERT(generator->currentExceptionHandler != this);
+ Q_ASSERT(!generator || generator->currentExceptionHandler != this);
}
+ bool isValid() const { return generator != nullptr; }
};
Label label() {
@@ -181,6 +179,18 @@ public:
return addJumpInstruction(data);
}
+ Q_REQUIRED_RESULT Jump jumpNotUndefined()
+ {
+ Instruction::JumpNotUndefined data;
+ return addJumpInstruction(data);
+ }
+
+ Q_REQUIRED_RESULT Jump jumpNoException()
+ {
+ Instruction::JumpNoException data;
+ return addJumpInstruction(data);
+ }
+
void jumpStrictEqual(const StackSlot &lhs, const Label &target)
{
Instruction::CmpStrictEqual cmp;
@@ -197,26 +207,10 @@ public:
addJumpInstruction(Instruction::JumpTrue()).link(target);
}
- Q_REQUIRED_RESULT Jump jumpStrictEqualStackSlotInt(const StackSlot &lhs, int rhs)
- {
- Instruction::JumpStrictEqualStackSlotInt data;
- data.lhs = lhs;
- data.rhs = rhs;
- return addJumpInstruction(data);
- }
-
- Q_REQUIRED_RESULT Jump jumpStrictNotEqualStackSlotInt(const StackSlot &lhs, int rhs)
- {
- Instruction::JumpStrictNotEqualStackSlotInt data;
- data.lhs = lhs;
- data.rhs = rhs;
- return addJumpInstruction(data);
- }
-
- void setExceptionHandler(ExceptionHandler *handler)
+ void setUnwindHandler(ExceptionHandler *handler)
{
currentExceptionHandler = handler;
- Instruction::SetExceptionHandler data;
+ Instruction::SetUnwindHandler data;
data.offset = 0;
if (!handler)
addInstruction(data);
@@ -224,6 +218,19 @@ public:
addJumpInstruction(data).link(*handler);
}
+ void unwindToLabel(int level, const Label &target)
+ {
+ if (level) {
+ Instruction::UnwindToLabel unwind;
+ unwind.level = level;
+ addJumpInstruction(unwind).link(target);
+ } else {
+ jump().link(target);
+ }
+ }
+
+
+
void setLocation(const QQmlJS::AST::SourceLocation &loc);
ExceptionHandler *exceptionHandler() const {
@@ -233,6 +240,7 @@ public:
int newRegister();
int newRegisterArray(int n);
int registerCount() const { return regCount; }
+ int currentRegister() const { return currentReg; }
void finalize(Compiler::Context *context);
@@ -252,6 +260,11 @@ public:
addJumpInstruction(Instruction::JumpTrue()).link(*trueLabel);
}
+ void clearLastInstruction()
+ {
+ lastInstrType = -1;
+ }
+
private:
friend struct Jump;
friend struct Label;
@@ -266,7 +279,7 @@ private:
int line;
int offsetForJump;
int linkedLabel;
- char packed[sizeof(Instr) + 2]; // 2 for instruction and prefix
+ unsigned char packed[sizeof(Instr) + 2]; // 2 for instruction type
};
void compressInstructions();
@@ -275,7 +288,7 @@ private:
QVector<I> instructions;
QVector<int> labels;
- ExceptionHandler *currentExceptionHandler;
+ ExceptionHandler *currentExceptionHandler = nullptr;
int regCount = 0;
public:
int currentReg = 0;
@@ -283,6 +296,9 @@ private:
int startLine = 0;
int currentLine = 0;
bool debugMode = false;
+
+ int lastInstrType = -1;
+ Moth::Instr lastInstr;
};
}
diff --git a/src/qml/compiler/qv4bytecodehandler.cpp b/src/qml/compiler/qv4bytecodehandler.cpp
new file mode 100644
index 0000000000..23f7051718
--- /dev/null
+++ b/src/qml/compiler/qv4bytecodehandler.cpp
@@ -0,0 +1,529 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 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 <private/qv4bytecodehandler_p.h>
+
+QT_USE_NAMESPACE
+using namespace QV4;
+using namespace Moth;
+
+ByteCodeHandler::~ByteCodeHandler()
+{
+}
+
+#define DISPATCH_INSTRUCTION(name, nargs, ...) \
+ generate_##name( \
+ __VA_ARGS__ \
+ );
+
+#define DECODE_AND_DISPATCH(instr) \
+ { \
+ INSTR_##instr(MOTH_DECODE_WITH_BASE) \
+ Q_UNUSED(base_ptr); \
+ startInstruction(Instr::Type::instr); \
+ _offset = code - start; \
+ INSTR_##instr(DISPATCH) \
+ endInstruction(Instr::Type::instr); \
+ continue; \
+ }
+
+void ByteCodeHandler::decode(const char *code, uint len)
+{
+ MOTH_JUMP_TABLE;
+
+ const char *start = code;
+ const char *end = code + len;
+ while (code < end) {
+ MOTH_DISPATCH()
+
+ FOR_EACH_MOTH_INSTR(DECODE_AND_DISPATCH)
+ }
+}
+
+#undef DECODE_AND_DISPATCH
+#undef DISPATCH_INSTRUCTION
+
+#define MOTH_UNUSED_ARGS0()
+#define MOTH_UNUSED_ARGS1(arg) \
+ Q_UNUSED(arg);
+#define MOTH_UNUSED_ARGS2(arg1, arg2) \
+ Q_UNUSED(arg1); \
+ Q_UNUSED(arg2);
+#define MOTH_UNUSED_ARGS3(arg1, arg2, arg3) \
+ Q_UNUSED(arg1); \
+ Q_UNUSED(arg2); \
+ Q_UNUSED(arg3);
+#define MOTH_UNUSED_ARGS4(arg1, arg2, arg3, arg4) \
+ Q_UNUSED(arg1); \
+ Q_UNUSED(arg2); \
+ Q_UNUSED(arg3); \
+ Q_UNUSED(arg4);
+
+#define MOTH_MARK_ARGS_UNUSED_PLEASE(nargs, ...) \
+ MOTH_EXPAND_FOR_MSVC(MOTH_UNUSED_ARGS##nargs(__VA_ARGS__))
+
+#define MOTH_MARK_ARGS_UNUSED_INSTRUCTION(name, nargs, ...) \
+ MOTH_MARK_ARGS_UNUSED_PLEASE(nargs, __VA_ARGS__)
+
+#define COLLECTOR_BEGIN_INSTR(instr) \
+ { \
+ INSTR_##instr(MOTH_DECODE_WITH_BASE) \
+ INSTR_##instr(MOTH_MARK_ARGS_UNUSED) \
+ Q_UNUSED(base_ptr);
+
+#define COLLECTOR_END_INSTR(instr) \
+ continue; \
+ }
+
+std::vector<int> ByteCodeHandler::collectLabelsInBytecode(const char *code, uint len)
+{
+ MOTH_JUMP_TABLE;
+
+ std::vector<int> labels;
+
+ const auto addLabel = [&labels,len](int offset) {
+ Q_ASSERT(offset >= 0 && offset < static_cast<int>(len));
+ labels.push_back(offset);
+ };
+
+ const char *start = code;
+ const char *end = code + len;
+ while (code < end) {
+ MOTH_DISPATCH()
+ Q_UNREACHABLE();
+
+ COLLECTOR_BEGIN_INSTR(LoadReg)
+ COLLECTOR_END_INSTR(LoadReg)
+
+ COLLECTOR_BEGIN_INSTR(StoreReg)
+ COLLECTOR_END_INSTR(StoreReg)
+
+ COLLECTOR_BEGIN_INSTR(MoveReg)
+ COLLECTOR_END_INSTR(MoveReg)
+
+ COLLECTOR_BEGIN_INSTR(LoadConst)
+ COLLECTOR_END_INSTR(LoadConst)
+
+ COLLECTOR_BEGIN_INSTR(LoadNull)
+ COLLECTOR_END_INSTR(LoadNull)
+
+ COLLECTOR_BEGIN_INSTR(LoadZero)
+ COLLECTOR_END_INSTR(LoadZero)
+
+ COLLECTOR_BEGIN_INSTR(LoadTrue)
+ COLLECTOR_END_INSTR(LoadTrue)
+
+ COLLECTOR_BEGIN_INSTR(LoadFalse)
+ COLLECTOR_END_INSTR(LoadFalse)
+
+ COLLECTOR_BEGIN_INSTR(LoadUndefined)
+ COLLECTOR_END_INSTR(LoadUndefined)
+
+ COLLECTOR_BEGIN_INSTR(LoadInt)
+ COLLECTOR_END_INSTR(LoadInt)
+
+ COLLECTOR_BEGIN_INSTR(MoveConst)
+ COLLECTOR_END_INSTR(MoveConst)
+
+ COLLECTOR_BEGIN_INSTR(LoadLocal)
+ COLLECTOR_END_INSTR(LoadLocal)
+
+ COLLECTOR_BEGIN_INSTR(StoreLocal)
+ COLLECTOR_END_INSTR(StoreLocal)
+
+ COLLECTOR_BEGIN_INSTR(LoadScopedLocal)
+ COLLECTOR_END_INSTR(LoadScopedLocal)
+
+ COLLECTOR_BEGIN_INSTR(StoreScopedLocal)
+ COLLECTOR_END_INSTR(StoreScopedLocal)
+
+ COLLECTOR_BEGIN_INSTR(LoadRuntimeString)
+ COLLECTOR_END_INSTR(LoadRuntimeString)
+
+ COLLECTOR_BEGIN_INSTR(MoveRegExp)
+ COLLECTOR_END_INSTR(MoveRegExp)
+
+ COLLECTOR_BEGIN_INSTR(LoadClosure)
+ COLLECTOR_END_INSTR(LoadClosure)
+
+ COLLECTOR_BEGIN_INSTR(LoadName)
+ COLLECTOR_END_INSTR(LoadName)
+
+ COLLECTOR_BEGIN_INSTR(LoadGlobalLookup)
+ COLLECTOR_END_INSTR(LoadGlobalLookup)
+
+ COLLECTOR_BEGIN_INSTR(StoreNameSloppy)
+ COLLECTOR_END_INSTR(StoreNameSloppy)
+
+ COLLECTOR_BEGIN_INSTR(StoreNameStrict)
+ COLLECTOR_END_INSTR(StoreNameStrict)
+
+ COLLECTOR_BEGIN_INSTR(LoadElement)
+ COLLECTOR_END_INSTR(LoadElement)
+
+ COLLECTOR_BEGIN_INSTR(StoreElement)
+ COLLECTOR_END_INSTR(StoreElement)
+
+ COLLECTOR_BEGIN_INSTR(LoadProperty)
+ COLLECTOR_END_INSTR(LoadProperty)
+
+ COLLECTOR_BEGIN_INSTR(GetLookup)
+ COLLECTOR_END_INSTR(GetLookup)
+
+ COLLECTOR_BEGIN_INSTR(StoreProperty)
+ COLLECTOR_END_INSTR(StoreProperty)
+
+ COLLECTOR_BEGIN_INSTR(SetLookup)
+ COLLECTOR_END_INSTR(SetLookup)
+
+ COLLECTOR_BEGIN_INSTR(LoadSuperProperty)
+ COLLECTOR_END_INSTR(LoadSuperProperty)
+
+ COLLECTOR_BEGIN_INSTR(StoreSuperProperty)
+ COLLECTOR_END_INSTR(StoreSuperProperty)
+
+ COLLECTOR_BEGIN_INSTR(StoreScopeObjectProperty)
+ COLLECTOR_END_INSTR(StoreScopeObjectProperty)
+
+ COLLECTOR_BEGIN_INSTR(LoadScopeObjectProperty)
+ COLLECTOR_END_INSTR(LoadScopeObjectProperty)
+
+ COLLECTOR_BEGIN_INSTR(StoreContextObjectProperty)
+ COLLECTOR_END_INSTR(StoreContextObjectProperty)
+
+ COLLECTOR_BEGIN_INSTR(LoadContextObjectProperty)
+ COLLECTOR_END_INSTR(LoadContextObjectProperty)
+
+ COLLECTOR_BEGIN_INSTR(LoadIdObject)
+ COLLECTOR_END_INSTR(LoadIdObject)
+
+ COLLECTOR_BEGIN_INSTR(Yield)
+ COLLECTOR_END_INSTR(Yield)
+
+ COLLECTOR_BEGIN_INSTR(Resume)
+ COLLECTOR_END_INSTR(Resume)
+
+ COLLECTOR_BEGIN_INSTR(CallValue)
+ COLLECTOR_END_INSTR(CallValue)
+
+ COLLECTOR_BEGIN_INSTR(CallProperty)
+ COLLECTOR_END_INSTR(CallProperty)
+
+ COLLECTOR_BEGIN_INSTR(CallPropertyLookup)
+ COLLECTOR_END_INSTR(CallPropertyLookup)
+
+ COLLECTOR_BEGIN_INSTR(CallElement)
+ COLLECTOR_END_INSTR(CallElement)
+
+ COLLECTOR_BEGIN_INSTR(CallName)
+ COLLECTOR_END_INSTR(CallName)
+
+ COLLECTOR_BEGIN_INSTR(CallPossiblyDirectEval)
+ COLLECTOR_END_INSTR(CallPossiblyDirectEval)
+
+ COLLECTOR_BEGIN_INSTR(CallGlobalLookup)
+ COLLECTOR_END_INSTR(CallGlobalLookup)
+
+ COLLECTOR_BEGIN_INSTR(CallScopeObjectProperty)
+ COLLECTOR_END_INSTR(CallScopeObjectProperty)
+
+ COLLECTOR_BEGIN_INSTR(CallContextObjectProperty)
+ COLLECTOR_END_INSTR(CallContextObjectProperty)
+
+ COLLECTOR_BEGIN_INSTR(CallWithSpread)
+ COLLECTOR_END_INSTR(CallWithSpread)
+
+ COLLECTOR_BEGIN_INSTR(Construct)
+ COLLECTOR_END_INSTR(Construct)
+
+ COLLECTOR_BEGIN_INSTR(ConstructWithSpread)
+ COLLECTOR_END_INSTR(ConstructWithSpread)
+
+ COLLECTOR_BEGIN_INSTR(SetUnwindHandler)
+ addLabel(code - start + offset);
+ COLLECTOR_END_INSTR(SetUnwindHandler)
+
+ COLLECTOR_BEGIN_INSTR(UnwindDispatch)
+ COLLECTOR_END_INSTR(UnwindDispatch)
+
+ COLLECTOR_BEGIN_INSTR(UnwindToLabel)
+ addLabel(code - start + offset);
+ COLLECTOR_END_INSTR(UnwindToLabel)
+
+ COLLECTOR_BEGIN_INSTR(ThrowException)
+ COLLECTOR_END_INSTR(ThrowException)
+
+ COLLECTOR_BEGIN_INSTR(GetException)
+ COLLECTOR_END_INSTR(HasException)
+
+ COLLECTOR_BEGIN_INSTR(SetException)
+ COLLECTOR_END_INSTR(SetExceptionFlag)
+
+ COLLECTOR_BEGIN_INSTR(CreateCallContext)
+ COLLECTOR_END_INSTR(CreateCallContext)
+
+ COLLECTOR_BEGIN_INSTR(PushCatchContext)
+ COLLECTOR_END_INSTR(PushCatchContext)
+
+ COLLECTOR_BEGIN_INSTR(PushWithContext)
+ COLLECTOR_END_INSTR(PushWithContext)
+
+ COLLECTOR_BEGIN_INSTR(PushBlockContext)
+ COLLECTOR_END_INSTR(PushBlockContext)
+
+ COLLECTOR_BEGIN_INSTR(CloneBlockContext)
+ COLLECTOR_END_INSTR(CloneBlockContext)
+
+ COLLECTOR_BEGIN_INSTR(PushScriptContext)
+ COLLECTOR_END_INSTR(PushScriptContext)
+
+ COLLECTOR_BEGIN_INSTR(PopScriptContext)
+ COLLECTOR_END_INSTR(PopScriptContext)
+
+ COLLECTOR_BEGIN_INSTR(PopContext)
+ COLLECTOR_END_INSTR(PopContext)
+
+ COLLECTOR_BEGIN_INSTR(GetIterator)
+ COLLECTOR_END_INSTR(GetIterator)
+
+ COLLECTOR_BEGIN_INSTR(IteratorNext)
+ COLLECTOR_END_INSTR(IteratorNext)
+
+ COLLECTOR_BEGIN_INSTR(IteratorClose)
+ COLLECTOR_END_INSTR(IteratorClose)
+
+ COLLECTOR_BEGIN_INSTR(DestructureRestElement)
+ COLLECTOR_END_INSTR(DestructureRestElement)
+
+ COLLECTOR_BEGIN_INSTR(DeleteProperty)
+ COLLECTOR_END_INSTR(DeleteProperty)
+
+ COLLECTOR_BEGIN_INSTR(DeleteName)
+ COLLECTOR_END_INSTR(DeleteName)
+
+ COLLECTOR_BEGIN_INSTR(TypeofName)
+ COLLECTOR_END_INSTR(TypeofName)
+
+ COLLECTOR_BEGIN_INSTR(TypeofValue)
+ COLLECTOR_END_INSTR(TypeofValue)
+
+ COLLECTOR_BEGIN_INSTR(DeclareVar)
+ COLLECTOR_END_INSTR(DeclareVar)
+
+ COLLECTOR_BEGIN_INSTR(DefineArray)
+ COLLECTOR_END_INSTR(DefineArray)
+
+ COLLECTOR_BEGIN_INSTR(DefineObjectLiteral)
+ COLLECTOR_END_INSTR(DefineObjectLiteral)
+
+ COLLECTOR_BEGIN_INSTR(CreateClass)
+ COLLECTOR_END_INSTR(CreateClass)
+
+ COLLECTOR_BEGIN_INSTR(CreateMappedArgumentsObject)
+ COLLECTOR_END_INSTR(CreateMappedArgumentsObject)
+
+ COLLECTOR_BEGIN_INSTR(CreateUnmappedArgumentsObject)
+ COLLECTOR_END_INSTR(CreateUnmappedArgumentsObject)
+
+ COLLECTOR_BEGIN_INSTR(CreateRestParameter)
+ COLLECTOR_END_INSTR(CreateRestParameter)
+
+ COLLECTOR_BEGIN_INSTR(ConvertThisToObject)
+ COLLECTOR_END_INSTR(ConvertThisToObject)
+
+ COLLECTOR_BEGIN_INSTR(LoadSuperConstructor)
+ COLLECTOR_END_INSTR(LoadSuperConstructor)
+
+ COLLECTOR_BEGIN_INSTR(ToObject)
+ COLLECTOR_END_INSTR(ToObject)
+
+ COLLECTOR_BEGIN_INSTR(Jump)
+ addLabel(code - start + offset);
+ COLLECTOR_END_INSTR(Jump)
+
+ COLLECTOR_BEGIN_INSTR(JumpTrue)
+ addLabel(code - start + offset);
+ COLLECTOR_END_INSTR(JumpTrue)
+
+ COLLECTOR_BEGIN_INSTR(JumpFalse)
+ addLabel(code - start + offset);
+ COLLECTOR_END_INSTR(JumpFalse)
+
+ COLLECTOR_BEGIN_INSTR(JumpNoException)
+ addLabel(code - start + offset);
+ COLLECTOR_END_INSTR(JumpNoException)
+
+ COLLECTOR_BEGIN_INSTR(JumpNotUndefined)
+ addLabel(code - start + offset);
+ COLLECTOR_END_INSTR(JumpNotUndefined)
+
+ COLLECTOR_BEGIN_INSTR(CmpEqNull)
+ COLLECTOR_END_INSTR(CmpEqNull)
+
+ COLLECTOR_BEGIN_INSTR(CmpNeNull)
+ COLLECTOR_END_INSTR(CmpNeNull)
+
+ COLLECTOR_BEGIN_INSTR(CmpEqInt)
+ COLLECTOR_END_INSTR(CmpEq)
+
+ COLLECTOR_BEGIN_INSTR(CmpNeInt)
+ COLLECTOR_END_INSTR(CmpNeInt)
+
+ COLLECTOR_BEGIN_INSTR(CmpEq)
+ COLLECTOR_END_INSTR(CmpEq)
+
+ COLLECTOR_BEGIN_INSTR(CmpNe)
+ COLLECTOR_END_INSTR(CmpNe)
+
+ COLLECTOR_BEGIN_INSTR(CmpGt)
+ COLLECTOR_END_INSTR(CmpGt)
+
+ COLLECTOR_BEGIN_INSTR(CmpGe)
+ COLLECTOR_END_INSTR(CmpGe)
+
+ COLLECTOR_BEGIN_INSTR(CmpLt)
+ COLLECTOR_END_INSTR(CmpLt)
+
+ COLLECTOR_BEGIN_INSTR(CmpLe)
+ COLLECTOR_END_INSTR(CmpLe)
+
+ COLLECTOR_BEGIN_INSTR(CmpStrictEqual)
+ COLLECTOR_END_INSTR(CmpStrictEqual)
+
+ COLLECTOR_BEGIN_INSTR(CmpStrictNotEqual)
+ COLLECTOR_END_INSTR(CmpStrictNotEqual)
+
+ COLLECTOR_BEGIN_INSTR(CmpIn)
+ COLLECTOR_END_INSTR(CmpIn)
+
+ COLLECTOR_BEGIN_INSTR(CmpInstanceOf)
+ COLLECTOR_END_INSTR(CmpInstanceOf)
+
+ COLLECTOR_BEGIN_INSTR(UNot)
+ COLLECTOR_END_INSTR(UNot)
+
+ COLLECTOR_BEGIN_INSTR(UPlus)
+ COLLECTOR_END_INSTR(UPlus)
+
+ COLLECTOR_BEGIN_INSTR(UMinus)
+ COLLECTOR_END_INSTR(UMinus)
+
+ COLLECTOR_BEGIN_INSTR(UCompl)
+ COLLECTOR_END_INSTR(UCompl)
+
+ COLLECTOR_BEGIN_INSTR(Increment)
+ COLLECTOR_END_INSTR(PreIncrement)
+
+ COLLECTOR_BEGIN_INSTR(Decrement)
+ COLLECTOR_END_INSTR(PreDecrement)
+
+ COLLECTOR_BEGIN_INSTR(Add)
+ COLLECTOR_END_INSTR(Add)
+
+ COLLECTOR_BEGIN_INSTR(BitAnd)
+ COLLECTOR_END_INSTR(BitAnd)
+
+ COLLECTOR_BEGIN_INSTR(BitOr)
+ COLLECTOR_END_INSTR(BitOr)
+
+ COLLECTOR_BEGIN_INSTR(BitXor)
+ COLLECTOR_END_INSTR(BitXor)
+
+ COLLECTOR_BEGIN_INSTR(UShr)
+ COLLECTOR_END_INSTR(UShr)
+
+ COLLECTOR_BEGIN_INSTR(Shr)
+ COLLECTOR_END_INSTR(Shr)
+
+ COLLECTOR_BEGIN_INSTR(Shl)
+ COLLECTOR_END_INSTR(Shl)
+
+ COLLECTOR_BEGIN_INSTR(BitAndConst)
+ COLLECTOR_END_INSTR(BitAndConst)
+
+ COLLECTOR_BEGIN_INSTR(BitOrConst)
+ COLLECTOR_END_INSTR(BitOr)
+
+ COLLECTOR_BEGIN_INSTR(BitXorConst)
+ COLLECTOR_END_INSTR(BitXor)
+
+ COLLECTOR_BEGIN_INSTR(UShrConst)
+ COLLECTOR_END_INSTR(UShrConst)
+
+ COLLECTOR_BEGIN_INSTR(ShrConst)
+ COLLECTOR_END_INSTR(ShrConst)
+
+ COLLECTOR_BEGIN_INSTR(ShlConst)
+ COLLECTOR_END_INSTR(ShlConst)
+
+ COLLECTOR_BEGIN_INSTR(Exp)
+ COLLECTOR_END_INSTR(Exp)
+
+ COLLECTOR_BEGIN_INSTR(Mul)
+ COLLECTOR_END_INSTR(Mul)
+
+ COLLECTOR_BEGIN_INSTR(Div)
+ COLLECTOR_END_INSTR(Div)
+
+ COLLECTOR_BEGIN_INSTR(Mod)
+ COLLECTOR_END_INSTR(Mod)
+
+ COLLECTOR_BEGIN_INSTR(Sub)
+ COLLECTOR_END_INSTR(Sub)
+
+ COLLECTOR_BEGIN_INSTR(Ret)
+ COLLECTOR_END_INSTR(Ret)
+
+#ifndef QT_NO_QML_DEBUGGER
+ COLLECTOR_BEGIN_INSTR(Debug)
+ COLLECTOR_END_INSTR(Debug)
+#endif // QT_NO_QML_DEBUGGER
+
+ COLLECTOR_BEGIN_INSTR(LoadQmlContext)
+ COLLECTOR_END_INSTR(LoadQmlContext)
+
+ COLLECTOR_BEGIN_INSTR(LoadQmlImportedScripts)
+ COLLECTOR_END_INSTR(LoadQmlImportedScripts)
+ }
+
+ return labels;
+}
+
+#undef COLLECTOR_BEGIN_INSTR
+#undef COLLECTOR_END_INSTR
diff --git a/src/qml/compiler/qv4bytecodehandler_p.h b/src/qml/compiler/qv4bytecodehandler_p.h
new file mode 100644
index 0000000000..5f1121306f
--- /dev/null
+++ b/src/qml/compiler/qv4bytecodehandler_p.h
@@ -0,0 +1,113 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 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$
+**
+****************************************************************************/
+
+#ifndef QV4BYTECODEHANDLER_P_H
+#define QV4BYTECODEHANDLER_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+#include <private/qv4instr_moth_p.h>
+
+QT_BEGIN_NAMESPACE
+
+namespace QV4 {
+namespace Moth {
+
+#define BYTECODE_HANDLER_DEFINE_ARGS(nargs, ...) \
+ MOTH_EXPAND_FOR_MSVC(BYTECODE_HANDLER_DEFINE_ARGS##nargs(__VA_ARGS__))
+
+#define BYTECODE_HANDLER_DEFINE_ARGS0()
+#define BYTECODE_HANDLER_DEFINE_ARGS1(arg) \
+ int arg
+#define BYTECODE_HANDLER_DEFINE_ARGS2(arg1, arg2) \
+ int arg1, \
+ int arg2
+#define BYTECODE_HANDLER_DEFINE_ARGS3(arg1, arg2, arg3) \
+ int arg1, \
+ int arg2, \
+ int arg3
+#define BYTECODE_HANDLER_DEFINE_ARGS4(arg1, arg2, arg3, arg4) \
+ int arg1, \
+ int arg2, \
+ int arg3, \
+ int arg4
+
+#define BYTECODE_HANDLER_DEFINE_VIRTUAL_BYTECODE_HANDLER_INSTRUCTION(name, nargs, ...) \
+ virtual void generate_##name( \
+ BYTECODE_HANDLER_DEFINE_ARGS(nargs, __VA_ARGS__) \
+ ) = 0;
+
+#define BYTECODE_HANDLER_DEFINE_VIRTUAL_BYTECODE_HANDLER(instr) \
+ INSTR_##instr(BYTECODE_HANDLER_DEFINE_VIRTUAL_BYTECODE_HANDLER)
+
+class ByteCodeHandler
+{
+public:
+ virtual ~ByteCodeHandler();
+
+ void decode(const char *code, uint len);
+
+ int instructionOffset() const { return _offset; }
+
+ static std::vector<int> collectLabelsInBytecode(const char *code, uint len);
+
+protected:
+ FOR_EACH_MOTH_INSTR(BYTECODE_HANDLER_DEFINE_VIRTUAL_BYTECODE_HANDLER)
+
+ virtual void startInstruction(Moth::Instr::Type instr) = 0;
+ virtual void endInstruction(Moth::Instr::Type instr) = 0;
+
+private:
+ int _offset = 0;
+};
+
+} // Moth namespace
+} // QV4 namespace
+
+QT_END_NAMESPACE
+
+#endif // QV4BYTECODEHANDLER_P_H
diff --git a/src/qml/compiler/qv4codegen.cpp b/src/qml/compiler/qv4codegen.cpp
index e831fd48f5..4b766accbd 100644
--- a/src/qml/compiler/qv4codegen.cpp
+++ b/src/qml/compiler/qv4codegen.cpp
@@ -58,6 +58,8 @@
#include <cmath>
#include <iostream>
+static const bool disable_lookups = false;
+
#ifdef CONST
#undef CONST
#endif
@@ -77,8 +79,6 @@ static inline void setJumpOutLocation(QV4::Moth::BytecodeGenerator *bytecodeGene
case Statement::Kind_ForEachStatement:
case Statement::Kind_ForStatement:
case Statement::Kind_IfStatement:
- case Statement::Kind_LocalForEachStatement:
- case Statement::Kind_LocalForStatement:
case Statement::Kind_WhileStatement:
bytecodeGenerator->setLocation(fallback);
break;
@@ -90,7 +90,7 @@ static inline void setJumpOutLocation(QV4::Moth::BytecodeGenerator *bytecodeGene
Codegen::Codegen(QV4::Compiler::JSUnitGenerator *jsUnitGenerator, bool strict)
: _module(nullptr)
- , _returnAddress(0)
+ , _returnAddress(-1)
, _context(nullptr)
, _labelledStatement(nullptr)
, jsUnitGenerator(jsUnitGenerator)
@@ -106,7 +106,7 @@ void Codegen::generateFromProgram(const QString &fileName,
const QString &sourceCode,
Program *node,
Module *module,
- CompilationMode mode)
+ ContextType contextType)
{
Q_ASSERT(node);
@@ -117,10 +117,13 @@ void Codegen::generateFromProgram(const QString &fileName,
_module->fileName = fileName;
_module->finalUrl = finalUrl;
- ScanFunctions scan(this, sourceCode, mode);
+ ScanFunctions scan(this, sourceCode, contextType);
scan(node);
- defineFunction(QStringLiteral("%entry"), node, nullptr, node->elements);
+ if (hasError)
+ return;
+
+ defineFunction(QStringLiteral("%entry"), node, nullptr, node->statements);
}
void Codegen::enterContext(Node *node)
@@ -132,19 +135,24 @@ void Codegen::enterContext(Node *node)
int Codegen::leaveContext()
{
Q_ASSERT(_context);
- Q_ASSERT(!_context->controlFlow);
int functionIndex = _context->functionIndex;
_context = _context->parent;
return functionIndex;
}
+Context *Codegen::enterBlock(Node *node)
+{
+ enterContext(node);
+ return _context;
+}
+
Codegen::Reference Codegen::unop(UnaryOperation op, const Reference &expr)
{
if (hasError)
return _expr.result();
#ifndef V4_BOOTSTRAP
- if (expr.isConst()) {
+ if (expr.isConstant()) {
auto v = Value::fromReturnedValue(expr.constant);
if (v.isNumber()) {
switch (op) {
@@ -267,9 +275,9 @@ void Codegen::statement(Statement *ast)
bytecodeGenerator->setLocation(ast->firstSourceLocation());
VolatileMemoryLocations vLocs = scanVolatileMemoryLocations(ast);
- qSwap(_volataleMemoryLocations, vLocs);
+ qSwap(_volatileMemoryLocations, vLocs);
accept(ast);
- qSwap(_volataleMemoryLocations, vLocs);
+ qSwap(_volatileMemoryLocations, vLocs);
}
void Codegen::statement(ExpressionNode *ast)
@@ -282,11 +290,11 @@ void Codegen::statement(ExpressionNode *ast)
Result r(nx);
qSwap(_expr, r);
VolatileMemoryLocations vLocs = scanVolatileMemoryLocations(ast);
- qSwap(_volataleMemoryLocations, vLocs);
+ qSwap(_volatileMemoryLocations, vLocs);
accept(ast);
- qSwap(_volataleMemoryLocations, vLocs);
+ qSwap(_volatileMemoryLocations, vLocs);
qSwap(_expr, r);
if (hasError)
@@ -337,60 +345,103 @@ Codegen::Reference Codegen::expression(ExpressionNode *ast)
return r.result();
}
-Codegen::Result Codegen::sourceElement(SourceElement *ast)
+void Codegen::program(Program *ast)
{
- Result r(nx);
if (ast) {
- qSwap(_expr, r);
- accept(ast);
- qSwap(_expr, r);
+ statementList(ast->statements);
}
- return r;
}
-void Codegen::functionBody(FunctionBody *ast)
-{
- if (ast)
- sourceElements(ast->elements);
-}
+enum class CompletionState {
+ Empty,
+ EmptyAbrupt,
+ NonEmpty
+};
-void Codegen::program(Program *ast)
-{
- if (ast) {
- sourceElements(ast->elements);
+static CompletionState completionState(StatementList *list)
+{
+ for (StatementList *it = list; it; it = it->next) {
+ if (it->statement->kind == Statement::Kind_BreakStatement ||
+ it->statement->kind == Statement::Kind_ContinueStatement)
+ return CompletionState::EmptyAbrupt;
+ if (it->statement->kind == Statement::Kind_EmptyStatement ||
+ it->statement->kind == Statement::Kind_VariableDeclaration ||
+ it->statement->kind == Statement::Kind_FunctionDeclaration)
+ continue;
+ if (it->statement->kind == Statement::Kind_Block) {
+ CompletionState subState = completionState(static_cast<Block *>(it->statement)->statements);
+ if (subState != CompletionState::Empty)
+ return subState;
+ continue;
+ }
+ return CompletionState::NonEmpty;
}
+ return CompletionState::Empty;
}
-void Codegen::sourceElements(SourceElements *ast)
+static Node *completionStatement(StatementList *list)
{
- bool _requiresReturnValue = false;
- qSwap(_requiresReturnValue, requiresReturnValue);
- for (SourceElements *it = ast; it; it = it->next) {
- if (!it->next)
- qSwap(_requiresReturnValue, requiresReturnValue);
- sourceElement(it->element);
- if (hasError)
- return;
- if (StatementSourceElement *sse = AST::cast<StatementSourceElement *>(it->element)) {
- if (AST::cast<ThrowStatement *>(sse->statement) ||
- AST::cast<ReturnStatement *>(sse->statement))
- return;
+ Node *completionStatement = nullptr;
+ for (StatementList *it = list; it; it = it->next) {
+ if (it->statement->kind == Statement::Kind_BreakStatement ||
+ it->statement->kind == Statement::Kind_ContinueStatement)
+ return completionStatement;
+ if (it->statement->kind == Statement::Kind_ThrowStatement ||
+ it->statement->kind == Statement::Kind_ReturnStatement)
+ return it->statement;
+ if (it->statement->kind == Statement::Kind_EmptyStatement ||
+ it->statement->kind == Statement::Kind_VariableStatement ||
+ it->statement->kind == Statement::Kind_FunctionDeclaration)
+ continue;
+ if (it->statement->kind == Statement::Kind_Block) {
+ CompletionState state = completionState(static_cast<Block *>(it->statement)->statements);
+ switch (state) {
+ case CompletionState::Empty:
+ continue;
+ case CompletionState::EmptyAbrupt:
+ return it->statement;
+ case CompletionState::NonEmpty:
+ break;
+ }
}
+ completionStatement = it->statement;
}
+ return completionStatement;
}
void Codegen::statementList(StatementList *ast)
{
+ if (!ast)
+ return;
+
bool _requiresReturnValue = requiresReturnValue;
- requiresReturnValue = false;
- for (StatementList *it = ast; it; it = it->next) {
- if (!it->next ||
- it->next->statement->kind == Statement::Kind_BreakStatement ||
- it->next->statement->kind == Statement::Kind_ContinueStatement ||
- it->next->statement->kind == Statement::Kind_ReturnStatement)
- requiresReturnValue = _requiresReturnValue;
- statement(it->statement);
+ // ### the next line is pessimizing a bit too much, as there are many cases, where the complietion from the break
+ // statement will not be used, but it's at least spec compliant
+ if (!controlFlow || !controlFlow->hasLoop())
requiresReturnValue = false;
+
+ Node *needsCompletion = nullptr;
+
+ if (_requiresReturnValue && !requiresReturnValue)
+ needsCompletion = completionStatement(ast);
+
+ if (requiresReturnValue && !needsCompletion && !insideSwitch) {
+ // break or continue is the first real statement, set the return value to undefined
+ Reference::fromConst(this, Encode::undefined()).storeOnStack(_returnAddress);
+ }
+
+ bool _insideSwitch = insideSwitch;
+ insideSwitch = false;
+
+ for (StatementList *it = ast; it; it = it->next) {
+ if (it->statement == needsCompletion)
+ requiresReturnValue = true;
+ if (Statement *s = it->statement->statementCast())
+ statement(s);
+ else
+ statement(static_cast<ExpressionNode *>(it->statement));
+ if (it->statement == needsCompletion)
+ requiresReturnValue = false;
if (it->statement->kind == Statement::Kind_ThrowStatement ||
it->statement->kind == Statement::Kind_BreakStatement ||
it->statement->kind == Statement::Kind_ContinueStatement ||
@@ -399,22 +450,16 @@ void Codegen::statementList(StatementList *ast)
break;
}
requiresReturnValue = _requiresReturnValue;
+ insideSwitch = _insideSwitch;
}
-void Codegen::variableDeclaration(VariableDeclaration *ast)
+void Codegen::variableDeclaration(PatternElement *ast)
{
RegisterScope scope(this);
- if (!ast->expression)
+ if (!ast->initializer)
return;
- Reference rhs = expression(ast->expression);
- if (hasError)
- return;
-
- Reference lhs = referenceForName(ast->name.toString(), true);
- //### if lhs is a temp, this won't generate a temp-to-temp move. Same for when rhs is a const
- rhs.loadInAccumulator();
- lhs.storeConsumeAccumulator();
+ initializeAndDestructureBindingElement(ast, Reference(), /*isDefinition*/ true);
}
void Codegen::variableDeclarationList(VariableDeclarationList *ast)
@@ -424,6 +469,200 @@ void Codegen::variableDeclarationList(VariableDeclarationList *ast)
}
}
+Codegen::Reference Codegen::targetForPatternElement(AST::PatternElement *p)
+{
+ if (!p->bindingIdentifier.isNull())
+ return referenceForName(p->bindingIdentifier.toString(), true);
+ if (!p->bindingTarget || p->destructuringPattern())
+ return Codegen::Reference::fromStackSlot(this);
+ Reference lhs = expression(p->bindingTarget);
+ if (hasError)
+ return lhs;
+ lhs = lhs.asLValue();
+ return lhs;
+}
+
+void Codegen::initializeAndDestructureBindingElement(AST::PatternElement *e, const Reference &base, bool isDefinition)
+{
+ Q_ASSERT(e->type == AST::PatternElement::Binding || e->type == AST::PatternElement::RestElement);
+ RegisterScope scope(this);
+ Reference baseRef = (base.isAccumulator()) ? base.storeOnStack() : base;
+ Reference varToStore = targetForPatternElement(e);
+ if (isDefinition)
+ varToStore.isReferenceToConst = false;
+ if (hasError)
+ return;
+
+ if (e->initializer) {
+ if (!baseRef.isValid()) {
+ // assignment
+ Reference expr = expression(e->initializer);
+ if (hasError)
+ return;
+ expr.loadInAccumulator();
+ varToStore.storeConsumeAccumulator();
+ } else if (baseRef == varToStore) {
+ baseRef.loadInAccumulator();
+ BytecodeGenerator::Jump jump = bytecodeGenerator->jumpNotUndefined();
+ Reference expr = expression(e->initializer);
+ if (hasError) {
+ jump.link();
+ return;
+ }
+ expr.loadInAccumulator();
+ varToStore.storeConsumeAccumulator();
+ jump.link();
+ } else {
+ baseRef.loadInAccumulator();
+ BytecodeGenerator::Jump jump = bytecodeGenerator->jumpNotUndefined();
+ Reference expr = expression(e->initializer);
+ if (hasError) {
+ jump.link();
+ return;
+ }
+ expr.loadInAccumulator();
+ jump.link();
+ varToStore.storeConsumeAccumulator();
+ }
+ } else if (baseRef != varToStore && baseRef.isValid()) {
+ baseRef.loadInAccumulator();
+ varToStore.storeConsumeAccumulator();
+ }
+ Pattern *p = e->destructuringPattern();
+ if (!p)
+ return;
+
+ if (!varToStore.isStackSlot())
+ varToStore = varToStore.storeOnStack();
+ if (PatternElementList *l = e->elementList()) {
+ destructureElementList(varToStore, l, isDefinition);
+ } else if (PatternPropertyList *p = e->propertyList()) {
+ destructurePropertyList(varToStore, p, isDefinition);
+ } else if (e->bindingTarget) {
+ // empty binding pattern. For spec compatibility, try to coerce the argument to an object
+ varToStore.loadInAccumulator();
+ Instruction::ToObject toObject;
+ bytecodeGenerator->addInstruction(toObject);
+ return;
+ }
+}
+
+Codegen::Reference Codegen::referenceForPropertyName(const Codegen::Reference &object, AST::PropertyName *name)
+{
+ AST::ComputedPropertyName *cname = AST::cast<AST::ComputedPropertyName *>(name);
+ Reference property;
+ if (cname) {
+ Reference computedName = expression(cname->expression);
+ if (hasError)
+ return Reference();
+ computedName = computedName.storeOnStack();
+ property = Reference::fromSubscript(object, computedName).asLValue();
+ } else {
+ QString propertyName = name->asString();
+ property = Reference::fromMember(object, propertyName);
+ }
+ return property;
+}
+
+void Codegen::destructurePropertyList(const Codegen::Reference &object, PatternPropertyList *bindingList, bool isDefinition)
+{
+ RegisterScope scope(this);
+
+ for (PatternPropertyList *it = bindingList; it; it = it->next) {
+ PatternProperty *p = it->property;
+ RegisterScope scope(this);
+ Reference property = referenceForPropertyName(object, p->name);
+ if (hasError)
+ return;
+ initializeAndDestructureBindingElement(p, property, isDefinition);
+ if (hasError)
+ return;
+ }
+}
+
+void Codegen::destructureElementList(const Codegen::Reference &array, PatternElementList *bindingList, bool isDefinition)
+{
+ RegisterScope scope(this);
+
+ Reference iterator = Reference::fromStackSlot(this);
+ Reference iteratorValue = Reference::fromStackSlot(this);
+ Reference iteratorDone = Reference::fromStackSlot(this);
+
+ array.loadInAccumulator();
+ Instruction::GetIterator iteratorObjInstr;
+ iteratorObjInstr.iterator = 1; // ForEachType::Of
+ bytecodeGenerator->addInstruction(iteratorObjInstr);
+ iterator.storeConsumeAccumulator();
+
+ bool hadNext = false;
+ bool hasRest = false;
+
+ BytecodeGenerator::Label end = bytecodeGenerator->newLabel();
+
+ for (PatternElementList *p = bindingList; p; p = p->next) {
+ PatternElement *e = p->element;
+ for (Elision *elision = p->elision; elision; elision = elision->next) {
+ iterator.loadInAccumulator();
+ Instruction::IteratorNext next;
+ next.value = iteratorValue.stackSlot();
+ bytecodeGenerator->addInstruction(next);
+ hadNext = true;
+ bool last = !elision->next && !e && !p->next;
+ if (last)
+ iteratorDone.storeConsumeAccumulator();
+ }
+
+ if (!e)
+ continue;
+
+ hadNext = true;
+ RegisterScope scope(this);
+ iterator.loadInAccumulator();
+
+ if (e->type == PatternElement::RestElement) {
+ bytecodeGenerator->addInstruction(Instruction::DestructureRestElement());
+ initializeAndDestructureBindingElement(e, Reference::fromAccumulator(this), isDefinition);
+ hasRest = true;
+ } else {
+ Instruction::IteratorNext next;
+ next.value = iteratorValue.stackSlot();
+ bytecodeGenerator->addInstruction(next);
+ bool last = !p->next || (!p->next->elision && !p->next->element);
+ if (last)
+ iteratorDone.storeConsumeAccumulator();
+ initializeAndDestructureBindingElement(e, iteratorValue, isDefinition);
+ if (hasError) {
+ end.link();
+ return;
+ }
+ }
+ }
+
+ if (!hadNext) {
+ Reference::storeConstOnStack(this, Encode(false), iteratorDone.stackSlot());
+ }
+
+ if (!hasRest) {
+ iterator.loadInAccumulator();
+ Instruction::IteratorClose close;
+ close.done = iteratorDone.stackSlot();
+ bytecodeGenerator->addInstruction(close);
+ }
+
+ end.link();
+}
+
+void Codegen::destructurePattern(Pattern *p, const Reference &rhs)
+{
+ RegisterScope scope(this);
+ if (auto *o = AST::cast<ObjectPattern *>(p))
+ destructurePropertyList(rhs, o->properties);
+ else if (auto *a = AST::cast<ArrayPattern *>(p))
+ destructureElementList(rhs, a->elements);
+ else
+ Q_UNREACHABLE();
+}
+
bool Codegen::visit(ArgumentList *)
{
@@ -461,12 +700,6 @@ bool Codegen::visit(DefaultClause *)
return false;
}
-bool Codegen::visit(ElementList *)
-{
- Q_UNREACHABLE();
- return false;
-}
-
bool Codegen::visit(Elision *)
{
Q_UNREACHABLE();
@@ -485,37 +718,31 @@ bool Codegen::visit(FormalParameterList *)
return false;
}
-bool Codegen::visit(FunctionBody *)
-{
- Q_UNREACHABLE();
- return false;
-}
-
bool Codegen::visit(Program *)
{
Q_UNREACHABLE();
return false;
}
-bool Codegen::visit(PropertyAssignmentList *)
+bool Codegen::visit(PatternElement *)
{
Q_UNREACHABLE();
return false;
}
-bool Codegen::visit(PropertyNameAndValue *)
+bool Codegen::visit(PatternElementList *)
{
Q_UNREACHABLE();
return false;
}
-bool Codegen::visit(PropertyGetterSetter *)
+bool Codegen::visit(PatternProperty *)
{
Q_UNREACHABLE();
return false;
}
-bool Codegen::visit(SourceElements *)
+bool Codegen::visit(PatternPropertyList *)
{
Q_UNREACHABLE();
return false;
@@ -587,15 +814,121 @@ bool Codegen::visit(UiQualifiedPragmaId *)
return false;
}
-bool Codegen::visit(VariableDeclaration *)
+bool Codegen::visit(VariableDeclarationList *)
{
Q_UNREACHABLE();
return false;
}
-bool Codegen::visit(VariableDeclarationList *)
+bool Codegen::visit(ClassExpression *ast)
{
- Q_UNREACHABLE();
+ Compiler::Class jsClass;
+ jsClass.nameIndex = registerString(ast->name.toString());
+
+ ClassElementList *constructor = nullptr;
+ int nComputedNames = 0;
+ int nStaticComputedNames = 0;
+
+ RegisterScope scope(this);
+ ControlFlowBlock controlFlow(this, ast);
+
+ for (auto *member = ast->elements; member; member = member->next) {
+ PatternProperty *p = member->property;
+ FunctionExpression *f = p->initializer->asFunctionDefinition();
+ Q_ASSERT(f);
+ AST::ComputedPropertyName *cname = AST::cast<ComputedPropertyName *>(p->name);
+ if (cname) {
+ ++nComputedNames;
+ if (member->isStatic)
+ ++nStaticComputedNames;
+ }
+ QString name = p->name->asString();
+ uint nameIndex = cname ? UINT_MAX : registerString(name);
+ Compiler::Class::Method::Type type = Compiler::Class::Method::Regular;
+ if (p->type == PatternProperty::Getter)
+ type = Compiler::Class::Method::Getter;
+ else if (p->type == PatternProperty::Setter)
+ type = Compiler::Class::Method::Setter;
+ Compiler::Class::Method m{ nameIndex, type, static_cast<uint>(defineFunction(name, f, f->formals, f->body)) };
+
+ if (member->isStatic) {
+ if (name == QStringLiteral("prototype")) {
+ throwSyntaxError(ast->firstSourceLocation(), QLatin1String("Cannot declare a static method named 'prototype'."));
+ return false;
+ }
+ jsClass.staticMethods << m;
+ } else {
+ if (name == QStringLiteral("constructor")) {
+ if (constructor) {
+ throwSyntaxError(ast->firstSourceLocation(), QLatin1String("Cannot declare a multiple constructors in a class."));
+ return false;
+ }
+ if (m.type != Compiler::Class::Method::Regular) {
+ throwSyntaxError(ast->firstSourceLocation(), QLatin1String("Cannot declare a getter or setter named 'constructor'."));
+ return false;
+ }
+ constructor = member;
+ jsClass.constructorIndex = m.functionIndex;
+ continue;
+ }
+
+ jsClass.methods << m;
+ }
+ }
+
+ int classIndex = _module->classes.size();
+ _module->classes.append(jsClass);
+
+ Reference heritage = Reference::fromStackSlot(this);
+ if (ast->heritage) {
+ bytecodeGenerator->setLocation(ast->heritage->firstSourceLocation());
+ Reference r = expression(ast->heritage);
+ if (hasError)
+ return false;
+ r.storeOnStack(heritage.stackSlot());
+ } else {
+ Reference::fromConst(this, Primitive::emptyValue().asReturnedValue()).loadInAccumulator();
+ heritage.storeConsumeAccumulator();
+ }
+
+ int computedNames = nComputedNames ? bytecodeGenerator->newRegisterArray(nComputedNames) : 0;
+ int currentStaticName = computedNames;
+ int currentNonStaticName = computedNames + nStaticComputedNames;
+
+ for (auto *member = ast->elements; member; member = member->next) {
+ AST::ComputedPropertyName *cname = AST::cast<AST::ComputedPropertyName *>(member->property->name);
+ if (!cname)
+ continue;
+ RegisterScope scope(this);
+ bytecodeGenerator->setLocation(cname->firstSourceLocation());
+ Reference computedName = expression(cname->expression);
+ if (hasError)
+ return false;
+ computedName.storeOnStack(member->isStatic ? currentStaticName++ : currentNonStaticName++);
+ }
+
+ Instruction::CreateClass createClass;
+ createClass.classIndex = classIndex;
+ createClass.heritage = heritage.stackSlot();
+ createClass.computedNames = computedNames;
+
+ bytecodeGenerator->addInstruction(createClass);
+
+ if (!ast->name.isEmpty()) {
+ Reference ctor = referenceForName(ast->name.toString(), true);
+ ctor.isReferenceToConst = false; // this is the definition
+ (void) ctor.storeRetainAccumulator();
+ }
+
+ _expr.setResult(Reference::fromAccumulator(this));
+ return false;
+}
+
+bool Codegen::visit(ClassDeclaration *ast)
+{
+ Reference outerVar = referenceForName(ast->name.toString(), true);
+ visit(static_cast<ClassExpression *>(ast));
+ (void) outerVar.storeRetainAccumulator();
return false;
}
@@ -609,50 +942,160 @@ bool Codegen::visit(Expression *ast)
return false;
}
-bool Codegen::visit(ArrayLiteral *ast)
+bool Codegen::visit(ArrayPattern *ast)
{
if (hasError)
return false;
- RegisterScope scope(this);
+ PatternElementList *it = ast->elements;
int argc = 0;
- int args = -1;
- auto push = [this, &argc, &args](AST::ExpressionNode *arg) {
- int temp = bytecodeGenerator->newRegister();
- if (args == -1)
- args = temp;
- if (!arg) {
- auto c = Reference::fromConst(this, Primitive::emptyValue().asReturnedValue());
- (void) c.storeOnStack(temp);
- } else {
- RegisterScope scope(this);
- (void) expression(arg).storeOnStack(temp);
+ {
+ RegisterScope scope(this);
+
+ int args = -1;
+ auto push = [this, &argc, &args](AST::ExpressionNode *arg) {
+ int temp = bytecodeGenerator->newRegister();
+ if (args == -1)
+ args = temp;
+ if (!arg) {
+ auto c = Reference::fromConst(this, Primitive::emptyValue().asReturnedValue());
+ (void) c.storeOnStack(temp);
+ } else {
+ RegisterScope scope(this);
+ Reference r = expression(arg);
+ if (hasError)
+ return;
+ (void) r.storeOnStack(temp);
+ }
+ ++argc;
+ };
+
+ for (; it; it = it->next) {
+ PatternElement *e = it->element;
+ if (e && e->type == PatternElement::SpreadElement)
+ break;
+ for (Elision *elision = it->elision; elision; elision = elision->next)
+ push(nullptr);
+
+ if (!e)
+ continue;
+
+ push(e->initializer);
+ if (hasError)
+ return false;
}
- ++argc;
- };
- for (ElementList *it = ast->elements; it; it = it->next) {
+ if (args == -1) {
+ Q_ASSERT(argc == 0);
+ args = 0;
+ }
- for (Elision *elision = it->elision; elision; elision = elision->next)
- push(nullptr);
+ Instruction::DefineArray call;
+ call.argc = argc;
+ call.args = Moth::StackSlot::createRegister(args);
+ bytecodeGenerator->addInstruction(call);
+ }
- push(it->expression);
- if (hasError)
- return false;
+ if (!it) {
+ _expr.setResult(Reference::fromAccumulator(this));
+ return false;
}
- for (Elision *elision = ast->elision; elision; elision = elision->next)
- push(nullptr);
+ Q_ASSERT(it->element && it->element->type == PatternElement::SpreadElement);
- if (args == -1) {
- Q_ASSERT(argc == 0);
- args = 0;
+ RegisterScope scope(this);
+ Reference array = Reference::fromStackSlot(this);
+ array.storeConsumeAccumulator();
+ Reference index = Reference::storeConstOnStack(this, Encode(argc));
+
+ auto pushAccumulator = [&]() {
+ Reference slot = Reference::fromSubscript(array, index);
+ slot.storeConsumeAccumulator();
+
+ index.loadInAccumulator();
+ Instruction::Increment inc;
+ bytecodeGenerator->addInstruction(inc);
+ index.storeConsumeAccumulator();
+ };
+
+ while (it) {
+ for (Elision *elision = it->elision; elision; elision = elision->next) {
+ Reference::fromConst(this, Primitive::emptyValue().asReturnedValue()).loadInAccumulator();
+ pushAccumulator();
+ }
+
+ if (!it->element) {
+ it = it->next;
+ continue;
+ }
+
+ // handle spread element
+ if (it->element->type == PatternElement::SpreadElement) {
+ RegisterScope scope(this);
+
+ Reference iterator = Reference::fromStackSlot(this);
+ Reference lhsValue = Reference::fromStackSlot(this);
+
+ // There should be a temporal block, so that variables declared in lhs shadow outside vars.
+ // This block should define a temporal dead zone for those variables, which is not yet implemented.
+ {
+ RegisterScope innerScope(this);
+ Reference expr = expression(it->element->initializer);
+ if (hasError)
+ return false;
+
+ expr.loadInAccumulator();
+ Instruction::GetIterator iteratorObjInstr;
+ iteratorObjInstr.iterator = /*ForEachType::Of*/ 1;
+ bytecodeGenerator->addInstruction(iteratorObjInstr);
+ iterator.storeConsumeAccumulator();
+ }
+
+ BytecodeGenerator::Label in = bytecodeGenerator->newLabel();
+ BytecodeGenerator::Label end = bytecodeGenerator->newLabel();
+ BytecodeGenerator::Label done = bytecodeGenerator->newLabel();
+
+ {
+ ControlFlowLoop flow(this, &end, &in, /*requiresUnwind*/ true);
+ bytecodeGenerator->jump().link(in);
+
+ BytecodeGenerator::Label body = bytecodeGenerator->label();
+
+ lhsValue.loadInAccumulator();
+ pushAccumulator();
+
+ in.link();
+ iterator.loadInAccumulator();
+ Instruction::IteratorNext next;
+ next.value = lhsValue.stackSlot();
+ bytecodeGenerator->addInstruction(next);
+ bytecodeGenerator->addJumpInstruction(Instruction::JumpFalse()).link(body);
+ bytecodeGenerator->jump().link(done);
+ }
+
+ end.link();
+
+ Reference iteratorDone = Reference::fromConst(this, Encode(false)).storeOnStack();
+ iterator.loadInAccumulator();
+ Instruction::IteratorClose close;
+ close.done = iteratorDone.stackSlot();
+ bytecodeGenerator->addInstruction(close);
+
+ done.link();
+ } else {
+ RegisterScope innerScope(this);
+ Reference expr = expression(it->element->initializer);
+ if (hasError)
+ return false;
+
+ expr.loadInAccumulator();
+ pushAccumulator();
+ }
+
+ it = it->next;
}
- Instruction::DefineArray call;
- call.argc = argc;
- call.args = Moth::StackSlot::createRegister(args);
- bytecodeGenerator->addInstruction(call);
+ array.loadInAccumulator();
_expr.setResult(Reference::fromAccumulator(this));
return false;
@@ -666,7 +1109,14 @@ bool Codegen::visit(ArrayMemberExpression *ast)
Reference base = expression(ast->base);
if (hasError)
return false;
+ if (base.isSuper()) {
+ Reference index = expression(ast->expression).storeOnStack();
+ _expr.setResult(Reference::fromSuperProperty(index));
+ return false;
+ }
base = base.storeOnStack();
+ if (hasError)
+ return false;
if (AST::StringLiteral *str = AST::cast<AST::StringLiteral *>(ast->expression)) {
QString s = str->value.toString();
uint arrayIndex = QV4::String::toArrayIndex(s);
@@ -692,6 +1142,7 @@ static QSOperator::Op baseOp(int op)
case QSOperator::InplaceAdd: return QSOperator::Add;
case QSOperator::InplaceLeftShift: return QSOperator::LShift;
case QSOperator::InplaceMod: return QSOperator::Mod;
+ case QSOperator::InplaceExp: return QSOperator::Exp;
case QSOperator::InplaceMul: return QSOperator::Mul;
case QSOperator::InplaceOr: return QSOperator::BitOr;
case QSOperator::InplaceRightShift: return QSOperator::RShift;
@@ -764,19 +1215,21 @@ bool Codegen::visit(BinaryExpression *ast)
_expr.setResult(Reference::fromAccumulator(this));
}
return false;
- }
-
- Reference left = expression(ast->left);
- if (hasError)
- return false;
-
- switch (ast->op) {
- case QSOperator::Or:
- case QSOperator::And:
- Q_UNREACHABLE(); // handled separately above
- break;
+ } else if (ast->op == QSOperator::Assign) {
+ if (AST::Pattern *p = ast->left->patternCast()) {
+ RegisterScope scope(this);
+ Reference right = expression(ast->right).storeOnStack();
+ destructurePattern(p, right);
+ if (!_expr.accept(nx)) {
+ right.loadInAccumulator();
+ _expr.setResult(Reference::fromAccumulator(this));
+ }
+ return false;
+ }
+ Reference left = expression(ast->left);
+ if (hasError)
+ return false;
- case QSOperator::Assign: {
if (!left.isLValue()) {
throwReferenceError(ast->operatorToken, QStringLiteral("left-hand side of assignment operator is not an lvalue"));
return false;
@@ -792,15 +1245,27 @@ bool Codegen::visit(BinaryExpression *ast)
_expr.setResult(left.storeConsumeAccumulator());
else
_expr.setResult(left.storeRetainAccumulator());
- break;
+ return false;
}
+ Reference left = expression(ast->left);
+ if (hasError)
+ return false;
+
+ switch (ast->op) {
+ case QSOperator::Or:
+ case QSOperator::And:
+ case QSOperator::Assign:
+ Q_UNREACHABLE(); // handled separately above
+ break;
+
case QSOperator::InplaceAnd:
case QSOperator::InplaceSub:
case QSOperator::InplaceDiv:
case QSOperator::InplaceAdd:
case QSOperator::InplaceLeftShift:
case QSOperator::InplaceMod:
+ case QSOperator::InplaceExp:
case QSOperator::InplaceMul:
case QSOperator::InplaceOr:
case QSOperator::InplaceRightShift:
@@ -830,7 +1295,7 @@ bool Codegen::visit(BinaryExpression *ast)
case QSOperator::BitAnd:
case QSOperator::BitOr:
case QSOperator::BitXor:
- if (left.isConst()) {
+ if (left.isConstant()) {
Reference right = expression(ast->right);
if (hasError)
return false;
@@ -850,6 +1315,7 @@ bool Codegen::visit(BinaryExpression *ast)
case QSOperator::StrictNotEqual:
case QSOperator::Add:
case QSOperator::Div:
+ case QSOperator::Exp:
case QSOperator::Mod:
case QSOperator::Mul:
case QSOperator::Sub:
@@ -890,7 +1356,7 @@ Codegen::Reference Codegen::binopHelper(QSOperator::Op oper, Reference &left, Re
break;
}
case QSOperator::Sub: {
- if (right.isConst() && right.constant == Encode(int(1))) {
+ if (right.isConstant() && right.constant == Encode(int(1))) {
left.loadInAccumulator();
bytecodeGenerator->addInstruction(Instruction::Decrement());
} else {
@@ -902,6 +1368,14 @@ Codegen::Reference Codegen::binopHelper(QSOperator::Op oper, Reference &left, Re
}
break;
}
+ case QSOperator::Exp: {
+ left = left.storeOnStack();
+ right.loadInAccumulator();
+ Instruction::Exp exp;
+ exp.lhs = left.stackSlot();
+ bytecodeGenerator->addInstruction(exp);
+ break;
+ }
case QSOperator::Mul: {
left = left.storeOnStack();
right.loadInAccumulator();
@@ -927,9 +1401,9 @@ Codegen::Reference Codegen::binopHelper(QSOperator::Op oper, Reference &left, Re
break;
}
case QSOperator::BitAnd:
- if (right.isConst()) {
+ if (right.isConstant()) {
int rightAsInt = Primitive::fromReturnedValue(right.constant).toInt32();
- if (left.isConst()) {
+ if (left.isConstant()) {
int result = Primitive::fromReturnedValue(left.constant).toInt32() & rightAsInt;
return Reference::fromConst(this, Encode(result));
}
@@ -945,9 +1419,9 @@ Codegen::Reference Codegen::binopHelper(QSOperator::Op oper, Reference &left, Re
}
break;
case QSOperator::BitOr:
- if (right.isConst()) {
+ if (right.isConstant()) {
int rightAsInt = Primitive::fromReturnedValue(right.constant).toInt32();
- if (left.isConst()) {
+ if (left.isConstant()) {
int result = Primitive::fromReturnedValue(left.constant).toInt32() | rightAsInt;
return Reference::fromConst(this, Encode(result));
}
@@ -963,9 +1437,9 @@ Codegen::Reference Codegen::binopHelper(QSOperator::Op oper, Reference &left, Re
}
break;
case QSOperator::BitXor:
- if (right.isConst()) {
+ if (right.isConstant()) {
int rightAsInt = Primitive::fromReturnedValue(right.constant).toInt32();
- if (left.isConst()) {
+ if (left.isConstant()) {
int result = Primitive::fromReturnedValue(left.constant).toInt32() ^ rightAsInt;
return Reference::fromConst(this, Encode(result));
}
@@ -981,7 +1455,7 @@ Codegen::Reference Codegen::binopHelper(QSOperator::Op oper, Reference &left, Re
}
break;
case QSOperator::URShift:
- if (right.isConst()) {
+ if (right.isConstant()) {
left.loadInAccumulator();
Instruction::UShrConst ushr;
ushr.rhs = Primitive::fromReturnedValue(right.constant).toInt32() & 0x1f;
@@ -994,7 +1468,7 @@ Codegen::Reference Codegen::binopHelper(QSOperator::Op oper, Reference &left, Re
}
break;
case QSOperator::RShift:
- if (right.isConst()) {
+ if (right.isConstant()) {
left.loadInAccumulator();
Instruction::ShrConst shr;
shr.rhs = Primitive::fromReturnedValue(right.constant).toInt32() & 0x1f;
@@ -1007,7 +1481,7 @@ Codegen::Reference Codegen::binopHelper(QSOperator::Op oper, Reference &left, Re
}
break;
case QSOperator::LShift:
- if (right.isConst()) {
+ if (right.isConstant()) {
left.loadInAccumulator();
Instruction::ShlConst shl;
shl.rhs = Primitive::fromReturnedValue(right.constant).toInt32() & 0x1f;
@@ -1146,12 +1620,12 @@ static QSOperator::Op operatorForSwappedOperands(QSOperator::Op oper)
Codegen::Reference Codegen::jumpBinop(QSOperator::Op oper, Reference &left, Reference &right)
{
- if (left.isConst()) {
+ if (left.isConstant()) {
oper = operatorForSwappedOperands(oper);
qSwap(left, right);
}
- if (right.isConst() && (oper == QSOperator::Equal || oper == QSOperator::NotEqual)) {
+ if (right.isConstant() && (oper == QSOperator::Equal || oper == QSOperator::NotEqual)) {
Value c = Primitive::fromReturnedValue(right.constant);
if (c.isNull() || c.isUndefined()) {
left.loadInAccumulator();
@@ -1259,6 +1733,7 @@ bool Codegen::visit(CallExpression *ast)
RegisterScope scope(this);
Reference base = expression(ast->base);
+
if (hasError)
return false;
switch (base.type) {
@@ -1270,15 +1745,50 @@ bool Codegen::visit(CallExpression *ast)
break;
case Reference::Name:
break;
+ case Reference::Super:
+ handleConstruct(base, ast->arguments);
+ return false;
default:
base = base.storeOnStack();
break;
}
+ int thisObject = bytecodeGenerator->newRegister();
+ int functionObject = bytecodeGenerator->newRegister();
+
auto calldata = pushArgs(ast->arguments);
if (hasError)
return false;
+ if (calldata.hasSpread) {
+ Reference baseObject = base.baseObject();
+ if (!baseObject.isStackSlot()) {
+ baseObject.storeOnStack(thisObject);
+ baseObject = Reference::fromStackSlot(this, thisObject);
+ }
+ if (!base.isStackSlot()) {
+ base.storeOnStack(functionObject);
+ base = Reference::fromStackSlot(this, functionObject);
+ }
+
+ Instruction::CallWithSpread call;
+ call.func = base.stackSlot();
+ call.thisObject = baseObject.stackSlot();
+ call.argc = calldata.argc;
+ call.argv = calldata.argv;
+ bytecodeGenerator->addInstruction(call);
+
+ _expr.setResult(Reference::fromAccumulator(this));
+ return false;
+
+ }
+
+ handleCall(base, calldata);
+ return false;
+}
+
+void Codegen::handleCall(Reference &base, Arguments calldata)
+{
//### Do we really need all these call instructions? can's we load the callee in a temp?
if (base.type == Reference::QmlScopeObject) {
Instruction::CallScopeObjectProperty call;
@@ -1295,7 +1805,7 @@ bool Codegen::visit(CallExpression *ast)
call.argv = calldata.argv;
bytecodeGenerator->addInstruction(call);
} else if (base.type == Reference::Member) {
- if (useFastLookups) {
+ if (!disable_lookups && useFastLookups) {
Instruction::CallPropertyLookup call;
call.base = base.propertyBase.stackSlot();
call.lookupIndex = registerGetterLookup(base.propertyNameIndex);
@@ -1323,7 +1833,7 @@ bool Codegen::visit(CallExpression *ast)
call.argc = calldata.argc;
call.argv = calldata.argv;
bytecodeGenerator->addInstruction(call);
- } else if (useFastLookups && base.global) {
+ } else if (!disable_lookups && useFastLookups && base.global) {
Instruction::CallGlobalLookup call;
call.index = registerGlobalGetterLookup(base.nameAsIndex());
call.argc = calldata.argc;
@@ -1346,36 +1856,69 @@ bool Codegen::visit(CallExpression *ast)
}
_expr.setResult(Reference::fromAccumulator(this));
- return false;
}
Codegen::Arguments Codegen::pushArgs(ArgumentList *args)
{
+ bool hasSpread = false;
int argc = 0;
- for (ArgumentList *it = args; it; it = it->next)
+ for (ArgumentList *it = args; it; it = it->next) {
+ if (it->isSpreadElement) {
+ hasSpread = true;
+ ++argc;
+ }
++argc;
+ }
if (!argc)
- return { 0, 0 };
+ return { 0, 0, false };
int calldata = bytecodeGenerator->newRegisterArray(argc);
argc = 0;
for (ArgumentList *it = args; it; it = it->next) {
+ if (it->isSpreadElement) {
+ Reference::fromConst(this, Primitive::emptyValue().asReturnedValue()).storeOnStack(calldata + argc);
+ ++argc;
+ }
RegisterScope scope(this);
Reference e = expression(it->expression);
if (hasError)
break;
- if (!argc && !it->next) {
+ if (!argc && !it->next && !hasSpread) {
// avoid copy for functions taking a single argument
if (e.isStackSlot())
- return { 1, e.stackSlot() };
+ return { 1, e.stackSlot(), hasSpread };
}
(void) e.storeOnStack(calldata + argc);
++argc;
}
- return { argc, calldata };
+ return { argc, calldata, hasSpread };
+}
+
+Codegen::Arguments Codegen::pushTemplateArgs(TemplateLiteral *args)
+{
+ int argc = 0;
+ for (TemplateLiteral *it = args; it; it = it->next)
+ ++argc;
+
+ if (!argc)
+ return { 0, 0, false };
+
+ int calldata = bytecodeGenerator->newRegisterArray(argc);
+
+ argc = 0;
+ for (TemplateLiteral *it = args; it && it->expression; it = it->next) {
+ RegisterScope scope(this);
+ Reference e = expression(it->expression);
+ if (hasError)
+ break;
+ (void) e.storeOnStack(calldata + argc);
+ ++argc;
+ }
+
+ return { argc, calldata, false };
}
bool Codegen::visit(ConditionalExpression *ast)
@@ -1413,11 +1956,15 @@ bool Codegen::visit(DeleteExpression *ast)
if (hasError)
return false;
+ RegisterScope scope(this);
Reference expr = expression(ast->expression);
if (hasError)
return false;
switch (expr.type) {
+ case Reference::SuperProperty:
+ // ### this should throw a reference error at runtime.
+ return false;
case Reference::StackSlot:
if (!expr.stackSlotIsLocalOrArgument)
break;
@@ -1444,9 +1991,14 @@ bool Codegen::visit(DeleteExpression *ast)
case Reference::Member: {
//### maybe add a variant where the base can be in the accumulator?
expr = expr.asLValue();
- Instruction::DeleteMember del;
+ Instruction::LoadRuntimeString instr;
+ instr.stringId = expr.propertyNameIndex;
+ bytecodeGenerator->addInstruction(instr);
+ Reference index = Reference::fromStackSlot(this);
+ index.storeConsumeAccumulator();
+ Instruction::DeleteProperty del;
del.base = expr.propertyBase.stackSlot();
- del.member = expr.propertyNameIndex;
+ del.index = index.stackSlot();
bytecodeGenerator->addInstruction(del);
_expr.setResult(Reference::fromAccumulator(this));
return false;
@@ -1454,7 +2006,7 @@ bool Codegen::visit(DeleteExpression *ast)
case Reference::Subscript: {
//### maybe add a variant where the index can be in the accumulator?
expr = expr.asLValue();
- Instruction::DeleteSubscript del;
+ Instruction::DeleteProperty del;
del.base = expr.elementBase;
del.index = expr.elementSubscript.stackSlot();
bytecodeGenerator->addInstruction(del);
@@ -1478,109 +2030,169 @@ bool Codegen::visit(FalseLiteral *)
return false;
}
+bool Codegen::visit(SuperLiteral *)
+{
+ if (hasError)
+ return false;
+
+ _expr.setResult(Reference::fromSuper(this));
+ return false;
+}
+
bool Codegen::visit(FieldMemberExpression *ast)
{
if (hasError)
return false;
+ if (AST::IdentifierExpression *id = AST::cast<AST::IdentifierExpression *>(ast->base)) {
+ if (id->name == QLatin1String("new")) {
+ // new.target
+ if (ast->name != QLatin1String("target")) {
+ throwSyntaxError(ast->identifierToken, QLatin1String("Expected 'target' after 'new.'."));
+ return false;
+ }
+ Reference r = Reference::fromStackSlot(this, CallData::NewTarget);
+ _expr.setResult(r);
+ return false;
+ }
+ }
+
Reference base = expression(ast->base);
if (hasError)
return false;
+ if (base.isSuper()) {
+ Instruction::LoadRuntimeString load;
+ load.stringId = registerString(ast->name.toString());
+ bytecodeGenerator->addInstruction(load);
+ Reference property = Reference::fromAccumulator(this).storeOnStack();
+ _expr.setResult(Reference::fromSuperProperty(property));
+ return false;
+ }
_expr.setResult(Reference::fromMember(base, ast->name.toString()));
return false;
}
-bool Codegen::visit(FunctionExpression *ast)
+bool Codegen::visit(TaggedTemplate *ast)
{
if (hasError)
return false;
RegisterScope scope(this);
- int function = defineFunction(ast->name.toString(), ast, ast->formals, ast->body ? ast->body->elements : nullptr);
- loadClosure(function);
- _expr.setResult(Reference::fromAccumulator(this));
+ Reference base = expression(ast->base);
+ if (hasError)
+ return false;
+ switch (base.type) {
+ case Reference::Member:
+ case Reference::Subscript:
+ base = base.asLValue();
+ break;
+ case Reference::Name:
+ break;
+ default:
+ base = base.storeOnStack();
+ break;
+ }
+
+ int arrayTemp = createTemplateArray(ast->templateLiteral);
+ Q_UNUSED(arrayTemp);
+ auto calldata = pushTemplateArgs(ast->templateLiteral);
+ if (hasError)
+ return false;
+ ++calldata.argc;
+ Q_ASSERT(calldata.argv == arrayTemp + 1);
+ --calldata.argv;
+
+ handleCall(base, calldata);
+ return false;
+
+
return false;
}
-Codegen::Reference Codegen::referenceForName(const QString &name, bool isLhs)
+int Codegen::createTemplateArray(TemplateLiteral *t)
{
- int scope = 0;
- Context *c = _context;
-
- // skip the innermost context if it's simple (as the runtime won't
- // create a context for it
- if (c->canUseSimpleCall()) {
- Context::Member m = c->findMember(name);
- if (m.type != Context::UndefinedMember) {
- Q_ASSERT((!m.canEscape));
- Reference r = Reference::fromStackSlot(this, m.index, true /*isLocal*/);
- if (name == QLatin1String("arguments") || name == QLatin1String("eval")) {
- r.isArgOrEval = true;
- if (isLhs && c->isStrict)
- // ### add correct source location
- throwSyntaxError(SourceLocation(), QStringLiteral("Variable name may not be eval or arguments in strict mode"));
- }
- return r;
- }
- const int argIdx = c->findArgument(name);
- if (argIdx != -1) {
- Q_ASSERT(!c->argumentsCanEscape && (c->usesArgumentsObject != Context::ArgumentsObjectUsed || c->isStrict));
- return Reference::fromArgument(this, argIdx, _volataleMemoryLocations.isVolatile(name));
- }
- c = c->parent;
- }
+ int arrayTemp = bytecodeGenerator->newRegister();
- while (c->parent) {
- if (c->forceLookupByName())
- goto loadByName;
+ RegisterScope scope(this);
- Context::Member m = c->findMember(name);
- if (m.type != Context::UndefinedMember) {
- Reference r = m.canEscape ? Reference::fromScopedLocal(this, m.index, scope)
- : Reference::fromStackSlot(this, m.index, true /*isLocal*/);
- if (name == QLatin1String("arguments") || name == QLatin1String("eval")) {
- r.isArgOrEval = true;
- if (isLhs && c->isStrict)
- // ### add correct source location
- throwSyntaxError(SourceLocation(), QStringLiteral("Variable name may not be eval or arguments in strict mode"));
- }
- return r;
- }
- const int argIdx = c->findArgument(name);
- if (argIdx != -1) {
- if (c->argumentsCanEscape || c->usesArgumentsObject == Context::ArgumentsObjectUsed) {
- int idx = argIdx + c->locals.size();
- return Reference::fromScopedLocal(this, idx, scope);
- } else {
- Q_ASSERT(scope == 0);
- return Reference::fromArgument(this, argIdx, _volataleMemoryLocations.isVolatile(name));
- }
- }
+ int argc = 0;
+ int args = -1;
+ auto push = [this, &argc, &args](const QStringRef &arg) {
+ int temp = bytecodeGenerator->newRegister();
+ if (args == -1)
+ args = temp;
+ Instruction::LoadRuntimeString instr;
+ instr.stringId = registerString(arg.toString());
+ bytecodeGenerator->addInstruction(instr);
+ Instruction::StoreReg store;
+ store.reg = temp;
+ bytecodeGenerator->addInstruction(store);
- if (!c->isStrict && c->hasDirectEval)
- goto loadByName;
+ ++argc;
+ };
- ++scope;
- c = c->parent;
- }
+ for (TemplateLiteral *it = t; it; it = it->next)
+ push(it->value);
- {
- // This hook allows implementing QML lookup semantics
- Reference fallback = fallbackNameLookup(name);
- if (fallback.type != Reference::Invalid)
- return fallback;
+ if (args == -1) {
+ Q_ASSERT(argc == 0);
+ args = 0;
}
- if (!c->parent && !c->forceLookupByName() && _context->compilationMode != EvalCode && c->compilationMode != QmlBinding) {
- Reference r = Reference::fromName(this, name);
- r.global = true;
+ Instruction::DefineArray call;
+ call.argc = argc;
+ call.args = Moth::StackSlot::createRegister(args);
+ bytecodeGenerator->addInstruction(call);
+
+ Instruction::StoreReg store;
+ store.reg = arrayTemp;
+ bytecodeGenerator->addInstruction(store);
+
+ return arrayTemp;
+}
+
+bool Codegen::visit(FunctionExpression *ast)
+{
+ if (hasError)
+ return false;
+
+ RegisterScope scope(this);
+
+ int function = defineFunction(ast->name.toString(), ast, ast->formals, ast->body);
+ if (hasError)
+ return false;
+ loadClosure(function);
+ _expr.setResult(Reference::fromAccumulator(this));
+ return false;
+}
+
+Codegen::Reference Codegen::referenceForName(const QString &name, bool isLhs)
+{
+ Context::ResolvedName resolved = _context->resolveName(name);
+
+ if (resolved.type == Context::ResolvedName::Local || resolved.type == Context::ResolvedName::Stack) {
+ if (resolved.isArgOrEval && isLhs)
+ // ### add correct source location
+ throwSyntaxError(SourceLocation(), QStringLiteral("Variable name may not be eval or arguments in strict mode"));
+ Reference r = (resolved.type == Context::ResolvedName::Local) ?
+ Reference::fromScopedLocal(this, resolved.index, resolved.scope) :
+ Reference::fromStackSlot(this, resolved.index, true /*isLocal*/);
+ if (r.isStackSlot() && _volatileMemoryLocations.isVolatile(name))
+ r.isVolatile = true;
+ r.isArgOrEval = resolved.isArgOrEval;
+ r.isReferenceToConst = resolved.isConst;
return r;
}
- // global context or with. Lookup by name
- loadByName:
- return Reference::fromName(this, name);
+ // This hook allows implementing QML lookup semantics
+ Reference fallback = fallbackNameLookup(name);
+ if (fallback.type != Reference::Invalid)
+ return fallback;
+
+ Reference r = Reference::fromName(this, name);
+ r.global = (resolved.type == Context::ResolvedName::Global);
+ return r;
}
void Codegen::loadClosure(int closureId)
@@ -1618,6 +2230,47 @@ bool Codegen::visit(NestedExpression *ast)
return false;
}
+void Codegen::handleConstruct(const Reference &base, ArgumentList *arguments)
+{
+ Reference constructor;
+ if (base.isSuper()) {
+ Instruction::LoadSuperConstructor super;
+ bytecodeGenerator->addInstruction(super);
+ constructor = Reference::fromAccumulator(this).storeOnStack();
+ } else {
+ constructor = base.storeOnStack();
+ }
+
+ auto calldata = pushArgs(arguments);
+ if (hasError)
+ return;
+
+ if (base.isSuper()) {
+ Reference::fromStackSlot(this, CallData::Function).loadInAccumulator();
+ } else {
+ constructor.loadInAccumulator();
+ }
+
+ if (calldata.hasSpread) {
+ Instruction::ConstructWithSpread create;
+ create.func = constructor.stackSlot();
+ create.argc = calldata.argc;
+ create.argv = calldata.argv;
+ bytecodeGenerator->addInstruction(create);
+ } else {
+ Instruction::Construct create;
+ create.func = constructor.stackSlot();
+ create.argc = calldata.argc;
+ create.argv = calldata.argv;
+ bytecodeGenerator->addInstruction(create);
+ }
+ if (base.isSuper())
+ // set the result up as the thisObject
+ Reference::fromAccumulator(this).storeOnStack(CallData::This);
+
+ _expr.setResult(Reference::fromAccumulator(this));
+}
+
bool Codegen::visit(NewExpression *ast)
{
if (hasError)
@@ -1628,15 +2281,12 @@ bool Codegen::visit(NewExpression *ast)
Reference base = expression(ast->expression);
if (hasError)
return false;
- //### Maybe create a ConstructA that takes an accumulator?
- base = base.storeOnStack();
+ if (base.isSuper()) {
+ throwSyntaxError(ast->expression->firstSourceLocation(), QStringLiteral("Cannot use new with super."));
+ return false;
+ }
- Instruction::Construct create;
- create.func = base.stackSlot();
- create.argc = 0;
- create.argv = 0;
- bytecodeGenerator->addInstruction(create);
- _expr.setResult(Reference::fromAccumulator(this));
+ handleConstruct(base, nullptr);
return false;
}
@@ -1650,18 +2300,12 @@ bool Codegen::visit(NewMemberExpression *ast)
Reference base = expression(ast->base);
if (hasError)
return false;
- base = base.storeOnStack();
-
- auto calldata = pushArgs(ast->arguments);
- if (hasError)
+ if (base.isSuper()) {
+ throwSyntaxError(ast->base->firstSourceLocation(), QStringLiteral("Cannot use new with super."));
return false;
+ }
- Instruction::Construct create;
- create.func = base.stackSlot();
- create.argc = calldata.argc;
- create.argv = calldata.argv;
- bytecodeGenerator->addInstruction(create);
- _expr.setResult(Reference::fromAccumulator(this));
+ handleConstruct(base, ast->arguments);
return false;
}
@@ -1696,133 +2340,105 @@ bool Codegen::visit(NumericLiteral *ast)
return false;
}
-bool Codegen::visit(ObjectLiteral *ast)
+bool Codegen::visit(ObjectPattern *ast)
{
if (hasError)
return false;
+ QVector<QPair<Reference, ObjectPropertyValue>> computedProperties;
QMap<QString, ObjectPropertyValue> valueMap;
RegisterScope scope(this);
- for (PropertyAssignmentList *it = ast->properties; it; it = it->next) {
- QString name = it->assignment->name->asString();
- if (PropertyNameAndValue *nv = AST::cast<AST::PropertyNameAndValue *>(it->assignment)) {
- Reference value = expression(nv->value);
- if (hasError)
- return false;
-
- ObjectPropertyValue &v = valueMap[name];
- if (v.hasGetter() || v.hasSetter() || (_context->isStrict && v.rvalue.isValid())) {
- throwSyntaxError(nv->lastSourceLocation(),
- QStringLiteral("Illegal duplicate key '%1' in object literal").arg(name));
- return false;
- }
-
- v.rvalue = value.storeOnStack();
- } else if (PropertyGetterSetter *gs = AST::cast<AST::PropertyGetterSetter *>(it->assignment)) {
- const int function = defineFunction(name, gs, gs->formals, gs->functionBody ? gs->functionBody->elements : nullptr);
- ObjectPropertyValue &v = valueMap[name];
- if (v.rvalue.isValid() ||
- (gs->type == PropertyGetterSetter::Getter && v.hasGetter()) ||
- (gs->type == PropertyGetterSetter::Setter && v.hasSetter())) {
- throwSyntaxError(gs->lastSourceLocation(),
- QStringLiteral("Illegal duplicate key '%1' in object literal").arg(name));
- return false;
- }
- if (gs->type == PropertyGetterSetter::Getter)
- v.getter = function;
- else
- v.setter = function;
- } else {
- Q_UNREACHABLE();
- }
- }
+ QStringList members;
- QVector<QString> nonArrayKey, arrayKeyWithValue, arrayKeyWithGetterSetter;
- bool needSparseArray = false; // set to true if any array index is bigger than 16
-
- for (QMap<QString, ObjectPropertyValue>::iterator it = valueMap.begin(), eit = valueMap.end();
- it != eit; ++it) {
- QString name = it.key();
- uint keyAsIndex = QV4::String::toArrayIndex(name);
- if (keyAsIndex != std::numeric_limits<uint>::max()) {
- it->keyAsIndex = keyAsIndex;
- if (keyAsIndex > 16)
- needSparseArray = true;
- if (it->hasSetter() || it->hasGetter())
- arrayKeyWithGetterSetter.append(name);
- else
- arrayKeyWithValue.append(name);
- } else {
- nonArrayKey.append(name);
- }
- }
-
- int args = -1;
- auto push = [this, &args](const Reference &arg) {
+ int argc = 0;
+ int args = 0;
+ auto push = [this, &args, &argc](const Reference &arg) {
int temp = bytecodeGenerator->newRegister();
- if (args == -1)
+ if (argc == 0)
args = temp;
(void) arg.storeOnStack(temp);
+ ++argc;
};
- QVector<QV4::Compiler::JSUnitGenerator::MemberInfo> members;
+ PatternPropertyList *it = ast->properties;
+ for (; it; it = it->next) {
+ PatternProperty *p = it->property;
+ AST::ComputedPropertyName *cname = AST::cast<AST::ComputedPropertyName *>(p->name);
+ if (cname || p->type == PatternProperty::Getter || p->type == PatternProperty::Setter)
+ break;
+ QString name = p->name->asString();
+ uint arrayIndex = QV4::String::toArrayIndex(name);
+ if (arrayIndex != UINT_MAX)
+ break;
+ if (members.contains(name))
+ break;
+ members.append(name);
- Reference acc = Reference::fromAccumulator(this);
- // generate the key/value pairs
- for (const QString &key : qAsConst(nonArrayKey)) {
- const ObjectPropertyValue &prop = valueMap[key];
-
- if (prop.hasGetter() || prop.hasSetter()) {
- Q_ASSERT(!prop.rvalue.isValid());
- loadClosure(prop.getter);
- push(acc);
- loadClosure(prop.setter);
- push(acc);
- members.append({ key, true });
- } else {
- Q_ASSERT(prop.rvalue.isValid());
- push(prop.rvalue);
- members.append({ key, false });
+ {
+ RegisterScope innerScope(this);
+ Reference value = expression(p->initializer);
+ if (hasError)
+ return false;
+ value.loadInAccumulator();
}
- }
-
- // generate array entries with values
- for (const QString &key : qAsConst(arrayKeyWithValue)) {
- const ObjectPropertyValue &prop = valueMap[key];
- Q_ASSERT(!prop.hasGetter() && !prop.hasSetter());
- push(Reference::fromConst(this, Encode(prop.keyAsIndex)));
- push(prop.rvalue);
- }
-
- // generate array entries with both a value and a setter
- for (const QString &key : qAsConst(arrayKeyWithGetterSetter)) {
- const ObjectPropertyValue &prop = valueMap[key];
- Q_ASSERT(!prop.rvalue.isValid());
- push(Reference::fromConst(this, Encode(prop.keyAsIndex)));
- loadClosure(prop.getter);
- push(acc);
- loadClosure(prop.setter);
- push(acc);
+ push(Reference::fromAccumulator(this));
}
int classId = jsUnitGenerator->registerJSClass(members);
- uint arrayGetterSetterCountAndFlags = arrayKeyWithGetterSetter.size();
- arrayGetterSetterCountAndFlags |= needSparseArray << 30;
-
- if (args == -1)
- args = 0;
+ // handle complex property setters
+ for (; it; it = it->next) {
+ PatternProperty *p = it->property;
+ AST::ComputedPropertyName *cname = AST::cast<AST::ComputedPropertyName *>(p->name);
+ ObjectLiteralArgument argType = ObjectLiteralArgument::Value;
+ if (p->type == PatternProperty::Getter)
+ argType = ObjectLiteralArgument::Getter;
+ else if (p->type == PatternProperty::Setter)
+ argType = ObjectLiteralArgument::Setter;
+
+ Reference::fromConst(this, Encode(int(argType))).loadInAccumulator();
+ push(Reference::fromAccumulator(this));
+
+ if (cname) {
+ RegisterScope innerScope(this);
+ Reference name = expression(cname->expression);
+ if (hasError)
+ return false;
+ name.loadInAccumulator();
+ } else {
+ QString name = p->name->asString();
+#if 0
+ uint arrayIndex = QV4::String::toArrayIndex(name);
+ if (arrayIndex != UINT_MAX) {
+ Reference::fromConst(this, Encode(arrayIndex)).loadInAccumulator();
+ } else
+#endif
+ {
+ Instruction::LoadRuntimeString instr;
+ instr.stringId = registerString(name);
+ bytecodeGenerator->addInstruction(instr);
+ }
+ }
+ push(Reference::fromAccumulator(this));
+ {
+ RegisterScope innerScope(this);
+ Reference value = expression(p->initializer);
+ if (hasError)
+ return false;
+ value.loadInAccumulator();
+ }
+ push(Reference::fromAccumulator(this));
+ }
Instruction::DefineObjectLiteral call;
call.internalClassId = classId;
- call.arrayValueCount = arrayKeyWithValue.size();
- call.arrayGetterSetterCountAndFlags = arrayGetterSetterCountAndFlags;
+ call.argc = argc;
call.args = Moth::StackSlot::createRegister(args);
bytecodeGenerator->addInstruction(call);
-
- _expr.setResult(Reference::fromAccumulator(this));
+ Reference result = Reference::fromAccumulator(this);
+ _expr.setResult(result);
return false;
}
@@ -1933,6 +2549,49 @@ bool Codegen::visit(StringLiteral *ast)
return false;
}
+bool Codegen::visit(TemplateLiteral *ast)
+{
+ if (hasError)
+ return false;
+
+ Instruction::LoadRuntimeString instr;
+ instr.stringId = registerString(ast->value.toString());
+ bytecodeGenerator->addInstruction(instr);
+
+ if (ast->expression) {
+ RegisterScope scope(this);
+ int temp = bytecodeGenerator->newRegister();
+ Instruction::StoreReg store;
+ store.reg = temp;
+ bytecodeGenerator->addInstruction(store);
+
+ Reference expr = expression(ast->expression);
+
+ if (ast->next) {
+ int temp2 = bytecodeGenerator->newRegister();
+ expr.storeOnStack(temp2);
+ visit(ast->next);
+
+ Instruction::Add instr;
+ instr.lhs = temp2;
+ bytecodeGenerator->addInstruction(instr);
+ } else {
+ expr.loadInAccumulator();
+ }
+
+ Instruction::Add instr;
+ instr.lhs = temp;
+ bytecodeGenerator->addInstruction(instr);
+ }
+
+ auto r = Reference::fromAccumulator(this);
+ r.isReadonly = true;
+
+ _expr.setResult(r);
+ return false;
+
+}
+
bool Codegen::visit(ThisExpression *)
{
if (hasError)
@@ -2023,13 +2682,40 @@ bool Codegen::visit(FunctionDeclaration * ast)
RegisterScope scope(this);
- if (_context->compilationMode == QmlBinding)
- Reference::fromName(this, ast->name.toString()).loadInAccumulator();
+ if (_functionContext->contextType == ContextType::Binding)
+ referenceForName(ast->name.toString(), true).loadInAccumulator();
_expr.accept(nx);
return false;
}
-static bool endsWithReturn(Node *node)
+bool Codegen::visit(YieldExpression *ast)
+{
+ if (ast->isYieldStar) {
+ throwSyntaxError(ast->firstSourceLocation(), QLatin1String("yield* is not currently supported"));
+ return false;
+ }
+ if (inFormalParameterList) {
+ throwSyntaxError(ast->firstSourceLocation(), QLatin1String("yield is not allowed inside parameter lists"));
+ return false;
+ }
+
+
+ Reference result = ast->expression ? expression(ast->expression) : Reference::fromConst(this, Encode::undefined());
+ if (hasError)
+ return false;
+ result.loadInAccumulator();
+ Instruction::Yield yield;
+ bytecodeGenerator->addInstruction(yield);
+ Instruction::Resume resume;
+ BytecodeGenerator::Jump jump = bytecodeGenerator->addJumpInstruction(resume);
+ Reference acc = Reference::fromAccumulator(this);
+ emitReturn(acc);
+ jump.link();
+ _expr.setResult(acc);
+ return false;
+}
+
+static bool endsWithReturn(Module *module, Node *node)
{
if (!node)
return false;
@@ -2038,32 +2724,29 @@ static bool endsWithReturn(Node *node)
if (AST::cast<ThrowStatement *>(node))
return true;
if (Program *p = AST::cast<Program *>(node))
- return endsWithReturn(p->elements);
- if (SourceElements *se = AST::cast<SourceElements *>(node)) {
- while (se->next)
- se = se->next;
- return endsWithReturn(se->element);
- }
- if (StatementSourceElement *sse = AST::cast<StatementSourceElement *>(node))
- return endsWithReturn(sse->statement);
+ return endsWithReturn(module, p->statements);
if (StatementList *sl = AST::cast<StatementList *>(node)) {
while (sl->next)
sl = sl->next;
- return endsWithReturn(sl->statement);
+ return endsWithReturn(module, sl->statement);
+ }
+ if (Block *b = AST::cast<Block *>(node)) {
+ Context *blockContext = module->contextMap.value(node);
+ if (blockContext->requiresExecutionContext)
+ // we need to emit a return statement here, because of the
+ // unwind handler
+ return false;
+ return endsWithReturn(module, b->statements);
}
- if (Block *b = AST::cast<Block *>(node))
- return endsWithReturn(b->statements);
if (IfStatement *is = AST::cast<IfStatement *>(node))
- return is->ko && endsWithReturn(is->ok) && endsWithReturn(is->ko);
+ return is->ko && endsWithReturn(module, is->ok) && endsWithReturn(module, is->ko);
return false;
}
int Codegen::defineFunction(const QString &name, AST::Node *ast,
AST::FormalParameterList *formals,
- AST::SourceElements *body)
+ AST::StatementList *body)
{
- Q_UNUSED(formals);
-
enterContext(ast);
if (_context->functionIndex >= 0)
@@ -2074,7 +2757,15 @@ int Codegen::defineFunction(const QString &name, AST::Node *ast,
_module->functions.append(_context);
_context->functionIndex = _module->functions.count() - 1;
- _context->hasDirectEval |= (_context->compilationMode == EvalCode || _context->compilationMode == GlobalCode || _module->debugMode); // Conditional breakpoints are like eval in the function
+ Context *savedFunctionContext = _functionContext;
+ _functionContext = _context;
+ ControlFlow *savedControlFlow = controlFlow;
+ controlFlow = nullptr;
+
+ if (_context->contextType == ContextType::Global) {
+ _module->blocks.append(_context);
+ _context->blockIndex = _module->blocks.count() - 1;
+ }
if (_module->debugMode) // allow the debugger to see overwritten arguments
_context->argumentsCanEscape = true;
@@ -2084,132 +2775,104 @@ int Codegen::defineFunction(const QString &name, AST::Node *ast,
// at all, because if the onSignal is a signal handler, the user is actually making it explicit
// that the binding is a function, so we should execute that. However, we don't know that during
// AOT compilation, so mark the surrounding function as only-returning-a-closure.
- _context->returnsClosure = cast<ExpressionStatement *>(ast) && cast<FunctionExpression *>(cast<ExpressionStatement *>(ast)->expression);
+ _context->returnsClosure = body && body->statement && cast<ExpressionStatement *>(body->statement) && cast<FunctionExpression *>(cast<ExpressionStatement *>(body->statement)->expression);
BytecodeGenerator bytecode(_context->line, _module->debugMode);
BytecodeGenerator *savedBytecodeGenerator;
savedBytecodeGenerator = bytecodeGenerator;
bytecodeGenerator = &bytecode;
bytecodeGenerator->setLocation(ast->firstSourceLocation());
+ BytecodeGenerator::Label *savedReturnLabel = _returnLabel;
+ _returnLabel = nullptr;
+
+ bool savedFunctionEndsWithReturn = functionEndsWithReturn;
+ functionEndsWithReturn = endsWithReturn(_module, body);
// reserve the js stack frame (Context & js Function & accumulator)
bytecodeGenerator->newRegisterArray(sizeof(CallData)/sizeof(Value) - 1 + _context->arguments.size());
+ bool _inFormalParameterList = false;
+ qSwap(_inFormalParameterList, inFormalParameterList);
+
int returnAddress = -1;
- bool _requiresReturnValue = (_context->compilationMode == QmlBinding || _context->compilationMode == EvalCode || _context->compilationMode == GlobalCode);
+ bool _requiresReturnValue = _context->requiresImplicitReturnValue();
qSwap(requiresReturnValue, _requiresReturnValue);
- if (requiresReturnValue)
- returnAddress = bytecodeGenerator->newRegister();
- if (!_context->parent || _context->usesArgumentsObject == Context::ArgumentsObjectUnknown)
- _context->usesArgumentsObject = Context::ArgumentsObjectNotUsed;
- if (_context->usesArgumentsObject == Context::ArgumentsObjectUsed)
- _context->addLocalVar(QStringLiteral("arguments"), Context::VariableDeclaration, AST::VariableDeclaration::FunctionScope);
-
- bool allVarsEscape = _context->hasWith || _context->hasTry || _context->hasDirectEval;
- bool needsCallContext = false;
- const QLatin1String exprForOn("expression for on");
- if (!_context->canUseSimpleCall() && _context->compilationMode != GlobalCode && (_context->compilationMode != EvalCode || _context->isStrict))
- needsCallContext = true;
- else if (_context->compilationMode == QmlBinding && name.length() > exprForOn.size() && name.startsWith(exprForOn) && name.at(exprForOn.size()).isUpper())
- // we don't really need this for bindings, but we do for signal handlers, and we don't know if the code is a signal handler or not.
- needsCallContext = true;
- if (needsCallContext) {
- Instruction::CreateCallContext createContext;
- bytecodeGenerator->addInstruction(createContext);
- }
-
- // variables in global code are properties of the global context object, not locals as with other functions.
- if (_context->compilationMode == FunctionCode || _context->compilationMode == QmlBinding) {
- for (Context::MemberMap::iterator it = _context->members.begin(), end = _context->members.end(); it != end; ++it) {
- const QString &local = it.key();
- if (allVarsEscape)
- it->canEscape = true;
- if (it->canEscape) {
- it->index = _context->locals.size();
- _context->locals.append(local);
- if (it->type == Context::ThisFunctionName) {
- // move the name from the stack to the call context
- Instruction::LoadReg load;
- load.reg = CallData::Function;
- bytecodeGenerator->addInstruction(load);
- Instruction::StoreLocal store;
- store.index = it->index;
- bytecodeGenerator->addInstruction(store);
- }
- } else {
- if (it->type == Context::ThisFunctionName)
- it->index = CallData::Function;
- else
- it->index = bytecodeGenerator->newRegister();
- }
- }
- } else {
- for (Context::MemberMap::const_iterator it = _context->members.constBegin(), cend = _context->members.constEnd(); it != cend; ++it) {
- const QString &local = it.key();
+ returnAddress = bytecodeGenerator->newRegister();
+ qSwap(_returnAddress, returnAddress);
- Instruction::DeclareVar declareVar;
- declareVar.isDeletable = false;
- declareVar.varName = registerString(local);
- bytecodeGenerator->addInstruction(declareVar);
- }
+ // register the lexical scope for global code
+ if (!_context->parent && _context->requiresExecutionContext) {
+ _module->blocks.append(_context);
+ _context->blockIndex = _module->blocks.count() - 1;
}
- qSwap(_returnAddress, returnAddress);
- for (const Context::Member &member : qAsConst(_context->members)) {
- if (member.function) {
- const int function = defineFunction(member.function->name.toString(), member.function, member.function->formals,
- member.function->body ? member.function->body->elements : nullptr);
- loadClosure(function);
- if (! _context->parent) {
- Reference::fromName(this, member.function->name.toString()).storeConsumeAccumulator();
- } else {
- Q_ASSERT(member.index >= 0);
- Reference local = member.canEscape ? Reference::fromScopedLocal(this, member.index, 0)
- : Reference::fromStackSlot(this, member.index, true);
- local.storeConsumeAccumulator();
- }
+ RegisterScope registerScope(this);
+ _context->emitBlockHeader(this);
+
+ inFormalParameterList = true;
+ int argc = 0;
+ while (formals) {
+ PatternElement *e = formals->element;
+ if (!e) {
+ if (!formals->next)
+ // trailing comma
+ break;
+ Q_UNREACHABLE();
}
- }
- if (_context->usesArgumentsObject == Context::ArgumentsObjectUsed) {
- if (_context->isStrict) {
- Instruction::CreateUnmappedArgumentsObject setup;
- bytecodeGenerator->addInstruction(setup);
+
+ Reference arg = referenceForName(e->bindingIdentifier.toString(), true);
+ if (e->type == PatternElement::RestElement) {
+ Q_ASSERT(!formals->next);
+ Instruction::CreateRestParameter rest;
+ rest.argIndex = argc;
+ bytecodeGenerator->addInstruction(rest);
+ arg.storeConsumeAccumulator();
} else {
- Instruction::CreateMappedArgumentsObject setup;
- bytecodeGenerator->addInstruction(setup);
+ if (e->bindingTarget || e->initializer) {
+ initializeAndDestructureBindingElement(e, arg);
+ if (hasError)
+ break;
+ }
}
- referenceForName(QStringLiteral("arguments"), false).storeConsumeAccumulator();
+ formals = formals->next;
+ ++argc;
}
- if (_context->usesThis && !_context->isStrict) {
- // make sure we convert this to an object
- Instruction::ConvertThisToObject convert;
- bytecodeGenerator->addInstruction(convert);
+ inFormalParameterList = false;
+
+ if (_context->isGenerator) {
+ Instruction::Yield yield;
+ bytecodeGenerator->addInstruction(yield);
}
beginFunctionBodyHook();
- sourceElements(body);
+ statementList(body);
- if (hasError || !endsWithReturn(body)) {
- bytecodeGenerator->setLocation(ast->lastSourceLocation());
- if (requiresReturnValue) {
- if (_returnAddress >= 0) {
- Instruction::LoadReg load;
- load.reg = Moth::StackSlot::createRegister(_returnAddress);
- bytecodeGenerator->addInstruction(load);
- }
+ bytecodeGenerator->setLocation(ast->lastSourceLocation());
+ _context->emitBlockFooter(this);
+
+ if (_returnLabel || hasError || !functionEndsWithReturn) {
+ if (_returnLabel)
+ _returnLabel->link();
+
+ if (_returnLabel || requiresReturnValue) {
+ Instruction::LoadReg load;
+ load.reg = Moth::StackSlot::createRegister(_returnAddress);
+ bytecodeGenerator->addInstruction(load);
} else {
Reference::fromConst(this, Encode::undefined()).loadInAccumulator();
}
+
bytecodeGenerator->addInstruction(Instruction::Ret());
}
+ Q_ASSERT(_context == _functionContext);
bytecodeGenerator->finalize(_context);
- _context->registerCount = bytecodeGenerator->registerCount();
+ _context->registerCountInFunction = bytecodeGenerator->registerCount();
static const bool showCode = qEnvironmentVariableIsSet("QV4_SHOW_BYTECODE");
if (showCode) {
qDebug() << "=== Bytecode for" << _context->name << "strict mode" << _context->isStrict
- << "register count" << _context->registerCount;
+ << "register count" << _context->registerCountInFunction << "implicit return" << requiresReturnValue;
QV4::Moth::dumpBytecode(_context->code, _context->locals.size(), _context->arguments.size(),
_context->line, _context->lineNumberMapping);
qDebug();
@@ -2217,29 +2880,17 @@ int Codegen::defineFunction(const QString &name, AST::Node *ast,
qSwap(_returnAddress, returnAddress);
qSwap(requiresReturnValue, _requiresReturnValue);
+ qSwap(_inFormalParameterList, inFormalParameterList);
bytecodeGenerator = savedBytecodeGenerator;
+ delete _returnLabel;
+ _returnLabel = savedReturnLabel;
+ controlFlow = savedControlFlow;
+ functionEndsWithReturn = savedFunctionEndsWithReturn;
+ _functionContext = savedFunctionContext;
return leaveContext();
}
-bool Codegen::visit(FunctionSourceElement *ast)
-{
- if (hasError)
- return false;
-
- statement(ast->declaration);
- return false;
-}
-
-bool Codegen::visit(StatementSourceElement *ast)
-{
- if (hasError)
- return false;
-
- statement(ast->statement);
- return false;
-}
-
bool Codegen::visit(Block *ast)
{
if (hasError)
@@ -2247,6 +2898,7 @@ bool Codegen::visit(Block *ast)
RegisterScope scope(this);
+ ControlFlowBlock controlFlow(this, ast);
statementList(ast->statements);
return false;
}
@@ -2256,13 +2908,13 @@ bool Codegen::visit(BreakStatement *ast)
if (hasError)
return false;
- if (!_context->controlFlow) {
+ if (!controlFlow) {
throwSyntaxError(ast->lastSourceLocation(), QStringLiteral("Break outside of loop"));
return false;
}
- ControlFlow::Handler h = _context->controlFlow->getHandler(ControlFlow::Break, ast->label.toString());
- if (h.type == ControlFlow::Invalid) {
+ ControlFlow::UnwindTarget target = controlFlow->unwindTarget(ControlFlow::Break, ast->label.toString());
+ if (!target.linkLabel.isValid()) {
if (ast->label.isEmpty())
throwSyntaxError(ast->lastSourceLocation(), QStringLiteral("Break outside of loop"));
else
@@ -2270,7 +2922,7 @@ bool Codegen::visit(BreakStatement *ast)
return false;
}
- _context->controlFlow->jumpToHandler(h);
+ bytecodeGenerator->unwindToLabel(target.unwindLevel, target.linkLabel);
return false;
}
@@ -2282,13 +2934,13 @@ bool Codegen::visit(ContinueStatement *ast)
RegisterScope scope(this);
- if (!_context->controlFlow) {
+ if (!controlFlow) {
throwSyntaxError(ast->lastSourceLocation(), QStringLiteral("Continue outside of loop"));
return false;
}
- ControlFlow::Handler h = _context->controlFlow->getHandler(ControlFlow::Continue, ast->label.toString());
- if (h.type == ControlFlow::Invalid) {
+ ControlFlow::UnwindTarget target = controlFlow->unwindTarget(ControlFlow::Continue, ast->label.toString());
+ if (!target.linkLabel.isValid()) {
if (ast->label.isEmpty())
throwSyntaxError(ast->lastSourceLocation(), QStringLiteral("Undefined label '%1'").arg(ast->label.toString()));
else
@@ -2296,7 +2948,7 @@ bool Codegen::visit(ContinueStatement *ast)
return false;
}
- _context->controlFlow->jumpToHandler(h);
+ bytecodeGenerator->unwindToLabel(target.unwindLevel, target.linkLabel);
return false;
}
@@ -2370,45 +3022,86 @@ bool Codegen::visit(ForEachStatement *ast)
RegisterScope scope(this);
- Reference nextIterObj = Reference::fromStackSlot(this);
- Reference iterObj = Reference::fromStackSlot(this);
- Reference expr = expression(ast->expression);
- if (hasError)
- return true;
+ Reference iterator = Reference::fromStackSlot(this);
+ Reference lhsValue = Reference::fromStackSlot(this);
- expr.loadInAccumulator();
- Instruction::ForeachIteratorObject iteratorObjInstr;
- bytecodeGenerator->addInstruction(iteratorObjInstr);
- iterObj.storeConsumeAccumulator();
+ // There should be a temporal block, so that variables declared in lhs shadow outside vars.
+ // This block should define a temporal dead zone for those variables, which is not yet implemented.
+ {
+ RegisterScope innerScope(this);
+ ControlFlowBlock controlFlow(this, ast);
+ Reference expr = expression(ast->expression);
+ if (hasError)
+ return true;
- Reference lhs = expression(ast->initialiser).asLValue();
+ expr.loadInAccumulator();
+ Instruction::GetIterator iteratorObjInstr;
+ iteratorObjInstr.iterator = (ast->type == ForEachType::Of) ? 1 : 0;
+ bytecodeGenerator->addInstruction(iteratorObjInstr);
+ iterator.storeConsumeAccumulator();
+ }
BytecodeGenerator::Label in = bytecodeGenerator->newLabel();
BytecodeGenerator::Label end = bytecodeGenerator->newLabel();
+ BytecodeGenerator::Label done = bytecodeGenerator->newLabel();
- bytecodeGenerator->jump().link(in);
-
- ControlFlowLoop flow(this, &end, &in);
+ {
+ ControlFlowLoop flow(this, &end, &in, /*requiresUnwind*/ true);
+ bytecodeGenerator->jump().link(in);
+
+ BytecodeGenerator::Label body = bytecodeGenerator->label();
+
+ // each iteration gets it's own context, as per spec
+ {
+ RegisterScope innerScope(this);
+ ControlFlowBlock controlFlow(this, ast);
+
+ if (ExpressionNode *e = ast->lhs->expressionCast()) {
+ if (AST::Pattern *p = e->patternCast()) {
+ RegisterScope scope(this);
+ destructurePattern(p, lhsValue);
+ } else {
+ Reference lhs = expression(e);
+ if (hasError)
+ goto error;
+ lhs = lhs.asLValue();
+ lhsValue.loadInAccumulator();
+ lhs.storeConsumeAccumulator();
+ }
+ } else if (PatternElement *p = AST::cast<PatternElement *>(ast->lhs)) {
+ initializeAndDestructureBindingElement(p, lhsValue, /*isDefinition =*/ true);
+ if (hasError)
+ goto error;
+ } else {
+ Q_UNREACHABLE();
+ }
- BytecodeGenerator::Label body = bytecodeGenerator->label();
+ statement(ast->statement);
+ setJumpOutLocation(bytecodeGenerator, ast->statement, ast->forToken);
- nextIterObj.loadInAccumulator();
- lhs.storeConsumeAccumulator();
+ }
- statement(ast->statement);
- setJumpOutLocation(bytecodeGenerator, ast->statement, ast->forToken);
+ error:
+ in.link();
+ iterator.loadInAccumulator();
+ Instruction::IteratorNext next;
+ next.value = lhsValue.stackSlot();
+ bytecodeGenerator->addInstruction(next);
+ bytecodeGenerator->addJumpInstruction(Instruction::JumpFalse()).link(body);
+ bytecodeGenerator->jump().link(done);
+ }
- in.link();
+ end.link();
- iterObj.loadInAccumulator();
- Instruction::ForeachNextPropertyName nextPropInstr;
- bytecodeGenerator->addInstruction(nextPropInstr);
- nextIterObj.storeConsumeAccumulator();
+ if (ast->type == ForEachType::Of) {
+ Reference iteratorDone = Reference::fromConst(this, Encode(false)).storeOnStack();
+ iterator.loadInAccumulator();
+ Instruction::IteratorClose close;
+ close.done = iteratorDone.stackSlot();
+ bytecodeGenerator->addInstruction(close);
+ }
- Reference::fromConst(this, QV4::Encode::null()).loadInAccumulator();
- bytecodeGenerator->jumpStrictNotEqual(nextIterObj.stackSlot(), body);
-
- end.link();
+ done.link();
return false;
}
@@ -2420,7 +3113,12 @@ bool Codegen::visit(ForStatement *ast)
RegisterScope scope(this);
- statement(ast->initialiser);
+ ControlFlowBlock controlFlow(this, ast);
+
+ if (ast->initialiser)
+ statement(ast->initialiser);
+ else if (ast->declarations)
+ variableDeclarationList(ast->declarations);
BytecodeGenerator::Label cond = bytecodeGenerator->label();
BytecodeGenerator::Label body = bytecodeGenerator->newLabel();
@@ -2436,6 +3134,10 @@ bool Codegen::visit(ForStatement *ast)
setJumpOutLocation(bytecodeGenerator, ast->statement, ast->forToken);
step.link();
+ if (_context->requiresExecutionContext) {
+ Instruction::CloneBlockContext clone;
+ bytecodeGenerator->addInstruction(clone);
+ }
statement(ast->expression);
bytecodeGenerator->jump().link(cond);
@@ -2458,7 +3160,7 @@ bool Codegen::visit(IfStatement *ast)
trueLabel.link();
statement(ast->ok);
if (ast->ko) {
- if (endsWithReturn(ast)) {
+ if (endsWithReturn(_module, ast)) {
falseLabel.link();
statement(ast->ko);
} else {
@@ -2482,7 +3184,7 @@ bool Codegen::visit(LabelledStatement *ast)
RegisterScope scope(this);
// check that no outer loop contains the label
- ControlFlow *l = _context->controlFlow;
+ ControlFlow *l = controlFlow;
while (l) {
if (l->label() == ast->label) {
QString error = QString(QStringLiteral("Label '%1' has already been declared")).arg(ast->label.toString());
@@ -2497,9 +3199,7 @@ bool Codegen::visit(LabelledStatement *ast)
AST::cast<AST::WhileStatement *>(ast->statement) ||
AST::cast<AST::DoWhileStatement *>(ast->statement) ||
AST::cast<AST::ForStatement *>(ast->statement) ||
- AST::cast<AST::ForEachStatement *>(ast->statement) ||
- AST::cast<AST::LocalForStatement *>(ast->statement) ||
- AST::cast<AST::LocalForEachStatement *>(ast->statement)) {
+ AST::cast<AST::ForEachStatement *>(ast->statement)) {
statement(ast->statement); // labelledStatement will be associated with the ast->statement's loop.
} else {
BytecodeGenerator::Label breakLabel = bytecodeGenerator->newLabel();
@@ -2511,85 +3211,17 @@ bool Codegen::visit(LabelledStatement *ast)
return false;
}
-bool Codegen::visit(LocalForEachStatement *ast)
+void Codegen::emitReturn(const Reference &expr)
{
- if (hasError)
- return true;
-
- RegisterScope scope(this);
-
- Reference nextIterObj = Reference::fromStackSlot(this);
- Reference iterObj = Reference::fromStackSlot(this);
- Reference expr = expression(ast->expression);
- if (hasError)
- return true;
-
- variableDeclaration(ast->declaration);
-
- expr.loadInAccumulator();
- Instruction::ForeachIteratorObject iteratorObjInstr;
- bytecodeGenerator->addInstruction(iteratorObjInstr);
- iterObj.storeConsumeAccumulator();
-
- BytecodeGenerator::Label in = bytecodeGenerator->newLabel();
- BytecodeGenerator::Label end = bytecodeGenerator->newLabel();
-
- bytecodeGenerator->jump().link(in);
- ControlFlowLoop flow(this, &end, &in);
-
- BytecodeGenerator::Label body = bytecodeGenerator->label();
-
- Reference it = referenceForName(ast->declaration->name.toString(), true).asLValue();
-
- nextIterObj.loadInAccumulator();
- it.storeConsumeAccumulator();
-
- statement(ast->statement);
- setJumpOutLocation(bytecodeGenerator, ast->statement, ast->forToken);
-
- in.link();
-
- iterObj.loadInAccumulator();
- Instruction::ForeachNextPropertyName nextPropInstr;
- bytecodeGenerator->addInstruction(nextPropInstr);
- nextIterObj.storeConsumeAccumulator();
-
- Reference::fromConst(this, QV4::Encode::null()).loadInAccumulator();
- bytecodeGenerator->jumpStrictNotEqual(nextIterObj.stackSlot(), body);
-
- end.link();
-
- return false;
-}
-
-bool Codegen::visit(LocalForStatement *ast)
-{
- if (hasError)
- return true;
-
- RegisterScope scope(this);
-
- variableDeclarationList(ast->declarations);
-
- BytecodeGenerator::Label cond = bytecodeGenerator->label();
- BytecodeGenerator::Label body = bytecodeGenerator->newLabel();
- BytecodeGenerator::Label step = bytecodeGenerator->newLabel();
- BytecodeGenerator::Label end = bytecodeGenerator->newLabel();
-
- ControlFlowLoop flow(this, &end, &step);
-
- condition(ast->condition, &body, &end, true);
-
- body.link();
- statement(ast->statement);
- setJumpOutLocation(bytecodeGenerator, ast->statement, ast->forToken);
-
- step.link();
- statement(ast->expression);
- bytecodeGenerator->jump().link(cond);
- end.link();
-
- return false;
+ ControlFlow::UnwindTarget target = controlFlow ? controlFlow->unwindTarget(ControlFlow::Return) : ControlFlow::UnwindTarget();
+ if (target.linkLabel.isValid() && target.unwindLevel) {
+ Q_ASSERT(_returnAddress >= 0);
+ (void) expr.storeOnStack(_returnAddress);
+ bytecodeGenerator->unwindToLabel(target.unwindLevel, target.linkLabel);
+ } else {
+ expr.loadInAccumulator();
+ bytecodeGenerator->addInstruction(Instruction::Ret());
+ }
}
bool Codegen::visit(ReturnStatement *ast)
@@ -2597,7 +3229,7 @@ bool Codegen::visit(ReturnStatement *ast)
if (hasError)
return true;
- if (_context->compilationMode != FunctionCode && _context->compilationMode != QmlBinding) {
+ if (_functionContext->contextType != ContextType::Function && _functionContext->contextType != ContextType::Binding) {
throwSyntaxError(ast->returnToken, QStringLiteral("Return statement outside of function"));
return false;
}
@@ -2610,17 +3242,8 @@ bool Codegen::visit(ReturnStatement *ast)
expr = Reference::fromConst(this, Encode::undefined());
}
- if (_context->controlFlow && _context->controlFlow->returnRequiresUnwind()) {
- if (_returnAddress >= 0)
- (void) expr.storeOnStack(_returnAddress);
- else
- expr.loadInAccumulator();
- ControlFlow::Handler h = _context->controlFlow->getHandler(ControlFlow::Return);
- _context->controlFlow->jumpToHandler(h);
- } else {
- expr.loadInAccumulator();
- bytecodeGenerator->addInstruction(Instruction::Ret());
- }
+ emitReturn(expr);
+
return false;
}
@@ -2629,9 +3252,14 @@ bool Codegen::visit(SwitchStatement *ast)
if (hasError)
return true;
+ if (requiresReturnValue)
+ Reference::fromConst(this, Encode::undefined()).storeOnStack(_returnAddress);
+
RegisterScope scope(this);
if (ast->block) {
+ ControlFlowBlock controlFlow(this, ast->block);
+
BytecodeGenerator::Label switchEnd = bytecodeGenerator->newLabel();
Reference lhs = expression(ast->expression);
@@ -2674,6 +3302,7 @@ bool Codegen::visit(SwitchStatement *ast)
ControlFlowLoop flow(this, &switchEnd);
+ insideSwitch = true;
for (CaseClauses *it = ast->block->clauses; it; it = it->next) {
CaseClause *clause = it->clause;
blockMap[clause].link();
@@ -2694,6 +3323,7 @@ bool Codegen::visit(SwitchStatement *ast)
statementList(clause->statements);
}
+ insideSwitch = false;
switchEnd.link();
@@ -2713,25 +3343,15 @@ bool Codegen::visit(ThrowStatement *ast)
if (hasError)
return false;
- if (_context->controlFlow) {
- _context->controlFlow->handleThrow(expr);
- } else {
- expr.loadInAccumulator();
- Instruction::ThrowException instr;
- bytecodeGenerator->addInstruction(instr);
- }
+ expr.loadInAccumulator();
+ Instruction::ThrowException instr;
+ bytecodeGenerator->addInstruction(instr);
return false;
}
void Codegen::handleTryCatch(TryStatement *ast)
{
Q_ASSERT(ast);
- if (_context->isStrict &&
- (ast->catchExpression->name == QLatin1String("eval") || ast->catchExpression->name == QLatin1String("arguments"))) {
- throwSyntaxError(ast->catchExpression->identifierToken, QStringLiteral("Catch variable name may not be eval or arguments in strict mode"));
- return;
- }
-
RegisterScope scope(this);
BytecodeGenerator::Label noException = bytecodeGenerator->newLabel();
{
@@ -2761,8 +3381,6 @@ bool Codegen::visit(TryStatement *ast)
if (hasError)
return true;
- Q_ASSERT(_context->hasTry);
-
RegisterScope scope(this);
if (ast->finallyExpression && ast->finallyExpression->statement) {
@@ -2817,17 +3435,18 @@ bool Codegen::visit(WithStatement *ast)
RegisterScope scope(this);
- _context->hasWith = true;
-
Reference src = expression(ast->expression);
if (hasError)
return false;
src = src.storeOnStack(); // trigger load before we setup the exception handler, so exceptions here go to the right place
src.loadInAccumulator();
- ControlFlowWith flow(this);
-
- statement(ast->statement);
+ enterContext(ast);
+ {
+ ControlFlowWith flow(this);
+ statement(ast->statement);
+ }
+ leaveContext();
return false;
}
@@ -3080,6 +3699,22 @@ Codegen::RValue Codegen::RValue::storeOnStack() const
}
}
+void Codegen::RValue::loadInAccumulator() const
+{
+ switch (type) {
+ case Accumulator:
+ // nothing to do
+ return;
+ case StackSlot:
+ return Reference::fromStackSlot(codegen, theStackSlot).loadInAccumulator();
+ case Const:
+ return Reference::fromConst(codegen, constant).loadInAccumulator();
+ default:
+ Q_UNREACHABLE();
+ }
+
+}
+
Codegen::Reference::Reference(const Codegen::Reference &other)
{
*this = other;
@@ -3093,6 +3728,11 @@ Codegen::Reference &Codegen::Reference::operator =(const Reference &other)
case Invalid:
case Accumulator:
break;
+ case Super:
+ break;
+ case SuperProperty:
+ property = other.property;
+ break;
case StackSlot:
theStackSlot = other.theStackSlot;
break;
@@ -3127,6 +3767,7 @@ Codegen::Reference &Codegen::Reference::operator =(const Reference &other)
isArgOrEval = other.isArgOrEval;
codegen = other.codegen;
isReadonly = other.isReadonly;
+ isReferenceToConst = other.isReferenceToConst;
stackSlotIsLocalOrArgument = other.stackSlotIsLocalOrArgument;
isVolatile = other.isVolatile;
global = other.global;
@@ -3141,6 +3782,10 @@ bool Codegen::Reference::operator==(const Codegen::Reference &other) const
case Invalid:
case Accumulator:
break;
+ case Super:
+ return true;
+ case SuperProperty:
+ return property == other.property;
case StackSlot:
return theStackSlot == other.theStackSlot;
case ScopedLocal:
@@ -3184,6 +3829,9 @@ Codegen::Reference Codegen::Reference::asLValue() const
case Invalid:
case Accumulator:
Q_UNREACHABLE();
+ case Super:
+ codegen->throwSyntaxError(AST::SourceLocation(), QStringLiteral("Super lvalues not implemented."));
+ return *this;
case Member:
if (!propertyBase.isStackSlot()) {
Reference r = *this;
@@ -3209,6 +3857,28 @@ Codegen::Reference Codegen::Reference::storeConsumeAccumulator() const
return Reference();
}
+Codegen::Reference Codegen::Reference::baseObject() const
+{
+ if (type == Reference::QmlScopeObject || type == Reference::QmlContextObject) {
+ return Reference::fromStackSlot(codegen, qmlBase.stackSlot());
+ } else if (type == Reference::Member) {
+ RValue rval = propertyBase;
+ if (!rval.isValid())
+ return Reference::fromConst(codegen, Encode::undefined());
+ if (rval.isAccumulator())
+ return Reference::fromAccumulator(codegen);
+ if (rval.isStackSlot())
+ Reference::fromStackSlot(codegen, rval.stackSlot());
+ if (rval.isConst())
+ return Reference::fromConst(codegen, rval.constantValue());
+ Q_UNREACHABLE();
+ } else if (type == Reference::Subscript) {
+ return Reference::fromStackSlot(codegen, elementBase.stackSlot());
+ } else {
+ return Reference::fromConst(codegen, Encode::undefined());
+ }
+}
+
Codegen::Reference Codegen::Reference::storeOnStack() const
{ return doStoreOnStack(-1); }
@@ -3230,7 +3900,7 @@ Codegen::Reference Codegen::Reference::doStoreOnStack(int slotIndex) const
}
Reference slot = Reference::fromStackSlot(codegen, slotIndex);
- if (isConst()) {
+ if (isConstant()) {
Instruction::MoveConst move;
move.constIndex = codegen->registerConstant(constant);
move.destTemp = slot.stackSlot();
@@ -3280,7 +3950,29 @@ bool Codegen::Reference::storeWipesAccumulator() const
void Codegen::Reference::storeAccumulator() const
{
+ if (isReferenceToConst) {
+ // throw a type error
+ RegisterScope scope(codegen);
+ Reference r = codegen->referenceForName(QStringLiteral("TypeError"), false);
+ r = r.storeOnStack();
+ Instruction::Construct construct;
+ construct.func = r.stackSlot();
+ construct.argc = 0;
+ construct.argv = 0;
+ codegen->bytecodeGenerator->addInstruction(construct);
+ Instruction::ThrowException throwException;
+ codegen->bytecodeGenerator->addInstruction(throwException);
+ return;
+ }
switch (type) {
+ case Super:
+ Q_UNREACHABLE();
+ return;
+ case SuperProperty:
+ Instruction::StoreSuperProperty store;
+ store.property = property.stackSlot();
+ codegen->bytecodeGenerator->addInstruction(store);
+ return;
case StackSlot: {
Instruction::StoreReg store;
store.reg = theStackSlot;
@@ -3313,7 +4005,7 @@ void Codegen::Reference::storeAccumulator() const
}
} return;
case Member:
- if (codegen->useFastLookups) {
+ if (!disable_lookups && codegen->useFastLookups) {
Instruction::SetLookup store;
store.base = propertyBase.stackSlot();
store.index = codegen->registerSetterLookup(propertyNameIndex);
@@ -3358,6 +4050,14 @@ void Codegen::Reference::loadInAccumulator() const
switch (type) {
case Accumulator:
return;
+ case Super:
+ Q_UNREACHABLE();
+ return;
+ case SuperProperty:
+ Instruction::LoadSuperProperty load;
+ load.property = property.stackSlot();
+ codegen->bytecodeGenerator->addInstruction(load);
+ return;
case Const: {
QT_WARNING_PUSH
QT_WARNING_DISABLE_GCC("-Wmaybe-uninitialized") // the loads below are empty structs.
@@ -3429,7 +4129,7 @@ QT_WARNING_POP
return;
}
}
- if (codegen->useFastLookups && global) {
+ if (!disable_lookups && codegen->useFastLookups && global) {
Instruction::LoadGlobalLookup load;
load.index = codegen->registerGlobalGetterLookup(nameAsIndex());
codegen->bytecodeGenerator->addInstruction(load);
@@ -3440,46 +4140,23 @@ QT_WARNING_POP
}
return;
case Member:
- if (codegen->useFastLookups) {
- if (propertyBase.isAccumulator()) {
- Instruction::GetLookupA load;
- load.index = codegen->registerGetterLookup(propertyNameIndex);
- codegen->bytecodeGenerator->addInstruction(load);
- } else {
- Instruction::GetLookup load;
- load.base = propertyBase.storeOnStack().stackSlot();
- load.index = codegen->registerGetterLookup(propertyNameIndex);
- codegen->bytecodeGenerator->addInstruction(load);
- }
- } else {
- if (propertyBase.isAccumulator()) {
- Instruction::LoadPropertyA load;
- load.name = propertyNameIndex;
- codegen->bytecodeGenerator->addInstruction(load);
- } else {
- Instruction::LoadProperty load;
- load.base = propertyBase.storeOnStack().stackSlot();
- load.name = propertyNameIndex;
- codegen->bytecodeGenerator->addInstruction(load);
- }
- }
- return;
- case Subscript: {
- if (elementSubscript.isAccumulator()) {
- Instruction::LoadElementA load;
- load.base = elementBase;
- codegen->bytecodeGenerator->addInstruction(load);
- } else if (elementSubscript.isConst()) {
- Reference::fromConst(codegen, elementSubscript.constantValue()).loadInAccumulator();
- Instruction::LoadElementA load;
- load.base = elementBase;
+ if (!disable_lookups && codegen->useFastLookups) {
+ propertyBase.loadInAccumulator();
+ Instruction::GetLookup load;
+ load.index = codegen->registerGetterLookup(propertyNameIndex);
codegen->bytecodeGenerator->addInstruction(load);
} else {
- Instruction::LoadElement load;
- load.base = elementBase;
- load.index = elementSubscript.storeOnStack().stackSlot();
+ propertyBase.loadInAccumulator();
+ Instruction::LoadProperty load;
+ load.name = propertyNameIndex;
codegen->bytecodeGenerator->addInstruction(load);
}
+ return;
+ case Subscript: {
+ elementSubscript.loadInAccumulator();
+ Instruction::LoadElement load;
+ load.base = elementBase;
+ codegen->bytecodeGenerator->addInstruction(load);
} return;
case QmlScopeObject: {
Instruction::LoadScopeObjectProperty load;
diff --git a/src/qml/compiler/qv4codegen_p.h b/src/qml/compiler/qv4codegen_p.h
index d51dc29517..337c4dbfe3 100644
--- a/src/qml/compiler/qv4codegen_p.h
+++ b/src/qml/compiler/qv4codegen_p.h
@@ -66,6 +66,7 @@
#endif
#include <private/qv4util_p.h>
#include <private/qv4bytecodegenerator_p.h>
+#include <private/qv4stackframe_p.h>
QT_BEGIN_NAMESPACE
@@ -101,7 +102,7 @@ public:
const QString &sourceCode,
AST::Program *ast,
Module *module,
- CompilationMode mode = GlobalCode);
+ ContextType contextType = ContextType::Global);
public:
class VolatileMemoryLocationScanner;
@@ -172,11 +173,14 @@ public:
}
Q_REQUIRED_RESULT RValue storeOnStack() const;
+ void loadInAccumulator() const;
};
struct Reference {
enum Type {
Invalid,
Accumulator,
+ Super,
+ SuperProperty,
StackSlot,
ScopedLocal,
Name,
@@ -197,6 +201,8 @@ public:
Reference &operator =(const Reference &other);
bool operator==(const Reference &other) const;
+ bool operator!=(const Reference &other) const
+ { return !(*this == other); }
bool isValid() const { return type != Invalid; }
bool loadTriggersSideEffect() const {
@@ -209,8 +215,10 @@ public:
return false;
}
}
- bool isConst() const { return type == Const; }
+ bool isConstant() const { return type == Const; }
bool isAccumulator() const { return type == Accumulator; }
+ bool isSuper() const { return type == Super; }
+ bool isSuperProperty() const { return type == SuperProperty; }
bool isStackSlot() const { return type == StackSlot; }
bool isRegister() const {
return isStackSlot();
@@ -241,6 +249,9 @@ public:
static Reference fromAccumulator(Codegen *cg) {
return Reference(cg, Accumulator);
}
+ static Reference fromSuper(Codegen *cg) {
+ return Reference(cg, Super);
+ }
static Reference fromStackSlot(Codegen *cg, int tempIndex = -1, bool isLocal = false) {
Reference r(cg, StackSlot);
if (tempIndex == -1)
@@ -273,6 +284,12 @@ public:
r.propertyNameIndex = r.codegen->registerString(name);
return r;
}
+ static Reference fromSuperProperty(const Reference &property) {
+ Q_ASSERT(property.isStackSlot());
+ Reference r(property.codegen, SuperProperty);
+ r.property = property.stackSlot();
+ return r;
+ }
static Reference fromSubscript(const Reference &baseRef, const Reference &subscript) {
Q_ASSERT(baseRef.isStackSlot());
Reference r(baseRef.codegen, Subscript);
@@ -322,6 +339,8 @@ public:
Q_REQUIRED_RESULT Reference storeRetainAccumulator() const;
Reference storeConsumeAccumulator() const;
+ Q_REQUIRED_RESULT Reference baseObject() const;
+
bool storeWipesAccumulator() const;
void loadInAccumulator() const;
@@ -357,10 +376,12 @@ public:
qint16 qmlNotifyIndex;
PropertyCapturePolicy capturePolicy;
};
+ Moth::StackSlot property; // super property
};
QString name;
mutable bool isArgOrEval = false;
bool isReadonly = false;
+ bool isReferenceToConst = false;
bool stackSlotIsLocalOrArgument = false;
bool isVolatile = false;
bool global = false;
@@ -464,7 +485,10 @@ protected:
void enterContext(AST::Node *node);
int leaveContext();
-
+public:
+ Context *enterBlock(AST::Node *node);
+ int leaveBlock() { return leaveContext(); }
+protected:
void leaveLoop();
enum UnaryOperation {
@@ -482,6 +506,7 @@ protected:
void addCJump();
+public:
int registerString(const QString &name) {
return jsUnitGenerator->registerString(name);
}
@@ -493,33 +518,37 @@ protected:
// Returns index in _module->functions
virtual int defineFunction(const QString &name, AST::Node *ast,
AST::FormalParameterList *formals,
- AST::SourceElements *body);
+ AST::StatementList *body);
+protected:
void statement(AST::Statement *ast);
void statement(AST::ExpressionNode *ast);
void condition(AST::ExpressionNode *ast, const BytecodeGenerator::Label *iftrue,
const BytecodeGenerator::Label *iffalse,
bool trueBlockFollowsCondition);
Reference expression(AST::ExpressionNode *ast);
- Result sourceElement(AST::SourceElement *ast);
void accept(AST::Node *node);
- void functionBody(AST::FunctionBody *ast);
void program(AST::Program *ast);
- void sourceElements(AST::SourceElements *ast);
void statementList(AST::StatementList *ast);
- void variableDeclaration(AST::VariableDeclaration *ast);
+ void variableDeclaration(AST::PatternElement *ast);
void variableDeclarationList(AST::VariableDeclarationList *ast);
- Reference referenceForName(const QString &name, bool lhs);
+ Reference targetForPatternElement(AST::PatternElement *p);
+ void initializeAndDestructureBindingElement(AST::PatternElement *e, const Reference &baseRef = Reference(), bool isDefinition = false);
+ void destructurePropertyList(const Reference &object, AST::PatternPropertyList *bindingList, bool isDefinition = false);
+ void destructureElementList(const Reference &array, AST::PatternElementList *bindingList, bool isDefinition = false);
+ void destructurePattern(AST::Pattern *p, const Reference &rhs);
- void loadClosure(int index);
+ Reference referenceForPropertyName(const Codegen::Reference &object, AST::PropertyName *name);
// Hook provided to implement QML lookup semantics
virtual Reference fallbackNameLookup(const QString &name);
virtual void beginFunctionBodyHook() {}
+ void emitReturn(const Reference &expr);
+
// nodes
bool visit(AST::ArgumentList *ast) override;
bool visit(AST::CaseBlock *ast) override;
@@ -527,16 +556,10 @@ protected:
bool visit(AST::CaseClauses *ast) override;
bool visit(AST::Catch *ast) override;
bool visit(AST::DefaultClause *ast) override;
- bool visit(AST::ElementList *ast) override;
bool visit(AST::Elision *ast) override;
bool visit(AST::Finally *ast) override;
bool visit(AST::FormalParameterList *ast) override;
- bool visit(AST::FunctionBody *ast) override;
bool visit(AST::Program *ast) override;
- bool visit(AST::PropertyNameAndValue *ast) override;
- bool visit(AST::PropertyAssignmentList *ast) override;
- bool visit(AST::PropertyGetterSetter *ast) override;
- bool visit(AST::SourceElements *ast) override;
bool visit(AST::StatementList *ast) override;
bool visit(AST::UiArrayMemberList *ast) override;
bool visit(AST::UiImport *ast) override;
@@ -548,19 +571,25 @@ protected:
bool visit(AST::UiProgram *ast) override;
bool visit(AST::UiQualifiedId *ast) override;
bool visit(AST::UiQualifiedPragmaId *ast) override;
- bool visit(AST::VariableDeclaration *ast) override;
bool visit(AST::VariableDeclarationList *ast) override;
+ bool visit(AST::PatternElement *ast) override;
+ bool visit(AST::PatternElementList *ast) override;
+ bool visit(AST::PatternProperty *ast) override;
+ bool visit(AST::PatternPropertyList *ast) override;
+
// expressions
bool visit(AST::Expression *ast) override;
- bool visit(AST::ArrayLiteral *ast) override;
+ bool visit(AST::ArrayPattern *ast) override;
bool visit(AST::ArrayMemberExpression *ast) override;
bool visit(AST::BinaryExpression *ast) override;
bool visit(AST::CallExpression *ast) override;
bool visit(AST::ConditionalExpression *ast) override;
bool visit(AST::DeleteExpression *ast) override;
bool visit(AST::FalseLiteral *ast) override;
+ bool visit(AST::SuperLiteral *ast) override;
bool visit(AST::FieldMemberExpression *ast) override;
+ bool visit(AST::TaggedTemplate *ast) override;
bool visit(AST::FunctionExpression *ast) override;
bool visit(AST::IdentifierExpression *ast) override;
bool visit(AST::NestedExpression *ast) override;
@@ -569,13 +598,14 @@ protected:
bool visit(AST::NotExpression *ast) override;
bool visit(AST::NullExpression *ast) override;
bool visit(AST::NumericLiteral *ast) override;
- bool visit(AST::ObjectLiteral *ast) override;
+ bool visit(AST::ObjectPattern *ast) override;
bool visit(AST::PostDecrementExpression *ast) override;
bool visit(AST::PostIncrementExpression *ast) override;
bool visit(AST::PreDecrementExpression *ast) override;
bool visit(AST::PreIncrementExpression *ast) override;
bool visit(AST::RegExpLiteral *ast) override;
bool visit(AST::StringLiteral *ast) override;
+ bool visit(AST::TemplateLiteral *ast) override;
bool visit(AST::ThisExpression *ast) override;
bool visit(AST::TildeExpression *ast) override;
bool visit(AST::TrueLiteral *ast) override;
@@ -584,10 +614,9 @@ protected:
bool visit(AST::UnaryPlusExpression *ast) override;
bool visit(AST::VoidExpression *ast) override;
bool visit(AST::FunctionDeclaration *ast) override;
-
- // source elements
- bool visit(AST::FunctionSourceElement *ast) override;
- bool visit(AST::StatementSourceElement *ast) override;
+ bool visit(AST::YieldExpression *ast) override;
+ bool visit(AST::ClassExpression *ast) override;
+ bool visit(AST::ClassDeclaration *ast) override;
// statements
bool visit(AST::Block *ast) override;
@@ -601,8 +630,6 @@ protected:
bool visit(AST::ForStatement *ast) override;
bool visit(AST::IfStatement *ast) override;
bool visit(AST::LabelledStatement *ast) override;
- bool visit(AST::LocalForEachStatement *ast) override;
- bool visit(AST::LocalForStatement *ast) override;
bool visit(AST::ReturnStatement *ast) override;
bool visit(AST::SwitchStatement *ast) override;
bool visit(AST::ThrowStatement *ast) override;
@@ -631,18 +658,36 @@ public:
Reference binopHelper(QSOperator::Op oper, Reference &left, Reference &right);
Reference jumpBinop(QSOperator::Op oper, Reference &left, Reference &right);
- struct Arguments { int argc; int argv; };
+ struct Arguments { int argc; int argv; bool hasSpread; };
Arguments pushArgs(AST::ArgumentList *args);
+ void handleCall(Reference &base, Arguments calldata);
+
+ Arguments pushTemplateArgs(AST::TemplateLiteral *args);
+ int createTemplateArray(AST::TemplateLiteral *t);
void setUseFastLookups(bool b) { useFastLookups = b; }
void handleTryCatch(AST::TryStatement *ast);
void handleTryFinally(AST::TryStatement *ast);
+
+ Reference referenceForName(const QString &name, bool lhs);
+
QQmlRefPointer<QV4::CompiledData::CompilationUnit> generateCompilationUnit(bool generateUnitData = true);
static QQmlRefPointer<QV4::CompiledData::CompilationUnit> createUnitForLoading();
Context *currentContext() const { return _context; }
+ BytecodeGenerator *generator() const { return bytecodeGenerator; }
+
+ void loadClosure(int index);
+
+ Module *module() const { return _module; }
+
+ BytecodeGenerator::Label returnLabel() {
+ if (!_returnLabel)
+ _returnLabel = new BytecodeGenerator::Label(bytecodeGenerator->newLabel());
+ return *_returnLabel;
+ }
protected:
friend class ScanFunctions;
@@ -650,16 +695,22 @@ protected:
friend struct ControlFlowCatch;
friend struct ControlFlowFinally;
Result _expr;
- VolatileMemoryLocations _volataleMemoryLocations;
+ VolatileMemoryLocations _volatileMemoryLocations;
Module *_module;
int _returnAddress;
Context *_context;
+ Context *_functionContext = nullptr;
AST::LabelledStatement *_labelledStatement;
QV4::Compiler::JSUnitGenerator *jsUnitGenerator;
BytecodeGenerator *bytecodeGenerator = nullptr;
+ Moth::BytecodeGenerator::Label *_returnLabel = nullptr;
bool _strictMode;
bool useFastLookups = true;
bool requiresReturnValue = false;
+ bool insideSwitch = false;
+ bool inFormalParameterList = false;
+ bool functionEndsWithReturn = false;
+ ControlFlow *controlFlow = nullptr;
bool _fileNameIsUrl;
bool hasError;
@@ -667,6 +718,7 @@ protected:
private:
VolatileMemoryLocations scanVolatileMemoryLocations(AST::Node *ast) const;
+ void handleConstruct(const Reference &base, AST::ArgumentList *args);
};
}
diff --git a/src/qml/compiler/qv4compilationunitmapper_unix.cpp b/src/qml/compiler/qv4compilationunitmapper_unix.cpp
index 8348613888..1fef4d38f4 100644
--- a/src/qml/compiler/qv4compilationunitmapper_unix.cpp
+++ b/src/qml/compiler/qv4compilationunitmapper_unix.cpp
@@ -92,8 +92,16 @@ CompiledData::Unit *CompilationUnitMapper::open(const QString &cacheFileName, co
void CompilationUnitMapper::close()
{
- if (dataPtr != nullptr)
- munmap(dataPtr, length);
+ // Do not unmap the data here.
+ if (dataPtr != nullptr) {
+ // Do not unmap cache files that are built with the StaticData flag. That's the majority of
+ // them and it's necessary to benefit from the QString literal optimization. There might
+ // still be QString instances around that point into that memory area. The memory is backed
+ // on the disk, so the kernel is free to release the pages and all that remains is the
+ // address space allocation.
+ if (!(reinterpret_cast<CompiledData::Unit*>(dataPtr)->flags & CompiledData::Unit::StaticData))
+ munmap(dataPtr, length);
+ }
dataPtr = nullptr;
}
diff --git a/src/qml/compiler/qv4compilationunitmapper_win.cpp b/src/qml/compiler/qv4compilationunitmapper_win.cpp
index 8b000021f8..3e44d045fc 100644
--- a/src/qml/compiler/qv4compilationunitmapper_win.cpp
+++ b/src/qml/compiler/qv4compilationunitmapper_win.cpp
@@ -90,14 +90,9 @@ CompiledData::Unit *CompilationUnitMapper::open(const QString &cacheFileName, co
if (!header.verifyHeader(sourceTimeStamp, errorString))
return nullptr;
- const uint mappingFlags = header.flags & QV4::CompiledData::Unit::ContainsMachineCode
- ? PAGE_EXECUTE_READ : PAGE_READONLY;
- const uint viewFlags = header.flags & QV4::CompiledData::Unit::ContainsMachineCode
- ? (FILE_MAP_READ | FILE_MAP_EXECUTE) : FILE_MAP_READ;
-
// Data structure and qt version matched, so now we can access the rest of the file safely.
- HANDLE fileMappingHandle = CreateFileMapping(handle, 0, mappingFlags, 0, 0, 0);
+ HANDLE fileMappingHandle = CreateFileMapping(handle, 0, PAGE_READONLY, 0, 0, 0);
if (!fileMappingHandle) {
*errorString = qt_error_string(GetLastError());
return nullptr;
@@ -107,7 +102,7 @@ CompiledData::Unit *CompilationUnitMapper::open(const QString &cacheFileName, co
CloseHandle(fileMappingHandle);
});
- dataPtr = MapViewOfFile(fileMappingHandle, viewFlags, 0, 0, 0);
+ dataPtr = MapViewOfFile(fileMappingHandle, FILE_MAP_READ, 0, 0, 0);
if (!dataPtr) {
*errorString = qt_error_string(GetLastError());
return nullptr;
@@ -118,8 +113,15 @@ CompiledData::Unit *CompilationUnitMapper::open(const QString &cacheFileName, co
void CompilationUnitMapper::close()
{
- if (dataPtr != nullptr)
- UnmapViewOfFile(dataPtr);
+ if (dataPtr != nullptr) {
+ // Do not unmap cache files that are built with the StaticData flag. That's the majority of
+ // them and it's necessary to benefit from the QString literal optimization. There might
+ // still be QString instances around that point into that memory area. The memory is backed
+ // on the disk, so the kernel is free to release the pages and all that remains is the
+ // address space allocation.
+ if (!(reinterpret_cast<CompiledData::Unit*>(dataPtr)->flags & CompiledData::Unit::StaticData))
+ UnmapViewOfFile(dataPtr);
+ }
dataPtr = nullptr;
}
diff --git a/src/qml/compiler/qv4compileddata.cpp b/src/qml/compiler/qv4compileddata.cpp
index 8dcc068a06..02aee47eea 100644
--- a/src/qml/compiler/qv4compileddata.cpp
+++ b/src/qml/compiler/qv4compileddata.cpp
@@ -78,26 +78,10 @@ namespace CompiledData {
static_assert(sizeof(Unit::libraryVersionHash) >= QML_COMPILE_HASH_LENGTH + 1, "Compile hash length exceeds reserved size in data structure. Please adjust and bump the format version");
-#if !defined(V4_BOOTSTRAP)
-static QString cacheFilePath(const QUrl &url)
+CompilationUnit::CompilationUnit(const Unit *unitData)
{
- const QString localSourcePath = QQmlFile::urlToLocalFileOrQrc(url);
- const QString localCachePath = localSourcePath + QLatin1Char('c');
-#ifndef Q_OS_ANDROID
- if (QFile::exists(localCachePath) || QFileInfo(QFileInfo(localSourcePath).dir().absolutePath()).isWritable())
- return localCachePath;
-#endif
- QCryptographicHash fileNameHash(QCryptographicHash::Sha1);
- fileNameHash.addData(localSourcePath.toUtf8());
- QString directory = QStandardPaths::writableLocation(QStandardPaths::CacheLocation) + QLatin1String("/qmlcache/");
- QDir::root().mkpath(directory);
- return directory + QString::fromUtf8(fileNameHash.result().toHex()) + QLatin1Char('.') + QFileInfo(localCachePath).completeSuffix();
+ data = unitData;
}
-#endif
-
-CompilationUnit::CompilationUnit(const Unit *unitData)
- : data(unitData)
-{}
#ifndef V4_BOOTSTRAP
CompilationUnit::~CompilationUnit()
@@ -108,6 +92,17 @@ CompilationUnit::~CompilationUnit()
data = nullptr;
}
+QString CompilationUnit::localCacheFilePath(const QUrl &url)
+{
+ const QString localSourcePath = QQmlFile::urlToLocalFileOrQrc(url);
+ const QString cacheFileSuffix = QFileInfo(localSourcePath + QLatin1Char('c')).completeSuffix();
+ QCryptographicHash fileNameHash(QCryptographicHash::Sha1);
+ fileNameHash.addData(localSourcePath.toUtf8());
+ QString directory = QStandardPaths::writableLocation(QStandardPaths::CacheLocation) + QLatin1String("/qmlcache/");
+ QDir::root().mkpath(directory);
+ return directory + QString::fromUtf8(fileNameHash.result().toHex()) + QLatin1Char('.') + cacheFileSuffix;
+}
+
QV4::Function *CompilationUnit::linkToEngine(ExecutionEngine *engine)
{
this->engine = engine;
@@ -157,15 +152,15 @@ QV4::Function *CompilationUnit::linkToEngine(ExecutionEngine *engine)
}
if (data->jsClassTableSize) {
- runtimeClasses = (QV4::InternalClass**)malloc(data->jsClassTableSize * sizeof(QV4::InternalClass*));
+ runtimeClasses = (QV4::Heap::InternalClass **)malloc(data->jsClassTableSize * sizeof(QV4::Heap::InternalClass *));
+ // memset the regexps to 0 in case a GC run happens while we're within the loop below
+ memset(runtimeClasses, 0, data->jsClassTableSize * sizeof(QV4::Heap::InternalClass *));
for (uint i = 0; i < data->jsClassTableSize; ++i) {
int memberCount = 0;
const CompiledData::JSClassMember *member = data->jsClassAt(i, &memberCount);
- QV4::InternalClass *klass = engine->internalClasses[QV4::ExecutionEngine::Class_Object];
+ runtimeClasses[i] = engine->internalClasses(QV4::ExecutionEngine::Class_Object);
for (int j = 0; j < memberCount; ++j, ++member)
- klass = klass->addMember(engine->identifierTable->identifier(runtimeStrings[member->nameOffset]), member->isAccessor ? QV4::Attr_Accessor : QV4::Attr_Data);
-
- runtimeClasses[i] = klass;
+ runtimeClasses[i] = runtimeClasses[i]->addMember(engine->identifierTable->asPropertyKey(runtimeStrings[member->nameOffset]), member->isAccessor ? QV4::Attr_Accessor : QV4::Attr_Data);
}
}
@@ -215,8 +210,6 @@ void CompilationUnit::unlink()
propertyCaches.clear();
- for (int ii = 0; ii < dependentScripts.count(); ++ii)
- dependentScripts.at(ii)->release();
dependentScripts.clear();
typeNameCache = nullptr;
@@ -244,29 +237,43 @@ void CompilationUnit::unlink()
void CompilationUnit::markObjects(QV4::MarkStack *markStack)
{
- for (uint i = 0; i < data->stringTableSize; ++i)
- if (runtimeStrings[i])
- runtimeStrings[i]->mark(markStack);
+ if (runtimeStrings) {
+ for (uint i = 0; i < data->stringTableSize; ++i)
+ if (runtimeStrings[i])
+ runtimeStrings[i]->mark(markStack);
+ }
if (runtimeRegularExpressions) {
for (uint i = 0; i < data->regexpTableSize; ++i)
runtimeRegularExpressions[i].mark(markStack);
}
+ if (runtimeClasses) {
+ for (uint i = 0; i < data->jsClassTableSize; ++i)
+ if (runtimeClasses[i])
+ runtimeClasses[i]->mark(markStack);
+ }
+ for (QV4::Function *f : qAsConst(runtimeFunctions))
+ if (f && f->internalClass)
+ f->internalClass->mark(markStack);
+ for (QV4::Heap::InternalClass *c : qAsConst(runtimeBlocks))
+ if (c)
+ c->mark(markStack);
+
+ if (runtimeLookups) {
+ for (uint i = 0; i < data->lookupTableSize; ++i)
+ runtimeLookups[i].markObjects(markStack);
+ }
}
-IdentifierHash CompilationUnit::namedObjectsPerComponent(int componentObjectIndex)
+IdentifierHash CompilationUnit::createNamedObjectsPerComponent(int componentObjectIndex)
{
- auto it = namedObjectsPerComponentCache.find(componentObjectIndex);
- if (it == namedObjectsPerComponentCache.end()) {
- IdentifierHash namedObjectCache(engine);
- const CompiledData::Object *component = data->objectAt(componentObjectIndex);
- const quint32_le *namedObjectIndexPtr = component->namedObjectsInComponentTable();
- for (quint32 i = 0; i < component->nNamedObjectsInComponent; ++i, ++namedObjectIndexPtr) {
- const CompiledData::Object *namedObject = data->objectAt(*namedObjectIndexPtr);
- namedObjectCache.add(runtimeStrings[namedObject->idNameIndex], namedObject->id);
- }
- it = namedObjectsPerComponentCache.insert(componentObjectIndex, namedObjectCache);
- }
- return *it;
+ IdentifierHash namedObjectCache(engine);
+ const CompiledData::Object *component = data->objectAt(componentObjectIndex);
+ const quint32_le *namedObjectIndexPtr = component->namedObjectsInComponentTable();
+ for (quint32 i = 0; i < component->nNamedObjectsInComponent; ++i, ++namedObjectIndexPtr) {
+ const CompiledData::Object *namedObject = data->objectAt(*namedObjectIndexPtr);
+ namedObjectCache.add(runtimeStrings[namedObject->idNameIndex], namedObject->id);
+ }
+ return *namedObjectsPerComponentCache.insert(componentObjectIndex, namedObjectCache);
}
void CompilationUnit::finalizeCompositeType(QQmlEnginePrivate *qmlEngine)
@@ -344,7 +351,11 @@ bool CompilationUnit::loadFromDisk(const QUrl &url, const QDateTime &sourceTimeS
const QString sourcePath = QQmlFile::urlToLocalFileOrQrc(url);
QScopedPointer<CompilationUnitMapper> cacheFile(new CompilationUnitMapper());
- CompiledData::Unit *mappedUnit = cacheFile->open(cacheFilePath(url), sourceTimeStamp, errorString);
+ QString cachePath = sourcePath + QLatin1Char('c');
+ if (!QFile::exists(cachePath))
+ cachePath = localCacheFilePath(url);
+
+ CompiledData::Unit *mappedUnit = cacheFile->open(cachePath, sourceTimeStamp, errorString);
if (!mappedUnit)
return false;
@@ -367,7 +378,22 @@ void CompilationUnit::linkBackendToEngine(ExecutionEngine *engine)
runtimeFunctions.resize(data->functionTableSize);
for (int i = 0 ;i < runtimeFunctions.size(); ++i) {
const QV4::CompiledData::Function *compiledFunction = data->functionAt(i);
- runtimeFunctions[i] = new QV4::Function(engine, this, compiledFunction, &Moth::VME::exec);
+ runtimeFunctions[i] = new QV4::Function(engine, this, compiledFunction);
+ }
+
+ Scope scope(engine);
+ Scoped<InternalClass> ic(scope);
+
+ runtimeBlocks.resize(data->blockTableSize);
+ for (int i = 0 ;i < runtimeBlocks.size(); ++i) {
+ const QV4::CompiledData::Block *compiledBlock = data->blockAt(i);
+ ic = engine->internalClasses(EngineBase::Class_CallContext);
+
+ // first locals
+ const quint32_le *localsIndices = compiledBlock->localsTable();
+ for (quint32 i = 0; i < compiledBlock->nLocals; ++i)
+ ic = ic->addMember(engine->identifierTable->asPropertyKey(runtimeStrings[localsIndices[i]]), Attr_NotConfigurable);
+ runtimeBlocks[i] = ic->d();
}
}
@@ -391,7 +417,7 @@ bool CompilationUnit::saveToDisk(const QUrl &unitUrl, QString *errorString)
*errorString = QStringLiteral("File has to be a local file.");
return false;
}
- const QString outputFileName = cacheFilePath(unitUrl);
+ const QString outputFileName = localCacheFilePath(unitUrl);
#endif
#if QT_CONFIG(temporaryfile)
@@ -476,8 +502,11 @@ Unit *CompilationUnit::createUnitData(QmlIR::Document *irDocument)
QQmlJS::AST::FormalParameterList *parameters = QQmlJS::AST::cast<QQmlJS::AST::FunctionDeclaration*>(foe->node)->formals;
changedSignalParameters << parameters;
- for (; parameters; parameters = parameters->next)
- stringTable.registerString(parameters->name.toString());
+ if (parameters) {
+ const QStringList formals = parameters->formals();
+ for (const QString &arg : formals)
+ stringTable.registerString(arg);
+ }
}
}
@@ -500,14 +529,14 @@ Unit *CompilationUnit::createUnitData(QmlIR::Document *irDocument)
function->formalsOffset = signalParameterNameTableOffset - jsUnit->functionOffsetTable()[functionIndex];
- for (QQmlJS::AST::FormalParameterList *parameters = changedSignalParameters.at(i);
- parameters; parameters = parameters->next) {
- signalParameterNameTable.append(stringTable.getStringId(parameters->name.toString()));
- function->nFormals = function->nFormals + 1;
- }
+ if (QQmlJS::AST::FormalParameterList *parameters = changedSignalParameters.at(i)) {
+ const QStringList formals = parameters->formals();
+ for (const QString &arg : formals)
+ signalParameterNameTable.append(stringTable.getStringId(arg));
- // Hack to ensure an activation is created.
- function->flags |= QV4::CompiledData::Function::HasCatchOrWith | QV4::CompiledData::Function::HasDirectEval;
+ function->nFormals = formals.size();
+ }
+ function->length = function->nFormals;
signalParameterNameTableOffset += function->nFormals * sizeof(quint32);
}
@@ -626,7 +655,7 @@ QString Binding::valueAsScriptString(const Unit *unit) const
/*!
Returns the property cache, if one alread exists. The cache is not referenced.
*/
-QQmlPropertyCache *ResolvedTypeReference::propertyCache() const
+QQmlRefPointer<QQmlPropertyCache> ResolvedTypeReference::propertyCache() const
{
if (type.isValid())
return typePropertyCache;
@@ -637,7 +666,7 @@ QQmlPropertyCache *ResolvedTypeReference::propertyCache() const
/*!
Returns the property cache, creating one if it doesn't already exist. The cache is not referenced.
*/
-QQmlPropertyCache *ResolvedTypeReference::createPropertyCache(QQmlEngine *engine)
+QQmlRefPointer<QQmlPropertyCache> ResolvedTypeReference::createPropertyCache(QQmlEngine *engine)
{
if (typePropertyCache) {
return typePropertyCache;
diff --git a/src/qml/compiler/qv4compileddata_p.h b/src/qml/compiler/qv4compileddata_p.h
index 1df9d6794f..c1be00ea27 100644
--- a/src/qml/compiler/qv4compileddata_p.h
+++ b/src/qml/compiler/qv4compileddata_p.h
@@ -186,32 +186,72 @@ struct JSClass
};
static_assert(sizeof(JSClass) == 4, "JSClass structure needs to have the expected size to be binary compatible on disk when generated by host compiler and loaded by target");
+// This data structure is intended to be binary compatible with QStringData/QStaticStringData on
+// 64-bit and 32-bit little-endian architectures, in all directions. So the same structure mapped
+// from a file must be castable to a QStringData regardless of the pointer size. With the first
+// few fields that's easy, they're always 32-bit. However the offset field of QArrayData is a
+// ptrdiff_t and thus variable in size.
+// On 64-bit systems compilers enforce an 8-byte alignment and thus place it at offset 16, while
+// on 32-bit systems offset 12 is sufficient. Therefore the two values don't overlap and contain
+// the same value.
struct String
{
+ qint32_le refcount; // -1
qint32_le size;
+ quint32_le allocAndCapacityReservedFlag; // 0
+ quint32_le offsetOn32Bit;
+ quint64_le offsetOn64Bit;
// uint16 strdata[]
static int calculateSize(const QString &str) {
- return (sizeof(String) + str.length() * sizeof(quint16) + 7) & ~0x7;
+ return (sizeof(String) + (str.length() + 1) * sizeof(quint16) + 7) & ~0x7;
}
};
-static_assert(sizeof(String) == 4, "String structure needs to have the expected size to be binary compatible on disk when generated by host compiler and loaded by target");
+static_assert(sizeof(String) == 24, "String structure needs to have the expected size to be binary compatible on disk when generated by host compiler and loaded by target");
+
+// Ensure compatibility with QString
+static_assert(offsetof(QArrayData, ref) == offsetof(String, refcount), "refcount must be at the same location");
+static_assert(offsetof(QArrayData, size) == offsetof(String, size), "size must be at the same location");
+static_assert(offsetof(String, offsetOn64Bit) == 16, "offset must be at 8-byte aligned location");
+static_assert(offsetof(String, offsetOn32Bit) == 12, "offset must be at 4-byte aligned location");
+#if QT_POINTER_SIZE == 8
+static_assert(offsetof(QArrayData, offset) == offsetof(String, offsetOn64Bit), "offset must be at the same location");
+#else
+static_assert(offsetof(QArrayData, offset) == offsetof(String, offsetOn32Bit), "offset must be at the same location");
+#endif
struct CodeOffsetToLine {
quint32_le codeOffset;
quint32_le line;
};
+struct Block
+{
+ quint32_le nLocals;
+ quint32_le localsOffset;
+
+ const quint32_le *localsTable() const { return reinterpret_cast<const quint32_le *>(reinterpret_cast<const char *>(this) + localsOffset); }
+
+ static int calculateSize(int nLocals) {
+ int trailingData = nLocals*sizeof (quint32);
+ size_t size = align(align(sizeof(Block)) + size_t(trailingData));
+ Q_ASSERT(size < INT_MAX);
+ return int(size);
+ }
+
+ static size_t align(size_t a) {
+ return (a + 7) & ~size_t(7);
+ }
+};
+
// Function is aligned on an 8-byte boundary to make sure there are no bus errors or penalties
// for unaligned access. The ordering of the fields is also from largest to smallest.
struct Function
{
enum Flags : unsigned int {
IsStrict = 0x1,
- HasDirectEval = 0x2,
- UsesArgumentsObject = 0x4,
-// Unused = 0x8,
- HasCatchOrWith = 0x10
+ IsArrowFunction = 0x2,
+ IsGenerator = 0x4
};
// Absolute offset into file where the code for this function is located.
@@ -219,6 +259,7 @@ struct Function
quint32_le codeSize;
quint32_le nameIndex;
+ quint32_le length;
quint32_le nFormals;
quint32_le formalsOffset;
quint32_le nLocals;
@@ -238,16 +279,14 @@ struct Function
quint32_le dependingScopePropertiesOffset; // Array of int pairs (property index and notify index)
// Qml Extensions End
-// quint32 formalsIndex[nFormals]
-// quint32 localsIndex[nLocals]
-// quint32 offsetForInnerFunctions[nInnerFunctions]
-// Function[nInnerFunctions]
-
// Keep all unaligned data at the end
quint8 flags;
quint8 padding1;
quint16_le padding2;
+ // quint32 formalsIndex[nFormals]
+ // quint32 localsIndex[nLocals]
+
const quint32_le *formalsTable() const { return reinterpret_cast<const quint32_le *>(reinterpret_cast<const char *>(this) + formalsOffset); }
const quint32_le *localsTable() const { return reinterpret_cast<const quint32_le *>(reinterpret_cast<const char *>(this) + localsOffset); }
const CodeOffsetToLine *lineNumberTable() const { return reinterpret_cast<const CodeOffsetToLine *>(reinterpret_cast<const char *>(this) + lineNumberOffset); }
@@ -260,7 +299,7 @@ struct Function
const quint32_le *formalsEnd() const { return formalsTable() + nFormals; }
// ---
- const uchar *code() const { return reinterpret_cast<const uchar *>(this) + codeOffset; }
+ const char *code() const { return reinterpret_cast<const char *>(this) + codeOffset; }
inline bool hasQmlDependencies() const { return nDependingIdObjects > 0 || nDependingContextProperties > 0 || nDependingScopeProperties > 0; }
@@ -276,7 +315,44 @@ struct Function
return (a + 7) & ~size_t(7);
}
};
-static_assert(sizeof(Function) == 76, "Function structure needs to have the expected size to be binary compatible on disk when generated by host compiler and loaded by target");
+static_assert(sizeof(Function) == 80, "Function structure needs to have the expected size to be binary compatible on disk when generated by host compiler and loaded by target");
+
+struct Method {
+ enum Type {
+ Regular,
+ Getter,
+ Setter
+ };
+
+ quint32_le name;
+ quint32_le type;
+ quint32_le function;
+};
+
+struct Class
+{
+ quint32_le nameIndex;
+ quint32_le scopeIndex;
+ quint32_le constructorFunction;
+ quint32_le nStaticMethods;
+ quint32_le nMethods;
+ quint32_le methodTableOffset;
+
+ const Method *methodTable() const { return reinterpret_cast<const Method *>(reinterpret_cast<const char *>(this) + methodTableOffset); }
+
+ static int calculateSize(int nStaticMethods, int nMethods) {
+ int trailingData = (nStaticMethods + nMethods) * sizeof(Method);
+ size_t size = align(sizeof(Class) + trailingData);
+ Q_ASSERT(size < INT_MAX);
+ return int(size);
+ }
+
+ static size_t align(size_t a) {
+ return (a + 7) & ~size_t(7);
+ }
+};
+static_assert(sizeof(Class) == 24, "Function structure needs to have the expected size to be binary compatible on disk when generated by host compiler and loaded by target");
+
// Qml data structures
@@ -385,8 +461,8 @@ struct Q_QML_PRIVATE_EXPORT Binding
static QString escapedString(const QString &string);
- bool containsTranslations() const { return type == Type_Translation || type == Type_TranslationById; }
- bool evaluatesToString() const { return type == Type_String || containsTranslations(); }
+ bool isTranslationBinding() const { return type == Type_Translation || type == Type_TranslationById; }
+ bool evaluatesToString() const { return type == Type_String || isTranslationBinding(); }
QString valueAsString(const Unit *unit) const;
QString valueAsScriptString(const Unit *unit) const;
@@ -701,14 +777,17 @@ struct Unit
StaticData = 0x4, // Unit data persistent in memory?
IsSingleton = 0x8,
IsSharedLibrary = 0x10, // .pragma shared?
- ContainsMachineCode = 0x20, // used to determine if we need to mmap with execute permissions
- PendingTypeCompilation = 0x40 // the QML data structures present are incomplete and require type compilation
+ PendingTypeCompilation = 0x20 // the QML data structures present are incomplete and require type compilation
};
quint32_le flags;
quint32_le stringTableSize;
quint32_le offsetToStringTable;
quint32_le functionTableSize;
quint32_le offsetToFunctionTable;
+ quint32_le classTableSize;
+ quint32_le offsetToClassTable;
+ quint32_le blockTableSize;
+ quint32_le offsetToBlockTable;
quint32_le lookupTableSize;
quint32_le offsetToLookupTable;
quint32_le regexpTableSize;
@@ -753,11 +832,11 @@ struct Unit
if (str->size == 0)
return QString();
#if Q_BYTE_ORDER == Q_LITTLE_ENDIAN
+ if (flags & StaticData) {
+ const QStringDataPtr holder = { const_cast<QStringData *>(reinterpret_cast<const QStringData*>(str)) };
+ return QString(holder);
+ }
const QChar *characters = reinterpret_cast<const QChar *>(str + 1);
- // Too risky to do this while we unmap disk backed compilation but keep pointers to string
- // data in the identifier tables.
- // if (flags & StaticData)
- // return QString::fromRawData(characters, str->size);
return QString(characters, str->size);
#else
const quint16_le *characters = reinterpret_cast<const quint16_le *>(str + 1);
@@ -770,6 +849,8 @@ struct Unit
}
const quint32_le *functionOffsetTable() const { return reinterpret_cast<const quint32_le*>((reinterpret_cast<const char *>(this)) + offsetToFunctionTable); }
+ const quint32_le *classOffsetTable() const { return reinterpret_cast<const quint32_le*>((reinterpret_cast<const char *>(this)) + offsetToClassTable); }
+ const quint32_le *blockOffsetTable() const { return reinterpret_cast<const quint32_le*>((reinterpret_cast<const char *>(this)) + offsetToBlockTable); }
const Function *functionAt(int idx) const {
const quint32_le *offsetTable = functionOffsetTable();
@@ -777,6 +858,18 @@ struct Unit
return reinterpret_cast<const Function*>(reinterpret_cast<const char *>(this) + offset);
}
+ const Class *classAt(int idx) const {
+ const quint32_le *offsetTable = classOffsetTable();
+ const quint32_le offset = offsetTable[idx];
+ return reinterpret_cast<const Class *>(reinterpret_cast<const char *>(this) + offset);
+ }
+
+ const Block *blockAt(int idx) const {
+ const quint32_le *offsetTable = blockOffsetTable();
+ const quint32_le offset = offsetTable[idx];
+ return reinterpret_cast<const Block *>(reinterpret_cast<const char *>(this) + offset);
+ }
+
const Lookup *lookupTable() const { return reinterpret_cast<const Lookup*>(reinterpret_cast<const char *>(this) + offsetToLookupTable); }
const RegExp *regexpAt(int index) const {
return reinterpret_cast<const RegExp*>(reinterpret_cast<const char *>(this) + offsetToRegexpTable + index * sizeof(RegExp));
@@ -795,7 +888,7 @@ struct Unit
}
};
-static_assert(sizeof(Unit) == 192, "Unit structure needs to have the expected size to be binary compatible on disk when generated by host compiler and loaded by target");
+static_assert(sizeof(Unit) == 208, "Unit structure needs to have the expected size to be binary compatible on disk when generated by host compiler and loaded by target");
struct TypeReference
{
@@ -832,9 +925,7 @@ struct TypeReferenceMap : QHash<int, TypeReference>
auto propEnd = obj->propertiesEnd();
for ( ; prop != propEnd; ++prop) {
if (prop->type >= QV4::CompiledData::Property::Custom) {
- // ### FIXME: We could report the more accurate location here by using prop->location, but the old
- // compiler can't and the tests expect it to be the object location right now.
- TypeReference &r = this->add(prop->customTypeNameIndex, obj->location);
+ TypeReference &r = this->add(prop->customTypeNameIndex, prop->location);
r.errorWhenNotFound = true;
}
}
@@ -881,6 +972,8 @@ struct Q_QML_PRIVATE_EXPORT CompilationUnitBase
QV4::Heap::String **runtimeStrings = nullptr; // Array
const Value* constants = nullptr;
QV4::Value *runtimeRegularExpressions = nullptr;
+ const Unit *data = nullptr;
+ QV4::Heap::InternalClass **runtimeClasses = nullptr;
};
Q_STATIC_ASSERT(std::is_standard_layout<CompilationUnitBase>::value);
@@ -915,8 +1008,6 @@ public:
return refCount.load();
}
- const Unit *data = nullptr;
-
// Called only when building QML, when we build the header for JS first and append QML data
QV4::CompiledData::Unit *createUnitData(QmlIR::Document *irDocument);
@@ -943,14 +1034,14 @@ public:
}
QV4::Lookup *runtimeLookups = nullptr;
- QV4::InternalClass **runtimeClasses = nullptr;
QVector<QV4::Function *> runtimeFunctions;
+ QVector<QV4::Heap::InternalClass *> runtimeBlocks;
mutable QQmlNullableValue<QUrl> m_url;
mutable QQmlNullableValue<QUrl> m_finalUrl;
// QML specific fields
QQmlPropertyCacheVector propertyCaches;
- QQmlPropertyCache *rootPropertyCache() const { return propertyCaches.at(/*root object*/0); }
+ QQmlRefPointer<QQmlPropertyCache> rootPropertyCache() const { return propertyCaches.at(/*root object*/0); }
QQmlRefPointer<QQmlTypeNameCache> typeNameCache;
@@ -962,7 +1053,7 @@ public:
// mapping from component object index (CompiledData::Unit object index that points to component) to identifier hash of named objects
// this is initialized on-demand by QQmlContextData
QHash<int, IdentifierHash> namedObjectsPerComponentCache;
- IdentifierHash namedObjectsPerComponent(int componentObjectIndex);
+ inline IdentifierHash namedObjectsPerComponent(int componentObjectIndex);
void finalizeCompositeType(QQmlEnginePrivate *qmlEngine);
@@ -970,7 +1061,7 @@ public:
int totalParserStatusCount = 0; // Number of instantiated types that are QQmlParserStatus subclasses
int totalObjectCount = 0; // Number of objects explicitly instantiated
- QVector<QQmlScriptData *> dependentScripts;
+ QVector<QQmlRefPointer<QQmlScriptData>> dependentScripts;
ResolvedTypeReferenceMap resolvedTypes;
bool verifyChecksum(const DependentTypesHasher &dependencyHasher) const;
@@ -1010,6 +1101,8 @@ public:
bool loadFromDisk(const QUrl &url, const QDateTime &sourceTimeStamp, QString *errorString);
+ static QString localCacheFilePath(const QUrl &url);
+
protected:
void linkBackendToEngine(QV4::ExecutionEngine *engine);
#endif // V4_BOOTSTRAP
@@ -1019,6 +1112,8 @@ private:
QAtomicInt refCount = 1;
+ Q_NEVER_INLINE IdentifierHash createNamedObjectsPerComponent(int componentObjectIndex);
+
public:
#if defined(V4_BOOTSTRAP)
bool saveToDisk(const QString &outputFileName, QString *errorString);
@@ -1046,17 +1141,24 @@ struct ResolvedTypeReference
// therefore cannot have a property cache installed when instantiated.
bool isFullyDynamicType;
- QQmlPropertyCache *propertyCache() const;
- QQmlPropertyCache *createPropertyCache(QQmlEngine *);
+ QQmlRefPointer<QQmlPropertyCache> propertyCache() const;
+ QQmlRefPointer<QQmlPropertyCache> createPropertyCache(QQmlEngine *);
bool addToHash(QCryptographicHash *hash, QQmlEngine *engine);
void doDynamicTypeCheck();
};
-#endif
+IdentifierHash CompilationUnit::namedObjectsPerComponent(int componentObjectIndex)
+{
+ auto it = namedObjectsPerComponentCache.find(componentObjectIndex);
+ if (Q_UNLIKELY(it == namedObjectsPerComponentCache.end()))
+ return createNamedObjectsPerComponent(componentObjectIndex);
+ return *it;
}
+#endif // V4_BOOTSTRAP
-}
+} // CompiledData namespace
+} // QV4 namespace
Q_DECLARE_TYPEINFO(QV4::CompiledData::JSClassMember, Q_PRIMITIVE_TYPE);
diff --git a/src/qml/compiler/qv4compiler.cpp b/src/qml/compiler/qv4compiler.cpp
index c9e535c93f..f1afad4965 100644
--- a/src/qml/compiler/qv4compiler.cpp
+++ b/src/qml/compiler/qv4compiler.cpp
@@ -90,7 +90,11 @@ void QV4::Compiler::StringTableGenerator::serialize(CompiledData::Unit *unit)
const QString &qstr = strings.at(i);
QV4::CompiledData::String *s = reinterpret_cast<QV4::CompiledData::String *>(stringData);
+ s->refcount = -1;
s->size = qstr.length();
+ s->allocAndCapacityReservedFlag = 0;
+ s->offsetOn32Bit = sizeof(QV4::CompiledData::String);
+ s->offsetOn64Bit = sizeof(QV4::CompiledData::String);
#if Q_BYTE_ORDER == Q_LITTLE_ENDIAN
memcpy(s + 1, qstr.constData(), qstr.length()*sizeof(ushort));
#else
@@ -98,6 +102,7 @@ void QV4::Compiler::StringTableGenerator::serialize(CompiledData::Unit *unit)
for (int i = 0; i < qstr.length(); ++i)
uc[i] = qToLittleEndian<ushort>(qstr.at(i).unicode());
#endif
+ reinterpret_cast<ushort *>(s + 1)[s->size] = 0;
stringData += QV4::CompiledData::String::calculateSize(qstr);
}
@@ -183,7 +188,7 @@ QV4::ReturnedValue QV4::Compiler::JSUnitGenerator::constant(int idx)
return constants.at(idx);
}
-int QV4::Compiler::JSUnitGenerator::registerJSClass(const QVector<MemberInfo> &members)
+int QV4::Compiler::JSUnitGenerator::registerJSClass(const QStringList &members)
{
// ### re-use existing class definitions.
@@ -197,31 +202,15 @@ int QV4::Compiler::JSUnitGenerator::registerJSClass(const QVector<MemberInfo> &m
jsClass->nMembers = members.size();
CompiledData::JSClassMember *member = reinterpret_cast<CompiledData::JSClassMember*>(jsClass + 1);
- for (const MemberInfo &memberInfo : members) {
- member->nameOffset = registerString(memberInfo.name);
- member->isAccessor = memberInfo.isAccessor;
+ for (const auto &name : members) {
+ member->nameOffset = registerString(name);
+ member->isAccessor = false;
++member;
}
return jsClassOffsets.size() - 1;
}
-int QV4::Compiler::JSUnitGenerator::registerJSClass(int count, CompiledData::JSClassMember *members)
-{
- const int size = CompiledData::JSClass::calculateSize(count);
- jsClassOffsets.append(jsClassData.size());
- const int oldSize = jsClassData.size();
- jsClassData.resize(jsClassData.size() + size);
- memset(jsClassData.data() + oldSize, 0, size);
-
- CompiledData::JSClass *jsClass = reinterpret_cast<CompiledData::JSClass*>(jsClassData.data() + oldSize);
- jsClass->nMembers = count;
- CompiledData::JSClassMember *jsClassMembers = reinterpret_cast<CompiledData::JSClassMember*>(jsClass + 1);
- memcpy(jsClassMembers, members, sizeof(CompiledData::JSClassMember)*count);
-
- return jsClassOffsets.size() - 1;
-}
-
QV4::CompiledData::Unit *QV4::Compiler::JSUnitGenerator::generateUnit(GeneratorOption option)
{
registerString(module->fileName);
@@ -233,28 +222,46 @@ QV4::CompiledData::Unit *QV4::Compiler::JSUnitGenerator::generateUnit(GeneratorO
for (int i = 0; i < f->locals.size(); ++i)
registerString(f->locals.at(i));
}
+ for (Context *c : qAsConst(module->blocks)) {
+ for (int i = 0; i < c->locals.size(); ++i)
+ registerString(c->locals.at(i));
+ }
- Q_ALLOCA_VAR(quint32_le, functionOffsets, module->functions.size() * sizeof(quint32_le));
+ Q_ALLOCA_VAR(quint32_le, blockClassAndFunctionOffsets, (module->functions.size() + module->classes.size() + module->blocks.size()) * sizeof(quint32_le));
uint jsClassDataOffset = 0;
char *dataPtr;
CompiledData::Unit *unit;
{
- QV4::CompiledData::Unit tempHeader = generateHeader(option, functionOffsets, &jsClassDataOffset);
+ QV4::CompiledData::Unit tempHeader = generateHeader(option, blockClassAndFunctionOffsets, &jsClassDataOffset);
dataPtr = reinterpret_cast<char *>(malloc(tempHeader.unitSize));
memset(dataPtr, 0, tempHeader.unitSize);
memcpy(&unit, &dataPtr, sizeof(CompiledData::Unit*));
memcpy(unit, &tempHeader, sizeof(tempHeader));
}
- memcpy(dataPtr + unit->offsetToFunctionTable, functionOffsets, unit->functionTableSize * sizeof(quint32_le));
+ memcpy(dataPtr + unit->offsetToFunctionTable, blockClassAndFunctionOffsets, unit->functionTableSize * sizeof(quint32_le));
+ memcpy(dataPtr + unit->offsetToClassTable, blockClassAndFunctionOffsets + unit->functionTableSize, unit->classTableSize * sizeof(quint32_le));
+ memcpy(dataPtr + unit->offsetToBlockTable, blockClassAndFunctionOffsets + unit->functionTableSize + unit->classTableSize, unit->blockTableSize * sizeof(quint32_le));
for (int i = 0; i < module->functions.size(); ++i) {
Context *function = module->functions.at(i);
if (function == module->rootContext)
unit->indexOfRootFunction = i;
- writeFunction(dataPtr + functionOffsets[i], function);
+ writeFunction(dataPtr + blockClassAndFunctionOffsets[i], function);
+ }
+
+ for (int i = 0; i < module->classes.size(); ++i) {
+ const Class &c = module->classes.at(i);
+
+ writeClass(dataPtr + blockClassAndFunctionOffsets[i + module->functions.size()], c);
+ }
+
+ for (int i = 0; i < module->blocks.size(); ++i) {
+ Context *block = module->blocks.at(i);
+
+ writeBlock(dataPtr + blockClassAndFunctionOffsets[i + module->classes.size() + module->functions.size()], block);
}
CompiledData::Lookup *lookupsToWrite = reinterpret_cast<CompiledData::Lookup*>(dataPtr + unit->offsetToLookupTable);
@@ -300,17 +307,16 @@ void QV4::Compiler::JSUnitGenerator::writeFunction(char *f, QV4::Compiler::Conte
function->nameIndex = getStringId(irFunction->name);
function->flags = 0;
- if (irFunction->hasDirectEval)
- function->flags |= CompiledData::Function::HasDirectEval;
- if (irFunction->usesArgumentsObject)
- function->flags |= CompiledData::Function::UsesArgumentsObject;
if (irFunction->isStrict)
function->flags |= CompiledData::Function::IsStrict;
- if (irFunction->hasTry || irFunction->hasWith)
- function->flags |= CompiledData::Function::HasCatchOrWith;
+ if (irFunction->isArrowFunction)
+ function->flags |= CompiledData::Function::IsArrowFunction;
+ if (irFunction->isGenerator)
+ function->flags |= CompiledData::Function::IsGenerator;
function->nestedFunctionIndex =
irFunction->returnsClosure ? quint32(module->functions.indexOf(irFunction->nestedContexts.first()))
: std::numeric_limits<uint32_t>::max();
+ function->length = irFunction->formals ? irFunction->formals->length() : 0;
function->nFormals = irFunction->arguments.size();
function->formalsOffset = currentOffset;
currentOffset += function->nFormals * sizeof(quint32);
@@ -324,7 +330,7 @@ void QV4::Compiler::JSUnitGenerator::writeFunction(char *f, QV4::Compiler::Conte
currentOffset += function->nLineNumbers * sizeof(CompiledData::CodeOffsetToLine);
- function->nRegisters = irFunction->registerCount;
+ function->nRegisters = irFunction->registerCountInFunction;
function->nDependingIdObjects = 0;
function->nDependingContextProperties = 0;
@@ -390,7 +396,84 @@ void QV4::Compiler::JSUnitGenerator::writeFunction(char *f, QV4::Compiler::Conte
memcpy(f + function->codeOffset, irFunction->code.constData(), irFunction->code.size());
}
-QV4::CompiledData::Unit QV4::Compiler::JSUnitGenerator::generateHeader(QV4::Compiler::JSUnitGenerator::GeneratorOption option, quint32_le *functionOffsets, uint *jsClassDataOffset)
+static_assert(int(QV4::Compiler::Class::Method::Regular) == int(QV4::CompiledData::Method::Regular), "Incompatible layout");
+static_assert(int(QV4::Compiler::Class::Method::Getter) == int(QV4::CompiledData::Method::Getter), "Incompatible layout");
+static_assert(int(QV4::Compiler::Class::Method::Setter) == int(QV4::CompiledData::Method::Setter), "Incompatible layout");
+
+void QV4::Compiler::JSUnitGenerator::writeClass(char *b, const QV4::Compiler::Class &c)
+{
+ QV4::CompiledData::Class *cls = reinterpret_cast<QV4::CompiledData::Class *>(b);
+
+ quint32 currentOffset = sizeof(QV4::CompiledData::Class);
+
+ QVector<Class::Method> allMethods = c.staticMethods;
+ allMethods += c.methods;
+
+ cls->constructorFunction = c.constructorIndex;
+ cls->nameIndex = c.nameIndex;
+ cls->nMethods = c.methods.size();
+ cls->nStaticMethods = c.staticMethods.size();
+ cls->methodTableOffset = currentOffset;
+ CompiledData::Method *method = reinterpret_cast<CompiledData::Method *>(b + currentOffset);
+
+ // write methods
+ for (int i = 0; i < allMethods.size(); ++i) {
+ method->name = allMethods.at(i).nameIndex;
+ method->type = allMethods.at(i).type;
+ method->function = allMethods.at(i).functionIndex;
+ ++method;
+ }
+
+ static const bool showCode = qEnvironmentVariableIsSet("QV4_SHOW_BYTECODE");
+ if (showCode) {
+ qDebug() << "=== Class " << stringForIndex(cls->nameIndex) << "static methods" << cls->nStaticMethods << "methods" << cls->nMethods;
+ qDebug() << " constructor:" << cls->constructorFunction;
+ const char *staticString = ": static ";
+ for (uint i = 0; i < cls->nStaticMethods + cls->nMethods; ++i) {
+ if (i == cls->nStaticMethods)
+ staticString = ": ";
+ const char *type;
+ switch (cls->methodTable()[i].type) {
+ case CompiledData::Method::Getter:
+ type = "get "; break;
+ case CompiledData::Method::Setter:
+ type = "set "; break;
+ default:
+ type = "";
+
+ }
+ qDebug() << " " << i << staticString << type << stringForIndex(cls->methodTable()[i].name) << cls->methodTable()[i].function;
+ }
+ qDebug();
+ }
+}
+
+void QV4::Compiler::JSUnitGenerator::writeBlock(char *b, QV4::Compiler::Context *irBlock) const
+{
+ QV4::CompiledData::Block *block = reinterpret_cast<QV4::CompiledData::Block *>(b);
+
+ quint32 currentOffset = sizeof(QV4::CompiledData::Block);
+ currentOffset = (currentOffset + 7) & ~quint32(0x7);
+
+ block->nLocals = irBlock->locals.size();
+ block->localsOffset = currentOffset;
+ currentOffset += block->nLocals * sizeof(quint32);
+
+ // write locals
+ quint32_le *locals = (quint32_le *)(b + block->localsOffset);
+ for (int i = 0; i < irBlock->locals.size(); ++i)
+ locals[i] = getStringId(irBlock->locals.at(i));
+
+ static const bool showCode = qEnvironmentVariableIsSet("QV4_SHOW_BYTECODE");
+ if (showCode) {
+ qDebug() << "=== Variables for block" << irBlock->blockIndex;
+ for (int i = 0; i < irBlock->locals.size(); ++i)
+ qDebug() << " " << i << ":" << locals[i];
+ qDebug();
+ }
+}
+
+QV4::CompiledData::Unit QV4::Compiler::JSUnitGenerator::generateHeader(QV4::Compiler::JSUnitGenerator::GeneratorOption option, quint32_le *blockAndFunctionOffsets, uint *jsClassDataOffset)
{
CompiledData::Unit unit;
memset(&unit, 0, sizeof(unit));
@@ -409,6 +492,14 @@ QV4::CompiledData::Unit QV4::Compiler::JSUnitGenerator::generateHeader(QV4::Comp
unit.offsetToFunctionTable = nextOffset;
nextOffset += unit.functionTableSize * sizeof(uint);
+ unit.classTableSize = module->classes.size();
+ unit.offsetToClassTable = nextOffset;
+ nextOffset += unit.classTableSize * sizeof(uint);
+
+ unit.blockTableSize = module->blocks.size();
+ unit.offsetToBlockTable = nextOffset;
+ nextOffset += unit.blockTableSize * sizeof(uint);
+
unit.lookupTableSize = lookups.count();
unit.offsetToLookupTable = nextOffset;
nextOffset += unit.lookupTableSize * sizeof(CompiledData::Lookup);
@@ -435,13 +526,29 @@ QV4::CompiledData::Unit QV4::Compiler::JSUnitGenerator::generateHeader(QV4::Comp
for (int i = 0; i < module->functions.size(); ++i) {
Context *f = module->functions.at(i);
- functionOffsets[i] = nextOffset;
+ blockAndFunctionOffsets[i] = nextOffset;
const int qmlIdDepsCount = f->idObjectDependencies.count();
const int qmlPropertyDepsCount = f->scopeObjectPropertyDependencies.count() + f->contextObjectPropertyDependencies.count();
nextOffset += QV4::CompiledData::Function::calculateSize(f->arguments.size(), f->locals.size(), f->lineNumberMapping.size(), f->nestedContexts.size(),
qmlIdDepsCount, qmlPropertyDepsCount, f->code.size());
}
+ blockAndFunctionOffsets += module->functions.size();
+
+ for (int i = 0; i < module->classes.size(); ++i) {
+ const Class &c = module->classes.at(i);
+ blockAndFunctionOffsets[i] = nextOffset;
+
+ nextOffset += QV4::CompiledData::Class::calculateSize(c.staticMethods.size(), c.methods.size());
+ }
+ blockAndFunctionOffsets += module->classes.size();
+
+ for (int i = 0; i < module->blocks.size(); ++i) {
+ Context *c = module->blocks.at(i);
+ blockAndFunctionOffsets[i] = nextOffset;
+
+ nextOffset += QV4::CompiledData::Block::calculateSize(c->locals.size());
+ }
if (option == GenerateWithStringTable) {
unit.stringTableSize = stringTable.stringCount();
diff --git a/src/qml/compiler/qv4compiler_p.h b/src/qml/compiler/qv4compiler_p.h
index 360af6540f..944c44b1ff 100644
--- a/src/qml/compiler/qv4compiler_p.h
+++ b/src/qml/compiler/qv4compiler_p.h
@@ -72,6 +72,8 @@ struct JSClassMember;
namespace Compiler {
+struct Class;
+
struct Q_QML_PRIVATE_EXPORT StringTableGenerator {
StringTableGenerator();
@@ -116,8 +118,7 @@ struct Q_QML_PRIVATE_EXPORT JSUnitGenerator {
int registerConstant(ReturnedValue v);
ReturnedValue constant(int idx);
- int registerJSClass(const QVector<MemberInfo> &members);
- int registerJSClass(int count, CompiledData::JSClassMember *members);
+ int registerJSClass(const QStringList &members);
enum GeneratorOption {
GenerateWithStringTable,
@@ -125,8 +126,9 @@ struct Q_QML_PRIVATE_EXPORT JSUnitGenerator {
};
QV4::CompiledData::Unit *generateUnit(GeneratorOption option = GenerateWithStringTable);
- // Returns bytes written
void writeFunction(char *f, Context *irFunction) const;
+ void writeClass(char *f, const Class &c);
+ void writeBlock(char *f, Context *irBlock) const;
StringTableGenerator stringTable;
QString codeGeneratorName;
diff --git a/src/qml/compiler/qv4compilercontext.cpp b/src/qml/compiler/qv4compilercontext.cpp
index 0a9d3d8efe..9dfe3be7e0 100644
--- a/src/qml/compiler/qv4compilercontext.cpp
+++ b/src/qml/compiler/qv4compilercontext.cpp
@@ -39,6 +39,7 @@
#include "qv4compilercontext_p.h"
#include "qv4compilercontrolflow_p.h"
+#include "qv4bytecodegenerator_p.h"
QT_USE_NAMESPACE
using namespace QV4;
@@ -47,11 +48,11 @@ using namespace QQmlJS::AST;
QT_BEGIN_NAMESPACE
-Context *Module::newContext(Node *node, Context *parent, CompilationMode compilationMode)
+Context *Module::newContext(Node *node, Context *parent, ContextType contextType)
{
Q_ASSERT(!contextMap.contains(node));
- Context *c = new Context(parent, compilationMode);
+ Context *c = new Context(parent, contextType);
if (node) {
SourceLocation loc = node->firstSourceLocation();
c->line = loc.startLine;
@@ -70,15 +71,252 @@ Context *Module::newContext(Node *node, Context *parent, CompilationMode compila
return c;
}
-bool Context::forceLookupByName()
+bool Context::addLocalVar(const QString &name, Context::MemberType type, VariableScope scope, FunctionExpression *function)
{
- ControlFlow *flow = controlFlow;
- while (flow) {
- if (flow->needsLookupByName)
+ // ### can this happen?
+ if (name.isEmpty())
+ return true;
+
+ if (type != FunctionDefinition) {
+ if (formals && formals->containsName(name))
+ return (scope == VariableScope::Var);
+ }
+ if (!isCatchBlock || name != caughtVariable) {
+ MemberMap::iterator it = members.find(name);
+ if (it != members.end()) {
+ if (scope != VariableScope::Var || (*it).scope != VariableScope::Var)
+ return false;
+ if ((*it).type <= type) {
+ (*it).type = type;
+ (*it).function = function;
+ }
return true;
- flow = flow->parent;
+ }
+ }
+
+ // hoist var declarations to the function level
+ if (contextType == ContextType::Block && (scope == VariableScope::Var && type != MemberType::FunctionDefinition))
+ return parent->addLocalVar(name, type, scope, function);
+
+ Member m;
+ m.type = type;
+ m.function = function;
+ m.scope = scope;
+ members.insert(name, m);
+ return true;
+}
+
+Context::ResolvedName Context::resolveName(const QString &name)
+{
+ int scope = 0;
+ Context *c = this;
+
+ ResolvedName result;
+
+ while (c) {
+ if (c->isWithBlock)
+ return result;
+
+ Context::Member m = c->findMember(name);
+ if (!c->parent && m.index < 0)
+ break;
+
+ if (m.type != Context::UndefinedMember) {
+ result.type = m.canEscape ? ResolvedName::Local : ResolvedName::Stack;
+ result.scope = scope;
+ result.index = m.index;
+ result.isConst = (m.scope == VariableScope::Const);
+ if (c->isStrict && (name == QLatin1String("arguments") || name == QLatin1String("eval")))
+ result.isArgOrEval = true;
+ return result;
+ }
+ const int argIdx = c->findArgument(name);
+ if (argIdx != -1) {
+ if (c->argumentsCanEscape) {
+ result.index = argIdx + c->locals.size();
+ result.scope = scope;
+ result.type = ResolvedName::Local;
+ result.isConst = false;
+ return result;
+ } else {
+ result.index = argIdx + sizeof(CallData)/sizeof(Value) - 1;
+ result.scope = 0;
+ result.type = ResolvedName::Stack;
+ result.isConst = false;
+ return result;
+ }
+ }
+ if (c->hasDirectEval) {
+ Q_ASSERT(!c->isStrict && c->contextType != ContextType::Block);
+ return result;
+ }
+
+ if (c->requiresExecutionContext)
+ ++scope;
+ c = c->parent;
+ }
+
+ // ### can we relax the restrictions here?
+ if (contextType == ContextType::Eval || c->contextType == ContextType::Binding)
+ return result;
+
+ result.type = ResolvedName::Global;
+ return result;
+}
+
+void Context::emitBlockHeader(Codegen *codegen)
+{
+ using Instruction = Moth::Instruction;
+ Moth::BytecodeGenerator *bytecodeGenerator = codegen->generator();
+
+ setupFunctionIndices(bytecodeGenerator);
+
+ if (requiresExecutionContext) {
+ if (blockIndex < 0) {
+ codegen->module()->blocks.append(this);
+ blockIndex = codegen->module()->blocks.count() - 1;
+ }
+
+ if (contextType == ContextType::Global) {
+ Instruction::PushScriptContext scriptContext;
+ scriptContext.index = blockIndex;
+ bytecodeGenerator->addInstruction(scriptContext);
+ } else if (contextType == ContextType::Block || (contextType == ContextType::Eval && !isStrict)) {
+ if (isCatchBlock) {
+ Instruction::PushCatchContext catchContext;
+ catchContext.index = blockIndex;
+ catchContext.name = codegen->registerString(caughtVariable);
+ bytecodeGenerator->addInstruction(catchContext);
+ } else {
+ Instruction::PushBlockContext blockContext;
+ blockContext.index = blockIndex;
+ bytecodeGenerator->addInstruction(blockContext);
+ }
+ } else {
+ Instruction::CreateCallContext createContext;
+ bytecodeGenerator->addInstruction(createContext);
+ }
+ }
+
+ if (usesThis) {
+ Q_ASSERT(!isStrict);
+ // make sure we convert this to an object
+ Instruction::ConvertThisToObject convert;
+ bytecodeGenerator->addInstruction(convert);
+ }
+
+ if (contextType == ContextType::Global || (contextType == ContextType::Eval && !isStrict)) {
+ // variables in global code are properties of the global context object, not locals as with other functions.
+ for (Context::MemberMap::const_iterator it = members.constBegin(), cend = members.constEnd(); it != cend; ++it) {
+ if (it->isLexicallyScoped())
+ continue;
+ const QString &local = it.key();
+
+ Instruction::DeclareVar declareVar;
+ declareVar.isDeletable = (contextType == ContextType::Eval);
+ declareVar.varName = codegen->registerString(local);
+ bytecodeGenerator->addInstruction(declareVar);
+ }
+ }
+
+ if (contextType == ContextType::Function || contextType == ContextType::Binding) {
+ for (Context::MemberMap::iterator it = members.begin(), end = members.end(); it != end; ++it) {
+ if (it->canEscape && it->type == Context::ThisFunctionName) {
+ // move the function from the stack to the call context
+ Instruction::LoadReg load;
+ load.reg = CallData::Function;
+ bytecodeGenerator->addInstruction(load);
+ Instruction::StoreLocal store;
+ store.index = it->index;
+ bytecodeGenerator->addInstruction(store);
+ }
+ }
+ }
+
+ if (usesArgumentsObject == Context::ArgumentsObjectUsed) {
+ Q_ASSERT(contextType != ContextType::Block);
+ if (isStrict || (formals && !formals->isSimpleParameterList())) {
+ Instruction::CreateUnmappedArgumentsObject setup;
+ bytecodeGenerator->addInstruction(setup);
+ } else {
+ Instruction::CreateMappedArgumentsObject setup;
+ bytecodeGenerator->addInstruction(setup);
+ }
+ codegen->referenceForName(QStringLiteral("arguments"), false).storeConsumeAccumulator();
+ }
+
+ for (const Context::Member &member : qAsConst(members)) {
+ if (member.function) {
+ const int function = codegen->defineFunction(member.function->name.toString(), member.function, member.function->formals, member.function->body);
+ codegen->loadClosure(function);
+ Codegen::Reference r = codegen->referenceForName(member.function->name.toString(), true);
+ r.storeConsumeAccumulator();
+ }
+ }
+}
+
+void Context::emitBlockFooter(Codegen *codegen)
+{
+ using Instruction = Moth::Instruction;
+ Moth::BytecodeGenerator *bytecodeGenerator = codegen->generator();
+
+ if (!requiresExecutionContext)
+ return;
+
+QT_WARNING_PUSH
+QT_WARNING_DISABLE_GCC("-Wmaybe-uninitialized") // the loads below are empty structs.
+ if (contextType == ContextType::Global)
+ bytecodeGenerator->addInstruction(Instruction::PopScriptContext());
+ else
+ bytecodeGenerator->addInstruction(Instruction::PopContext());
+QT_WARNING_POP
+}
+
+void Context::setupFunctionIndices(Moth::BytecodeGenerator *bytecodeGenerator)
+{
+ if (registerOffset != -1) {
+ // already computed, check for consistency
+ Q_ASSERT(registerOffset == bytecodeGenerator->currentRegister());
+ bytecodeGenerator->newRegisterArray(nRegisters);
+ return;
+ }
+ Q_ASSERT(locals.size() == 0);
+ Q_ASSERT(nRegisters == 0);
+ registerOffset = bytecodeGenerator->currentRegister();
+
+ switch (contextType) {
+ case ContextType::Block:
+ case ContextType::Function:
+ case ContextType::Binding: {
+ for (Context::MemberMap::iterator it = members.begin(), end = members.end(); it != end; ++it) {
+ const QString &local = it.key();
+ if (it->canEscape) {
+ it->index = locals.size();
+ locals.append(local);
+ } else {
+ if (it->type == Context::ThisFunctionName)
+ it->index = CallData::Function;
+ else
+ it->index = bytecodeGenerator->newRegister();
+ }
+ }
+ break;
+ }
+ case ContextType::Global:
+ case ContextType::Eval:
+ for (Context::MemberMap::iterator it = members.begin(), end = members.end(); it != end; ++it) {
+ if (!it->isLexicallyScoped() && (contextType == ContextType::Global || !isStrict))
+ continue;
+ if (it->canEscape) {
+ it->index = locals.size();
+ locals.append(it.key());
+ } else {
+ it->index = bytecodeGenerator->newRegister();
+ }
+ }
+ break;
}
- return false;
+ nRegisters = bytecodeGenerator->currentRegister() - registerOffset;
}
QT_END_NAMESPACE
diff --git a/src/qml/compiler/qv4compilercontext_p.h b/src/qml/compiler/qv4compilercontext_p.h
index 455a76c729..52c3fc5b05 100644
--- a/src/qml/compiler/qv4compilercontext_p.h
+++ b/src/qml/compiler/qv4compilercontext_p.h
@@ -66,18 +66,37 @@ namespace Compiler {
struct ControlFlow;
-enum CompilationMode {
- GlobalCode,
- EvalCode,
- FunctionCode,
- QmlBinding // This is almost the same as EvalCode, except:
+enum class ContextType {
+ Global,
+ Function,
+ Eval,
+ Binding, // This is almost the same as Eval, except:
// * function declarations are moved to the return address when encountered
// * return statements are allowed everywhere (like in FunctionCode)
// * variable declarations are treated as true locals (like in FunctionCode)
+ Block
};
struct Context;
+struct Class {
+ struct Method {
+ enum Type {
+ Regular,
+ Getter,
+ Setter
+ };
+ uint nameIndex;
+ Type type;
+ uint functionIndex;
+ };
+
+ uint nameIndex;
+ uint constructorIndex = UINT_MAX;
+ QVector<Method> staticMethods;
+ QVector<Method> methods;
+};
+
struct Module {
Module(bool debugMode)
: debugMode(debugMode)
@@ -86,10 +105,12 @@ struct Module {
qDeleteAll(contextMap);
}
- Context *newContext(QQmlJS::AST::Node *node, Context *parent, CompilationMode compilationMode);
+ Context *newContext(QQmlJS::AST::Node *node, Context *parent, ContextType compilationMode);
QHash<QQmlJS::AST::Node *, Context *> contextMap;
QList<Context *> functions;
+ QList<Context *> blocks;
+ QVector<Class> classes;
Context *rootContext;
QString fileName;
QString finalUrl;
@@ -104,8 +125,9 @@ struct Context {
QString name;
int line = 0;
int column = 0;
- int registerCount = 0;
+ int registerCountInFunction = 0;
int functionIndex = -1;
+ int blockIndex = -1;
enum MemberType {
UndefinedMember,
@@ -118,11 +140,11 @@ struct Context {
struct Member {
MemberType type = UndefinedMember;
int index = -1;
- QQmlJS::AST::VariableDeclaration::VariableScope scope = QQmlJS::AST::VariableDeclaration::FunctionScope;
+ QQmlJS::AST::VariableScope scope = QQmlJS::AST::VariableScope::Var;
mutable bool canEscape = false;
QQmlJS::AST::FunctionExpression *function = nullptr;
- bool isLexicallyScoped() const { return this->scope != QQmlJS::AST::VariableDeclaration::FunctionScope; }
+ bool isLexicallyScoped() const { return this->scope != QQmlJS::AST::VariableScope::Var; }
};
typedef QMap<QString, Member> MemberMap;
@@ -137,15 +159,22 @@ struct Context {
QByteArray code;
QVector<CompiledData::CodeOffsetToLine> lineNumberMapping;
- int maxNumberOfArguments = 0;
+ int nRegisters = 0;
+ int registerOffset = -1;
bool hasDirectEval = false;
+ bool allVarsEscape = false;
bool hasNestedFunctions = false;
bool isStrict = false;
+ bool isArrowFunction = false;
+ bool isGenerator = false;
bool usesThis = false;
bool hasTry = false;
- bool hasWith = false;
bool returnsClosure = false;
mutable bool argumentsCanEscape = false;
+ bool requiresExecutionContext = false;
+ bool isWithBlock = false;
+ bool isCatchBlock = false;
+ QString caughtVariable;
enum UsesArgumentsObject {
ArgumentsObjectUnknown,
@@ -155,7 +184,7 @@ struct Context {
UsesArgumentsObject usesArgumentsObject = ArgumentsObjectUnknown;
- CompilationMode compilationMode;
+ ContextType contextType;
template <typename T>
class SmallSet: public QVarLengthArray<T, 8>
@@ -204,24 +233,14 @@ struct Context {
PropertyDependencyMap contextObjectPropertyDependencies;
PropertyDependencyMap scopeObjectPropertyDependencies;
- Context(Context *parent, CompilationMode mode)
+ Context(Context *parent, ContextType type)
: parent(parent)
- , compilationMode(mode)
+ , contextType(type)
{
if (parent && parent->isStrict)
isStrict = true;
}
- bool forceLookupByName();
-
-
- bool canUseSimpleCall() const {
- return nestedContexts.isEmpty() &&
- locals.isEmpty() &&
- !hasTry && !hasWith &&
- (usesArgumentsObject == ArgumentsObjectNotUsed || isStrict) && !hasDirectEval;
- }
-
int findArgument(const QString &name)
{
// search backwards to handle duplicate argument names correctly
@@ -253,37 +272,37 @@ struct Context {
return true;
}
+ bool requiresImplicitReturnValue() const {
+ return contextType == ContextType::Binding ||
+ contextType == ContextType::Eval ||
+ contextType == ContextType::Global;
+ }
+
void addUsedVariable(const QString &name) {
usedVariables.insert(name);
}
- bool addLocalVar(const QString &name, MemberType type, QQmlJS::AST::VariableDeclaration::VariableScope scope, QQmlJS::AST::FunctionExpression *function = nullptr)
- {
- if (name.isEmpty())
- return true;
+ bool addLocalVar(const QString &name, MemberType contextType, QQmlJS::AST::VariableScope scope, QQmlJS::AST::FunctionExpression *function = nullptr);
+
+ struct ResolvedName {
+ enum Type {
+ Unresolved,
+ Global,
+ Local,
+ Stack
+ };
+ Type type = Unresolved;
+ bool isArgOrEval = false;
+ bool isConst = false;
+ int scope = -1;
+ int index = -1;
+ bool isValid() const { return type != Unresolved; }
+ };
+ ResolvedName resolveName(const QString &name);
+ void emitBlockHeader(Compiler::Codegen *codegen);
+ void emitBlockFooter(Compiler::Codegen *codegen);
- if (type != FunctionDefinition) {
- for (QQmlJS::AST::FormalParameterList *it = formals; it; it = it->next)
- if (it->name == name)
- return (scope == QQmlJS::AST::VariableDeclaration::FunctionScope);
- }
- MemberMap::iterator it = members.find(name);
- if (it != members.end()) {
- if (scope != QQmlJS::AST::VariableDeclaration::FunctionScope || (*it).scope != QQmlJS::AST::VariableDeclaration::FunctionScope)
- return false;
- if ((*it).type <= type) {
- (*it).type = type;
- (*it).function = function;
- }
- return true;
- }
- Member m;
- m.type = type;
- m.function = function;
- m.scope = scope;
- members.insert(name, m);
- return true;
- }
+ void setupFunctionIndices(Moth::BytecodeGenerator *bytecodeGenerator);
};
diff --git a/src/qml/compiler/qv4compilercontrolflow_p.h b/src/qml/compiler/qv4compilercontrolflow_p.h
index 9bda20905a..1ef290ea56 100644
--- a/src/qml/compiler/qv4compilercontrolflow_p.h
+++ b/src/qml/compiler/qv4compilercontrolflow_p.h
@@ -53,6 +53,7 @@
#include <private/qv4global_p.h>
#include <private/qv4codegen_p.h>
#include <private/qqmljsast_p.h>
+#include <private/qv4bytecodegenerator_p.h>
QT_BEGIN_NAMESPACE
@@ -68,117 +69,84 @@ struct ControlFlow {
enum Type {
Loop,
With,
+ Block,
Finally,
Catch
};
- enum HandlerType {
- Invalid,
+ enum UnwindType {
Break,
Continue,
- Return,
- Throw
+ Return
};
- struct Handler {
- HandlerType type;
- QString label;
+ struct UnwindTarget {
BytecodeGenerator::Label linkLabel;
- int tempIndex;
- int value;
+ int unwindLevel;
};
Codegen *cg;
ControlFlow *parent;
Type type;
- bool needsLookupByName = false;
ControlFlow(Codegen *cg, Type type)
- : cg(cg), parent(cg->_context->controlFlow), type(type)
+ : cg(cg), parent(cg->controlFlow), type(type)
{
- cg->_context->controlFlow = this;
+ cg->controlFlow = this;
}
virtual ~ControlFlow() {
- cg->_context->controlFlow = parent;
+ cg->controlFlow = parent;
}
- void emitReturnStatement() const {
- if (cg->_returnAddress >= 0) {
- Instruction::LoadReg load;
- load.reg = Moth::StackSlot::createRegister(cg->_returnAddress);
- generator()->addInstruction(load);
+ UnwindTarget unwindTarget(UnwindType type, const QString &label = QString())
+ {
+ Q_ASSERT(type == Break || type == Continue || type == Return);
+ ControlFlow *flow = this;
+ int level = 0;
+ while (flow) {
+ BytecodeGenerator::Label l = flow->getUnwindTarget(type, label);
+ if (l.isValid())
+ return UnwindTarget{l, level};
+ if (flow->requiresUnwind())
+ ++level;
+ flow = flow->parent;
}
- Instruction::Ret ret;
- cg->bytecodeGenerator->addInstruction(ret);
+ if (type == Return)
+ return UnwindTarget{ cg->returnLabel(), level };
+ return UnwindTarget();
}
- void jumpToHandler(const Handler &h) {
- if (h.linkLabel.isReturn()) {
- emitReturnStatement();
- } else {
- if (h.tempIndex >= 0)
- Reference::storeConstOnStack(cg, QV4::Encode(h.value), h.tempIndex);
- cg->bytecodeGenerator->jump().link(h.linkLabel);
- }
- }
+ virtual QString label() const { return QString(); }
- bool returnRequiresUnwind() const {
- const ControlFlow *f = this;
- while (f) {
- if (f->type == Finally)
+ bool hasLoop() const {
+ const ControlFlow *flow = this;
+ while (flow) {
+ if (flow->type == Loop)
return true;
- f = f->parent;
+ flow = flow->parent;
}
return false;
}
- virtual QString label() const { return QString(); }
-
- bool isSimple() const {
- return type == Loop;
+protected:
+ virtual BytecodeGenerator::Label getUnwindTarget(UnwindType, const QString & = QString()) {
+ return BytecodeGenerator::Label();
}
-
- Handler getParentHandler(HandlerType type, const QString &label = QString()) {
- if (parent)
- return parent->getHandler(type, label);
- switch (type) {
- case Break:
- case Continue:
- return { Invalid, QString(), {}, -1, 0 };
- case Return:
- case Throw:
- return { type, QString(), BytecodeGenerator::Label::returnLabel(), -1, 0 };
- case Invalid:
- break;
- }
- Q_ASSERT(false);
- Q_UNREACHABLE();
+ virtual bool requiresUnwind() {
+ return false;
}
- virtual Handler getHandler(HandlerType type, const QString &label = QString()) = 0;
-
- BytecodeGenerator::ExceptionHandler *parentExceptionHandler() {
- return parent ? parent->exceptionHandler() : nullptr;
+public:
+ BytecodeGenerator::ExceptionHandler *parentUnwindHandler() {
+ return parent ? parent->unwindHandler() : nullptr;
}
- virtual BytecodeGenerator::ExceptionHandler *exceptionHandler() {
- return parentExceptionHandler();
+ virtual BytecodeGenerator::ExceptionHandler *unwindHandler() {
+ return parentUnwindHandler();
}
- virtual void handleThrow(const Reference &expr) {
- Reference e = expr;
- Handler h = getHandler(ControlFlow::Throw);
- if (h.tempIndex >= 0) {
- e = e.storeOnStack();
- Reference::storeConstOnStack(cg, QV4::Encode(h.value), h.tempIndex);
- }
- e.loadInAccumulator();
- Instruction::ThrowException instr;
- generator()->addInstruction(instr);
- }
-
protected:
QString loopLabel() const {
QString label;
@@ -193,136 +161,145 @@ protected:
}
};
-struct ControlFlowLoop : public ControlFlow
+struct ControlFlowUnwind : public ControlFlow
{
- QString loopLabel;
- BytecodeGenerator::Label *breakLabel = nullptr;
- BytecodeGenerator::Label *continueLabel = nullptr;
+ BytecodeGenerator::ExceptionHandler unwindLabel;
- ControlFlowLoop(Codegen *cg, BytecodeGenerator::Label *breakLabel, BytecodeGenerator::Label *continueLabel = nullptr)
- : ControlFlow(cg, Loop), loopLabel(ControlFlow::loopLabel()), breakLabel(breakLabel), continueLabel(continueLabel)
+ ControlFlowUnwind(Codegen *cg, Type type)
+ : ControlFlow(cg, type)
{
}
- virtual QString label() const { return loopLabel; }
+ void setupUnwindHandler()
+ {
+ unwindLabel = generator()->newExceptionHandler();
+ }
- virtual Handler getHandler(HandlerType type, const QString &label = QString()) {
- switch (type) {
- case Break:
- if (breakLabel && (label.isEmpty() || label == loopLabel))
- return { type, loopLabel, *breakLabel, -1, 0 };
- break;
- case Continue:
- if (continueLabel && (label.isEmpty() || label == loopLabel))
- return { type, loopLabel, *continueLabel, -1, 0 };
- break;
- case Return:
- case Throw:
- break;
- case Invalid:
- Q_ASSERT(false);
- Q_UNREACHABLE();
- }
- return getParentHandler(type, label);
+ void emitUnwindHandler()
+ {
+ Q_ASSERT(requiresUnwind());
+
+ Instruction::UnwindDispatch dispatch;
+ generator()->addInstruction(dispatch);
}
+ virtual BytecodeGenerator::ExceptionHandler *unwindHandler() override {
+ return unwindLabel.isValid() ? &unwindLabel : parentUnwindHandler();
+ }
};
-struct ControlFlowUnwind : public ControlFlow
+struct ControlFlowLoop : public ControlFlowUnwind
{
- BytecodeGenerator::ExceptionHandler unwindLabel;
- int controlFlowTemp;
- QVector<Handler> handlers;
+ QString loopLabel;
+ BytecodeGenerator::Label *breakLabel = nullptr;
+ BytecodeGenerator::Label *continueLabel = nullptr;
+ bool _requiresUnwind;
- ControlFlowUnwind(Codegen *cg, Type type)
- : ControlFlow(cg, type), unwindLabel(generator()->newExceptionHandler())
+ ControlFlowLoop(Codegen *cg, BytecodeGenerator::Label *breakLabel, BytecodeGenerator::Label *continueLabel = nullptr, bool requiresUnwind = false)
+ : ControlFlowUnwind(cg, Loop), loopLabel(ControlFlow::loopLabel()), breakLabel(breakLabel), continueLabel(continueLabel), _requiresUnwind(requiresUnwind)
{
- Q_ASSERT(type != Loop);
- controlFlowTemp = static_cast<int>(generator()->newRegister());
- Reference::storeConstOnStack(cg, QV4::Encode::undefined(), controlFlowTemp);
- // we'll need at least a handler for throw
- getHandler(Throw);
+ if (_requiresUnwind) {
+ setupUnwindHandler();
+ generator()->setUnwindHandler(&unwindLabel);
+ }
}
- void emitUnwindHandler()
- {
- Q_ASSERT(!isSimple());
-
- Reference temp = Reference::fromStackSlot(cg, controlFlowTemp);
- for (const auto &h : qAsConst(handlers)) {
- Handler parentHandler = getParentHandler(h.type, h.label);
-
- if (h.type == Throw || parentHandler.tempIndex >= 0) {
- BytecodeGenerator::Label skip = generator()->newLabel();
- generator()->jumpStrictNotEqualStackSlotInt(temp.stackSlot(), h.value).link(skip);
- if (h.type == Throw)
- emitForThrowHandling();
- jumpToHandler(parentHandler);
- skip.link();
- } else {
- if (parentHandler.linkLabel.isReturn()) {
- BytecodeGenerator::Label skip = generator()->newLabel();
- generator()->jumpStrictNotEqualStackSlotInt(temp.stackSlot(), h.value).link(skip);
- emitReturnStatement();
- skip.link();
- } else {
- generator()->jumpStrictEqualStackSlotInt(temp.stackSlot(), h.value).link(parentHandler.linkLabel);
- }
- }
+ ~ControlFlowLoop() {
+ if (_requiresUnwind) {
+ unwindLabel.link();
+ generator()->setUnwindHandler(parentUnwindHandler());
+ emitUnwindHandler();
}
}
- virtual Handler getHandler(HandlerType type, const QString &label = QString()) {
- for (const auto &h : qAsConst(handlers)) {
- if (h.type == type && h.label == label)
- return h;
- }
- Handler h = {
- type,
- label,
- unwindLabel,
- controlFlowTemp,
- handlers.size()
- };
- handlers.append(h);
- return h;
+ bool requiresUnwind() override {
+ return _requiresUnwind;
}
- virtual BytecodeGenerator::ExceptionHandler *exceptionHandler() {
- return &unwindLabel;
+ BytecodeGenerator::Label getUnwindTarget(UnwindType type, const QString &label) override {
+ switch (type) {
+ case Break:
+ if (breakLabel && (label.isEmpty() || label == loopLabel))
+ return *breakLabel;
+ break;
+ case Continue:
+ if (continueLabel && (label.isEmpty() || label == loopLabel))
+ return *continueLabel;
+ break;
+ default:
+ break;
+ }
+ return BytecodeGenerator::Label();
}
- virtual void emitForThrowHandling() { }
+ QString label() const override { return loopLabel; }
};
+
struct ControlFlowWith : public ControlFlowUnwind
{
ControlFlowWith(Codegen *cg)
: ControlFlowUnwind(cg, With)
{
- needsLookupByName = true;
-
- savedContextRegister = Moth::StackSlot::createRegister(generator()->newRegister());
+ setupUnwindHandler();
// assumes the with object is in the accumulator
Instruction::PushWithContext pushScope;
- pushScope.reg = savedContextRegister;
generator()->addInstruction(pushScope);
- generator()->setExceptionHandler(&unwindLabel);
+ generator()->setUnwindHandler(&unwindLabel);
}
- virtual ~ControlFlowWith() {
+ ~ControlFlowWith() {
// emit code for unwinding
unwindLabel.link();
- generator()->setExceptionHandler(parentExceptionHandler());
+ generator()->setUnwindHandler(parentUnwindHandler());
Instruction::PopContext pop;
- pop.reg = savedContextRegister;
generator()->addInstruction(pop);
emitUnwindHandler();
}
- Moth::StackSlot savedContextRegister;
+
+ bool requiresUnwind() override {
+ return true;
+ }
+
+
+};
+
+struct ControlFlowBlock : public ControlFlowUnwind
+{
+ ControlFlowBlock(Codegen *cg, AST::Node *ast)
+ : ControlFlowUnwind(cg, Block)
+ {
+ block = cg->enterBlock(ast);
+ block->emitBlockHeader(cg);
+
+ if (block->requiresExecutionContext) {
+ setupUnwindHandler();
+ generator()->setUnwindHandler(&unwindLabel);
+ }
+ }
+
+ virtual ~ControlFlowBlock() {
+ // emit code for unwinding
+ if (block->requiresExecutionContext) {
+ unwindLabel.link();
+ generator()->setUnwindHandler(parentUnwindHandler());
+ }
+
+ block->emitBlockFooter(cg);
+
+ if (block->requiresExecutionContext )
+ emitUnwindHandler();
+ cg->leaveBlock();
+ }
+
+ virtual bool requiresUnwind() override {
+ return block->requiresExecutionContext;
+ }
+
+ Context *block;
};
struct ControlFlowCatch : public ControlFlowUnwind
@@ -330,71 +307,57 @@ struct ControlFlowCatch : public ControlFlowUnwind
AST::Catch *catchExpression;
bool insideCatch = false;
BytecodeGenerator::ExceptionHandler exceptionLabel;
- BytecodeGenerator::ExceptionHandler catchUnwindLabel;
+ bool oldLookupByName;
ControlFlowCatch(Codegen *cg, AST::Catch *catchExpression)
: ControlFlowUnwind(cg, Catch), catchExpression(catchExpression),
- exceptionLabel(generator()->newExceptionHandler()),
- catchUnwindLabel(generator()->newExceptionHandler())
+ exceptionLabel(generator()->newExceptionHandler())
{
- generator()->setExceptionHandler(&exceptionLabel);
+ generator()->setUnwindHandler(&exceptionLabel);
}
- virtual Handler getHandler(HandlerType type, const QString &label = QString()) {
- Handler h = getParentHandler(type, label);
- if (h.type == Invalid)
- return h;
- h = ControlFlowUnwind::getHandler(type, label);
- if (insideCatch)
- // if we're inside the catch block, we need to jump to the pop scope
- // instruction at the end of the catch block, not the unwind handler
- h.linkLabel = catchUnwindLabel;
- else if (type == Throw)
- // if we're inside the try block, we need to jump to the catch block,
- // not the unwind handler
- h.linkLabel = exceptionLabel;
- return h;
+ virtual bool requiresUnwind() override {
+ return true;
}
- virtual BytecodeGenerator::ExceptionHandler *exceptionHandler() {
- return insideCatch ? &catchUnwindLabel : &exceptionLabel;
+ BytecodeGenerator::ExceptionHandler *unwindHandler() override {
+ return insideCatch ? &unwindLabel : &exceptionLabel;
}
~ControlFlowCatch() {
// emit code for unwinding
-
- needsLookupByName = true;
insideCatch = true;
+ setupUnwindHandler();
Codegen::RegisterScope scope(cg);
// exceptions inside the try block go here
exceptionLabel.link();
- Moth::StackSlot savedContextReg = Moth::StackSlot::createRegister(generator()->newRegister());
- Instruction::PushCatchContext pushCatch;
- pushCatch.name = cg->registerString(catchExpression->name.toString());
- pushCatch.reg = savedContextReg;
- generator()->addInstruction(pushCatch);
- // clear the unwind temp for exceptions, we want to resume normal code flow afterwards
- Reference::storeConstOnStack(cg, QV4::Encode::undefined(), controlFlowTemp);
- generator()->setExceptionHandler(&catchUnwindLabel);
+ BytecodeGenerator::Jump noException = generator()->jumpNoException();
- cg->statement(catchExpression->statement);
+ Context *block = cg->enterBlock(catchExpression);
- insideCatch = false;
- needsLookupByName = false;
+ block->emitBlockHeader(cg);
- // exceptions inside catch and break/return statements go here
- catchUnwindLabel.link();
- Instruction::PopContext pop;
- pop.reg = savedContextReg;
- generator()->addInstruction(pop);
+ generator()->setUnwindHandler(&unwindLabel);
+
+ if (catchExpression->patternElement->bindingIdentifier.isEmpty())
+ // destructuring pattern
+ cg->initializeAndDestructureBindingElement(catchExpression->patternElement, Reference::fromName(cg, QStringLiteral("@caught")));
+ // skip the additional block
+ cg->statementList(catchExpression->statement->statements);
- // break/continue/return statements in try go here
+ // exceptions inside catch and break/return statements go here
unwindLabel.link();
- generator()->setExceptionHandler(parentExceptionHandler());
+ block->emitBlockFooter(cg);
+
+ cg->leaveBlock();
+
+ noException.link();
+ generator()->setUnwindHandler(parentUnwindHandler());
emitUnwindHandler();
+ insideCatch = false;
}
};
@@ -402,25 +365,21 @@ struct ControlFlowFinally : public ControlFlowUnwind
{
AST::Finally *finally;
bool insideFinally = false;
- int exceptionTemp = -1;
ControlFlowFinally(Codegen *cg, AST::Finally *finally)
: ControlFlowUnwind(cg, Finally), finally(finally)
{
Q_ASSERT(finally != nullptr);
- generator()->setExceptionHandler(&unwindLabel);
+ setupUnwindHandler();
+ generator()->setUnwindHandler(&unwindLabel);
}
- virtual Handler getHandler(HandlerType type, const QString &label = QString()) {
- // if we're inside the finally block, any exceptions etc. should
- // go directly to the parent handler
- if (insideFinally)
- return getParentHandler(type, label);
- return ControlFlowUnwind::getHandler(type, label);
+ virtual bool requiresUnwind() override {
+ return !insideFinally;
}
- virtual BytecodeGenerator::ExceptionHandler *exceptionHandler() {
- return insideFinally ? parentExceptionHandler() : ControlFlowUnwind::exceptionHandler();
+ BytecodeGenerator::ExceptionHandler *unwindHandler() override {
+ return insideFinally ? parentUnwindHandler() : ControlFlowUnwind::unwindHandler();
}
~ControlFlowFinally() {
@@ -429,34 +388,35 @@ struct ControlFlowFinally : public ControlFlowUnwind
Codegen::RegisterScope scope(cg);
- Moth::StackSlot retVal = Moth::StackSlot::createRegister(generator()->newRegister());
- Instruction::StoreReg storeRetVal;
- storeRetVal.reg = retVal;
- generator()->addInstruction(storeRetVal);
-
insideFinally = true;
- exceptionTemp = generator()->newRegister();
+ int returnValueTemp = -1;
+ if (cg->requiresReturnValue) {
+ returnValueTemp = generator()->newRegister();
+ Instruction::MoveReg move;
+ move.srcReg = cg->_returnAddress;
+ move.destReg = returnValueTemp;
+ generator()->addInstruction(move);
+ }
+ int exceptionTemp = generator()->newRegister();
Instruction::GetException instr;
generator()->addInstruction(instr);
Reference::fromStackSlot(cg, exceptionTemp).storeConsumeAccumulator();
- generator()->setExceptionHandler(parentExceptionHandler());
+ generator()->setUnwindHandler(parentUnwindHandler());
cg->statement(finally->statement);
insideFinally = false;
- Instruction::LoadReg loadRetVal;
- loadRetVal.reg = retVal;
- generator()->addInstruction(loadRetVal);
-
- emitUnwindHandler();
- }
-
- virtual void emitForThrowHandling() {
- // reset the exception flag, that got cleared before executing the statements in finally
+ if (cg->requiresReturnValue) {
+ Instruction::MoveReg move;
+ move.srcReg = returnValueTemp;
+ move.destReg = cg->_returnAddress;
+ generator()->addInstruction(move);
+ }
Reference::fromStackSlot(cg, exceptionTemp).loadInAccumulator();
Instruction::SetException setException;
- Q_ASSERT(exceptionTemp != -1);
generator()->addInstruction(setException);
+
+ emitUnwindHandler();
}
};
diff --git a/src/qml/compiler/qv4compilerscanfunctions.cpp b/src/qml/compiler/qv4compilerscanfunctions.cpp
index 84ee452332..9be55c6ad0 100644
--- a/src/qml/compiler/qv4compilerscanfunctions.cpp
+++ b/src/qml/compiler/qv4compilerscanfunctions.cpp
@@ -56,12 +56,12 @@ using namespace QV4;
using namespace QV4::Compiler;
using namespace QQmlJS::AST;
-ScanFunctions::ScanFunctions(Codegen *cg, const QString &sourceCode, CompilationMode defaultProgramMode)
+ScanFunctions::ScanFunctions(Codegen *cg, const QString &sourceCode, ContextType defaultProgramType)
: _cg(cg)
, _sourceCode(sourceCode)
, _context(nullptr)
, _allowFuncDecls(true)
- , defaultProgramMode(defaultProgramMode)
+ , defaultProgramType(defaultProgramType)
{
}
@@ -73,18 +73,19 @@ void ScanFunctions::operator()(Node *node)
calcEscapingVariables();
}
-void ScanFunctions::enterGlobalEnvironment(CompilationMode compilationMode)
+void ScanFunctions::enterGlobalEnvironment(ContextType compilationMode)
{
- enterEnvironment(astNodeForGlobalEnvironment, compilationMode);
+ enterEnvironment(astNodeForGlobalEnvironment, compilationMode, QStringLiteral("%GlobalCode"));
}
-void ScanFunctions::enterEnvironment(Node *node, CompilationMode compilationMode)
+void ScanFunctions::enterEnvironment(Node *node, ContextType compilationMode, const QString &name)
{
Context *c = _cg->_module->contextMap.value(node);
if (!c)
c = _cg->_module->newContext(node, _context, compilationMode);
if (!c->isStrict)
c->isStrict = _cg->_strictMode;
+ c->name = name;
_contextStack.append(c);
_context = c;
}
@@ -92,28 +93,26 @@ void ScanFunctions::enterEnvironment(Node *node, CompilationMode compilationMode
void ScanFunctions::leaveEnvironment()
{
_contextStack.pop();
- _context = _contextStack.isEmpty() ? 0 : _contextStack.top();
-}
-
-void ScanFunctions::checkDirectivePrologue(SourceElements *ast)
-{
- for (SourceElements *it = ast; it; it = it->next) {
- if (StatementSourceElement *stmt = cast<StatementSourceElement *>(it->element)) {
- if (ExpressionStatement *expr = cast<ExpressionStatement *>(stmt->statement)) {
- if (StringLiteral *strLit = cast<StringLiteral *>(expr->expression)) {
- // Use the source code, because the StringLiteral's
- // value might have escape sequences in it, which is not
- // allowed.
- if (strLit->literalToken.length < 2)
- continue;
- QStringRef str = _sourceCode.midRef(strLit->literalToken.offset + 1, strLit->literalToken.length - 2);
- if (str == QLatin1String("use strict")) {
- _context->isStrict = true;
- } else {
- // TODO: give a warning.
- }
+ _context = _contextStack.isEmpty() ? nullptr : _contextStack.top();
+}
+
+void ScanFunctions::checkDirectivePrologue(StatementList *ast)
+{
+ for (StatementList *it = ast; it; it = it->next) {
+ if (ExpressionStatement *expr = cast<ExpressionStatement *>(it->statement)) {
+ if (StringLiteral *strLit = cast<StringLiteral *>(expr->expression)) {
+ // Use the source code, because the StringLiteral's
+ // value might have escape sequences in it, which is not
+ // allowed.
+ if (strLit->literalToken.length < 2)
continue;
+ QStringRef str = _sourceCode.midRef(strLit->literalToken.offset + 1, strLit->literalToken.length - 2);
+ if (str == QLatin1String("use strict")) {
+ _context->isStrict = true;
+ } else {
+ // TODO: give a warning.
}
+ continue;
}
}
@@ -138,20 +137,10 @@ void ScanFunctions::checkName(const QStringRef &name, const SourceLocation &loc)
}
}
-bool ScanFunctions::formalsContainName(AST::FormalParameterList *parameters, const QString &name)
-{
- while (parameters) {
- if (parameters->name == name)
- return true;
- parameters = parameters->next;
- }
- return false;
-}
-
bool ScanFunctions::visit(Program *ast)
{
- enterEnvironment(ast, defaultProgramMode);
- checkDirectivePrologue(ast->elements);
+ enterEnvironment(ast, defaultProgramType, QStringLiteral("%ProgramCode"));
+ checkDirectivePrologue(ast->statements);
return true;
}
@@ -162,7 +151,7 @@ void ScanFunctions::endVisit(Program *)
bool ScanFunctions::visit(CallExpression *ast)
{
- if (! _context->hasDirectEval) {
+ if (!_context->hasDirectEval) {
if (IdentifierExpression *id = cast<IdentifierExpression *>(ast->base)) {
if (id->name == QLatin1String("eval")) {
if (_context->usesArgumentsObject == Context::ArgumentsObjectUnknown)
@@ -171,53 +160,31 @@ bool ScanFunctions::visit(CallExpression *ast)
}
}
}
- int argc = 0;
- for (ArgumentList *it = ast->arguments; it; it = it->next)
- ++argc;
- _context->maxNumberOfArguments = qMax(_context->maxNumberOfArguments, argc);
return true;
}
-bool ScanFunctions::visit(NewMemberExpression *ast)
+bool ScanFunctions::visit(PatternElement *ast)
{
- int argc = 0;
- for (ArgumentList *it = ast->arguments; it; it = it->next)
- ++argc;
- _context->maxNumberOfArguments = qMax(_context->maxNumberOfArguments, argc);
- return true;
-}
+ if (!ast->isVariableDeclaration())
+ return true;
-bool ScanFunctions::visit(ArrayLiteral *ast)
-{
- int index = 0;
- for (ElementList *it = ast->elements; it; it = it->next) {
- for (Elision *elision = it->elision; elision; elision = elision->next)
- ++index;
- ++index;
- }
- if (ast->elision) {
- for (Elision *elision = ast->elision->next; elision; elision = elision->next)
- ++index;
- }
- _context->maxNumberOfArguments = qMax(_context->maxNumberOfArguments, index);
- return true;
-}
+ QStringList names;
+ ast->boundNames(&names);
-bool ScanFunctions::visit(VariableDeclaration *ast)
-{
- if (_context->isStrict && (ast->name == QLatin1String("eval") || ast->name == QLatin1String("arguments")))
- _cg->throwSyntaxError(ast->identifierToken, QStringLiteral("Variable name may not be eval or arguments in strict mode"));
- checkName(ast->name, ast->identifierToken);
- if (ast->name == QLatin1String("arguments"))
- _context->usesArgumentsObject = Context::ArgumentsObjectNotUsed;
- if (ast->scope == AST::VariableDeclaration::VariableScope::ReadOnlyBlockScope && !ast->expression) {
- _cg->throwSyntaxError(ast->identifierToken, QStringLiteral("Missing initializer in const declaration"));
- return false;
- }
- QString name = ast->name.toString();
- if (!_context->addLocalVar(ast->name.toString(), ast->expression ? Context::VariableDefinition : Context::VariableDeclaration, ast->scope)) {
- _cg->throwSyntaxError(ast->identifierToken, QStringLiteral("Identifier %1 has already been declared").arg(name));
- return false;
+ for (const QString &name : qAsConst(names)) {
+ if (_context->isStrict && (name == QLatin1String("eval") || name == QLatin1String("arguments")))
+ _cg->throwSyntaxError(ast->identifierToken, QStringLiteral("Variable name may not be eval or arguments in strict mode"));
+ checkName(QStringRef(&name), ast->identifierToken);
+ if (name == QLatin1String("arguments"))
+ _context->usesArgumentsObject = Context::ArgumentsObjectNotUsed;
+ if (ast->scope == VariableScope::Const && !ast->initializer && !ast->destructuringPattern()) {
+ _cg->throwSyntaxError(ast->identifierToken, QStringLiteral("Missing initializer in const declaration"));
+ return false;
+ }
+ if (!_context->addLocalVar(name, ast->initializer ? Context::VariableDefinition : Context::VariableDeclaration, ast->scope)) {
+ _cg->throwSyntaxError(ast->identifierToken, QStringLiteral("Identifier %1 has already been declared").arg(name));
+ return false;
+ }
}
return true;
}
@@ -237,7 +204,8 @@ bool ScanFunctions::visit(ExpressionStatement *ast)
if (!_allowFuncDecls)
_cg->throwSyntaxError(expr->functionToken, QStringLiteral("conditional function or closure declaration"));
- enterFunction(expr, /*enterName*/ true);
+ if (!enterFunction(expr, /*enterName*/ true))
+ return false;
Node::accept(expr->formals, this);
Node::accept(expr->body, this);
leaveEnvironment();
@@ -253,79 +221,98 @@ bool ScanFunctions::visit(ExpressionStatement *ast)
bool ScanFunctions::visit(FunctionExpression *ast)
{
- enterFunction(ast, /*enterName*/ false);
+ return enterFunction(ast, /*enterName*/ false);
+}
+
+bool ScanFunctions::visit(ClassExpression *ast)
+{
+ enterEnvironment(ast, ContextType::Block, QStringLiteral("%Class"));
+ _context->isStrict = true;
+ _context->hasNestedFunctions = true;
+ if (!ast->name.isEmpty())
+ _context->addLocalVar(ast->name.toString(), Context::VariableDefinition, AST::VariableScope::Const);
return true;
}
-void ScanFunctions::enterFunction(FunctionExpression *ast, bool enterName)
+void ScanFunctions::endVisit(ClassExpression *)
{
- if (_context->isStrict && (ast->name == QLatin1String("eval") || ast->name == QLatin1String("arguments")))
- _cg->throwSyntaxError(ast->identifierToken, QStringLiteral("Function name may not be eval or arguments in strict mode"));
- enterFunction(ast, ast->name.toString(), ast->formals, ast->body, enterName ? ast : nullptr);
+ leaveEnvironment();
}
-void ScanFunctions::endVisit(FunctionExpression *)
+bool ScanFunctions::visit(ClassDeclaration *ast)
+{
+ if (!ast->name.isEmpty())
+ _context->addLocalVar(ast->name.toString(), Context::VariableDeclaration, AST::VariableScope::Let);
+
+ enterEnvironment(ast, ContextType::Block, QStringLiteral("%Class"));
+ _context->isStrict = true;
+ _context->hasNestedFunctions = true;
+ if (!ast->name.isEmpty())
+ _context->addLocalVar(ast->name.toString(), Context::VariableDefinition, AST::VariableScope::Const);
+ return true;
+}
+
+void ScanFunctions::endVisit(ClassDeclaration *)
{
leaveEnvironment();
}
-bool ScanFunctions::visit(ObjectLiteral *ast)
+bool ScanFunctions::visit(TemplateLiteral *ast)
{
- int argc = 0;
- for (PropertyAssignmentList *it = ast->properties; it; it = it->next) {
- QString key = it->assignment->name->asString();
- if (QV4::String::toArrayIndex(key) != UINT_MAX)
- ++argc;
- ++argc;
- if (AST::cast<AST::PropertyGetterSetter *>(it->assignment))
- ++argc;
+ while (ast) {
+ if (ast->expression)
+ Node::accept(ast->expression, this);
+ ast = ast->next;
}
- _context->maxNumberOfArguments = qMax(_context->maxNumberOfArguments, argc);
+ return true;
- TemporaryBoolAssignment allowFuncDecls(_allowFuncDecls, true);
- Node::accept(ast->properties, this);
- return false;
}
-bool ScanFunctions::visit(PropertyGetterSetter *ast)
+bool ScanFunctions::enterFunction(FunctionExpression *ast, bool enterName)
{
- TemporaryBoolAssignment allowFuncDecls(_allowFuncDecls, true);
- enterFunction(ast, QString(), ast->formals, ast->functionBody, /*FunctionExpression*/nullptr);
- return true;
+ if (_context->isStrict && (ast->name == QLatin1String("eval") || ast->name == QLatin1String("arguments")))
+ _cg->throwSyntaxError(ast->identifierToken, QStringLiteral("Function name may not be eval or arguments in strict mode"));
+ return enterFunction(ast, ast->name.toString(), ast->formals, ast->body, enterName);
}
-void ScanFunctions::endVisit(PropertyGetterSetter *)
+void ScanFunctions::endVisit(FunctionExpression *)
{
leaveEnvironment();
}
-bool ScanFunctions::visit(FunctionDeclaration *ast)
+bool ScanFunctions::visit(ObjectPattern *ast)
{
- enterFunction(ast, /*enterName*/ true);
- return true;
+ TemporaryBoolAssignment allowFuncDecls(_allowFuncDecls, true);
+ Node::accept(ast->properties, this);
+ return false;
}
-void ScanFunctions::endVisit(FunctionDeclaration *)
+bool ScanFunctions::visit(PatternProperty *ast)
{
- leaveEnvironment();
+ Q_UNUSED(ast);
+ // ### Shouldn't be required anymore
+// if (ast->type == PatternProperty::Getter || ast->type == PatternProperty::Setter) {
+// TemporaryBoolAssignment allowFuncDecls(_allowFuncDecls, true);
+// return enterFunction(ast, QString(), ast->formals, ast->functionBody, /*enterName */ false);
+// }
+ return true;
}
-bool ScanFunctions::visit(TryStatement *)
+void ScanFunctions::endVisit(PatternProperty *)
{
- // ### should limit to catch(), as try{} finally{} should be ok without
- _context->hasTry = true;
- return true;
+ // ###
+// if (ast->type == PatternProperty::Getter || ast->type == PatternProperty::Setter)
+// leaveEnvironment();
}
-bool ScanFunctions::visit(WithStatement *ast)
+bool ScanFunctions::visit(FunctionDeclaration *ast)
{
- if (_context->isStrict) {
- _cg->throwSyntaxError(ast->withToken, QStringLiteral("'with' statement is not allowed in strict mode"));
- return false;
- }
+ return enterFunction(ast, /*enterName*/ true);
+}
- _context->hasWith = true;
- return true;
+void ScanFunctions::endVisit(FunctionDeclaration *)
+{
+ leaveEnvironment();
}
bool ScanFunctions::visit(DoWhileStatement *ast) {
@@ -338,7 +325,9 @@ bool ScanFunctions::visit(DoWhileStatement *ast) {
}
bool ScanFunctions::visit(ForStatement *ast) {
+ enterEnvironment(ast, ContextType::Block, QStringLiteral("%For"));
Node::accept(ast->initialiser, this);
+ Node::accept(ast->declarations, this);
Node::accept(ast->condition, this);
Node::accept(ast->expression, this);
@@ -348,9 +337,14 @@ bool ScanFunctions::visit(ForStatement *ast) {
return false;
}
-bool ScanFunctions::visit(LocalForStatement *ast) {
- Node::accept(ast->declarations, this);
- Node::accept(ast->condition, this);
+void ScanFunctions::endVisit(ForStatement *)
+{
+ leaveEnvironment();
+}
+
+bool ScanFunctions::visit(ForEachStatement *ast) {
+ enterEnvironment(ast, ContextType::Block, QStringLiteral("%Foreach"));
+ Node::accept(ast->lhs, this);
Node::accept(ast->expression, this);
TemporaryBoolAssignment allowFuncDecls(_allowFuncDecls, !_context->isStrict);
@@ -359,90 +353,154 @@ bool ScanFunctions::visit(LocalForStatement *ast) {
return false;
}
-bool ScanFunctions::visit(ForEachStatement *ast) {
- Node::accept(ast->initialiser, this);
- Node::accept(ast->expression, this);
+void ScanFunctions::endVisit(ForEachStatement *)
+{
+ leaveEnvironment();
+}
- TemporaryBoolAssignment allowFuncDecls(_allowFuncDecls, !_context->isStrict);
- Node::accept(ast->statement, this);
+bool ScanFunctions::visit(ThisExpression *)
+{
+ _context->usesThis = true;
+ return false;
+}
+bool ScanFunctions::visit(Block *ast)
+{
+ TemporaryBoolAssignment allowFuncDecls(_allowFuncDecls, _context->isStrict ? false : _allowFuncDecls);
+ enterEnvironment(ast, ContextType::Block, QStringLiteral("%Block"));
+ Node::accept(ast->statements, this);
return false;
}
-bool ScanFunctions::visit(LocalForEachStatement *ast) {
- Node::accept(ast->declaration, this);
- Node::accept(ast->expression, this);
+void ScanFunctions::endVisit(Block *)
+{
+ leaveEnvironment();
+}
- TemporaryBoolAssignment allowFuncDecls(_allowFuncDecls, !_context->isStrict);
- Node::accept(ast->statement, this);
+bool ScanFunctions::visit(CaseBlock *ast)
+{
+ enterEnvironment(ast, ContextType::Block, QStringLiteral("%CaseBlock"));
+ return true;
+}
- return false;
+void ScanFunctions::endVisit(CaseBlock *)
+{
+ leaveEnvironment();
}
-bool ScanFunctions::visit(ThisExpression *)
+bool ScanFunctions::visit(Catch *ast)
{
- _context->usesThis = true;
+ TemporaryBoolAssignment allowFuncDecls(_allowFuncDecls, _context->isStrict ? false : _allowFuncDecls);
+ enterEnvironment(ast, ContextType::Block, QStringLiteral("%CatchBlock"));
+ _context->isCatchBlock = true;
+ QString caughtVar = ast->patternElement->bindingIdentifier.toString();
+ if (caughtVar.isEmpty())
+ caughtVar = QStringLiteral("@caught");
+ _context->addLocalVar(caughtVar, Context::MemberType::VariableDefinition, VariableScope::Let);
+
+ _context->caughtVariable = caughtVar;
+ if (_context->isStrict &&
+ (caughtVar == QLatin1String("eval") || caughtVar == QLatin1String("arguments"))) {
+ _cg->throwSyntaxError(ast->identifierToken, QStringLiteral("Catch variable name may not be eval or arguments in strict mode"));
+ return false;
+ }
+ Node::accept(ast->patternElement, this);
+ // skip the block statement
+ Node::accept(ast->statement->statements, this);
return false;
}
-bool ScanFunctions::visit(Block *ast) {
+void ScanFunctions::endVisit(Catch *)
+{
+ leaveEnvironment();
+}
+
+bool ScanFunctions::visit(WithStatement *ast)
+{
+ Node::accept(ast->expression, this);
+
TemporaryBoolAssignment allowFuncDecls(_allowFuncDecls, _context->isStrict ? false : _allowFuncDecls);
- Node::accept(ast->statements, this);
+ enterEnvironment(ast, ContextType::Block, QStringLiteral("%WithBlock"));
+ _context->isWithBlock = true;
+
+ if (_context->isStrict) {
+ _cg->throwSyntaxError(ast->withToken, QStringLiteral("'with' statement is not allowed in strict mode"));
+ return false;
+ }
+ Node::accept(ast->statement, this);
+
return false;
}
-void ScanFunctions::enterFunction(Node *ast, const QString &name, FormalParameterList *formals, FunctionBody *body, FunctionExpression *expr)
+void ScanFunctions::endVisit(WithStatement *)
+{
+ leaveEnvironment();
+}
+
+bool ScanFunctions::enterFunction(Node *ast, const QString &name, FormalParameterList *formals, StatementList *body, bool enterName)
{
Context *outerContext = _context;
- enterEnvironment(ast, FunctionCode);
+ enterEnvironment(ast, ContextType::Function, name);
+ FunctionExpression *expr = AST::cast<FunctionExpression *>(ast);
+ if (!expr)
+ expr = AST::cast<FunctionDeclaration *>(ast);
if (outerContext) {
outerContext->hasNestedFunctions = true;
// The identifier of a function expression cannot be referenced from the enclosing environment.
- if (expr) {
- if (!outerContext->addLocalVar(name, Context::FunctionDefinition, AST::VariableDeclaration::FunctionScope, expr)) {
+ if (enterName) {
+ if (!outerContext->addLocalVar(name, Context::FunctionDefinition, VariableScope::Var, expr)) {
_cg->throwSyntaxError(ast->firstSourceLocation(), QStringLiteral("Identifier %1 has already been declared").arg(name));
- return;
+ return false;
}
+ outerContext->addLocalVar(name, Context::FunctionDefinition, VariableScope::Var, expr);
}
if (name == QLatin1String("arguments"))
outerContext->usesArgumentsObject = Context::ArgumentsObjectNotUsed;
}
- if (formalsContainName(formals, QStringLiteral("arguments")))
+ _context->name = name;
+ if (formals && formals->containsName(QStringLiteral("arguments")))
_context->usesArgumentsObject = Context::ArgumentsObjectNotUsed;
+ if (expr) {
+ if (expr->isArrowFunction)
+ _context->isArrowFunction = true;
+ else if (expr->isGenerator)
+ _context->isGenerator = true;
+ }
- if (!name.isEmpty() && !formalsContainName(formals, name))
- _context->addLocalVar(name, Context::ThisFunctionName, QQmlJS::AST::VariableDeclaration::FunctionScope);
+ if (!name.isEmpty() && (!formals || !formals->containsName(name)))
+ _context->addLocalVar(name, Context::ThisFunctionName, VariableScope::Var);
_context->formals = formals;
if (body && !_context->isStrict)
- checkDirectivePrologue(body->elements);
-
- for (FormalParameterList *it = formals; it; it = it->next) {
- QString arg = it->name.toString();
- int duplicateIndex = _context->arguments.indexOf(arg);
- if (duplicateIndex != -1) {
- if (_context->isStrict) {
- _cg->throwSyntaxError(it->identifierToken, QStringLiteral("Duplicate parameter name '%1' is not allowed in strict mode").arg(arg));
- return;
- } else {
- // change the name of the earlier argument to enforce the specified lookup semantics
- QString modified = arg;
- while (_context->arguments.contains(modified))
- modified += QString(0xfffe);
- _context->arguments[duplicateIndex] = modified;
+ checkDirectivePrologue(body);
+
+ bool isSimpleParameterList = formals && formals->isSimpleParameterList();
+
+ _context->arguments = formals ? formals->formals() : QStringList();
+
+ const QStringList boundNames = formals ? formals->boundNames() : QStringList();
+ for (int i = 0; i < boundNames.size(); ++i) {
+ const QString &arg = boundNames.at(i);
+ if (_context->isStrict || !isSimpleParameterList) {
+ bool duplicate = (boundNames.indexOf(arg, i + 1) != -1);
+ if (duplicate) {
+ _cg->throwSyntaxError(formals->firstSourceLocation(), QStringLiteral("Duplicate parameter name '%1' is not allowed.").arg(arg));
+ return false;
}
}
if (_context->isStrict) {
if (arg == QLatin1String("eval") || arg == QLatin1String("arguments")) {
- _cg->throwSyntaxError(it->identifierToken, QStringLiteral("'%1' cannot be used as parameter name in strict mode").arg(arg));
- return;
+ _cg->throwSyntaxError(formals->firstSourceLocation(), QStringLiteral("'%1' cannot be used as parameter name in strict mode").arg(arg));
+ return false;
}
}
- _context->arguments += arg;
+ if (!_context->arguments.contains(arg))
+ _context->addLocalVar(arg, Context::VariableDefinition, VariableScope::Var);
}
+ return true;
}
void ScanFunctions::calcEscapingVariables()
@@ -450,27 +508,114 @@ void ScanFunctions::calcEscapingVariables()
Module *m = _cg->_module;
for (Context *inner : qAsConst(m->contextMap)) {
+ if (inner->contextType == ContextType::Block && inner->usesArgumentsObject == Context::ArgumentsObjectUsed) {
+ Context *c = inner->parent;
+ while (c->contextType == ContextType::Block)
+ c = c->parent;
+ c->usesArgumentsObject = Context::ArgumentsObjectUsed;
+ inner->usesArgumentsObject = Context::ArgumentsObjectNotUsed;
+ }
+ }
+ for (Context *inner : qAsConst(m->contextMap)) {
+ if (!inner->parent || inner->usesArgumentsObject == Context::ArgumentsObjectUnknown)
+ inner->usesArgumentsObject = Context::ArgumentsObjectNotUsed;
+ if (inner->usesArgumentsObject == Context::ArgumentsObjectUsed) {
+ QString arguments = QStringLiteral("arguments");
+ inner->addLocalVar(arguments, Context::VariableDeclaration, AST::VariableScope::Var);
+ if (!inner->isStrict) {
+ inner->argumentsCanEscape = true;
+ inner->requiresExecutionContext = true;
+ }
+ }
+ }
+
+ for (Context *inner : qAsConst(m->contextMap)) {
for (const QString &var : qAsConst(inner->usedVariables)) {
Context *c = inner;
while (c) {
+ Context *current = c;
+ c = c->parent;
+ if (current->isWithBlock || current->contextType != ContextType::Block)
+ break;
+ }
+ Q_ASSERT(c != inner);
+ while (c) {
Context::MemberMap::const_iterator it = c->members.find(var);
if (it != c->members.end()) {
- if (c != inner)
+ if (c->parent || it->isLexicallyScoped()) {
it->canEscape = true;
+ c->requiresExecutionContext = true;
+ }
break;
}
if (c->findArgument(var) != -1) {
- if (c != inner)
- c->argumentsCanEscape = true;
+ c->argumentsCanEscape = true;
+ c->requiresExecutionContext = true;
break;
}
c = c->parent;
}
}
- Context *c = inner->parent;
- while (c) {
- c->hasDirectEval |= inner->hasDirectEval;
- c = c->parent;
+ if (inner->hasDirectEval) {
+ inner->hasDirectEval = false;
+ if (!inner->isStrict) {
+ Context *c = inner;
+ while (c->contextType == ContextType::Block) {
+ c = c->parent;
+ }
+ Q_ASSERT(c);
+ c->hasDirectEval = true;
+ }
+ Context *c = inner;
+ while (c) {
+ c->allVarsEscape = true;
+ c = c->parent;
+ }
+ }
+ if (inner->usesThis) {
+ inner->usesThis = false;
+ if (!inner->isStrict) {
+ Context *c = inner;
+ while (c->contextType == ContextType::Block) {
+ c = c->parent;
+ }
+ Q_ASSERT(c);
+ c->usesThis = true;
+ }
+ }
+ }
+ for (Context *c : qAsConst(m->contextMap)) {
+ if (c->allVarsEscape && c->contextType == ContextType::Block && c->members.isEmpty())
+ c->allVarsEscape = false;
+ if (c->contextType == ContextType::Global || (!c->isStrict && c->contextType == ContextType::Eval) || m->debugMode)
+ c->allVarsEscape = true;
+ if (c->allVarsEscape) {
+ if (c->parent) {
+ c->requiresExecutionContext = true;
+ c->argumentsCanEscape = true;
+ } else {
+ for (const auto &m : qAsConst(c->members)) {
+ if (m.isLexicallyScoped()) {
+ c->requiresExecutionContext = true;
+ break;
+ }
+ }
+ }
+ }
+ if (c->contextType == ContextType::Block && c->isCatchBlock) {
+ c->requiresExecutionContext = true;
+ auto m = c->members.find(c->caughtVariable);
+ m->canEscape = true;
+ }
+ const QLatin1String exprForOn("expression for on");
+ if (c->contextType == ContextType::Binding && c->name.length() > exprForOn.size() &&
+ c->name.startsWith(exprForOn) && c->name.at(exprForOn.size()).isUpper())
+ // we don't really need this for bindings, but we do for signal handlers, and in this case,
+ // we don't know if the code is a signal handler or not.
+ c->requiresExecutionContext = true;
+ if (c->allVarsEscape) {
+ for (auto &m : c->members)
+ m.canEscape = true;
}
}
@@ -478,10 +623,12 @@ void ScanFunctions::calcEscapingVariables()
if (showEscapingVars) {
qDebug() << "==== escaping variables ====";
for (Context *c : qAsConst(m->contextMap)) {
- qDebug() << "Context" << c->name << ":";
- qDebug() << " Arguments escape" << c->argumentsCanEscape;
+ qDebug() << "Context" << c << c->name << "requiresExecutionContext" << c->requiresExecutionContext << "isStrict" << c->isStrict;
+ qDebug() << " parent:" << c->parent;
+ if (c->argumentsCanEscape)
+ qDebug() << " Arguments escape";
for (auto it = c->members.constBegin(); it != c->members.constEnd(); ++it) {
- qDebug() << " " << it.key() << it.value().canEscape;
+ qDebug() << " " << it.key() << it.value().index << it.value().canEscape << "isLexicallyScoped:" << it.value().isLexicallyScoped();
}
}
}
diff --git a/src/qml/compiler/qv4compilerscanfunctions_p.h b/src/qml/compiler/qv4compilerscanfunctions_p.h
index 87b7210879..4c273600b3 100644
--- a/src/qml/compiler/qv4compilerscanfunctions_p.h
+++ b/src/qml/compiler/qv4compilerscanfunctions_p.h
@@ -58,6 +58,7 @@
#include <private/qv4util_p.h>
#include <QtCore/QStringList>
#include <QStack>
+#include <QScopedValueRollback>
QT_BEGIN_NAMESPACE
@@ -79,13 +80,13 @@ class Codegen;
class ScanFunctions: protected QQmlJS::AST::Visitor
{
- typedef QV4::TemporaryAssignment<bool> TemporaryBoolAssignment;
+ typedef QScopedValueRollback<bool> TemporaryBoolAssignment;
public:
- ScanFunctions(Codegen *cg, const QString &sourceCode, CompilationMode defaultProgramMode);
+ ScanFunctions(Codegen *cg, const QString &sourceCode, ContextType defaultProgramType);
void operator()(AST::Node *node);
- void enterGlobalEnvironment(CompilationMode compilationMode);
- void enterEnvironment(AST::Node *node, CompilationMode compilationMode);
+ void enterGlobalEnvironment(ContextType compilationMode);
+ void enterEnvironment(AST::Node *node, ContextType compilationMode, const QString &name);
void leaveEnvironment();
void enterQmlFunction(AST::FunctionDeclaration *ast)
@@ -95,48 +96,60 @@ protected:
using Visitor::visit;
using Visitor::endVisit;
- void checkDirectivePrologue(AST::SourceElements *ast);
+ void checkDirectivePrologue(AST::StatementList *ast);
void checkName(const QStringRef &name, const AST::SourceLocation &loc);
- bool formalsContainName(AST::FormalParameterList *parameters, const QString &name);
bool visit(AST::Program *ast) override;
void endVisit(AST::Program *) override;
bool visit(AST::CallExpression *ast) override;
- bool visit(AST::NewMemberExpression *ast) override;
- bool visit(AST::ArrayLiteral *ast) override;
- bool visit(AST::VariableDeclaration *ast) override;
+ bool visit(AST::PatternElement *ast) override;
bool visit(AST::IdentifierExpression *ast) override;
bool visit(AST::ExpressionStatement *ast) override;
bool visit(AST::FunctionExpression *ast) override;
+ bool visit(AST::TemplateLiteral *ast) override;
- void enterFunction(AST::FunctionExpression *ast, bool enterName);
+ bool enterFunction(AST::FunctionExpression *ast, bool enterName);
void endVisit(AST::FunctionExpression *) override;
- bool visit(AST::ObjectLiteral *ast) override;
+ bool visit(AST::ObjectPattern *ast) override;
- bool visit(AST::PropertyGetterSetter *ast) override;
- void endVisit(AST::PropertyGetterSetter *) override;
+ bool visit(AST::PatternProperty *ast) override;
+ void endVisit(AST::PatternProperty *) override;
bool visit(AST::FunctionDeclaration *ast) override;
void endVisit(AST::FunctionDeclaration *) override;
- bool visit(AST::TryStatement *ast) override;
- bool visit(AST::WithStatement *ast) override;
+ bool visit(AST::ClassExpression *ast) override;
+ void endVisit(AST::ClassExpression *) override;
+
+ bool visit(AST::ClassDeclaration *ast) override;
+ void endVisit(AST::ClassDeclaration *) override;
bool visit(AST::DoWhileStatement *ast) override;
bool visit(AST::ForStatement *ast) override;
- bool visit(AST::LocalForStatement *ast) override;
+ void endVisit(AST::ForStatement *) override;
bool visit(AST::ForEachStatement *ast) override;
- bool visit(AST::LocalForEachStatement *ast) override;
+ void endVisit(AST::ForEachStatement *) override;
+
bool visit(AST::ThisExpression *ast) override;
bool visit(AST::Block *ast) override;
+ void endVisit(AST::Block *ast) override;
+
+ bool visit(AST::CaseBlock *ast) override;
+ void endVisit(AST::CaseBlock *ast) override;
+
+ bool visit(AST::Catch *ast) override;
+ void endVisit(AST::Catch *ast) override;
+
+ bool visit(AST::WithStatement *ast) override;
+ void endVisit(AST::WithStatement *ast) override;
protected:
- void enterFunction(AST::Node *ast, const QString &name, AST::FormalParameterList *formals, AST::FunctionBody *body, AST::FunctionExpression *expr);
+ bool enterFunction(AST::Node *ast, const QString &name, AST::FormalParameterList *formals, AST::StatementList *body, bool enterName);
void calcEscapingVariables();
// fields:
@@ -146,7 +159,7 @@ protected:
QStack<Context *> _contextStack;
bool _allowFuncDecls;
- CompilationMode defaultProgramMode;
+ ContextType defaultProgramType;
private:
static constexpr AST::Node *astNodeForGlobalEnvironment = nullptr;
diff --git a/src/qml/compiler/qv4instr_moth.cpp b/src/qml/compiler/qv4instr_moth.cpp
index 450fa50528..8e474b3783 100644
--- a/src/qml/compiler/qv4instr_moth.cpp
+++ b/src/qml/compiler/qv4instr_moth.cpp
@@ -39,15 +39,16 @@
#include "qv4instr_moth_p.h"
#include <private/qv4compileddata_p.h>
+#include <private/qv4stackframe_p.h>
using namespace QV4;
using namespace QV4::Moth;
int InstrInfo::size(Instr::Type type)
{
-#define MOTH_RETURN_INSTR_SIZE(I) case Instr::Type::I: return InstrMeta<int(Instr::Type::I)>::Size;
+#define MOTH_RETURN_INSTR_SIZE(I) case Instr::Type::I: case Instr::Type::I##_Wide: return InstrMeta<int(Instr::Type::I)>::Size;
switch (type) {
- FOR_EACH_MOTH_INSTR(MOTH_RETURN_INSTR_SIZE)
+ FOR_EACH_MOTH_INSTR_ALL(MOTH_RETURN_INSTR_SIZE)
}
#undef MOTH_RETURN_INSTR_SIZE
Q_UNREACHABLE();
@@ -110,6 +111,8 @@ static QString toString(QV4::ReturnedValue v)
QDebug d = qDebug(); \
d.noquote(); \
d.nospace(); \
+ if (static_cast<int>(Instr::Type::instr) >= 0x100) \
+ --base_ptr; \
d << alignedLineNumber(line) << alignedNumber(codeOffset).constData() << ": " \
<< rawBytes(base_ptr, int(code - base_ptr)) << #instr << " ";
@@ -122,7 +125,7 @@ namespace QV4 {
namespace Moth {
const int InstrInfo::argumentCount[] = {
- FOR_EACH_MOTH_INSTR(MOTH_COLLECT_NARGS)
+ FOR_EACH_MOTH_INSTR_ALL(MOTH_COLLECT_NARGS)
};
@@ -147,6 +150,8 @@ QString dumpRegister(int reg, int nFormals)
return QStringLiteral("(context)");
else if (reg == CallData::Accumulator)
return QStringLiteral("(accumulator)");
+ else if (reg == CallData::NewTarget)
+ return QStringLiteral("(new.target)");
else if (reg == CallData::This)
return QStringLiteral("(this)");
else if (reg == CallData::Argc)
@@ -286,10 +291,6 @@ void dumpBytecode(const char *code, int len, int nLocals, int nFormals, int /*st
MOTH_END_INSTR(StoreNameStrict)
MOTH_BEGIN_INSTR(LoadElement)
- d << dumpRegister(base, nFormals) << "[" << dumpRegister(index, nFormals) << "]";
- MOTH_END_INSTR(LoadElement)
-
- MOTH_BEGIN_INSTR(LoadElementA)
d << dumpRegister(base, nFormals) << "[acc]";
MOTH_END_INSTR(LoadElement)
@@ -298,20 +299,12 @@ void dumpBytecode(const char *code, int len, int nLocals, int nFormals, int /*st
MOTH_END_INSTR(StoreElement)
MOTH_BEGIN_INSTR(LoadProperty)
- d << dumpRegister(base, nFormals) << "[" << name << "]";
- MOTH_END_INSTR(LoadProperty)
-
- MOTH_BEGIN_INSTR(LoadPropertyA)
d << "acc[" << name << "]";
- MOTH_END_INSTR(LoadElementA)
+ MOTH_END_INSTR(LoadProperty)
MOTH_BEGIN_INSTR(GetLookup)
- d << dumpRegister(base, nFormals) << "(" << index << ")";
- MOTH_END_INSTR(GetLookup)
-
- MOTH_BEGIN_INSTR(GetLookupA)
d << "acc(" << index << ")";
- MOTH_END_INSTR(GetLookupA)
+ MOTH_END_INSTR(GetLookup)
MOTH_BEGIN_INSTR(StoreProperty)
d << dumpRegister(base, nFormals) << "[" << name<< "]";
@@ -321,6 +314,14 @@ void dumpBytecode(const char *code, int len, int nLocals, int nFormals, int /*st
d << dumpRegister(base, nFormals) << "(" << index << ")";
MOTH_END_INSTR(SetLookup)
+ MOTH_BEGIN_INSTR(LoadSuperProperty)
+ d << dumpRegister(property, nFormals);
+ MOTH_END_INSTR(LoadSuperProperty)
+
+ MOTH_BEGIN_INSTR(StoreSuperProperty)
+ d << dumpRegister(property, nFormals);
+ MOTH_END_INSTR(StoreSuperProperty)
+
MOTH_BEGIN_INSTR(StoreScopeObjectProperty)
d << dumpRegister(base, nFormals) << "[" << propertyIndex << "]";
MOTH_END_INSTR(StoreScopeObjectProperty)
@@ -341,6 +342,13 @@ void dumpBytecode(const char *code, int len, int nLocals, int nFormals, int /*st
d << dumpRegister(base, nFormals) << "[" << index << "]";
MOTH_END_INSTR(LoadIdObject)
+ MOTH_BEGIN_INSTR(Yield)
+ MOTH_END_INSTR(Yield)
+
+ MOTH_BEGIN_INSTR(Resume)
+ d << ABSOLUTE_OFFSET();
+ MOTH_END_INSTR(Resume)
+
MOTH_BEGIN_INSTR(CallValue)
d << dumpRegister(name, nFormals) << dumpArguments(argc, argv, nFormals);
MOTH_END_INSTR(CallValue)
@@ -377,12 +385,31 @@ void dumpBytecode(const char *code, int len, int nLocals, int nFormals, int /*st
d << dumpRegister(base, nFormals) << "." << name << dumpArguments(argc, argv, nFormals);
MOTH_END_INSTR(CallContextObjectProperty)
- MOTH_BEGIN_INSTR(SetExceptionHandler)
+ MOTH_BEGIN_INSTR(CallWithSpread)
+ d << "new" << dumpRegister(func, nFormals) << dumpRegister(thisObject, nFormals) << dumpArguments(argc, argv, nFormals);
+ MOTH_END_INSTR(CallWithSpread)
+
+ MOTH_BEGIN_INSTR(Construct)
+ d << "new" << dumpRegister(func, nFormals) << dumpArguments(argc, argv, nFormals);
+ MOTH_END_INSTR(Construct)
+
+ MOTH_BEGIN_INSTR(ConstructWithSpread)
+ d << "new" << dumpRegister(func, nFormals) << dumpArguments(argc, argv, nFormals);
+ MOTH_END_INSTR(ConstructWithSpread)
+
+ MOTH_BEGIN_INSTR(SetUnwindHandler)
if (offset)
d << ABSOLUTE_OFFSET();
else
d << "<null>";
- MOTH_END_INSTR(SetExceptionHandler)
+ MOTH_END_INSTR(SetUnwindHandler)
+
+ MOTH_BEGIN_INSTR(UnwindDispatch)
+ MOTH_END_INSTR(UnwindDispatch)
+
+ MOTH_BEGIN_INSTR(UnwindToLabel)
+ d << "(" << level << ") " << ABSOLUTE_OFFSET();
+ MOTH_END_INSTR(UnwindToLabel)
MOTH_BEGIN_INSTR(ThrowException)
MOTH_END_INSTR(ThrowException)
@@ -397,30 +424,47 @@ void dumpBytecode(const char *code, int len, int nLocals, int nFormals, int /*st
MOTH_END_INSTR(CreateCallContext)
MOTH_BEGIN_INSTR(PushCatchContext)
- d << dumpRegister(reg, nFormals) << ", " << name;
+ d << index << ", " << name;
MOTH_END_INSTR(PushCatchContext)
MOTH_BEGIN_INSTR(PushWithContext)
- d << dumpRegister(reg, nFormals);
MOTH_END_INSTR(PushWithContext)
+ MOTH_BEGIN_INSTR(PushBlockContext)
+ d << index;
+ MOTH_END_INSTR(PushBlockContext)
+
+ MOTH_BEGIN_INSTR(CloneBlockContext)
+ MOTH_END_INSTR(CloneBlockContext)
+
+ MOTH_BEGIN_INSTR(PushScriptContext)
+ d << index;
+ MOTH_END_INSTR(PushScriptContext)
+
+ MOTH_BEGIN_INSTR(PopScriptContext)
+ MOTH_END_INSTR(PopScriptContext)
+
MOTH_BEGIN_INSTR(PopContext)
- d << dumpRegister(reg, nFormals);
MOTH_END_INSTR(PopContext)
- MOTH_BEGIN_INSTR(ForeachIteratorObject)
- MOTH_END_INSTR(ForeachIteratorObject)
+ MOTH_BEGIN_INSTR(GetIterator)
+ d << iterator;
+ MOTH_END_INSTR(GetIterator)
+
+ MOTH_BEGIN_INSTR(IteratorNext)
+ d << dumpRegister(value, nFormals);
+ MOTH_END_INSTR(IteratorNext)
- MOTH_BEGIN_INSTR(ForeachNextPropertyName)
- MOTH_END_INSTR(ForeachNextPropertyName)
+ MOTH_BEGIN_INSTR(IteratorClose)
+ d << dumpRegister(done, nFormals);
+ MOTH_END_INSTR(IteratorClose)
- MOTH_BEGIN_INSTR(DeleteMember)
- d << dumpRegister(base, nFormals) << "[" << member << "]";
- MOTH_END_INSTR(DeleteMember)
+ MOTH_BEGIN_INSTR(DestructureRestElement)
+ MOTH_END_INSTR(DestructureRestElement)
- MOTH_BEGIN_INSTR(DeleteSubscript)
+ MOTH_BEGIN_INSTR(DeleteProperty)
d << dumpRegister(base, nFormals) << "[" << dumpRegister(index, nFormals) << "]";
- MOTH_END_INSTR(DeleteSubscript)
+ MOTH_END_INSTR(DeleteProperty)
MOTH_BEGIN_INSTR(DeleteName)
d << name;
@@ -442,24 +486,35 @@ void dumpBytecode(const char *code, int len, int nLocals, int nFormals, int /*st
MOTH_END_INSTR(DefineArray)
MOTH_BEGIN_INSTR(DefineObjectLiteral)
- d << dumpRegister(args, nFormals)
- << ", " << internalClassId
- << ", " << arrayValueCount
- << ", " << arrayGetterSetterCountAndFlags;
+ d << internalClassId
+ << ", " << argc
+ << ", " << dumpRegister(args, nFormals);
MOTH_END_INSTR(DefineObjectLiteral)
+ MOTH_BEGIN_INSTR(CreateClass)
+ d << classIndex
+ << ", " << dumpRegister(heritage, nFormals)
+ << ", " << dumpRegister(computedNames, nFormals);
+ MOTH_END_INSTR(CreateClass)
+
MOTH_BEGIN_INSTR(CreateMappedArgumentsObject)
MOTH_END_INSTR(CreateMappedArgumentsObject)
MOTH_BEGIN_INSTR(CreateUnmappedArgumentsObject)
MOTH_END_INSTR(CreateUnmappedArgumentsObject)
+ MOTH_BEGIN_INSTR(CreateRestParameter)
+ d << argIndex;
+ MOTH_END_INSTR(CreateRestParameter)
+
MOTH_BEGIN_INSTR(ConvertThisToObject)
MOTH_END_INSTR(ConvertThisToObject)
- MOTH_BEGIN_INSTR(Construct)
- d << "new" << dumpRegister(func, nFormals) << dumpArguments(argc, argv, nFormals);
- MOTH_END_INSTR(Construct)
+ MOTH_BEGIN_INSTR(LoadSuperConstructor)
+ MOTH_END_INSTR(LoadSuperConstructor)
+
+ MOTH_BEGIN_INSTR(ToObject)
+ MOTH_END_INSTR(ToObject)
MOTH_BEGIN_INSTR(Jump)
d << ABSOLUTE_OFFSET();
@@ -473,6 +528,14 @@ void dumpBytecode(const char *code, int len, int nLocals, int nFormals, int /*st
d << ABSOLUTE_OFFSET();
MOTH_END_INSTR(JumpFalse)
+ MOTH_BEGIN_INSTR(JumpNotUndefined)
+ d << ABSOLUTE_OFFSET();
+ MOTH_END_INSTR(JumpNotUndefined)
+
+ MOTH_BEGIN_INSTR(JumpNoException)
+ d << ABSOLUTE_OFFSET();
+ MOTH_END_INSTR(JumpNoException)
+
MOTH_BEGIN_INSTR(CmpEqNull)
MOTH_END_INSTR(CmpEqNull)
@@ -519,14 +582,6 @@ void dumpBytecode(const char *code, int len, int nLocals, int nFormals, int /*st
d << dumpRegister(lhs, nFormals);
MOTH_END_INSTR(CmpStrictNotEqual)
- MOTH_BEGIN_INSTR(JumpStrictEqualStackSlotInt)
- d << dumpRegister(lhs, nFormals) << ", " << rhs << " " << ABSOLUTE_OFFSET();
- MOTH_END_INSTR(JumpStrictEqualStackSlotInt)
-
- MOTH_BEGIN_INSTR(JumpStrictNotEqualStackSlotInt)
- d << dumpRegister(lhs, nFormals) << ", " << rhs << " " << ABSOLUTE_OFFSET();
- MOTH_END_INSTR(JumpStrictNotEqualStackSlotInt)
-
MOTH_BEGIN_INSTR(UNot)
MOTH_END_INSTR(UNot)
@@ -597,6 +652,10 @@ void dumpBytecode(const char *code, int len, int nLocals, int nFormals, int /*st
d << "acc, " << rhs;
MOTH_END_INSTR(ShlConst)
+ MOTH_BEGIN_INSTR(Exp)
+ d << dumpRegister(lhs, nFormals) << ", acc";
+ MOTH_END_INSTR(Exp)
+
MOTH_BEGIN_INSTR(Mul)
d << dumpRegister(lhs, nFormals) << ", acc";
MOTH_END_INSTR(Mul)
diff --git a/src/qml/compiler/qv4instr_moth_p.h b/src/qml/compiler/qv4instr_moth_p.h
index df9182e924..ce92a31590 100644
--- a/src/qml/compiler/qv4instr_moth_p.h
+++ b/src/qml/compiler/qv4instr_moth_p.h
@@ -53,6 +53,8 @@
#include <private/qv4global_p.h>
#include <private/qv4value_p.h>
#include <private/qv4runtime_p.h>
+#include <private/qv4compileddata_p.h> // for CompiledData::CodeOffsetToLine used by the dumper
+#include <qendian.h>
QT_BEGIN_NAMESPACE
@@ -60,6 +62,7 @@ QT_BEGIN_NAMESPACE
op##_INSTRUCTION(name, nargs, __VA_ARGS__)
/* for all jump instructions, the offset has to come last, to simplify the job of the bytecode generator */
+#define INSTR_Nop(op) INSTRUCTION(op, Nop, 0)
#define INSTR_Ret(op) INSTRUCTION(op, Ret, 0)
#define INSTR_Debug(op) INSTRUCTION(op, Debug, 0)
#define INSTR_LoadConst(op) INSTRUCTION(op, LoadConst, 1, index)
@@ -84,19 +87,20 @@ QT_BEGIN_NAMESPACE
#define INSTR_LoadGlobalLookup(op) INSTRUCTION(op, LoadGlobalLookup, 1, index)
#define INSTR_StoreNameSloppy(op) INSTRUCTION(op, StoreNameSloppy, 1, name)
#define INSTR_StoreNameStrict(op) INSTRUCTION(op, StoreNameStrict, 1, name)
-#define INSTR_LoadProperty(op) INSTRUCTION(op, LoadProperty, 2, name, base)
-#define INSTR_LoadPropertyA(op) INSTRUCTION(op, LoadPropertyA, 1, name)
-#define INSTR_GetLookup(op) INSTRUCTION(op, GetLookup, 2, index, base)
-#define INSTR_GetLookupA(op) INSTRUCTION(op, GetLookupA, 1, index)
+#define INSTR_LoadProperty(op) INSTRUCTION(op, LoadProperty, 1, name)
+#define INSTR_GetLookup(op) INSTRUCTION(op, GetLookup, 1, index)
#define INSTR_LoadScopeObjectProperty(op) INSTRUCTION(op, LoadScopeObjectProperty, 3, propertyIndex, base, captureRequired)
#define INSTR_LoadContextObjectProperty(op) INSTRUCTION(op, LoadContextObjectProperty, 3, propertyIndex, base, captureRequired)
#define INSTR_LoadIdObject(op) INSTRUCTION(op, LoadIdObject, 2, index, base)
+#define INSTR_Yield(op) INSTRUCTION(op, Yield, 0)
+#define INSTR_Resume(op) INSTRUCTION(op, Resume, 1, offset)
#define INSTR_StoreProperty(op) INSTRUCTION(op, StoreProperty, 2, name, base)
#define INSTR_SetLookup(op) INSTRUCTION(op, SetLookup, 2, index, base)
+#define INSTR_LoadSuperProperty(op) INSTRUCTION(op, LoadSuperProperty, 1, property)
+#define INSTR_StoreSuperProperty(op) INSTRUCTION(op, StoreSuperProperty, 1, property)
#define INSTR_StoreScopeObjectProperty(op) INSTRUCTION(op, StoreScopeObjectProperty, 2, base, propertyIndex)
#define INSTR_StoreContextObjectProperty(op) INSTRUCTION(op, StoreContextObjectProperty, 2, base, propertyIndex)
-#define INSTR_LoadElement(op) INSTRUCTION(op, LoadElement, 2, base, index)
-#define INSTR_LoadElementA(op) INSTRUCTION(op, LoadElementA, 1, base)
+#define INSTR_LoadElement(op) INSTRUCTION(op, LoadElement, 1, base)
#define INSTR_StoreElement(op) INSTRUCTION(op, StoreElement, 2, base, index)
#define INSTR_CallValue(op) INSTRUCTION(op, CallValue, 3, name, argc, argv)
#define INSTR_CallProperty(op) INSTRUCTION(op, CallProperty, 4, name, base, argc, argv)
@@ -107,32 +111,46 @@ QT_BEGIN_NAMESPACE
#define INSTR_CallGlobalLookup(op) INSTRUCTION(op, CallGlobalLookup, 3, index, argc, argv)
#define INSTR_CallScopeObjectProperty(op) INSTRUCTION(op, CallScopeObjectProperty, 4, name, base, argc, argv)
#define INSTR_CallContextObjectProperty(op) INSTRUCTION(op, CallContextObjectProperty, 4, name, base, argc, argv)
-#define INSTR_SetExceptionHandler(op) INSTRUCTION(op, SetExceptionHandler, 1, offset)
+#define INSTR_CallWithSpread(op) INSTRUCTION(op, CallWithSpread, 4, func, thisObject, argc, argv)
+#define INSTR_Construct(op) INSTRUCTION(op, Construct, 3, func, argc, argv)
+#define INSTR_ConstructWithSpread(op) INSTRUCTION(op, ConstructWithSpread, 3, func, argc, argv)
+#define INSTR_SetUnwindHandler(op) INSTRUCTION(op, SetUnwindHandler, 1, offset)
+#define INSTR_UnwindDispatch(op) INSTRUCTION(op, UnwindDispatch, 0)
+#define INSTR_UnwindToLabel(op) INSTRUCTION(op, UnwindToLabel, 2, level, offset)
#define INSTR_ThrowException(op) INSTRUCTION(op, ThrowException, 0)
#define INSTR_GetException(op) INSTRUCTION(op, GetException, 0)
#define INSTR_SetException(op) INSTRUCTION(op, SetException, 0)
#define INSTR_CreateCallContext(op) INSTRUCTION(op, CreateCallContext, 0)
-#define INSTR_PushCatchContext(op) INSTRUCTION(op, PushCatchContext, 2, name, reg)
-#define INSTR_PushWithContext(op) INSTRUCTION(op, PushWithContext, 1, reg)
-#define INSTR_PopContext(op) INSTRUCTION(op, PopContext, 1, reg)
-#define INSTR_ForeachIteratorObject(op) INSTRUCTION(op, ForeachIteratorObject, 0)
-#define INSTR_ForeachNextPropertyName(op) INSTRUCTION(op, ForeachNextPropertyName, 0)
-#define INSTR_DeleteMember(op) INSTRUCTION(op, DeleteMember, 2, member, base)
-#define INSTR_DeleteSubscript(op) INSTRUCTION(op, DeleteSubscript, 2, base, index)
+#define INSTR_PushCatchContext(op) INSTRUCTION(op, PushCatchContext, 2, index, name)
+#define INSTR_PushWithContext(op) INSTRUCTION(op, PushWithContext, 0)
+#define INSTR_PushBlockContext(op) INSTRUCTION(op, PushBlockContext, 1, index)
+#define INSTR_CloneBlockContext(op) INSTRUCTION(op, CloneBlockContext, 0)
+#define INSTR_PushScriptContext(op) INSTRUCTION(op, PushScriptContext, 1, index)
+#define INSTR_PopScriptContext(op) INSTRUCTION(op, PopScriptContext, 0)
+#define INSTR_PopContext(op) INSTRUCTION(op, PopContext, 0)
+#define INSTR_GetIterator(op) INSTRUCTION(op, GetIterator, 1, iterator)
+#define INSTR_IteratorNext(op) INSTRUCTION(op, IteratorNext, 1, value)
+#define INSTR_IteratorClose(op) INSTRUCTION(op, IteratorClose, 1, done)
+#define INSTR_DestructureRestElement(op) INSTRUCTION(op, DestructureRestElement, 0)
+#define INSTR_DeleteProperty(op) INSTRUCTION(op, DeleteProperty, 2, base, index)
#define INSTR_DeleteName(op) INSTRUCTION(op, DeleteName, 1, name)
#define INSTR_TypeofName(op) INSTRUCTION(op, TypeofName, 1, name)
#define INSTR_TypeofValue(op) INSTRUCTION(op, TypeofValue, 0)
#define INSTR_DeclareVar(op) INSTRUCTION(op, DeclareVar, 2, varName, isDeletable)
#define INSTR_DefineArray(op) INSTRUCTION(op, DefineArray, 2, argc, args)
-// arrayGetterSetterCountAndFlags contains 30 bits for count, 1 bit for needsSparseArray boolean
-#define INSTR_DefineObjectLiteral(op) INSTRUCTION(op, DefineObjectLiteral, 4, internalClassId, arrayValueCount, arrayGetterSetterCountAndFlags, args)
+#define INSTR_DefineObjectLiteral(op) INSTRUCTION(op, DefineObjectLiteral, 3, internalClassId, argc, args)
+#define INSTR_CreateClass(op) INSTRUCTION(op, CreateClass, 3, classIndex, heritage, computedNames)
#define INSTR_CreateMappedArgumentsObject(op) INSTRUCTION(op, CreateMappedArgumentsObject, 0)
#define INSTR_CreateUnmappedArgumentsObject(op) INSTRUCTION(op, CreateUnmappedArgumentsObject, 0)
+#define INSTR_CreateRestParameter(op) INSTRUCTION(op, CreateRestParameter, 1, argIndex)
#define INSTR_ConvertThisToObject(op) INSTRUCTION(op, ConvertThisToObject, 0)
-#define INSTR_Construct(op) INSTRUCTION(op, Construct, 3, func, argc, argv)
+#define INSTR_LoadSuperConstructor(op) INSTRUCTION(op, LoadSuperConstructor, 0)
+#define INSTR_ToObject(op) INSTRUCTION(op, ToObject, 0)
#define INSTR_Jump(op) INSTRUCTION(op, Jump, 1, offset)
#define INSTR_JumpTrue(op) INSTRUCTION(op, JumpTrue, 1, offset)
#define INSTR_JumpFalse(op) INSTRUCTION(op, JumpFalse, 1, offset)
+#define INSTR_JumpNotUndefined(op) INSTRUCTION(op, JumpNotUndefined, 1, offset)
+#define INSTR_JumpNoException(op) INSTRUCTION(op, JumpNoException, 1, offset)
#define INSTR_CmpEqNull(op) INSTRUCTION(op, CmpEqNull, 0)
#define INSTR_CmpNeNull(op) INSTRUCTION(op, CmpNeNull, 0)
#define INSTR_CmpEqInt(op) INSTRUCTION(op, CmpEqInt, 1, lhs)
@@ -147,8 +165,6 @@ QT_BEGIN_NAMESPACE
#define INSTR_CmpStrictNotEqual(op) INSTRUCTION(op, CmpStrictNotEqual, 1, lhs)
#define INSTR_CmpIn(op) INSTRUCTION(op, CmpIn, 1, lhs)
#define INSTR_CmpInstanceOf(op) INSTRUCTION(op, CmpInstanceOf, 1, lhs)
-#define INSTR_JumpStrictEqualStackSlotInt(op) INSTRUCTION(op, JumpStrictEqualStackSlotInt, 3, lhs, rhs, offset)
-#define INSTR_JumpStrictNotEqualStackSlotInt(op) INSTRUCTION(op, JumpStrictNotEqualStackSlotInt, 3, lhs, rhs, offset)
#define INSTR_UNot(op) INSTRUCTION(op, UNot, 0)
#define INSTR_UPlus(op) INSTRUCTION(op, UPlus, 0)
#define INSTR_UMinus(op) INSTRUCTION(op, UMinus, 0)
@@ -168,6 +184,7 @@ QT_BEGIN_NAMESPACE
#define INSTR_UShrConst(op) INSTRUCTION(op, UShrConst, 1, rhs)
#define INSTR_ShrConst(op) INSTRUCTION(op, ShrConst, 1, rhs)
#define INSTR_ShlConst(op) INSTRUCTION(op, ShlConst, 1, rhs)
+#define INSTR_Exp(op) INSTRUCTION(op, Exp, 1, lhs)
#define INSTR_Mul(op) INSTRUCTION(op, Mul, 1, lhs)
#define INSTR_Div(op) INSTRUCTION(op, Div, 1, lhs)
#define INSTR_Mod(op) INSTRUCTION(op, Mod, 1, lhs)
@@ -175,10 +192,12 @@ QT_BEGIN_NAMESPACE
#define INSTR_LoadQmlContext(op) INSTRUCTION(op, LoadQmlContext, 1, result)
#define INSTR_LoadQmlImportedScripts(op) INSTRUCTION(op, LoadQmlImportedScripts, 1, result)
+#define FOR_EACH_MOTH_INSTR_ALL(F) \
+ F(Nop) \
+ FOR_EACH_MOTH_INSTR(F)
#define FOR_EACH_MOTH_INSTR(F) \
F(Ret) \
- F(Debug) \
F(LoadConst) \
F(LoadZero) \
F(LoadTrue) \
@@ -186,6 +205,7 @@ QT_BEGIN_NAMESPACE
F(LoadNull) \
F(LoadUndefined) \
F(LoadInt) \
+ F(LoadRuntimeString) \
F(MoveConst) \
F(LoadReg) \
F(StoreReg) \
@@ -194,7 +214,6 @@ QT_BEGIN_NAMESPACE
F(StoreLocal) \
F(LoadScopedLocal) \
F(StoreScopedLocal) \
- F(LoadRuntimeString) \
F(MoveRegExp) \
F(LoadClosure) \
F(LoadName) \
@@ -202,53 +221,25 @@ QT_BEGIN_NAMESPACE
F(StoreNameSloppy) \
F(StoreNameStrict) \
F(LoadElement) \
- F(LoadElementA) \
F(StoreElement) \
F(LoadProperty) \
- F(LoadPropertyA) \
F(GetLookup) \
- F(GetLookupA) \
F(StoreProperty) \
F(SetLookup) \
+ F(LoadSuperProperty) \
+ F(StoreSuperProperty) \
F(StoreScopeObjectProperty) \
F(StoreContextObjectProperty) \
F(LoadScopeObjectProperty) \
F(LoadContextObjectProperty) \
F(LoadIdObject) \
- F(CallValue) \
- F(CallProperty) \
- F(CallPropertyLookup) \
- F(CallElement) \
- F(CallName) \
- F(CallPossiblyDirectEval) \
- F(CallGlobalLookup) \
- F(CallScopeObjectProperty) \
- F(CallContextObjectProperty) \
- F(SetExceptionHandler) \
- F(ThrowException) \
- F(GetException) \
- F(SetException) \
- F(CreateCallContext) \
- F(PushCatchContext) \
- F(PushWithContext) \
- F(PopContext) \
- F(ForeachIteratorObject) \
- F(ForeachNextPropertyName) \
- F(DeleteMember) \
- F(DeleteSubscript) \
- F(DeleteName) \
- F(TypeofName) \
- F(TypeofValue) \
- F(DeclareVar) \
- F(DefineArray) \
- F(DefineObjectLiteral) \
- F(CreateMappedArgumentsObject) \
- F(CreateUnmappedArgumentsObject) \
F(ConvertThisToObject) \
- F(Construct) \
+ F(ToObject) \
F(Jump) \
F(JumpTrue) \
F(JumpFalse) \
+ F(JumpNoException) \
+ F(JumpNotUndefined) \
F(CmpEqNull) \
F(CmpNeNull) \
F(CmpEqInt) \
@@ -263,8 +254,6 @@ QT_BEGIN_NAMESPACE
F(CmpStrictNotEqual) \
F(CmpIn) \
F(CmpInstanceOf) \
- F(JumpStrictEqualStackSlotInt) \
- F(JumpStrictNotEqualStackSlotInt) \
F(UNot) \
F(UPlus) \
F(UMinus) \
@@ -284,13 +273,60 @@ QT_BEGIN_NAMESPACE
F(UShrConst) \
F(ShrConst) \
F(ShlConst) \
+ F(Exp) \
F(Mul) \
F(Div) \
F(Mod) \
F(Sub) \
+ F(CallValue) \
+ F(CallProperty) \
+ F(CallPropertyLookup) \
+ F(CallElement) \
+ F(CallName) \
+ F(CallPossiblyDirectEval) \
+ F(CallGlobalLookup) \
+ F(CallScopeObjectProperty) \
+ F(CallContextObjectProperty) \
+ F(CallWithSpread) \
+ F(Construct) \
+ F(ConstructWithSpread) \
+ F(SetUnwindHandler) \
+ F(UnwindDispatch) \
+ F(UnwindToLabel) \
+ F(ThrowException) \
+ F(GetException) \
+ F(SetException) \
+ F(CreateCallContext) \
+ F(PushCatchContext) \
+ F(PushWithContext) \
+ F(PushBlockContext) \
+ F(CloneBlockContext) \
+ F(PopContext) \
+ F(GetIterator) \
+ F(IteratorNext) \
+ F(IteratorClose) \
+ F(DestructureRestElement) \
+ F(DeleteProperty) \
+ F(DeleteName) \
+ F(TypeofName) \
+ F(TypeofValue) \
+ F(DeclareVar) \
+ F(DefineArray) \
+ F(DefineObjectLiteral) \
+ F(CreateMappedArgumentsObject) \
+ F(CreateUnmappedArgumentsObject) \
+ F(CreateRestParameter) \
F(LoadQmlContext) \
- F(LoadQmlImportedScripts)
-#define MOTH_NUM_INSTRUCTIONS() (static_cast<int>(Moth::Instr::Type::LoadQmlImportedScripts) + 1)
+ F(LoadQmlImportedScripts) \
+ F(Yield) \
+ F(Resume) \
+ F(CreateClass) \
+ F(LoadSuperConstructor) \
+ F(PushScriptContext) \
+ F(PopScriptContext) \
+ F(Debug) \
+
+#define MOTH_NUM_INSTRUCTIONS() (static_cast<int>(Moth::Instr::Type::Debug_Wide) + 1)
#if defined(Q_CC_GNU) && !defined(Q_CC_INTEL)
// icc before version 1200 doesn't support computed goto, and at least up to version 18.0.0 the
@@ -301,7 +337,7 @@ QT_BEGIN_NAMESPACE
#define MOTH_INSTR_ALIGN_MASK (Q_ALIGNOF(QV4::Moth::Instr) - 1)
-#define MOTH_INSTR_ENUM(I) I,
+#define MOTH_INSTR_ENUM(I) I, I##_Wide,
#define MOTH_INSTR_SIZE(I) (sizeof(QV4::Moth::Instr::instr_##I))
#define MOTH_EXPAND_FOR_MSVC(x) x
@@ -344,7 +380,7 @@ QT_BEGIN_NAMESPACE
#define MOTH_COLLECT_NARGS(instr) \
INSTR_##instr(MOTH_COLLECT_ARG_COUNT)
#define MOTH_COLLECT_ARG_COUNT_INSTRUCTION(name, nargs, ...) \
- nargs,
+ nargs, nargs,
#define MOTH_DECODE_ARG(arg, type, nargs, offset) \
arg = qFromLittleEndian<type>(qFromUnaligned<type>(reinterpret_cast<const type *>(code) - nargs + offset));
@@ -397,38 +433,53 @@ QT_BEGIN_NAMESPACE
#ifdef MOTH_COMPUTED_GOTO
/* collect jump labels */
#define COLLECT_LABELS(instr) \
- INSTR_##instr(GET_LABEL)
+ INSTR_##instr(GET_LABEL) \
+ INSTR_##instr(GET_LABEL_WIDE)
#define GET_LABEL_INSTRUCTION(name, ...) \
&&op_byte_##name,
-#define COLLECT_LABELS_WIDE(instr) \
- INSTR_##instr(GET_LABEL_WIDE)
#define GET_LABEL_WIDE_INSTRUCTION(name, ...) \
&&op_int_##name,
#define MOTH_JUMP_TABLE \
static const void *jumpTable[] = { \
- FOR_EACH_MOTH_INSTR(COLLECT_LABELS) \
- FOR_EACH_MOTH_INSTR(COLLECT_LABELS_WIDE) \
+ FOR_EACH_MOTH_INSTR_ALL(COLLECT_LABELS) \
};
-#define MOTH_DISPATCH() \
+#define MOTH_DISPATCH_SINGLE() \
goto *jumpTable[*reinterpret_cast<const uchar *>(code)];
+
+#define MOTH_DISPATCH() \
+ MOTH_DISPATCH_SINGLE() \
+ op_byte_Nop: \
+ ++code; \
+ MOTH_DISPATCH_SINGLE() \
+ op_int_Nop: /* wide prefix */ \
+ ++code; \
+ goto *jumpTable[0x100 | *reinterpret_cast<const uchar *>(code)];
#else
#define MOTH_JUMP_TABLE
#define MOTH_INSTR_CASE_AND_JUMP(instr) \
- INSTR_##instr(GET_CASE_AND_JUMP)
-#define GET_CASE_AND_JUMP_INSTRUCTION(name, ...) \
- case static_cast<uchar>(Instr::Type::name): goto op_byte_##name;
-#define MOTH_INSTR_CASE_AND_JUMP_WIDE(instr) \
+ INSTR_##instr(GET_CASE_AND_JUMP) \
INSTR_##instr(GET_CASE_AND_JUMP_WIDE)
+#define GET_CASE_AND_JUMP_INSTRUCTION(name, ...) \
+ case Instr::Type::name: goto op_byte_##name;
#define GET_CASE_AND_JUMP_WIDE_INSTRUCTION(name, ...) \
- case (static_cast<uchar>(Instr::Type::name) + MOTH_NUM_INSTRUCTIONS()): goto op_int_##name;
+ case Instr::Type::name##_Wide: goto op_int_##name;
#define MOTH_DISPATCH() \
- switch (static_cast<uchar>(*code)) { \
+ Instr::Type type = Instr::Type(static_cast<uchar>(*code)); \
+ dispatch: \
+ switch (type) { \
+ case Instr::Type::Nop: \
+ ++code; \
+ type = Instr::Type(static_cast<uchar>(*code)); \
+ goto dispatch; \
+ case Instr::Type::Nop_Wide: /* wide prefix */ \
+ ++code; \
+ type = Instr::Type(0x100 | static_cast<uchar>(*code)); \
+ goto dispatch; \
FOR_EACH_MOTH_INSTR(MOTH_INSTR_CASE_AND_JUMP) \
- FOR_EACH_MOTH_INSTR(MOTH_INSTR_CASE_AND_JUMP_WIDE) \
}
#endif
@@ -471,12 +522,29 @@ inline void dumpBytecode(const QByteArray &bytecode, int nLocals, int nFormals,
union Instr
{
enum class Type {
- FOR_EACH_MOTH_INSTR(MOTH_INSTR_ENUM)
+ FOR_EACH_MOTH_INSTR_ALL(MOTH_INSTR_ENUM)
};
- FOR_EACH_MOTH_INSTR(MOTH_EMIT_STRUCTS)
+ static Type wideInstructionType(Type t) { return Type(int(t) | 1); }
+ static Type narrowInstructionType(Type t) { return Type(int(t) & ~1); }
+ static bool isWide(Type t) { return int(t) & 1; }
+ static bool isNarrow(Type t) { return !(int(t) & 1); }
+ static int encodedLength(Type t) { return int(t) >= 256 ? 2 : 1; }
+
+ static Type unpack(const uchar *c) { if (c[0] == 0x1) return Type(0x100 + c[1]); return Type(c[0]); }
+ static uchar *pack(uchar *c, Type t) {
+ if (uint(t) >= 256) {
+ c[0] = 0x1;
+ c[1] = uint(t) &0xff;
+ return c + 2;
+ }
+ c[0] = uchar(uint(t));
+ return c + 1;
+ }
+
+ FOR_EACH_MOTH_INSTR_ALL(MOTH_EMIT_STRUCTS)
- FOR_EACH_MOTH_INSTR(MOTH_EMIT_INSTR_MEMBERS)
+ FOR_EACH_MOTH_INSTR_ALL(MOTH_EMIT_INSTR_MEMBERS)
int argumentsAsInts[4];
};
@@ -487,8 +555,6 @@ struct InstrInfo
static int size(Instr::Type type);
};
-Q_STATIC_ASSERT(MOTH_NUM_INSTRUCTIONS() < 128);
-
template<int N>
struct InstrMeta {
};
@@ -506,7 +572,7 @@ QT_WARNING_DISABLE_GCC("-Wmaybe-uninitialized")
reinterpret_cast<const char *>(&v), \
Size); } \
};
-FOR_EACH_MOTH_INSTR(MOTH_INSTR_META_TEMPLATE);
+FOR_EACH_MOTH_INSTR_ALL(MOTH_INSTR_META_TEMPLATE);
#undef MOTH_INSTR_META_TEMPLATE
QT_WARNING_POP
@@ -517,7 +583,7 @@ class InstrData : public InstrMeta<InstrType>::DataType
struct Instruction {
#define MOTH_INSTR_DATA_TYPEDEF(I) typedef InstrData<int(Instr::Type::I)> I;
-FOR_EACH_MOTH_INSTR(MOTH_INSTR_DATA_TYPEDEF)
+FOR_EACH_MOTH_INSTR_ALL(MOTH_INSTR_DATA_TYPEDEF)
#undef MOTH_INSTR_DATA_TYPEDEF
private:
Instruction();