1// Copyright (C) 2018 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#ifndef QV4STACKFRAME_H
4#define QV4STACKFRAME_H
5
6//
7// W A R N I N G
8// -------------
9//
10// This file is not part of the Qt API. It exists purely as an
11// implementation detail. This header file may change from version to
12// version without notice, or even be removed.
13//
14// We mean it.
15//
16
17#include <private/qv4scopedvalue_p.h>
18#include <private/qv4context_p.h>
19#include <private/qv4enginebase_p.h>
20#include <private/qv4calldata_p.h>
21#include <private/qv4function_p.h>
22
23#include <type_traits>
24
25QT_BEGIN_NAMESPACE
26
27namespace QV4 {
28
29struct CppStackFrame;
30struct Q_QML_EXPORT CppStackFrameBase
31{
32 enum class Kind : quint8 { JS, Meta };
33
34 CppStackFrame *parent;
35 Function *v4Function;
36 int originalArgumentsCount;
37 int instructionPointer;
38
39 QT_WARNING_PUSH
40 QT_WARNING_DISABLE_MSVC(4201) // nonstandard extension used: nameless struct/union
41 union {
42 struct {
43 Value *savedStackTop;
44 CallData *jsFrame;
45 const Value *originalArguments;
46 const char *yield;
47 const char *unwindHandler;
48 const char *unwindLabel;
49 int unwindLevel;
50 bool yieldIsIterator;
51 bool callerCanHandleTailCall;
52 bool pendingTailCall;
53 bool isTailCalling;
54 };
55 struct {
56 ExecutionContext *context;
57 QObject *thisObject;
58 const QMetaType *metaTypes;
59 const QQmlPrivate::AOTTrackedLocalsStorage *locals;
60 void **returnAndArgs;
61 bool returnValueIsUndefined;
62 };
63 };
64 QT_WARNING_POP
65
66 Kind kind;
67};
68
69struct Q_QML_EXPORT CppStackFrame : protected CppStackFrameBase
70{
71 // We want to have those public but we can't declare them as public without making the struct
72 // non-standard layout. So we have this other struct with "using" in between.
73 using CppStackFrameBase::instructionPointer;
74 using CppStackFrameBase::locals;
75 using CppStackFrameBase::v4Function;
76
77 void init(Function *v4Function, int argc, Kind kind) {
78 this->v4Function = v4Function;
79 originalArgumentsCount = argc;
80 instructionPointer = 0;
81 locals = nullptr;
82 this->kind = kind;
83 }
84
85 bool isJSTypesFrame() const { return kind == Kind::JS; }
86 bool isMetaTypesFrame() const { return kind == Kind::Meta; }
87
88 QString source() const;
89 QString function() const;
90 int lineNumber() const;
91 int statementNumber() const;
92
93 int missingLineNumber() const;
94
95 CppStackFrame *parentFrame() const { return parent; }
96 void setParentFrame(CppStackFrame *parentFrame) { parent = parentFrame; }
97
98 int argc() const { return originalArgumentsCount; }
99
100 inline ExecutionContext *context() const;
101
102 Heap::CallContext *callContext() const { return callContext(ctx: context()->d()); }
103 ReturnedValue thisObject() const;
104
105protected:
106 CppStackFrame() = default;
107
108 void push(EngineBase *engine)
109 {
110 Q_ASSERT(kind == Kind::JS || kind == Kind::Meta);
111 parent = engine->currentStackFrame;
112 engine->currentStackFrame = this;
113 }
114
115 void pop(EngineBase *engine)
116 {
117 engine->currentStackFrame = parent;
118 }
119
120 Heap::CallContext *callContext(Heap::ExecutionContext *ctx) const
121 {
122 while (ctx->type != Heap::ExecutionContext::Type_CallContext)
123 ctx = ctx->outer;
124 return static_cast<Heap::CallContext *>(ctx);
125 }
126};
127
128struct Q_QML_EXPORT MetaTypesStackFrame : public CppStackFrame
129{
130 using CppStackFrame::push;
131 using CppStackFrame::pop;
132
133 void init(Function *v4Function, QObject *thisObject, ExecutionContext *context,
134 void **returnAndArgs, const QMetaType *metaTypes, int argc)
135 {
136 CppStackFrame::init(v4Function, argc, kind: Kind::Meta);
137 CppStackFrameBase::context = context;
138 CppStackFrameBase::thisObject = thisObject;
139 CppStackFrameBase::metaTypes = metaTypes;
140 CppStackFrameBase::locals = nullptr;
141 CppStackFrameBase::returnAndArgs = returnAndArgs;
142 CppStackFrameBase::returnValueIsUndefined = false;
143 }
144
145 QMetaType returnType() const { return metaTypes[0]; }
146 void *returnValue() const { return returnAndArgs[0]; }
147
148 bool isReturnValueUndefined() const { return CppStackFrameBase::returnValueIsUndefined; }
149 void setReturnValueUndefined() { CppStackFrameBase::returnValueIsUndefined = true; }
150
151 const QMetaType *argTypes() const { return metaTypes + 1; }
152 void **argv() const { return returnAndArgs + 1; }
153
154 const QMetaType *returnAndArgTypes() const { return metaTypes; }
155 void **returnAndArgValues() const { return returnAndArgs; }
156
157 QObject *thisObject() const { return CppStackFrameBase::thisObject; }
158
159 ExecutionContext *context() const { return CppStackFrameBase::context; }
160 void setContext(ExecutionContext *context) { CppStackFrameBase::context = context; }
161
162 const QQmlPrivate::AOTTrackedLocalsStorage *locals() const { return CppStackFrameBase::locals; }
163 void setLocals(const QQmlPrivate::AOTTrackedLocalsStorage *locals)
164 {
165 CppStackFrameBase::locals = locals;
166 }
167
168 Heap::CallContext *callContext() const
169 {
170 return CppStackFrame::callContext(ctx: CppStackFrameBase::context->d());
171 }
172};
173
174struct Q_QML_EXPORT JSTypesStackFrame : public CppStackFrame
175{
176 using CppStackFrame::jsFrame;
177
178 // The JIT needs to poke directly into those using offsetof
179 using CppStackFrame::unwindHandler;
180 using CppStackFrame::unwindLabel;
181 using CppStackFrame::unwindLevel;
182
183 void init(Function *v4Function, const Value *argv, int argc,
184 bool callerCanHandleTailCall = false)
185 {
186 CppStackFrame::init(v4Function, argc, kind: Kind::JS);
187 CppStackFrame::originalArguments = argv;
188 CppStackFrame::yield = nullptr;
189 CppStackFrame::unwindHandler = nullptr;
190 CppStackFrame::yieldIsIterator = false;
191 CppStackFrame::callerCanHandleTailCall = callerCanHandleTailCall;
192 CppStackFrame::pendingTailCall = false;
193 CppStackFrame::isTailCalling = false;
194 CppStackFrame::unwindLabel = nullptr;
195 CppStackFrame::unwindLevel = 0;
196 }
197
198 const Value *argv() const { return originalArguments; }
199
200 static uint requiredJSStackFrameSize(uint nRegisters) {
201 return CallData::HeaderSize() + nRegisters;
202 }
203 static uint requiredJSStackFrameSize(Function *v4Function) {
204 return CallData::HeaderSize() + v4Function->compiledFunction->nRegisters;
205 }
206 uint requiredJSStackFrameSize() const {
207 return requiredJSStackFrameSize(v4Function);
208 }
209
210 void setupJSFrame(Value *stackSpace, const Value &function, const Heap::ExecutionContext *scope,
211 const Value &thisObject, const Value &newTarget = Value::undefinedValue()) {
212 setupJSFrame(stackSpace, function, scope, thisObject, newTarget,
213 nFormals: v4Function->compiledFunction->nFormals,
214 nRegisters: v4Function->compiledFunction->nRegisters);
215 }
216
217 void setupJSFrame(
218 Value *stackSpace, const Value &function, const Heap::ExecutionContext *scope,
219 const Value &thisObject, const Value &newTarget, uint nFormals, uint nRegisters)
220 {
221 jsFrame = reinterpret_cast<CallData *>(stackSpace);
222 jsFrame->function = function;
223 jsFrame->context = scope->asReturnedValue();
224 jsFrame->accumulator = Encode::undefined();
225 jsFrame->thisObject = thisObject;
226 jsFrame->newTarget = newTarget;
227
228 uint argc = uint(originalArgumentsCount);
229 if (argc > nFormals)
230 argc = nFormals;
231 jsFrame->setArgc(argc);
232
233 // memcpy requires non-null ptr, even if argc * sizeof(Value) == 0
234 if (originalArguments)
235 memcpy(dest: jsFrame->args, src: originalArguments, n: argc * sizeof(Value));
236 Q_STATIC_ASSERT(Encode::undefined() == 0);
237 memset(s: jsFrame->args + argc, c: 0, n: (nRegisters - argc) * sizeof(Value));
238
239 if (v4Function && v4Function->compiledFunction) {
240 const int firstDeadZoneRegister
241 = v4Function->compiledFunction->firstTemporalDeadZoneRegister;
242 const int registerDeadZoneSize
243 = v4Function->compiledFunction->sizeOfRegisterTemporalDeadZone;
244
245 const Value * tdzEnd = stackSpace + firstDeadZoneRegister + registerDeadZoneSize;
246 for (Value *v = stackSpace + firstDeadZoneRegister; v < tdzEnd; ++v)
247 *v = Value::emptyValue().asReturnedValue();
248 }
249 }
250
251 ExecutionContext *context() const
252 {
253 return static_cast<ExecutionContext *>(&jsFrame->context);
254 }
255
256 void setContext(ExecutionContext *context)
257 {
258 jsFrame->context = context;
259 }
260
261 Heap::CallContext *callContext() const
262 {
263 return CppStackFrame::callContext(ctx: static_cast<ExecutionContext &>(jsFrame->context).d());
264 }
265
266 bool isTailCalling() const { return CppStackFrame::isTailCalling; }
267 void setTailCalling(bool tailCalling) { CppStackFrame::isTailCalling = tailCalling; }
268
269 bool pendingTailCall() const { return CppStackFrame::pendingTailCall; }
270 void setPendingTailCall(bool pending) { CppStackFrame::pendingTailCall = pending; }
271
272 const char *yield() const { return CppStackFrame::yield; }
273 void setYield(const char *yield) { CppStackFrame::yield = yield; }
274
275 bool yieldIsIterator() const { return CppStackFrame::yieldIsIterator; }
276 void setYieldIsIterator(bool isIter) { CppStackFrame::yieldIsIterator = isIter; }
277
278 bool callerCanHandleTailCall() const { return CppStackFrame::callerCanHandleTailCall; }
279
280 ReturnedValue thisObject() const
281 {
282 return jsFrame->thisObject.asReturnedValue();
283 }
284
285 Value *framePointer() const { return savedStackTop; }
286
287 void push(EngineBase *engine) {
288 CppStackFrame::push(engine);
289 savedStackTop = engine->jsStackTop;
290 }
291
292 void pop(EngineBase *engine) {
293 CppStackFrame::pop(engine);
294 engine->jsStackTop = savedStackTop;
295 }
296};
297
298inline ExecutionContext *CppStackFrame::context() const
299{
300 if (isJSTypesFrame())
301 return static_cast<const JSTypesStackFrame *>(this)->context();
302
303 Q_ASSERT(isMetaTypesFrame());
304 return static_cast<const MetaTypesStackFrame *>(this)->context();
305}
306
307struct ScopedStackFrame
308{
309 ScopedStackFrame(const Scope &scope, ExecutionContext *context)
310 : engine(scope.engine)
311 {
312 if (auto currentFrame = engine->currentStackFrame) {
313 frame.init(v4Function: currentFrame->v4Function, thisObject: nullptr, context, returnAndArgs: nullptr, metaTypes: nullptr, argc: 0);
314 frame.instructionPointer = currentFrame->instructionPointer;
315 } else {
316 frame.init(v4Function: nullptr, thisObject: nullptr, context, returnAndArgs: nullptr, metaTypes: nullptr, argc: 0);
317 }
318 frame.push(engine);
319 }
320
321 ~ScopedStackFrame()
322 {
323 frame.pop(engine);
324 }
325
326private:
327 ExecutionEngine *engine = nullptr;
328 MetaTypesStackFrame frame;
329};
330
331Q_STATIC_ASSERT(sizeof(CppStackFrame) == sizeof(JSTypesStackFrame));
332Q_STATIC_ASSERT(sizeof(CppStackFrame) == sizeof(MetaTypesStackFrame));
333Q_STATIC_ASSERT(std::is_standard_layout_v<CppStackFrame>);
334Q_STATIC_ASSERT(std::is_standard_layout_v<JSTypesStackFrame>);
335Q_STATIC_ASSERT(std::is_standard_layout_v<MetaTypesStackFrame>);
336
337}
338
339QT_END_NAMESPACE
340
341#endif
342

source code of qtdeclarative/src/qml/jsruntime/qv4stackframe_p.h