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