aboutsummaryrefslogtreecommitdiffstats
path: root/src/qml/compiler/qv4compilercontext.cpp
diff options
context:
space:
mode:
authorLars Knoll <lars.knoll@qt.io>2018-03-28 10:58:58 +0200
committerLars Knoll <lars.knoll@qt.io>2018-05-02 14:17:21 +0000
commit851c28d140c398990513640047a20aab36ccc655 (patch)
treedd9391f74c6a98a234cf2b5ac09bc41b7b7d3b98 /src/qml/compiler/qv4compilercontext.cpp
parent13cc936859518b5fa378c7b8242d56ebf49ebce9 (diff)
Refactor variable resolving
Move variable resolving into the context, and avoid creating ExecutionContext's whereever we can. This prepares things for block scoping, where this becomes rather important to be able to achieve decent performance. Change-Id: Idf3d3c12cf348a2c3da01989c26c8529ceb36c12 Reviewed-by: Simon Hausmann <simon.hausmann@qt.io>
Diffstat (limited to 'src/qml/compiler/qv4compilercontext.cpp')
-rw-r--r--src/qml/compiler/qv4compilercontext.cpp145
1 files changed, 145 insertions, 0 deletions
diff --git a/src/qml/compiler/qv4compilercontext.cpp b/src/qml/compiler/qv4compilercontext.cpp
index 61b9254681..1c583b5cf2 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;
@@ -81,4 +82,148 @@ bool Context::forceLookupByName()
return false;
}
+bool Context::addLocalVar(const QString &name, Context::MemberType type, VariableScope scope, FunctionExpression *function)
+{
+ if (name.isEmpty())
+ return true;
+
+ if (type != FunctionDefinition) {
+ if (formals && formals->containsName(name))
+ return (scope == QQmlJS::AST::VariableScope::Var);
+ }
+ MemberMap::iterator it = members.find(name);
+ if (it != members.end()) {
+ if (scope != QQmlJS::AST::VariableScope::Var || (*it).scope != QQmlJS::AST::VariableScope::Var)
+ 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;
+}
+
+Context::ResolvedName Context::resolveName(const QString &name)
+{
+ int scope = 0;
+ Context *c = this;
+
+ ResolvedName result;
+
+ while (c->parent) {
+ if (c->forceLookupByName())
+ return result;
+
+ Context::Member m = c->findMember(name);
+ if (m.type != Context::UndefinedMember) {
+ result.type = m.canEscape ? ResolvedName::Local : ResolvedName::Stack;
+ result.scope = scope;
+ result.index = m.index;
+ if (c->isStrict && (name == QLatin1String("arguments") || name == QLatin1String("eval")))
+ result.isArgOrEval = true;
+ return result;
+ Q_ASSERT(result.type != ResolvedName::Stack || result.scope == 0);
+ }
+ 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;
+ return result;
+ } else {
+ Q_ASSERT(scope == 0);
+ result.index = argIdx + sizeof(CallData)/sizeof(Value) - 1;
+ result.scope = 0;
+ result.type = ResolvedName::Stack;
+ return result;
+ }
+ }
+ if (!c->isStrict && c->hasDirectEval)
+ return result;
+
+ if (c->requiresExecutionContext)
+ ++scope;
+ c = c->parent;
+ }
+
+ // ### can we relax the restrictions here?
+ if (c->forceLookupByName() || type == ContextType::Eval || c->type == ContextType::Binding)
+ return result;
+
+ result.type = ResolvedName::Global;
+ return result;
+}
+
+void Context::emitHeaderBytecode(Codegen *codegen)
+{
+ using Instruction = Moth::Instruction;
+ Moth::BytecodeGenerator *bytecodeGenerator = codegen->generator();
+
+ bool allVarsEscape = hasWith || hasTry || hasDirectEval;
+ if (requiresExecutionContext ||
+ type == ContextType::Binding) { // 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.
+ Instruction::CreateCallContext createContext;
+ bytecodeGenerator->addInstruction(createContext);
+ }
+ if (usesThis && !isStrict) {
+ // make sure we convert this to an object
+ Instruction::ConvertThisToObject convert;
+ bytecodeGenerator->addInstruction(convert);
+ }
+
+ // variables in global code are properties of the global context object, not locals as with other functions.
+ if (type == ContextType::Function || type == ContextType::Binding) {
+ for (Context::MemberMap::iterator it = members.begin(), end = members.end(); it != end; ++it) {
+ const QString &local = it.key();
+ if (allVarsEscape)
+ it->canEscape = true;
+ if (it->canEscape) {
+ it->index = locals.size();
+ 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 = members.constBegin(), cend = members.constEnd(); it != cend; ++it) {
+ const QString &local = it.key();
+
+ Instruction::DeclareVar declareVar;
+ declareVar.isDeletable = false;
+ declareVar.varName = codegen->registerString(local);
+ bytecodeGenerator->addInstruction(declareVar);
+ }
+ }
+
+ if (usesArgumentsObject == Context::ArgumentsObjectUsed) {
+ if (isStrict || (formals && !formals->isSimpleParameterList())) {
+ Instruction::CreateUnmappedArgumentsObject setup;
+ bytecodeGenerator->addInstruction(setup);
+ } else {
+ Instruction::CreateMappedArgumentsObject setup;
+ bytecodeGenerator->addInstruction(setup);
+ }
+ codegen->referenceForName(QStringLiteral("arguments"), false).storeConsumeAccumulator();
+ }
+}
+
QT_END_NAMESPACE