1/*
2 * Copyright (c) 2021-2024 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 *     http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16#ifndef ECMASCRIPT_ECMA_RUNTIM_CALL_INFO_H
17#define ECMASCRIPT_ECMA_RUNTIM_CALL_INFO_H
18
19#include <algorithm>
20
21#include "ecmascript/common.h"
22#include "ecmascript/js_handle.h"
23#include "ecmascript/js_tagged_value.h"
24#include "ecmascript/tagged_array.h"
25
26namespace panda::ecmascript {
27struct EcmaRuntimeCallInfo;
28class JSThread;
29using EcmaEntrypoint = JSTaggedValue (*)(EcmaRuntimeCallInfo *);
30
31struct EcmaRuntimeCallInfo : public base::AlignedStruct<base::AlignedPointer::Size(),
32                                                        base::AlignedPointer,
33                                                        base::AlignedPointer,
34                                                        base::AlignedPointer> {
35    enum class Index : size_t {
36        ThreadIndex = 0,
37        NumArgsIndex,
38        StackArgsIndex,
39        NumOfMembers
40    };
41    static_assert(static_cast<size_t>(Index::NumOfMembers) == NumOfTypes);
42
43public:
44    inline JSThread *GetThread() const
45    {
46        return thread_;
47    }
48
49    static size_t GetThreadOffset(bool isArch32)
50    {
51        return GetOffset<static_cast<size_t>(Index::ThreadIndex)>(isArch32);
52    }
53
54    static size_t GetNumArgsOffset(bool isArch32)
55    {
56        return GetOffset<static_cast<size_t>(Index::NumArgsIndex)>(isArch32);
57    }
58
59    static size_t GetStackArgsOffset(bool isArch32)
60    {
61        return GetOffset<static_cast<size_t>(Index::StackArgsIndex)>(isArch32);
62    }
63
64    static size_t GetNewTargetOffset(bool isArch32)
65    {
66        return GetOffset<static_cast<size_t>(Index::StackArgsIndex)>(isArch32) +
67            NEW_TARGET_INDEX * sizeof(JSTaggedType);
68    }
69
70    static size_t GetThisOffset(bool isArch32)
71    {
72        return GetOffset<static_cast<size_t>(Index::StackArgsIndex)>(isArch32) +
73            THIS_INDEX * sizeof(JSTaggedType);
74    }
75
76    static size_t GetCallArgOffset(bool isArch32)
77    {
78        return GetOffset<static_cast<size_t>(Index::StackArgsIndex)>(isArch32) +
79            FIRST_ARGS_INDEX * sizeof(JSTaggedType);
80    }
81
82    inline void SetNewTarget(const JSTaggedValue tagged)
83    {
84        SetArg(NEW_TARGET_INDEX, tagged);
85    }
86
87    inline void SetFunction(const JSTaggedValue tagged)
88    {
89        SetArg(FUNC_INDEX, tagged);
90    }
91
92    inline void SetThis(const JSTaggedValue tagged)
93    {
94        SetArg(THIS_INDEX, tagged);
95    }
96
97    inline void SetCallArg(uint32_t idx, const JSTaggedValue tagged)
98    {
99        ASSERT_PRINT(idx < GetArgsNumber(), "Can not set values out of index range");
100        SetArg(idx + FIRST_ARGS_INDEX, tagged);
101    }
102
103    inline void SetCallArg(const JSTaggedValue arg)
104    {
105        ASSERT_PRINT(GetArgsNumber() == 1, "args number is not 1");
106        SetArg(FIRST_ARGS_INDEX, arg);
107    }
108
109    inline void SetCallArg(const JSTaggedValue arg0, const JSTaggedValue arg1)
110    {
111        ASSERT_PRINT(GetArgsNumber() == 2, "args number is not 2");  // 2: args number
112        SetArg(FIRST_ARGS_INDEX, arg0);
113        SetArg(FIRST_ARGS_INDEX + 1, arg1);
114    }
115
116    inline void SetCallArg(const JSTaggedValue arg0, const JSTaggedValue arg1, const JSTaggedValue arg2)
117    {
118        ASSERT_PRINT(GetArgsNumber() == 3, "args number is not 3");  // 3: args number
119        SetArg(FIRST_ARGS_INDEX, arg0);
120        SetArg(FIRST_ARGS_INDEX + 1, arg1);
121        SetArg(FIRST_ARGS_INDEX + 2, arg2);  // 2: second index
122    }
123
124    inline void SetCallArg(const JSTaggedValue arg0, const JSTaggedValue arg1, const JSTaggedValue arg2,
125                           const JSTaggedValue arg3)
126    {
127        ASSERT_PRINT(GetArgsNumber() == 4, "args number is not 4");  // 4: args number
128        SetArg(FIRST_ARGS_INDEX, arg0);
129        SetArg(FIRST_ARGS_INDEX + 1, arg1);
130        SetArg(FIRST_ARGS_INDEX + 2, arg2);  // 2: second index
131        SetArg(FIRST_ARGS_INDEX + 3, arg3);  // 3: third index
132    }
133
134    inline void SetCallArg(const JSTaggedValue arg0, const JSTaggedValue arg1, const JSTaggedValue arg2,
135                           const JSTaggedValue arg3, const JSTaggedValue arg4)
136    {
137        ASSERT_PRINT(GetArgsNumber() == 5, "args number is not 5");  // 5: args number
138        SetArg(FIRST_ARGS_INDEX, arg0);
139        SetArg(FIRST_ARGS_INDEX + 1, arg1);
140        SetArg(FIRST_ARGS_INDEX + 2, arg2);  // 2: second index
141        SetArg(FIRST_ARGS_INDEX + 3, arg3);  // 3: third index
142        SetArg(FIRST_ARGS_INDEX + 4, arg4);  // 4: fourth index
143    }
144
145    inline void SetCallArg(int32_t argc, const JSTaggedType argv[])
146    {
147        for (int32_t i = 0; i < argc; i++) {
148            SetCallArg(i, JSTaggedValue(argv[i]));
149        }
150    }
151
152    inline void SetCallArg(uint32_t argsLength, const JSHandle<TaggedArray> args)
153    {
154        for (uint32_t i = 0; i < argsLength; i++) {
155            SetCallArg(i, args->Get(GetThread(), i));
156        }
157    }
158
159    inline void SetCallArg(uint32_t argsLength, const TaggedArray* args)
160    {
161        for (uint32_t i = 0; i < argsLength; i++) {
162            SetCallArg(i, args->Get(GetThread(), i));
163        }
164    }
165
166    inline void SetCallArg(uint32_t argsLength, uint32_t startIndex, const EcmaRuntimeCallInfo* argv, int32_t offset)
167    {
168        for (uint32_t i = startIndex; i < argsLength; i++) {
169            SetCallArg(i, argv->GetCallArgValue(i - startIndex + offset));
170        }
171    }
172
173    inline JSHandle<JSTaggedValue> GetFunction() const
174    {
175        return GetArg(FUNC_INDEX);
176    }
177
178    inline JSHandle<JSTaggedValue> GetNewTarget() const
179    {
180        return GetArg(NEW_TARGET_INDEX);
181    }
182
183    inline JSHandle<JSTaggedValue> GetThis() const
184    {
185        return GetArg(THIS_INDEX);
186    }
187
188    inline JSHandle<JSTaggedValue> GetCallArg(uint32_t idx) const
189    {
190        return GetArg(idx + FIRST_ARGS_INDEX);
191    }
192
193    inline JSTaggedValue GetFunctionValue() const
194    {
195        JSHandle<JSTaggedValue> func = GetFunction();
196        return func.GetTaggedValue();
197    }
198
199    inline JSTaggedValue GetNewTargetValue() const
200    {
201        JSHandle<JSTaggedValue> newTarget = GetNewTarget();
202        return newTarget.GetTaggedValue();
203    }
204
205    inline JSTaggedValue GetThisValue() const
206    {
207        JSHandle<JSTaggedValue> thisObj = GetThis();
208        return thisObj.GetTaggedValue();
209    }
210
211    inline JSTaggedValue GetCallArgValue(uint32_t idx) const
212    {
213        JSHandle<JSTaggedValue> arg = GetCallArg(idx);
214        return arg.GetTaggedValue();
215    }
216
217    /*
218     * The number of arguments pairs excluding the 'func', 'new.target' and 'this'. For instance:
219     * for code fragment: " foo(v1); ", GetArgsNumber() returns 1
220     */
221    inline uint32_t GetArgsNumber() const
222    {
223        return numArgs_ - NUM_MANDATORY_JSFUNC_ARGS;
224    }
225
226    inline JSTaggedType *GetArgs()
227    {
228        return stackArgs_;
229    }
230
231private:
232    enum ArgsIndex : uint8_t { FUNC_INDEX = 0, NEW_TARGET_INDEX, THIS_INDEX, FIRST_ARGS_INDEX };
233
234    inline uintptr_t GetArgAddress(uint32_t idx) const
235    {
236        if (idx < GetArgsNumber() + NUM_MANDATORY_JSFUNC_ARGS) {
237            return reinterpret_cast<uintptr_t>(&stackArgs_[idx]);
238        }
239        return 0U;
240    }
241
242    inline void SetArg(uint32_t idx, const JSTaggedValue tagged)
243    {
244        uintptr_t addr = GetArgAddress(idx);
245        if (addr != 0U) {
246            *reinterpret_cast<JSTaggedValue *>(addr) = tagged;
247        }
248    }
249
250    inline JSHandle<JSTaggedValue> GetArg(uint32_t idx) const
251    {
252        return JSHandle<JSTaggedValue>(GetArgAddress(idx));
253    }
254
255private:
256    alignas(sizeof(JSTaggedType)) JSThread *thread_ {nullptr};
257    alignas(sizeof(JSTaggedType)) uint32_t numArgs_ {0};  // include func, newTarget, this, equal to stackArgs size.
258    __extension__ alignas(sizeof(JSTaggedType)) JSTaggedType stackArgs_[0];  // NOLINT(modernize-avoid-c-arrays)
259};
260}  // namespace panda::ecmascript
261
262#endif  // ECMASCRIPT_ECMA_RUNTIM_CALL_INFO_H
263