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

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