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
54 using namespace QV4;
55
56 DEFINE_MANAGED_VTABLE(ExecutionContext);
57 DEFINE_MANAGED_VTABLE(CallContext);
58
newBlockContext(CppStackFrame * frame,int blockIndex)59 Heap::CallContext *ExecutionContext::newBlockContext(CppStackFrame *frame, int blockIndex)
60 {
61 Function *function = frame->v4Function;
62
63 Heap::InternalClass *ic = function->executableCompilationUnit()->runtimeBlocks.at(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>(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(v4, outer);
74 c->function.set(v4, static_cast<Heap::FunctionObject *>(
75 Value::fromStaticValue(frame->jsFrame->function).m()));
76
77 c->locals.size = nLocals;
78 c->locals.alloc = nLocals;
79
80 c->setupLocalTemporalDeadZone(function->executableCompilationUnit()->unitData()->blockAt(blockIndex));
81
82 return c;
83 }
84
cloneBlockContext(ExecutionEngine * engine,Heap::CallContext * callContext)85 Heap::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 requiredMemory, callContext->internalClass);
93 memcpy(c, callContext, requiredMemory);
94
95 return c;
96 }
97
newCallContext(CppStackFrame * frame)98 Heap::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(static_cast<uint>(frame->originalArgumentsCount), 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>(requiredMemory, function->internalClass);
109 c->init();
110
111 c->outer.set(v4, outer);
112 c->function.set(v4, static_cast<Heap::FunctionObject *>(
113 Value::fromStaticValue(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(args, frame->originalArguments, 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
newWithContext(Heap::Object * with) const133 Heap::ExecutionContext *ExecutionContext::newWithContext(Heap::Object *with) const
134 {
135 Heap::ExecutionContext *c = engine()->memoryManager->alloc<ExecutionContext>(Heap::ExecutionContext::Type_WithContext);
136 c->outer.set(engine(), d());
137 c->activation.set(engine(), with);
138
139 return c;
140 }
141
newCatchContext(CppStackFrame * frame,int blockIndex,Heap::String * exceptionVarName)142 Heap::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(nullptr));
147 ScopedContext ctx(scope, newBlockContext(frame, blockIndex));
148 ctx->setProperty(name, val);
149 return ctx->d();
150 }
151
createMutableBinding(String * name,bool deletable)152 void 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(scope.engine, 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, desc, attrs))
196 scope.engine->throwTypeError();
197 }
198
unscopable(ExecutionEngine * engine,Heap::Object * withObject,PropertyKey id)199 static 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(scope.engine->symbol_unscopables()));
206 if (o) {
207 ScopedValue blocked(scope, o->get(id));
208 return blocked->toBoolean();
209 }
210 return false;
211 }
212
deleteProperty(String * name)213 bool 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, 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
setProperty(String * name,const Value & value)264 ExecutionContext::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, ctx->activation, id);
278 if (engine->hasException)
279 return TypeError;
280 if (u)
281 break;
282 if (!w->put(name, 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(engine, index, 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(member.index, 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, value))
314 return TypeError;
315 return NoError;
316 }
317 }
318
319 }
320
321 return RangeError;
322 }
323
getProperty(String * name)324 ReturnedValue 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, 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, nullptr, &hasProperty);
363 if (hasProperty)
364 return v;
365 }
366 break;
367 }
368 }
369 }
370 return engine->throwReferenceError(*name);
371 }
372
getPropertyAndBase(String * name,Value * base)373 ReturnedValue 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);
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, 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, nullptr, &hasProperty);
423 if (hasProperty) {
424 base->setM(o->d());
425 return v;
426 }
427 break;
428 }
429 }
430 }
431 return engine->throwReferenceError(*name);
432 }
433
setArg(uint index,Value v)434 void Heap::CallContext::setArg(uint index, Value v)
435 {
436 locals.set(internalClass->engine, locals.size + index, v);
437 }
438