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 ((*it).type == ThisFunctionName) {
72 // The name of a named function expression is bound in an implicit
73 // scope of its own, distinct from the function's parameter, var and
74 // lexical scope. Any real declaration sharing that name therefore
75 // shadows it rather than conflicting with it, so let it replace the
76 // placeholder entry below instead of rejecting the declaration.
77 members.erase(it);
78 } else {
79 if (scope != VariableScope::Var || (*it).scope != VariableScope::Var)
80 return false;
81 if ((*it).type <= type) {
82 (*it).type = type;
83 (*it).function = function;
84 }
85 return true;
86 }
87 }
88 }
89
90 // hoist var declarations to the function level
91 if (contextType == ContextType::Block && (scope == VariableScope::Var && type != MemberType::FunctionDefinition))
92 return parent->addLocalVar(name, type, scope, function, declarationLocation);
93
94 Member m;
95 m.type = type;
96 m.function = function;
97 m.scope = scope;
98 m.declarationLocation = declarationLocation;
99 m.isInjected = isInjected;
100 members.insert(name, m);
101 return true;
102}
103
104// A formal parameter whose default value initializer references a
105// parameter of the same formal parameter list (itself, or one that
106// hasn't been bound yet) can't be resolved as a plain argument: reading
107// it before its own initialization must throw, the same way it would
108// for a "let" binding used before declaration. Give such a parameter a
109// real lexically-scoped Member (bypassing addLocalVar()'s formal-name
110// collision guard) so it goes through the normal TDZ machinery instead of
111// the fast argument-register path.
112// Used to detect self- or forward-references to another
113// (possibly not-yet-bound) parameter of the same formal parameter list, e.g.
114// "(x = x) => {}" or "(a = b, b) => {}".
115void Context::promoteFormalParameterForTDZ(const QString &name, const QQmlJS::SourceLocation &loc)
116{
117 if (members.contains(name))
118 return;
119
120 Member m;
122 m.scope = VariableScope::Let;
123 m.declarationLocation = loc;
125 members.insert(name, m);
126}
127
128Context::ResolvedName Context::resolveName(const QString &name, const QQmlJS::SourceLocation &accessLocation)
129{
130 int scope = 0;
131 Context *c = this;
132
133 ResolvedName result;
134
135 while (c) {
136 if (c->isWithBlock)
137 return result;
138
139 Context::Member m = c->findMember(name);
140 if (!c->parent && m.index < 0)
141 break;
142
145 result.memberType = m.type;
146 result.scope = scope;
147 result.index = m.index;
148 result.isConst = (m.scope == VariableScope::Const);
149 result.requiresTDZCheck = m.requiresTDZCheck(accessLocation, c != this) || c->isCaseBlock();
150 if (c->isStrict && (name == QLatin1String("arguments") || name == QLatin1String("eval")))
151 result.isArgOrEval = true;
152 result.declarationLocation = m.declarationLocation;
153 result.isInjected = m.isInjected;
154 return result;
155 }
156 const int argIdx = c->findArgument(name, &result.isInjected);
157 if (argIdx != -1) {
159 result.index = argIdx + c->locals.size();
160 result.scope = scope;
162 result.isConst = false;
163 return result;
164 } else {
165 result.index = argIdx + sizeof(CallData) / sizeof(StaticValue) - 1;
166 result.scope = 0;
168 result.isConst = false;
169 return result;
170 }
171 }
172 if (c->hasDirectEval) {
174 return result;
175 }
176
178 ++scope;
179 c = c->parent;
180 }
181
182 if (!c)
183 return result;
184
186 for (int i = 0; i < c->importEntries.size(); ++i) {
187 if (c->importEntries.at(i).localName == name) {
188 result.index = i;
190 result.isConst = true;
191 // We don't know at compile time whether the imported value is let/const or not.
192 result.requiresTDZCheck = true;
193 return result;
194 }
195 }
196 }
197
198 // ### can we relax the restrictions here?
200 return result;
201
204 else
206 return result;
207}
208
209void Context::emitBlockHeader(Codegen *codegen)
210{
211 using Instruction = Moth::Instruction;
212 Moth::BytecodeGenerator *bytecodeGenerator = codegen->generator();
213
214 setupFunctionIndices(bytecodeGenerator);
215
217 if (blockIndex < 0) {
218 codegen->module()->blocks.append(this);
219 blockIndex = codegen->module()->blocks.size() - 1;
220 }
221
223 Instruction::PushScriptContext scriptContext;
224 scriptContext.index = blockIndex;
225 bytecodeGenerator->addInstruction(scriptContext);
227 if (isCatchBlock) {
228 Instruction::PushCatchContext catchContext;
229 catchContext.index = blockIndex;
230 catchContext.name = codegen->registerString(caughtVariable);
231 bytecodeGenerator->addInstruction(catchContext);
232 } else {
233 Instruction::PushBlockContext blockContext;
234 blockContext.index = blockIndex;
235 bytecodeGenerator->addInstruction(blockContext);
236 }
238 Instruction::CreateCallContext createContext;
239 bytecodeGenerator->addInstruction(createContext);
240 }
241 }
242
244 Instruction::InitializeBlockDeadTemporalZone tdzInit;
246 tdzInit.count = sizeOfRegisterTemporalDeadZone;
247 bytecodeGenerator->addInstruction(tdzInit);
248 }
249
250 if (usesThis) {
251 Q_ASSERT(!isStrict);
252 // make sure we convert this to an object
253 Instruction::ConvertThisToObject convert;
254 bytecodeGenerator->addInstruction(convert);
255 }
257 Instruction::LoadReg load;
258 load.reg = CallData::This;
259 bytecodeGenerator->addInstruction(load);
260 Codegen::Reference r = codegen->referenceForName(QStringLiteral("this"), true);
261 r.storeConsumeAccumulator();
262 }
264 Instruction::LoadReg load;
265 load.reg = CallData::NewTarget;
266 bytecodeGenerator->addInstruction(load);
267 Codegen::Reference r = codegen->referenceForName(QStringLiteral("new.target"), true);
268 r.storeConsumeAccumulator();
269 }
270
272 // variables in global code are properties of the global context object, not locals as with other functions.
273 for (Context::MemberMap::const_iterator it = members.constBegin(), cend = members.constEnd(); it != cend; ++it) {
274 if (it->isLexicallyScoped())
275 continue;
276 const QString &local = it.key();
277
278 Instruction::DeclareVar declareVar;
279 declareVar.isDeletable = (contextType == ContextType::Eval);
280 declareVar.varName = codegen->registerString(local);
281 bytecodeGenerator->addInstruction(declareVar);
282 }
283 }
284
286 for (Context::MemberMap::iterator it = members.begin(), end = members.end(); it != end; ++it) {
287 if (it->canEscape && it->type == Context::ThisFunctionName) {
288 // move the function from the stack to the call context
289 Instruction::LoadReg load;
290 load.reg = CallData::Function;
291 bytecodeGenerator->addInstruction(load);
292 Instruction::StoreLocal store;
293 store.index = it->index;
294 bytecodeGenerator->addInstruction(store);
295 }
296 }
297 }
298
300 Q_ASSERT(contextType != ContextType::Block);
301 if (isStrict || (formals && !formals->isSimpleParameterList())) {
302 Instruction::CreateUnmappedArgumentsObject setup;
303 bytecodeGenerator->addInstruction(setup);
304 } else {
305 Instruction::CreateMappedArgumentsObject setup;
306 bytecodeGenerator->addInstruction(setup);
307 }
308 codegen->referenceForName(QStringLiteral("arguments"), false).storeConsumeAccumulator();
309 }
310
311 for (const Context::Member &member : std::as_const(members)) {
312 if (member.function) {
313 const int function = codegen->defineFunction(member.function->name.toString(), member.function, member.function->formals, member.function->body);
314 codegen->loadClosure(function);
315 Codegen::Reference r = codegen->referenceForName(member.function->name.toString(), true);
316 r.storeConsumeAccumulator();
317 }
318 }
319}
320
321void Context::emitBlockFooter(Codegen *codegen)
322{
323 using Instruction = Moth::Instruction;
324 Moth::BytecodeGenerator *bytecodeGenerator = codegen->generator();
325
327 return;
328
329QT_WARNING_PUSH
330QT_WARNING_DISABLE_GCC("-Wmaybe-uninitialized") // the loads below are empty structs.
331 if (contextType == ContextType::Global)
332 bytecodeGenerator->addInstruction(Instruction::PopScriptContext());
333 else if (contextType != ContextType::ESModule && contextType != ContextType::ScriptImportedByQML)
334 bytecodeGenerator->addInstruction(Instruction::PopContext());
335QT_WARNING_POP
336}
337
339{
340 if (registerOffset != -1) {
341 // already computed, check for consistency
342 Q_ASSERT(registerOffset == bytecodeGenerator->currentRegister());
343 bytecodeGenerator->newRegisterArray(nRegisters);
344 return;
345 }
346 Q_ASSERT(locals.size() == 0);
347 Q_ASSERT(nRegisters == 0);
348 registerOffset = bytecodeGenerator->currentRegister();
349
350 QList<Context::MemberMap::Iterator> localsInTDZ;
351 const auto registerLocal = [this, &localsInTDZ](Context::MemberMap::iterator member) {
352 if (member->isLexicallyScoped()) {
353 localsInTDZ << member;
354 } else {
355 member->index = locals.size();
356 locals.append(member.key());
357 }
358 };
359
360 QList<Context::MemberMap::Iterator> registersInTDZ;
361 const auto allocateRegister = [bytecodeGenerator, &registersInTDZ](Context::MemberMap::iterator member) {
362 if (member->isLexicallyScoped())
363 registersInTDZ << member;
364 else
365 member->index = bytecodeGenerator->newRegister();
366 };
367
368 switch (contextType) {
373 for (Context::MemberMap::iterator it = members.begin(), end = members.end(); it != end; ++it) {
374 if (it->canEscape) {
375 registerLocal(it);
376 } else {
377 if (it->type == Context::ThisFunctionName)
378 it->index = CallData::Function;
379 else
380 allocateRegister(it);
381 }
382 }
383 break;
384 }
388 for (Context::MemberMap::iterator it = members.begin(), end = members.end(); it != end; ++it) {
389 if (!it->isLexicallyScoped() && (contextType == ContextType::Global || contextType == ContextType::ScriptImportedByQML || !isStrict))
390 continue;
391 if (it->canEscape)
392 registerLocal(it);
393 else
394 allocateRegister(it);
395 }
396 break;
397 }
398
399 sizeOfLocalTemporalDeadZone = localsInTDZ.size();
400 for (auto &member: std::as_const(localsInTDZ)) {
401 member->index = locals.size();
402 locals.append(member.key());
403 }
404
405 if (contextType == ContextType::ESModule && !localNameForDefaultExport.isEmpty()) {
406 if (!members.contains(localNameForDefaultExport)) {
407 // allocate a local slot for the default export, to be used in
408 // CodeGen::visit(ExportDeclaration*).
409 locals.append(localNameForDefaultExport);
411 }
412 }
413
414 sizeOfRegisterTemporalDeadZone = registersInTDZ.size();
416 for (auto &member: std::as_const(registersInTDZ))
417 member->index = bytecodeGenerator->newRegister();
418
419 nRegisters = bytecodeGenerator->currentRegister() - registerOffset;
420}
421
422QT_END_NAMESPACE
Definition qjsvalue.h:24
void emitBlockFooter(Compiler::Codegen *codegen)
void emitBlockHeader(Compiler::Codegen *codegen)
UsesArgumentsObject usesArgumentsObject
void setupFunctionIndices(Moth::BytecodeGenerator *bytecodeGenerator)