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
qv4context.cpp
Go to the documentation of this file.
1// Copyright (C) 2016 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 <QString>
6#include <qv4context_p.h>
7#include <qv4object_p.h>
8#include <qv4objectproto_p.h>
9#include <private/qv4mm_p.h>
10#include <qv4argumentsobject_p.h>
11#include "qv4function_p.h"
13#include "qv4symbol_p.h"
14
15using namespace QV4;
16
19
20Heap::CallContext *ExecutionContext::newBlockContext(CppStackFrame *frame, int blockIndex)
21{
22 Function *function = frame->v4Function;
23
24 Heap::InternalClass *ic = function->executableCompilationUnit()->runtimeBlocks.at(blockIndex);
25 uint nLocals = ic->size;
26 size_t requiredMemory = sizeof(CallContext::Data) - sizeof(Value) + sizeof(Value) * nLocals;
27
28 ExecutionEngine *v4 = function->internalClass->engine;
29 Heap::CallContext *c = v4->memoryManager->allocManaged<CallContext>(requiredMemory, ic);
30 c->init();
31 c->type = Heap::ExecutionContext::Type_BlockContext;
32
33 Heap::ExecutionContext *outer = static_cast<Heap::ExecutionContext *>(frame->context()->m());
34 c->outer.set(v4, outer);
35 if (frame->isJSTypesFrame()) {
36 c->function.set(v4, static_cast<Heap::JavaScriptFunctionObject *>(
37 Value::fromStaticValue(
38 static_cast<JSTypesStackFrame *>(frame)->jsFrame->function).m()));
39 } else {
40 c->function.set(v4, nullptr);
41 }
42
43 c->locals.size = nLocals;
44 c->locals.alloc = nLocals;
45
46 c->setupLocalTemporalDeadZone(function->executableCompilationUnit()->unitData()->blockAt(blockIndex));
47
48 return c;
49}
50
51Heap::CallContext *ExecutionContext::cloneBlockContext(ExecutionEngine *engine,
52 Heap::CallContext *callContext)
53{
54 uint nLocals = callContext->locals.alloc;
55 size_t requiredMemory = sizeof(CallContext::Data) - sizeof(Value) + sizeof(Value) * nLocals;
56
57 Heap::CallContext *c = engine->memoryManager->allocManaged<CallContext>(
58 requiredMemory, callContext->internalClass);
59 memcpy(c, callContext, requiredMemory);
60
61 return c;
62}
63
64Heap::CallContext *ExecutionContext::newCallContext(JSTypesStackFrame *frame)
65{
66 Function *function = frame->v4Function;
67 Heap::ExecutionContext *outer = static_cast<Heap::ExecutionContext *>(frame->context()->m());
68
69 uint nFormals = qMax(static_cast<uint>(frame->argc()), function->nFormals);
70 uint localsAndFormals = function->compiledFunction->nLocals + nFormals;
71 size_t requiredMemory = sizeof(CallContext::Data) - sizeof(Value) + sizeof(Value) * (localsAndFormals);
72
73 ExecutionEngine *v4 = outer->internalClass->engine;
74 Heap::CallContext *c = v4->memoryManager->allocManaged<CallContext>(requiredMemory, function->internalClass);
75 c->init();
76
77 c->outer.set(v4, outer);
78 c->function.set(v4, static_cast<Heap::JavaScriptFunctionObject *>(
79 Value::fromStaticValue(frame->jsFrame->function).m()));
80
81 const CompiledData::Function *compiledFunction = function->compiledFunction;
82 uint nLocals = compiledFunction->nLocals;
83 c->locals.size = nLocals;
84 c->locals.alloc = localsAndFormals;
85 // memory allocated from the JS heap is 0 initialized, so check if empty is 0
86 Q_ASSERT(Value::undefinedValue().asReturnedValue() == 0);
87
88 c->setupLocalTemporalDeadZone(compiledFunction);
89
90 Value *args = c->locals.values + nLocals;
91 ::memcpy(args, frame->argv(), frame->argc() * sizeof(Value));
92 c->nArgs = frame->argc();
93 for (uint i = frame->argc(); i < function->nFormals; ++i)
94 args[i] = Encode::undefined();
95
96 return c;
97}
98
99Heap::ExecutionContext *ExecutionContext::newWithContext(Heap::Object *with) const
100{
101 Heap::ExecutionContext *c = engine()->memoryManager->alloc<ExecutionContext>(Heap::ExecutionContext::Type_WithContext);
102 c->outer.set(engine(), d());
103 c->activation.set(engine(), with);
104
105 return c;
106}
107
108Heap::ExecutionContext *ExecutionContext::newCatchContext(CppStackFrame *frame, int blockIndex, Heap::String *exceptionVarName)
109{
110 Scope scope(frame->context());
111 ScopedString name(scope, exceptionVarName);
112 ScopedValue val(scope, scope.engine->catchException(nullptr));
113 ScopedContext ctx(scope, newBlockContext(frame, blockIndex));
114 ctx->setProperty(name, val);
115 return ctx->d();
116}
117
118void ExecutionContext::createMutableBinding(String *name, bool deletable)
119{
120 Scope scope(this);
121
122 // find the right context to create the binding on
123 ScopedObject activation(scope);
124 ScopedContext ctx(scope, this);
125 while (ctx) {
126 switch (ctx->d()->type) {
127 case Heap::ExecutionContext::Type_CallContext:
128 if (!activation) {
129 Heap::CallContext *c = static_cast<Heap::CallContext *>(ctx->d());
130 if (!c->activation)
131 c->activation.set(scope.engine, scope.engine->newObject());
132 activation = c->activation;
133 }
134 break;
135 case Heap::ExecutionContext::Type_QmlContext: {
136 // this is ugly, as it overrides the inner callcontext, but has to stay as long
137 // as bindings still get their own callcontext
138 activation = ctx->d()->activation;
139 break;
140 }
141 case Heap::ExecutionContext::Type_GlobalContext: {
142 Q_ASSERT(scope.engine->globalObject->d() == ctx->d()->activation);
143 if (!activation)
144 activation = ctx->d()->activation;
145 break;
146 }
147 case Heap::ExecutionContext::Type_BlockContext:
148 // never create activation records on block contexts
149 default:
150 break;
151 }
152 ctx = ctx->d()->outer;
153 }
154
155 PropertyKey id = name->toPropertyKey();
156 if (activation->getOwnProperty(id) != Attr_Invalid)
157 return;
158 ScopedProperty desc(scope);
159 PropertyAttributes attrs(Attr_Data);
160 attrs.setConfigurable(deletable);
161 if (!activation->defineOwnProperty(id, desc, attrs))
162 scope.engine->throwTypeError();
163}
164
165static bool unscopable(ExecutionEngine *engine, Heap::Object *withObject, PropertyKey id)
166{
167 if (!withObject)
168 return false;
169 Scope scope(engine);
170 ScopedObject w(scope, withObject);
171 ScopedObject o(scope, w->get(scope.engine->symbol_unscopables()));
172 if (o) {
173 ScopedValue blocked(scope, o->get(id));
174 return blocked->toBoolean();
175 }
176 return false;
177}
178
179bool ExecutionContext::deleteProperty(String *name)
180{
181 PropertyKey id = name->toPropertyKey();
182
183 Heap::ExecutionContext *ctx = d();
184 ExecutionEngine *engine = ctx->internalClass->engine;
185
186 for (; ctx; ctx = ctx->outer) {
187 switch (ctx->type) {
188 case Heap::ExecutionContext::Type_BlockContext:
189 case Heap::ExecutionContext::Type_CallContext: {
190 Heap::CallContext *c = static_cast<Heap::CallContext *>(ctx);
191 uint index = c->internalClass->indexOfValueOrGetter(id);
192 if (index < UINT_MAX)
193 // ### throw in strict mode?
194 return false;
195 Q_FALLTHROUGH();
196 }
197 case Heap::ExecutionContext::Type_WithContext: {
198 if (ctx->activation) {
199 Scope scope(this);
200 ScopedObject object(scope, ctx->activation);
201 if (object && object->hasProperty(id)) {
202 bool u = ::unscopable(engine, ctx->activation, id);
203 if (engine->hasException)
204 return false;
205 if (u)
206 break;
207 return object->deleteProperty(id);
208 }
209 }
210 break;
211 }
212 case Heap::ExecutionContext::Type_GlobalContext: {
213 if (ctx->activation) {
214 Scope scope(this);
215 ScopedObject object(scope, ctx->activation);
216 if (object && object->hasProperty(id))
217 return object->deleteProperty(id);
218 }
219 break;
220 }
221 case Heap::ExecutionContext::Type_QmlContext:
222 // can't delete properties on qml objects
223 break;
224 }
225 }
226
227 return !engine->currentStackFrame->v4Function->isStrict();
228}
229
230ExecutionContext::Error ExecutionContext::setProperty(String *name, const Value &value)
231{
232 PropertyKey id = name->toPropertyKey();
233
234 Heap::ExecutionContext *ctx = d();
235 QV4::ExecutionEngine *engine = ctx->internalClass->engine;
236
237 for (; ctx; ctx = ctx->outer) {
238 switch (ctx->type) {
239 case Heap::ExecutionContext::Type_WithContext: {
240 Scope scope(engine);
241 ScopedObject w(scope, ctx->activation);
242 if (w->hasProperty(id)) {
243 bool u = ::unscopable(engine, ctx->activation, id);
244 if (engine->hasException)
245 return TypeError;
246 if (u)
247 break;
248 if (!w->put(name, value))
249 return TypeError;
250 return NoError;
251 }
252 break;
253 }
254 case Heap::ExecutionContext::Type_BlockContext:
255 case Heap::ExecutionContext::Type_CallContext: {
256 Heap::CallContext *c = static_cast<Heap::CallContext *>(ctx);
257 uint index = c->internalClass->indexOfValueOrGetter(id);
258 if (index < UINT_MAX) {
259 static_cast<Heap::CallContext *>(c)->locals.set(engine, index, value);
260 return NoError;
261 }
262 }
263 Q_FALLTHROUGH();
264 case Heap::ExecutionContext::Type_GlobalContext:
265 if (ctx->activation) {
266 auto member = ctx->activation->internalClass->findValueOrSetter(id);
267 if (member.index < UINT_MAX) {
268 Scope scope(engine);
269 ScopedObject a(scope, ctx->activation);
270 if (!a->putValue(member.index, member.attrs, value))
271 return TypeError;
272 return NoError;
273 }
274 }
275 break;
276 case Heap::ExecutionContext::Type_QmlContext: {
277 Scope scope(engine);
278 ScopedObject activation(scope, ctx->activation);
279 if (!activation->put(name, value))
280 return TypeError;
281 return NoError;
282 }
283 }
284
285 }
286
287 return RangeError;
288}
289
290ReturnedValue ExecutionContext::getProperty(String *name)
291{
292 PropertyKey id = name->toPropertyKey();
293
294 Heap::ExecutionContext *ctx = d();
295 QV4::ExecutionEngine *engine = ctx->internalClass->engine;
296
297 for (; ctx; ctx = ctx->outer) {
298 switch (ctx->type) {
299 case Heap::ExecutionContext::Type_BlockContext:
300 case Heap::ExecutionContext::Type_CallContext: {
301 Heap::CallContext *c = static_cast<Heap::CallContext *>(ctx);
302
303 const uint index = c->internalClass->indexOfValueOrGetter(id);
304 if (index < c->locals.alloc)
305 return c->locals[index].asReturnedValue();
306
307 // TODO: We should look up the module imports here, but those are part of the CU:
308 // imports[index - c->locals.size];
309 // See QTBUG-118478
310
311 Q_FALLTHROUGH();
312 }
313 case Heap::ExecutionContext::Type_WithContext:
314 if (ctx->activation) {
315 Scope scope(this);
316 ScopedObject activation(scope, ctx->activation);
317 if (activation->hasProperty(id)) {
318 bool u = ::unscopable(engine, ctx->activation, id);
319 if (engine->hasException)
320 return false;
321 if (u)
322 break;
323 return activation->get(id);
324 }
325 }
326 break;
327 case Heap::ExecutionContext::Type_GlobalContext:
328 case Heap::ExecutionContext::Type_QmlContext: {
329 if (ctx->activation) {
330 Scope scope(this);
331 ScopedObject activation(scope, ctx->activation);
332 bool hasProperty = false;
333 ReturnedValue v = activation->get(id, nullptr, &hasProperty);
334 if (hasProperty)
335 return v;
336 }
337 break;
338 }
339 }
340 }
341 return engine->throwReferenceError(*name);
342}
343
344ReturnedValue ExecutionContext::getPropertyAndBase(String *name, Value *base)
345{
346 base->setM(nullptr);
347 PropertyKey id = name->toPropertyKey();
348
349 Heap::ExecutionContext *ctx = d();
350 QV4::ExecutionEngine *engine = ctx->internalClass->engine;
351
352 for (; ctx; ctx = ctx->outer) {
353 switch (ctx->type) {
354 case Heap::ExecutionContext::Type_BlockContext:
355 case Heap::ExecutionContext::Type_CallContext: {
356 Heap::CallContext *c = static_cast<Heap::CallContext *>(ctx);
357
358 const uint index = c->internalClass->indexOfValueOrGetter(id);
359 if (index < c->locals.alloc)
360 return c->locals[index].asReturnedValue();
361
362 // TODO: We should look up the module imports here, but those are part of the CU:
363 // imports[index - c->locals.size];
364 // See QTBUG-118478
365
366 Q_FALLTHROUGH();
367 }
368 case Heap::ExecutionContext::Type_GlobalContext: {
369 if (ctx->activation) {
370 Scope scope(this);
371 ScopedObject activation(scope, ctx->activation);
372 bool hasProperty = false;
373 ReturnedValue v = activation->get(name, &hasProperty);
374 if (hasProperty)
375 return v;
376 }
377 break;
378 }
379 case Heap::ExecutionContext::Type_WithContext:
380 if (ctx->activation) {
381 Scope scope(this);
382 ScopedObject activation(scope, ctx->activation);
383 if (activation->hasProperty(id)) {
384 bool u = ::unscopable(engine, ctx->activation, id);
385 if (engine->hasException)
386 return false;
387 if (u)
388 break;
389 base->setM(activation->d());
390 return activation->get(id);
391 }
392 }
393 break;
394 case Heap::ExecutionContext::Type_QmlContext: {
395 Scope scope(this);
396 ScopedObject o(scope, ctx->activation);
397 bool hasProperty = false;
398 ReturnedValue v = o->get(id, nullptr, &hasProperty);
399 if (hasProperty) {
400 base->setM(o->d());
401 return v;
402 }
403 break;
404 }
405 }
406 }
407 return engine->throwReferenceError(*name);
408}
409
410void Heap::CallContext::setArg(uint index, Value v)
411{
412 locals.set(internalClass->engine, locals.size + index, v);
413}
DEFINE_MANAGED_VTABLE(ExecutionContext)
DEFINE_MANAGED_VTABLE(CallContext)
static bool unscopable(ExecutionEngine *engine, Heap::Object *withObject, PropertyKey id)