1 /*
2 * Copyright (c) 2021-2022 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 FOUNDATION_ACE_FRAMEWORKS_BRIDGE_JS_FRONTEND_ENGINE_QUICKJS_QJS_UTILS_H
17 #define FOUNDATION_ACE_FRAMEWORKS_BRIDGE_JS_FRONTEND_ENGINE_QUICKJS_QJS_UTILS_H
18
19 #include <cassert>
20 #include <cstring>
21 #include <stack>
22 #include <vector>
23
24 #include "frameworks/bridge/js_frontend/js_ace_page.h"
25 #include "third_party/quickjs/quickjs.h"
26 #ifdef __cplusplus
27 extern "C" {
28 #endif
29 #include "third_party/quickjs/cutils.h"
30 #include "third_party/quickjs/quickjs-libc.h"
31 #ifdef __cplusplus
32 }
33 #endif
34
35 namespace OHOS::Ace::Framework {
36
37 enum class JsErrorType {
38 JS_CRASH = 0,
39 JS_CALLBACK_ERROR,
40 FIRE_EVENT_ERROR,
41 EVAL_BUFFER_ERROR,
42 READ_OBJECT_ERROR,
43 DESTROY_APP_ERROR,
44 DESTROY_PAGE_ERROR,
45 LOAD_JS_BUNDLE_ERROR,
46 COMPILE_AND_RUN_BUNDLE_ERROR,
47 LOAD_JS_FRAMEWORK_ERROR,
48 };
49
50 class ScopedString {
51 public:
52 ScopedString(JSContext* ctx, JSValueConst val);
53 ScopedString(JSContext* ctx, JSAtom val);
54 explicit ScopedString(JSValueConst val);
55 explicit ScopedString(JSAtom val);
56 ScopedString(const ScopedString& rhs) = delete;
57 ScopedString& operator=(const ScopedString& rhs) = delete;
58 ScopedString(ScopedString&& rhs) = delete;
59 ScopedString& operator=(ScopedString&& rhs) = delete;
60 ~ScopedString();
61
62 bool operator==(const char* rhs);
63 bool operator==(const std::string& rhs);
64 bool operator==(const ScopedString& rhs);
65
66 const char* get() const;
67 std::string str();
68
69 static std::string Stringify(JSValueConst val);
70 static std::string Stringify(JSContext* ctx, JSValueConst val);
71
72 operator std::string() const;
73
74 private:
75 JSContext* context_ = nullptr;
76 const char* stringValue_ = nullptr;
77 };
78
79 class QJSUtils {
80 public:
81 using FilterFunction = bool (*)(std::string);
82
83 static JSValue NewStringLen(JSContext* ctx, const char* str, size_t len);
84 static JSValue NewString(JSContext* ctx, const char* str);
85 static JSValue ParseJSON(JSContext* ctx, const char* buf, size_t bufLen, const char* filename = nullptr);
86 static JSValue NewObject(JSContext* ctx);
87 static JSValue Call(JSContext* ctx, JSValueConst funcObj, JSValueConst thisObj, int32_t argc, JSValueConst* argv);
88 static JSValue Eval(JSContext* ctx, const char* input, size_t inputLen, const char* filename, int32_t evalFlags);
89 static JSValue GetPropertyStr(JSContext* ctx, JSValueConst thisObj, const char* prop);
90 static void JsStdDumpErrorAce(JSContext* ctx, JsErrorType errorType = JsErrorType::JS_CRASH, int32_t instanceId = 0,
91 const char* pageUrl = nullptr, const RefPtr<JsAcePage>& page = nullptr, bool isEts = false);
92 static void ExtractEachInfo(const std::string& tempStack, std::vector<std::string>& res);
93 static void GetPosInfo(const std::string& temp, int32_t start, std::string& line);
94 static std::string GetSourceInfo(const std::string& line, const RefPtr<RevSourceMap>& pageMap,
95 const RefPtr<RevSourceMap>& appMap, bool isAppPage, bool isEts = false);
96 static void JsDumpMemoryStats(JSContext* ctx);
97 static int32_t JsGetArrayLength(JSContext* ctx, JSValueConst arrayObject);
98
99 static std::vector<std::string> GetObjectKeys(JSContext* ctx, JSValueConst obj, int flags = JS_GPN_STRING_MASK);
100 static std::vector<std::string> GetObjectKeys(JSValueConst obj, int flags = JS_GPN_STRING_MASK);
101 static std::vector<std::string> GetFilteredObjectKeys(
102 JSContext* ctx, JSValueConst obj, FilterFunction filterFunc, int flags = JS_GPN_STRING_MASK);
103 static bool CheckAndGetJsProperty(JSContext* ctx, JSValueConst jsObj, JSPropertyEnum** pTab, uint32_t* len);
104 static std::string typeAsString(JSValueConst val);
105 static JSValue GetArgvSafe(int idx, int argc, JSValueConst* argv);
106 static void DefineGlobalFunction(JSContext* ctx, JSCFunction jsFunc, const char* name, const int paramNum);
107 static void DefineGlobalModuleFunction(
108 JSContext* ctx, JSCFunction cFunc, const char* moduleName, const char* funcName, const int paramNum);
109 static std::string JsDumpSourceFile(const char* stack, const RefPtr<RevSourceMap>& pageMap,
110 const RefPtr<RevSourceMap>& appMap, bool isEts = false);
111 };
112
113 /**
114 * @brief A class maintaining a stack of JSContext pointers.
115 *
116 */
117 class QJSContext {
118 public:
119 /**
120 * @brief A class that acquires a JSContext to be retrieved further in a call stack by QJSContext::Current().
121 *
122 * This is used to relieve the QJS embedder of passing JSContext* as an argument.
123 *
124 * @note QJSContext::Scope is thread-safe, but not reentrant.
125 */
126 class Scope {
127 public:
128 explicit Scope(JSContext* ctx);
129 ~Scope();
130
131 static void* operator new(size_t) = delete;
132 static void* operator new[](size_t) = delete;
133 static void operator delete(void*) = delete;
134 static void operator delete[](void*) = delete;
135 Scope(const Scope&) = delete;
136 void operator=(const Scope&) = delete;
137 };
138
139 /**
140 * @brief Get the context on the top of the stack
141 *
142 * @return JSContext*
143 */
144 static JSContext* Current();
145
146 static void* operator new(size_t) = delete;
147 static void* operator new[](size_t) = delete;
148 static void operator delete(void*) = delete;
149 static void operator delete[](void*) = delete;
150 QJSContext(const QJSContext&) = delete;
151 void operator=(const QJSContext&) = delete;
152
153 private:
154 #if defined(USE_CLANG_COVERAGE) || defined(CLANG_COVERAGE)
155 static std::stack<JSContext*> s_qjsContextStack;
156 #else
157 static thread_local std::stack<JSContext*> s_qjsContextStack;
158 #endif
159 };
160
161 class QJSHandleScope {
162 public:
163 explicit QJSHandleScope(JSContext* ctx);
164 ~QJSHandleScope();
165
166 static void* operator new(size_t) = delete;
167 static void* operator new[](size_t) = delete;
168 static void operator delete(void*) = delete;
169 static void operator delete[](void*) = delete;
170
171 private:
172 friend class QJSUtils;
173 friend class QJSValue;
174 #if defined(USE_CLANG_COVERAGE) || defined(CLANG_COVERAGE)
175 static std::stack<QJSHandleScope*> qjsHandleScopeStack_;
176 #else
177 static thread_local std::stack<QJSHandleScope*> qjsHandleScopeStack_;
178 #endif
179
180 static QJSHandleScope* GetCurrent();
181 void Push(JSValue val);
182
183 int32_t scopeId_ = 0;
184 std::vector<JSValueConst> jsValues_;
185 JSContext* context_ = nullptr;
186 };
187
CheckAndGetJsProperty(JSContext * ctx,JSValueConst fromMap,JSPropertyEnum ** pTab,uint32_t * len)188 inline bool CheckAndGetJsProperty(JSContext* ctx, JSValueConst fromMap, JSPropertyEnum** pTab, uint32_t* len)
189 {
190 if (!JS_IsObject(fromMap)) {
191 return false;
192 }
193
194 JS_GetOwnPropertyNames(ctx, pTab, len, fromMap, JS_GPN_STRING_MASK);
195 if (*pTab == nullptr) {
196 return false;
197 }
198 return true;
199 }
200
ThrowJsError(JSContext * ctx,const std::string & message,int32_t code)201 inline void ThrowJsError(JSContext* ctx, const std::string& message, int32_t code)
202 {
203 JSValue error = JS_NewError(ctx);
204 JS_SetPropertyStr(ctx, error, "code", JS_DupValue(ctx, JS_NewUint32(ctx, code)));
205 JS_SetPropertyStr(ctx, error, "message", JS_DupValue(ctx, JS_NewString(ctx, message.c_str())));
206
207 JS_Throw(ctx, error);
208 }
209
210 } // namespace OHOS::Ace::Framework
211
212 #endif // FOUNDATION_ACE_FRAMEWORKS_BRIDGE_JS_FRONTEND_ENGINE_QUICKJS_QJS_UTILS_H
213