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

Provided by KDAB

Privacy Policy
Learn to use CMake with our Intro Training
Find out more

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