1/*
2 * Copyright (c) 2021 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#include "frameworks/base/utils/string_utils.h"
17#include "frameworks/bridge/js_frontend/engine/jsi/ark_js_runtime.h"
18#include "frameworks/bridge/declarative_frontend/engine/jsi/jsi_ref.h"
19#include "frameworks/bridge/declarative_frontend/engine/jsi/jsi_types.h"
20
21namespace OHOS::Ace::Framework {
22
23template<typename T>
24JsiType<T>::JsiType(panda::Local<T> val)
25{
26    if (!val.IsEmpty()) {
27        auto runtime = std::static_pointer_cast<ArkJSRuntime>(JsiDeclarativeEngineInstance::GetCurrentRuntime());
28        handle_ = panda::CopyableGlobal(runtime->GetEcmaVm(), val);
29    }
30}
31
32template<typename T>
33JsiType<T>::JsiType(const EcmaVM *vm, panda::Local<T> val)
34{
35    if (!val.IsEmpty()) {
36        handle_ = panda::CopyableGlobal(vm, val);
37    }
38}
39
40template<typename T>
41template<typename S>
42JsiType<T>::JsiType(panda::Local<S> val)
43{
44    if (!val.IsEmpty()) {
45        auto runtime = std::static_pointer_cast<ArkJSRuntime>(JsiDeclarativeEngineInstance::GetCurrentRuntime());
46        handle_ = panda::CopyableGlobal(runtime->GetEcmaVm(), val);
47    }
48}
49
50template<typename T>
51JsiType<T>::JsiType(const panda::CopyableGlobal<T>& other) : handle_(other)
52{
53}
54
55template<typename T>
56JsiType<T>::JsiType(const JsiType<T>& rhs) : handle_(rhs.handle_)
57{
58}
59
60template<typename T>
61JsiType<T>::JsiType(JsiType<T>&& rhs) : handle_(std::move(rhs.handle_))
62{
63}
64
65template<typename T>
66JsiType<T>& JsiType<T>::operator=(const JsiType<T>& rhs)
67{
68    handle_ = rhs.handle_;
69    return *this;
70}
71
72template<typename T>
73JsiType<T>& JsiType<T>::operator=(JsiType<T>&& rhs)
74{
75    handle_ = std::move(rhs.handle_);
76    return *this;
77}
78
79template<typename T>
80void JsiType<T>::SetWeakCallback(void *ref, panda::WeakRefClearCallBack callback)
81{
82    if (!handle_.IsEmpty()) {
83        handle_.SetWeakCallback(ref, callback, nullptr);
84    }
85}
86
87template<typename T>
88const EcmaVM* JsiType<T>::GetEcmaVM() const
89{
90    return handle_.GetEcmaVM();
91}
92
93template<typename T>
94const panda::CopyableGlobal<T>& JsiType<T>::GetHandle() const
95{
96    return handle_;
97}
98
99template<typename T>
100panda::Local<T> JsiType<T>::GetLocalHandle() const
101{
102    return handle_.ToLocal();
103}
104
105template<typename T>
106bool JsiType<T>::IsEmpty() const
107{
108    return handle_.IsEmpty();
109}
110
111template<typename T>
112bool JsiType<T>::IsWeak() const
113{
114    return handle_.IsWeak();
115}
116
117template<typename T>
118void JsiType<T>::Reset()
119{
120    handle_.Reset();
121}
122
123template<typename T>
124const panda::CopyableGlobal<T>& JsiType<T>::operator->() const
125{
126    return handle_;
127}
128
129template<typename T>
130JsiType<T>::operator panda::CopyableGlobal<T>() const
131{
132    return handle_;
133}
134
135template<typename T>
136template<class... Args>
137JsiType<T> JsiType<T>::New(Args&&... args)
138{
139    auto runtime = std::static_pointer_cast<ArkJSRuntime>(JsiDeclarativeEngineInstance::GetCurrentRuntime());
140    return JsiType<T>(T::New(runtime->GetEcmaVm(), std::forward<Args>(args)...));
141}
142
143template<typename T>
144T JsiValue::ToNumber() const
145{
146    return JsiValueConvertor::fromJsiValue<T>(GetEcmaVM(), GetLocalHandle());
147}
148
149template<typename T>
150T* JsiObject::Unwrap() const
151{
152    if (GetHandle()->GetNativePointerFieldCount(GetEcmaVM()) < 1) {
153        return nullptr;
154    }
155    return static_cast<T*>(GetHandle()->GetNativePointerField(GetEcmaVM(), INSTANCE));
156}
157
158template<typename T>
159void JsiObject::Wrap(T* data) const
160{
161    GetHandle()->SetNativePointerField(GetEcmaVM(), INSTANCE, static_cast<void*>(data));
162}
163
164template<typename T>
165void JsiObject::SetProperty(const char* prop, T value) const
166{
167    auto stringRef = panda::StringRef::NewFromUtf8(GetEcmaVM(), prop);
168    GetHandle()->Set(GetEcmaVM(), stringRef, JsiValueConvertor::toJsiValueWithVM<T>(GetEcmaVM(), value));
169}
170
171template<typename T>
172void JsiObject::SetProperty(int32_t propertyIndex, T value) const
173{
174    Local<StringRef> stringRef = panda::ExternalStringCache::GetCachedString(GetEcmaVM(), propertyIndex);
175    GetHandle()->Set(GetEcmaVM(), stringRef, JsiValueConvertor::toJsiValueWithVM<T>(GetEcmaVM(), value));
176}
177
178template<typename T>
179T JsiObject::GetPropertyValue(const char* prop, T defaultValue) const
180{
181    static_assert(!std::is_const_v<T> && !std::is_reference_v<T>,
182        "Cannot convert value to reference or cv-qualified types!");
183
184    const EcmaVM* vm = GetEcmaVM();
185    Local<StringRef> stringRef = panda::StringRef::NewFromUtf8(vm, prop);
186    Local<JSValueRef> valueRef = GetHandle()->Get(vm, stringRef);
187    if constexpr (std::is_same<T, bool>::value) {
188        return valueRef->IsBoolean() ? valueRef->BooleaValue(vm) : defaultValue;
189    } else if constexpr (std::is_arithmetic<T>::value) {
190        return valueRef->IsNumber() ? JsiValueConvertor::fromJsiValue<T>(vm, valueRef) : defaultValue;
191    } else if constexpr (std::is_same_v<T, std::string>) {
192        return valueRef->IsString(vm) ? valueRef->ToString(vm)->ToString(vm) : defaultValue;
193    } else {
194        LOGW("Get property value failed.");
195    }
196    return defaultValue;
197}
198
199template<typename T>
200T JsiObject::GetPropertyValue(int32_t propertyIndex, T defaultValue) const
201{
202    static_assert(!std::is_const_v<T> && !std::is_reference_v<T>,
203        "Cannot convert value to reference or cv-qualified types!");
204
205    const EcmaVM* vm = GetEcmaVM();
206    Local<StringRef> stringRef = panda::ExternalStringCache::GetCachedString(vm, propertyIndex);
207    Local<JSValueRef> valueRef = GetHandle()->Get(vm, stringRef);
208    if constexpr (std::is_same<T, bool>::value) {
209        return valueRef->IsBoolean() ? valueRef->BooleaValue(vm) : defaultValue;
210    } else if constexpr (std::is_arithmetic<T>::value) {
211        return valueRef->IsNumber() ? JsiValueConvertor::fromJsiValue<T>(vm, valueRef) : defaultValue;
212    } else if constexpr (std::is_same_v<T, std::string>) {
213        return valueRef->IsString(vm) ? valueRef->ToString(vm)->ToString(vm) : defaultValue;
214    } else {
215        LOGW("Get property value failed.");
216    }
217    return defaultValue;
218}
219
220template<typename T>
221void JsiCallbackInfo::SetReturnValue(T* instance) const
222{
223    retVal_ = instance;
224}
225
226template<typename T>
227void JsiCallbackInfo::SetReturnValue(JsiRef<T> val) const
228{
229    retVal_ = panda::CopyableGlobal<panda::JSValueRef>(val.Get().GetHandle());
230}
231
232template<typename T>
233T* JsiCallbackInfo::UnwrapArg(size_t index) const
234{
235    auto arg = info_->GetCallArgRef(index);
236    if (arg.IsEmpty() || !arg->IsObject(info_->GetVM())) {
237        return nullptr;
238    }
239    return static_cast<T*>(arg->ToEcmaObject(info_->GetVM())->GetNativePointerField(info_->GetVM(), 0));
240}
241
242template<typename... Args>
243void JsiException::Throw(const char* format, Args... args)
244{
245    const std::string str = StringUtils::FormatString(format, args...);
246    auto runtime = std::static_pointer_cast<ArkJSRuntime>(JsiDeclarativeEngineInstance::GetCurrentRuntime());
247    auto vm = runtime->GetEcmaVm();
248    panda::JSNApi::ThrowException(vm, panda::Exception::Error(vm, panda::StringRef::NewFromUtf8(vm, str.c_str())));
249}
250
251template<typename... Args>
252void JsiException::Throw(int32_t code, const char* format, Args... args)
253{
254    const std::string str = StringUtils::FormatString(format, args...);
255    auto runtime = std::static_pointer_cast<ArkJSRuntime>(JsiDeclarativeEngineInstance::GetCurrentRuntime());
256    auto vm = runtime->GetEcmaVm();
257    LocalScope scope(vm);
258    Local<JSValueRef> error(JSValueRef::Undefined(vm));
259    error = panda::Exception::Error(vm, StringRef::NewFromUtf8(vm, str.c_str()));
260    Local<JSValueRef> codeKey = StringRef::NewFromUtf8(vm, "code");
261    Local<JSValueRef> codeValue = StringRef::NewFromUtf8(vm, std::to_string(code).c_str());
262    Local<ObjectRef> errorObj(error);
263    errorObj->Set(vm, codeKey, codeValue);
264    panda::JSNApi::ThrowException(vm, error);
265}
266
267template<typename... Args>
268void JsiException::ThrowRangeError(const char* format, Args... args)
269{
270    const std::string str = StringUtils::FormatString(format, args...);
271    auto runtime = std::static_pointer_cast<ArkJSRuntime>(JsiDeclarativeEngineInstance::GetCurrentRuntime());
272    auto vm = runtime->GetEcmaVm();
273    panda::JSNApi::ThrowException(vm, panda::Exception::RangeError(vm, panda::StringRef::NewFromUtf8(vm, str.c_str())));
274}
275
276template<typename... Args>
277void JsiException::ThrowReferenceError(const char* format, Args... args)
278{
279    const std::string str = StringUtils::FormatString(format, args...);
280    auto runtime = std::static_pointer_cast<ArkJSRuntime>(JsiDeclarativeEngineInstance::GetCurrentRuntime());
281    auto vm = runtime->GetEcmaVm();
282    panda::JSNApi::ThrowException(
283        vm, panda::Exception::ReferenceError(vm, panda::StringRef::NewFromUtf8(vm, str.c_str())));
284}
285
286template<typename... Args>
287void JsiException::ThrowSyntaxError(const char* format, Args... args)
288{
289    const std::string str = StringUtils::FormatString(format, args...);
290    auto runtime = std::static_pointer_cast<ArkJSRuntime>(JsiDeclarativeEngineInstance::GetCurrentRuntime());
291    auto vm = runtime->GetEcmaVm();
292    panda::JSNApi::ThrowException(
293        vm, panda::Exception::SyntaxError(vm, panda::StringRef::NewFromUtf8(vm, str.c_str())));
294}
295
296template<typename... Args>
297void JsiException::ThrowTypeError(const char* format, Args... args)
298{
299    const std::string str = StringUtils::FormatString(format, args...);
300    auto runtime = std::static_pointer_cast<ArkJSRuntime>(JsiDeclarativeEngineInstance::GetCurrentRuntime());
301    auto vm = runtime->GetEcmaVm();
302    panda::JSNApi::ThrowException(vm, panda::Exception::TypeError(vm, panda::StringRef::NewFromUtf8(vm, str.c_str())));
303}
304
305template<typename... Args>
306void JsiException::ThrowEvalError(const char* format, Args... args)
307{
308    const std::string str = StringUtils::FormatString(format, args...);
309    auto runtime = std::static_pointer_cast<ArkJSRuntime>(JsiDeclarativeEngineInstance::GetCurrentRuntime());
310    auto vm = runtime->GetEcmaVm();
311    panda::JSNApi::ThrowException(vm, panda::Exception::EvalError(vm, panda::StringRef::NewFromUtf8(vm, str.c_str())));
312}
313} // namespace OHOS::Ace::Framework
314