1/****************************************************************************
2**
3** Copyright (C) 2016 The Qt Company Ltd.
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the QtQml module of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:LGPL$
9** Commercial License Usage
10** Licensees holding valid commercial Qt licenses may use this file in
11** accordance with the commercial license agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and The Qt Company. For licensing terms
14** and conditions see https://www.qt.io/terms-conditions. For further
15** information use the contact form at https://www.qt.io/contact-us.
16**
17** GNU Lesser General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU Lesser
19** General Public License version 3 as published by the Free Software
20** Foundation and appearing in the file LICENSE.LGPL3 included in the
21** packaging of this file. Please review the following information to
22** ensure the GNU Lesser General Public License version 3 requirements
23** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24**
25** GNU General Public License Usage
26** Alternatively, this file may be used under the terms of the GNU
27** General Public License version 2.0 or (at your option) the GNU General
28** Public license version 3 or any later version approved by the KDE Free
29** Qt Foundation. The licenses are as published by the Free Software
30** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31** included in the packaging of this file. Please review the following
32** information to ensure the GNU General Public License requirements will
33** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34** https://www.gnu.org/licenses/gpl-3.0.html.
35**
36** $QT_END_LICENSE$
37**
38****************************************************************************/
39
40#include <QString>
41#include "qv4debugging_p.h"
42#include <qv4context_p.h>
43#include <qv4object_p.h>
44#include <qv4objectproto_p.h>
45#include <private/qv4mm_p.h>
46#include <qv4argumentsobject_p.h>
47#include "qv4function_p.h"
48#include "qv4errorobject_p.h"
49#include "qv4string_p.h"
50#include "qv4qmlcontext_p.h"
51#include "qv4stackframe_p.h"
52#include "qv4symbol_p.h"
53
54using namespace QV4;
55
56DEFINE_MANAGED_VTABLE(ExecutionContext);
57DEFINE_MANAGED_VTABLE(CallContext);
58
59Heap::CallContext *ExecutionContext::newBlockContext(CppStackFrame *frame, int blockIndex)
60{
61 Function *function = frame->v4Function;
62
63 Heap::InternalClass *ic = function->executableCompilationUnit()->runtimeBlocks.at(i: blockIndex);
64 uint nLocals = ic->size;
65 size_t requiredMemory = sizeof(CallContext::Data) - sizeof(Value) + sizeof(Value) * nLocals;
66
67 ExecutionEngine *v4 = function->internalClass->engine;
68 Heap::CallContext *c = v4->memoryManager->allocManaged<CallContext>(size: requiredMemory, ic);
69 c->init();
70 c->type = Heap::ExecutionContext::Type_BlockContext;
71
72 Heap::ExecutionContext *outer = static_cast<Heap::ExecutionContext *>(frame->context()->m());
73 c->outer.set(e: v4, newVal: outer);
74 c->function.set(e: v4, newVal: static_cast<Heap::FunctionObject *>(
75 Value::fromStaticValue(staticValue: frame->jsFrame->function).m()));
76
77 c->locals.size = nLocals;
78 c->locals.alloc = nLocals;
79
80 c->setupLocalTemporalDeadZone(function->executableCompilationUnit()->unitData()->blockAt(idx: blockIndex));
81
82 return c;
83}
84
85Heap::CallContext *ExecutionContext::cloneBlockContext(ExecutionEngine *engine,
86 Heap::CallContext *callContext)
87{
88 uint nLocals = callContext->locals.alloc;
89 size_t requiredMemory = sizeof(CallContext::Data) - sizeof(Value) + sizeof(Value) * nLocals;
90
91 Heap::CallContext *c = engine->memoryManager->allocManaged<CallContext>(
92 size: requiredMemory, ic: callContext->internalClass);
93 memcpy(dest: c, src: callContext, n: requiredMemory);
94
95 return c;
96}
97
98Heap::CallContext *ExecutionContext::newCallContext(CppStackFrame *frame)
99{
100 Function *function = frame->v4Function;
101 Heap::ExecutionContext *outer = static_cast<Heap::ExecutionContext *>(frame->context()->m());
102
103 uint nFormals = qMax(a: static_cast<uint>(frame->originalArgumentsCount), b: function->nFormals);
104 uint localsAndFormals = function->compiledFunction->nLocals + nFormals;
105 size_t requiredMemory = sizeof(CallContext::Data) - sizeof(Value) + sizeof(Value) * (localsAndFormals);
106
107 ExecutionEngine *v4 = outer->internalClass->engine;
108 Heap::CallContext *c = v4->memoryManager->allocManaged<CallContext>(size: requiredMemory, ic: function->internalClass);
109 c->init();
110
111 c->outer.set(e: v4, newVal: outer);
112 c->function.set(e: v4, newVal: static_cast<Heap::FunctionObject *>(
113 Value::fromStaticValue(staticValue: frame->jsFrame->function).m()));
114
115 const CompiledData::Function *compiledFunction = function->compiledFunction;
116 uint nLocals = compiledFunction->nLocals;
117 c->locals.size = nLocals;
118 c->locals.alloc = localsAndFormals;
119 // memory allocated from the JS heap is 0 initialized, so check if empty is 0
120 Q_ASSERT(Value::undefinedValue().asReturnedValue() == 0);
121
122 c->setupLocalTemporalDeadZone(compiledFunction);
123
124 Value *args = c->locals.values + nLocals;
125 ::memcpy(dest: args, src: frame->originalArguments, n: frame->originalArgumentsCount * sizeof(Value));
126 c->nArgs = frame->originalArgumentsCount;
127 for (uint i = frame->originalArgumentsCount; i < function->nFormals; ++i)
128 args[i] = Encode::undefined();
129
130 return c;
131}
132
133Heap::ExecutionContext *ExecutionContext::newWithContext(Heap::Object *with) const
134{
135 Heap::ExecutionContext *c = engine()->memoryManager->alloc<ExecutionContext>(args: Heap::ExecutionContext::Type_WithContext);
136 c->outer.set(e: engine(), newVal: d());
137 c->activation.set(e: engine(), newVal: with);
138
139 return c;
140}
141
142Heap::ExecutionContext *ExecutionContext::newCatchContext(CppStackFrame *frame, int blockIndex, Heap::String *exceptionVarName)
143{
144 Scope scope(frame->context());
145 ScopedString name(scope, exceptionVarName);
146 ScopedValue val(scope, scope.engine->catchException(trace: nullptr));
147 ScopedContext ctx(scope, newBlockContext(frame, blockIndex));
148 ctx->setProperty(name, value: val);
149 return ctx->d();
150}
151
152void ExecutionContext::createMutableBinding(String *name, bool deletable)
153{
154 Scope scope(this);
155
156 // find the right context to create the binding on
157 ScopedObject activation(scope);
158 ScopedContext ctx(scope, this);
159 while (ctx) {
160 switch (ctx->d()->type) {
161 case Heap::ExecutionContext::Type_CallContext:
162 if (!activation) {
163 Heap::CallContext *c = static_cast<Heap::CallContext *>(ctx->d());
164 if (!c->activation)
165 c->activation.set(e: scope.engine, newVal: scope.engine->newObject());
166 activation = c->activation;
167 }
168 break;
169 case Heap::ExecutionContext::Type_QmlContext: {
170 // this is ugly, as it overrides the inner callcontext, but has to stay as long
171 // as bindings still get their own callcontext
172 activation = ctx->d()->activation;
173 break;
174 }
175 case Heap::ExecutionContext::Type_GlobalContext: {
176 Q_ASSERT(scope.engine->globalObject->d() == ctx->d()->activation);
177 if (!activation)
178 activation = ctx->d()->activation;
179 break;
180 }
181 case Heap::ExecutionContext::Type_BlockContext:
182 // never create activation records on block contexts
183 default:
184 break;
185 }
186 ctx = ctx->d()->outer;
187 }
188
189 PropertyKey id = name->toPropertyKey();
190 if (activation->getOwnProperty(id) != Attr_Invalid)
191 return;
192 ScopedProperty desc(scope);
193 PropertyAttributes attrs(Attr_Data);
194 attrs.setConfigurable(deletable);
195 if (!activation->defineOwnProperty(id, p: desc, attrs))
196 scope.engine->throwTypeError();
197}
198
199static bool unscopable(ExecutionEngine *engine, Heap::Object *withObject, PropertyKey id)
200{
201 if (!withObject)
202 return false;
203 Scope scope(engine);
204 ScopedObject w(scope, withObject);
205 ScopedObject o(scope, w->get(name: scope.engine->symbol_unscopables()));
206 if (o) {
207 ScopedValue blocked(scope, o->get(id));
208 return blocked->toBoolean();
209 }
210 return false;
211}
212
213bool ExecutionContext::deleteProperty(String *name)
214{
215 PropertyKey id = name->toPropertyKey();
216
217 Heap::ExecutionContext *ctx = d();
218 ExecutionEngine *engine = ctx->internalClass->engine;
219
220 for (; ctx; ctx = ctx->outer) {
221 switch (ctx->type) {
222 case Heap::ExecutionContext::Type_BlockContext:
223 case Heap::ExecutionContext::Type_CallContext: {
224 Heap::CallContext *c = static_cast<Heap::CallContext *>(ctx);
225 uint index = c->internalClass->indexOfValueOrGetter(id);
226 if (index < UINT_MAX)
227 // ### throw in strict mode?
228 return false;
229 Q_FALLTHROUGH();
230 }
231 case Heap::ExecutionContext::Type_WithContext: {
232 if (ctx->activation) {
233 Scope scope(this);
234 ScopedObject object(scope, ctx->activation);
235 if (object && object->hasProperty(id)) {
236 bool u = ::unscopable(engine, withObject: ctx->activation, id);
237 if (engine->hasException)
238 return false;
239 if (u)
240 break;
241 return object->deleteProperty(id);
242 }
243 }
244 break;
245 }
246 case Heap::ExecutionContext::Type_GlobalContext: {
247 if (ctx->activation) {
248 Scope scope(this);
249 ScopedObject object(scope, ctx->activation);
250 if (object && object->hasProperty(id))
251 return object->deleteProperty(id);
252 }
253 break;
254 }
255 case Heap::ExecutionContext::Type_QmlContext:
256 // can't delete properties on qml objects
257 break;
258 }
259 }
260
261 return !engine->currentStackFrame->v4Function->isStrict();
262}
263
264ExecutionContext::Error ExecutionContext::setProperty(String *name, const Value &value)
265{
266 PropertyKey id = name->toPropertyKey();
267
268 Heap::ExecutionContext *ctx = d();
269 QV4::ExecutionEngine *engine = ctx->internalClass->engine;
270
271 for (; ctx; ctx = ctx->outer) {
272 switch (ctx->type) {
273 case Heap::ExecutionContext::Type_WithContext: {
274 Scope scope(engine);
275 ScopedObject w(scope, ctx->activation);
276 if (w->hasProperty(id)) {
277 bool u = ::unscopable(engine, withObject: ctx->activation, id);
278 if (engine->hasException)
279 return TypeError;
280 if (u)
281 break;
282 if (!w->put(name, v: value))
283 return TypeError;
284 return NoError;
285 }
286 break;
287 }
288 case Heap::ExecutionContext::Type_BlockContext:
289 case Heap::ExecutionContext::Type_CallContext: {
290 Heap::CallContext *c = static_cast<Heap::CallContext *>(ctx);
291 uint index = c->internalClass->indexOfValueOrGetter(id);
292 if (index < UINT_MAX) {
293 static_cast<Heap::CallContext *>(c)->locals.set(e: engine, index, v: value);
294 return NoError;
295 }
296 }
297 Q_FALLTHROUGH();
298 case Heap::ExecutionContext::Type_GlobalContext:
299 if (ctx->activation) {
300 auto member = ctx->activation->internalClass->findValueOrSetter(id);
301 if (member.index < UINT_MAX) {
302 Scope scope(engine);
303 ScopedObject a(scope, ctx->activation);
304 if (!a->putValue(memberIndex: member.index, attrs: member.attrs, value))
305 return TypeError;
306 return NoError;
307 }
308 }
309 break;
310 case Heap::ExecutionContext::Type_QmlContext: {
311 Scope scope(engine);
312 ScopedObject activation(scope, ctx->activation);
313 if (!activation->put(name, v: value))
314 return TypeError;
315 return NoError;
316 }
317 }
318
319 }
320
321 return RangeError;
322}
323
324ReturnedValue ExecutionContext::getProperty(String *name)
325{
326 PropertyKey id = name->toPropertyKey();
327
328 Heap::ExecutionContext *ctx = d();
329 QV4::ExecutionEngine *engine = ctx->internalClass->engine;
330
331 for (; ctx; ctx = ctx->outer) {
332 switch (ctx->type) {
333 case Heap::ExecutionContext::Type_BlockContext:
334 case Heap::ExecutionContext::Type_CallContext: {
335 Heap::CallContext *c = static_cast<Heap::CallContext *>(ctx);
336
337 uint index = c->internalClass->indexOfValueOrGetter(id);
338 if (index < UINT_MAX)
339 return c->locals[index].asReturnedValue();
340 Q_FALLTHROUGH();
341 }
342 case Heap::ExecutionContext::Type_WithContext:
343 if (ctx->activation) {
344 Scope scope(this);
345 ScopedObject activation(scope, ctx->activation);
346 if (activation->hasProperty(id)) {
347 bool u = ::unscopable(engine, withObject: ctx->activation, id);
348 if (engine->hasException)
349 return false;
350 if (u)
351 break;
352 return activation->get(id);
353 }
354 }
355 break;
356 case Heap::ExecutionContext::Type_GlobalContext:
357 case Heap::ExecutionContext::Type_QmlContext: {
358 if (ctx->activation) {
359 Scope scope(this);
360 ScopedObject activation(scope, ctx->activation);
361 bool hasProperty = false;
362 ReturnedValue v = activation->get(id, receiver: nullptr, hasProperty: &hasProperty);
363 if (hasProperty)
364 return v;
365 }
366 break;
367 }
368 }
369 }
370 return engine->throwReferenceError(value: *name);
371}
372
373ReturnedValue ExecutionContext::getPropertyAndBase(String *name, Value *base)
374{
375 base->setM(nullptr);
376 PropertyKey id = name->toPropertyKey();
377
378 Heap::ExecutionContext *ctx = d();
379 QV4::ExecutionEngine *engine = ctx->internalClass->engine;
380
381 for (; ctx; ctx = ctx->outer) {
382 switch (ctx->type) {
383 case Heap::ExecutionContext::Type_BlockContext:
384 case Heap::ExecutionContext::Type_CallContext: {
385 Heap::CallContext *c = static_cast<Heap::CallContext *>(ctx);
386
387 uint index = c->internalClass->indexOfValueOrGetter(id);
388 if (index < UINT_MAX)
389 return c->locals[index].asReturnedValue();
390 Q_FALLTHROUGH();
391 }
392 case Heap::ExecutionContext::Type_GlobalContext: {
393 if (ctx->activation) {
394 Scope scope(this);
395 ScopedObject activation(scope, ctx->activation);
396 bool hasProperty = false;
397 ReturnedValue v = activation->get(name, hasProperty: &hasProperty);
398 if (hasProperty)
399 return v;
400 }
401 break;
402 }
403 case Heap::ExecutionContext::Type_WithContext:
404 if (ctx->activation) {
405 Scope scope(this);
406 ScopedObject activation(scope, ctx->activation);
407 if (activation->hasProperty(id)) {
408 bool u = ::unscopable(engine, withObject: ctx->activation, id);
409 if (engine->hasException)
410 return false;
411 if (u)
412 break;
413 base->setM(activation->d());
414 return activation->get(id);
415 }
416 }
417 break;
418 case Heap::ExecutionContext::Type_QmlContext: {
419 Scope scope(this);
420 ScopedObject o(scope, ctx->activation);
421 bool hasProperty = false;
422 ReturnedValue v = o->get(id, receiver: nullptr, hasProperty: &hasProperty);
423 if (hasProperty) {
424 base->setM(o->d());
425 return v;
426 }
427 break;
428 }
429 }
430 }
431 return engine->throwReferenceError(value: *name);
432}
433
434void Heap::CallContext::setArg(uint index, Value v)
435{
436 locals.set(e: internalClass->engine, index: locals.size + index, v);
437}
438

source code of qtdeclarative/src/qml/jsruntime/qv4context.cpp