Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qv4compilercontext.cpp
Go to the documentation of this file.
1// Copyright (C) 2017 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
4#include "qv4codegen_p.h"
7#include <QtQml/private/qv4calldata_p.h>
8
9QT_USE_NAMESPACE
10using namespace QV4;
11using namespace QV4::Compiler;
12using namespace QQmlJS::AST;
13using namespace QQmlJS;
14
15QT_BEGIN_NAMESPACE
16
17Context *Module::newContext(Node *node, Context *parent, ContextType contextType)
18{
19 Q_ASSERT(!contextMap.contains(node));
20
21 Context *c = new Context(parent, contextType);
22 if (node) {
23 SourceLocation loc = node->firstSourceLocation();
24 c->line = loc.startLine;
25 c->column = loc.startColumn;
26 }
27
28 contextMap.insert(node, c);
29
30 if (!parent)
31 rootContext = c;
32 else {
33 parent->nestedContexts.append(c);
34 c->isStrict = parent->isStrict;
35 }
36
37 return c;
38}
39
40bool Context::Member::requiresTDZCheck(const SourceLocation &accessLocation, bool accessAcrossContextBoundaries) const
41{
43 return false;
44
45 if (accessAcrossContextBoundaries)
46 return true;
47
48 if (!accessLocation.isValid() || !declarationLocation.isValid())
49 return true;
50
51 return accessLocation.begin() < declarationLocation.end();
52}
53
54bool Context::addLocalVar(
55 const QString &name, Context::MemberType type, VariableScope scope,
56 FunctionExpression *function, const QQmlJS::SourceLocation &declarationLocation,
57 bool isInjected)
58{
59 // ### can this happen?
60 if (name.isEmpty())
61 return true;
62
63 if (type != FunctionDefinition) {
64 if (formals && formals->containsName(name))
65 return (scope == VariableScope::Var);
66 }
67 if (!isCatchBlock || name != caughtVariable) {
68 MemberMap::iterator it = members.find(name);
69 if (it != members.end()) {
70 if (scope != VariableScope::Var || (*it).scope != VariableScope::Var)
71 return false;
72 if ((*it).type <= type) {
73 (*it).type = type;
74 (*it).function = function;
75 }
76 return true;
77 }
78 }
79
80 // hoist var declarations to the function level
81 if (contextType == ContextType::Block && (scope == VariableScope::Var && type != MemberType::FunctionDefinition))
82 return parent->addLocalVar(name, type, scope, function, declarationLocation);
83
84 Member m;
85 m.type = type;
86 m.function = function;
87 m.scope = scope;
88 m.declarationLocation = declarationLocation;
89 m.isInjected = isInjected;
90 members.insert(name, m);
91 return true;
92}
93
94Context::ResolvedName Context::resolveName(const QString &name, const QQmlJS::SourceLocation &accessLocation)
95{
96 int scope = 0;
97 Context *c = this;
98
99 ResolvedName result;
100
101 while (c) {
102 if (c->isWithBlock)
103 return result;
104
105 Context::Member m = c->findMember(name);
106 if (!c->parent && m.index < 0)
107 break;
108
111 result.memberType = m.type;
112 result.scope = scope;
113 result.index = m.index;
114 result.isConst = (m.scope == VariableScope::Const);
115 result.requiresTDZCheck = m.requiresTDZCheck(accessLocation, c != this) || c->isCaseBlock();
116 if (c->isStrict && (name == QLatin1String("arguments") || name == QLatin1String("eval")))
117 result.isArgOrEval = true;
118 result.declarationLocation = m.declarationLocation;
119 result.isInjected = m.isInjected;
120 return result;
121 }
122 const int argIdx = c->findArgument(name, &result.isInjected);
123 if (argIdx != -1) {
125 result.index = argIdx + c->locals.size();
126 result.scope = scope;
128 result.isConst = false;
129 return result;
130 } else {
131 result.index = argIdx + sizeof(CallData) / sizeof(StaticValue) - 1;
132 result.scope = 0;
134 result.isConst = false;
135 return result;
136 }
137 }
138 if (c->hasDirectEval) {
140 return result;
141 }
142
144 ++scope;
145 c = c->parent;
146 }
147
148 if (!c)
149 return result;
150
152 for (int i = 0; i < c->importEntries.size(); ++i) {
153 if (c->importEntries.at(i).localName == name) {
154 result.index = i;
156 result.isConst = true;
157 // We don't know at compile time whether the imported value is let/const or not.
158 result.requiresTDZCheck = true;
159 return result;
160 }
161 }
162 }
163
164 // ### can we relax the restrictions here?
166 return result;
167
170 else
172 return result;
173}
174
175void Context::emitBlockHeader(Codegen *codegen)
176{
177 using Instruction = Moth::Instruction;
178 Moth::BytecodeGenerator *bytecodeGenerator = codegen->generator();
179
180 setupFunctionIndices(bytecodeGenerator);
181
183 if (blockIndex < 0) {
184 codegen->module()->blocks.append(this);
185 blockIndex = codegen->module()->blocks.size() - 1;
186 }
187
189 Instruction::PushScriptContext scriptContext;
190 scriptContext.index = blockIndex;
191 bytecodeGenerator->addInstruction(scriptContext);
193 if (isCatchBlock) {
194 Instruction::PushCatchContext catchContext;
195 catchContext.index = blockIndex;
196 catchContext.name = codegen->registerString(caughtVariable);
197 bytecodeGenerator->addInstruction(catchContext);
198 } else {
199 Instruction::PushBlockContext blockContext;
200 blockContext.index = blockIndex;
201 bytecodeGenerator->addInstruction(blockContext);
202 }
204 Instruction::CreateCallContext createContext;
205 bytecodeGenerator->addInstruction(createContext);
206 }
207 }
208
210 Instruction::InitializeBlockDeadTemporalZone tdzInit;
212 tdzInit.count = sizeOfRegisterTemporalDeadZone;
213 bytecodeGenerator->addInstruction(tdzInit);
214 }
215
216 if (usesThis) {
217 Q_ASSERT(!isStrict);
218 // make sure we convert this to an object
219 Instruction::ConvertThisToObject convert;
220 bytecodeGenerator->addInstruction(convert);
221 }
223 Instruction::LoadReg load;
224 load.reg = CallData::This;
225 bytecodeGenerator->addInstruction(load);
226 Codegen::Reference r = codegen->referenceForName(QStringLiteral("this"), true);
227 r.storeConsumeAccumulator();
228 }
230 Instruction::LoadReg load;
231 load.reg = CallData::NewTarget;
232 bytecodeGenerator->addInstruction(load);
233 Codegen::Reference r = codegen->referenceForName(QStringLiteral("new.target"), true);
234 r.storeConsumeAccumulator();
235 }
236
238 // variables in global code are properties of the global context object, not locals as with other functions.
239 for (Context::MemberMap::const_iterator it = members.constBegin(), cend = members.constEnd(); it != cend; ++it) {
240 if (it->isLexicallyScoped())
241 continue;
242 const QString &local = it.key();
243
244 Instruction::DeclareVar declareVar;
245 declareVar.isDeletable = (contextType == ContextType::Eval);
246 declareVar.varName = codegen->registerString(local);
247 bytecodeGenerator->addInstruction(declareVar);
248 }
249 }
250
252 for (Context::MemberMap::iterator it = members.begin(), end = members.end(); it != end; ++it) {
253 if (it->canEscape && it->type == Context::ThisFunctionName) {
254 // move the function from the stack to the call context
255 Instruction::LoadReg load;
256 load.reg = CallData::Function;
257 bytecodeGenerator->addInstruction(load);
258 Instruction::StoreLocal store;
259 store.index = it->index;
260 bytecodeGenerator->addInstruction(store);
261 }
262 }
263 }
264
266 Q_ASSERT(contextType != ContextType::Block);
267 if (isStrict || (formals && !formals->isSimpleParameterList())) {
268 Instruction::CreateUnmappedArgumentsObject setup;
269 bytecodeGenerator->addInstruction(setup);
270 } else {
271 Instruction::CreateMappedArgumentsObject setup;
272 bytecodeGenerator->addInstruction(setup);
273 }
274 codegen->referenceForName(QStringLiteral("arguments"), false).storeConsumeAccumulator();
275 }
276
277 for (const Context::Member &member : std::as_const(members)) {
278 if (member.function) {
279 const int function = codegen->defineFunction(member.function->name.toString(), member.function, member.function->formals, member.function->body);
280 codegen->loadClosure(function);
281 Codegen::Reference r = codegen->referenceForName(member.function->name.toString(), true);
282 r.storeConsumeAccumulator();
283 }
284 }
285}
286
287void Context::emitBlockFooter(Codegen *codegen)
288{
289 using Instruction = Moth::Instruction;
290 Moth::BytecodeGenerator *bytecodeGenerator = codegen->generator();
291
293 return;
294
295QT_WARNING_PUSH
296QT_WARNING_DISABLE_GCC("-Wmaybe-uninitialized") // the loads below are empty structs.
297 if (contextType == ContextType::Global)
298 bytecodeGenerator->addInstruction(Instruction::PopScriptContext());
299 else if (contextType != ContextType::ESModule && contextType != ContextType::ScriptImportedByQML)
300 bytecodeGenerator->addInstruction(Instruction::PopContext());
301QT_WARNING_POP
302}
303
305{
306 if (registerOffset != -1) {
307 // already computed, check for consistency
308 Q_ASSERT(registerOffset == bytecodeGenerator->currentRegister());
309 bytecodeGenerator->newRegisterArray(nRegisters);
310 return;
311 }
312 Q_ASSERT(locals.size() == 0);
313 Q_ASSERT(nRegisters == 0);
314 registerOffset = bytecodeGenerator->currentRegister();
315
316 QVector<Context::MemberMap::Iterator> localsInTDZ;
317 const auto registerLocal = [this, &localsInTDZ](Context::MemberMap::iterator member) {
318 if (member->isLexicallyScoped()) {
319 localsInTDZ << member;
320 } else {
321 member->index = locals.size();
322 locals.append(member.key());
323 }
324 };
325
326 QVector<Context::MemberMap::Iterator> registersInTDZ;
327 const auto allocateRegister = [bytecodeGenerator, &registersInTDZ](Context::MemberMap::iterator member) {
328 if (member->isLexicallyScoped())
329 registersInTDZ << member;
330 else
331 member->index = bytecodeGenerator->newRegister();
332 };
333
334 switch (contextType) {
339 for (Context::MemberMap::iterator it = members.begin(), end = members.end(); it != end; ++it) {
340 if (it->canEscape) {
341 registerLocal(it);
342 } else {
343 if (it->type == Context::ThisFunctionName)
344 it->index = CallData::Function;
345 else
346 allocateRegister(it);
347 }
348 }
349 break;
350 }
354 for (Context::MemberMap::iterator it = members.begin(), end = members.end(); it != end; ++it) {
355 if (!it->isLexicallyScoped() && (contextType == ContextType::Global || contextType == ContextType::ScriptImportedByQML || !isStrict))
356 continue;
357 if (it->canEscape)
358 registerLocal(it);
359 else
360 allocateRegister(it);
361 }
362 break;
363 }
364
365 sizeOfLocalTemporalDeadZone = localsInTDZ.size();
366 for (auto &member: std::as_const(localsInTDZ)) {
367 member->index = locals.size();
368 locals.append(member.key());
369 }
370
371 if (contextType == ContextType::ESModule && !localNameForDefaultExport.isEmpty()) {
372 if (!members.contains(localNameForDefaultExport)) {
373 // allocate a local slot for the default export, to be used in
374 // CodeGen::visit(ExportDeclaration*).
375 locals.append(localNameForDefaultExport);
377 }
378 }
379
380 sizeOfRegisterTemporalDeadZone = registersInTDZ.size();
382 for (auto &member: std::as_const(registersInTDZ))
383 member->index = bytecodeGenerator->newRegister();
384
385 nRegisters = bytecodeGenerator->currentRegister() - registerOffset;
386}
387
388QT_END_NAMESPACE
Definition qjsvalue.h:23
void emitBlockFooter(Compiler::Codegen *codegen)
void emitBlockHeader(Compiler::Codegen *codegen)
UsesArgumentsObject usesArgumentsObject
void setupFunctionIndices(Moth::BytecodeGenerator *bytecodeGenerator)