aboutsummaryrefslogtreecommitdiffstats
path: root/src/qml/compiler/qv4compilercontext.cpp
diff options
context:
space:
mode:
authorLars Knoll <lars.knoll@qt.io>2018-03-26 22:59:28 +0200
committerLars Knoll <lars.knoll@qt.io>2018-05-02 14:17:46 +0000
commit20d30b6b3a253eebedc927dbb91685bbec52cfee (patch)
tree0e91a6c519829bd279258f3fe807519e6c8a1e3d /src/qml/compiler/qv4compilercontext.cpp
parentd499a995292629d3522f5e77b7c958bacdf5d0ae (diff)
Add support for proper lexical scoping
This is still to some extend work in progress as lexically scoped for loops won't yet do the right thing. let and const variables are still accessible before they are declared, and the global scope doesn't yet have a proper context for lexically declared variables. Change-Id: Ie39f74a8fccdaead437fbf07f9fc228a444c26ed Reviewed-by: Simon Hausmann <simon.hausmann@qt.io>
Diffstat (limited to 'src/qml/compiler/qv4compilercontext.cpp')
-rw-r--r--src/qml/compiler/qv4compilercontext.cpp95
1 files changed, 71 insertions, 24 deletions
diff --git a/src/qml/compiler/qv4compilercontext.cpp b/src/qml/compiler/qv4compilercontext.cpp
index e2e978be7c..4893a06af8 100644
--- a/src/qml/compiler/qv4compilercontext.cpp
+++ b/src/qml/compiler/qv4compilercontext.cpp
@@ -71,19 +71,12 @@ Context *Module::newContext(Node *node, Context *parent, ContextType contextType
return c;
}
-bool Context::forceLookupByName()
-{
- ControlFlow *flow = controlFlow;
- while (flow) {
- if (flow->needsLookupByName)
- return true;
- flow = flow->parent;
- }
- return false;
-}
-
bool Context::addLocalVar(const QString &name, Context::MemberType type, VariableScope scope, FunctionExpression *function)
{
+ // hoist var declarations to the function level
+ if (contextType == ContextType::Block && (scope == VariableScope::Var && type != MemberType::FunctionDefinition))
+ return parent->addLocalVar(name, type, scope, function);
+
if (name.isEmpty())
return true;
@@ -117,7 +110,7 @@ Context::ResolvedName Context::resolveName(const QString &name)
ResolvedName result;
while (c->parent) {
- if (c->forceLookupByName())
+ if (c->forceLookupByName)
return result;
Context::Member m = c->findMember(name);
@@ -128,7 +121,6 @@ Context::ResolvedName Context::resolveName(const QString &name)
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) {
@@ -145,7 +137,7 @@ Context::ResolvedName Context::resolveName(const QString &name)
return result;
}
}
- if (!c->isStrict && c->hasDirectEval)
+ if (!c->isStrict && c->hasDirectEval && c->contextType != ContextType::Block)
return result;
if (c->requiresExecutionContext)
@@ -154,25 +146,32 @@ Context::ResolvedName Context::resolveName(const QString &name)
}
// ### can we relax the restrictions here?
- if (c->forceLookupByName() || type == ContextType::Eval || c->type == ContextType::Binding)
+ if (c->forceLookupByName || contextType == ContextType::Eval || c->contextType == ContextType::Binding)
return result;
result.type = ResolvedName::Global;
return result;
}
-void Context::emitHeaderBytecode(Codegen *codegen)
+int Context::emitBlockHeader(Codegen *codegen)
{
-
using Instruction = Moth::Instruction;
Moth::BytecodeGenerator *bytecodeGenerator = codegen->generator();
setupFunctionIndices(bytecodeGenerator);
+ int contextReg = -1;
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);
+ contextType == 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.
+ if (contextType == ContextType::Block) {
+ Instruction::PushBlockContext blockContext;
+ blockContext.index = blockIndex;
+ blockContext.reg = contextReg = bytecodeGenerator->newRegister();
+ bytecodeGenerator->addInstruction(blockContext);
+ } else {
+ Instruction::CreateCallContext createContext;
+ bytecodeGenerator->addInstruction(createContext);
+ }
}
if (usesThis && !isStrict) {
// make sure we convert this to an object
@@ -180,8 +179,10 @@ void Context::emitHeaderBytecode(Codegen *codegen)
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) {
+ switch (contextType) {
+ case ContextType::Block:
+ case ContextType::Function:
+ case 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
@@ -193,7 +194,11 @@ void Context::emitHeaderBytecode(Codegen *codegen)
bytecodeGenerator->addInstruction(store);
}
}
- } else {
+ break;
+ }
+ case ContextType::Global:
+ case ContextType::Eval: {
+ // 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) {
const QString &local = it.key();
@@ -203,8 +208,10 @@ void Context::emitHeaderBytecode(Codegen *codegen)
bytecodeGenerator->addInstruction(declareVar);
}
}
+ }
if (usesArgumentsObject == Context::ArgumentsObjectUsed) {
+ Q_ASSERT(contextType != ContextType::Block);
if (isStrict || (formals && !formals->isSimpleParameterList())) {
Instruction::CreateUnmappedArgumentsObject setup;
bytecodeGenerator->addInstruction(setup);
@@ -214,6 +221,38 @@ void Context::emitHeaderBytecode(Codegen *codegen)
}
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);
+ if (!parent) {
+ Codegen::Reference::fromName(codegen, member.function->name.toString()).storeConsumeAccumulator();
+ } else {
+ int idx = member.index;
+ Q_ASSERT(idx >= 0);
+ Codegen::Reference local = member.canEscape ? Codegen::Reference::fromScopedLocal(codegen, idx, 0)
+ : Codegen::Reference::fromStackSlot(codegen, idx, true);
+ local.storeConsumeAccumulator();
+ }
+ }
+ }
+
+
+
+ return contextReg;
+}
+
+void Context::emitBlockFooter(Codegen *codegen, int oldContextReg)
+{
+ using Instruction = Moth::Instruction;
+ Moth::BytecodeGenerator *bytecodeGenerator = codegen->generator();
+
+ if (oldContextReg != -1) {
+ Instruction::PopContext popContext;
+ popContext.reg = oldContextReg;
+ bytecodeGenerator->addInstruction(popContext);
+ }
}
void Context::setupFunctionIndices(Moth::BytecodeGenerator *bytecodeGenerator)
@@ -222,7 +261,10 @@ void Context::setupFunctionIndices(Moth::BytecodeGenerator *bytecodeGenerator)
Q_ASSERT(nRegisters == 0);
registerOffset = bytecodeGenerator->registerCount();
- if (type == ContextType::Function || type == ContextType::Binding) {
+ 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) {
@@ -235,6 +277,11 @@ void Context::setupFunctionIndices(Moth::BytecodeGenerator *bytecodeGenerator)
it->index = bytecodeGenerator->newRegister();
}
}
+ break;
+ }
+ case ContextType::Global:
+ case ContextType::Eval:
+ break;
}
nRegisters = bytecodeGenerator->registerCount() - registerOffset;
}