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