diff options
author | Lars Knoll <lars.knoll@qt.io> | 2018-03-28 10:58:58 +0200 |
---|---|---|
committer | Lars Knoll <lars.knoll@qt.io> | 2018-05-02 14:17:21 +0000 |
commit | 851c28d140c398990513640047a20aab36ccc655 (patch) | |
tree | dd9391f74c6a98a234cf2b5ac09bc41b7b7d3b98 /src/qml/compiler/qv4compilercontext.cpp | |
parent | 13cc936859518b5fa378c7b8242d56ebf49ebce9 (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.cpp | 145 |
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 |