1 /**
2 * Copyright (c) 2025 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 #include "plugins/ets/runtime/types/ets_type.h"
16 #include "plugins/ets/runtime/ets_vm.h"
17 #include "plugins/ets/runtime/types/ets_string.h"
18 #include "plugins/ets/runtime/ets_exceptions.h"
19
20 #include "runtime/handle_scope.h"
21 #include "runtime/handle_scope-inl.h"
22 #include "runtime/include/thread.h"
23
24 namespace ark::ets::intrinsics {
25
26 /* make sure the class the intrinsic being called from belongs to the boot context */
EnsureBootContext()27 static bool EnsureBootContext()
28 {
29 auto *coro = EtsCoroutine::GetCurrent();
30 auto ctx = StackWalker::Create(coro).GetMethod()->GetClass()->GetLoadContext();
31 if (!ctx->IsBootContext()) {
32 auto e = panda_file_items::class_descriptors::ILLEGAL_STATE_EXCEPTION;
33 auto msg = "Unsafe intrinsics cannot be called outside of the boot context";
34 ThrowEtsException(coro, e, msg);
35 return false;
36 }
37 return true;
38 }
39
40 template <typename T>
UnsafeMemoryRead(void * addr)41 static T UnsafeMemoryRead(void *addr)
42 {
43 if (EnsureBootContext()) {
44 return *reinterpret_cast<T *>(addr);
45 }
46 return static_cast<T>(-1);
47 }
48
49 template <typename T>
UnsafeMemoryWrite(void * addr,T val)50 static void UnsafeMemoryWrite(void *addr, T val)
51 {
52 if (EnsureBootContext()) {
53 *reinterpret_cast<T *>(addr) = val;
54 }
55 }
56
UnsafeMemoryReadBoolean(void * addr)57 extern "C" EtsByte UnsafeMemoryReadBoolean(void *addr)
58 {
59 return UnsafeMemoryRead<EtsByte>(addr);
60 }
61
UnsafeMemoryReadInt8(void * addr)62 extern "C" EtsByte UnsafeMemoryReadInt8(void *addr)
63 {
64 return UnsafeMemoryRead<EtsByte>(addr);
65 }
66
UnsafeMemoryReadInt16(void * addr)67 extern "C" EtsShort UnsafeMemoryReadInt16(void *addr)
68 {
69 return UnsafeMemoryRead<EtsShort>(addr);
70 }
71
UnsafeMemoryReadInt32(void * addr)72 extern "C" EtsInt UnsafeMemoryReadInt32(void *addr)
73 {
74 return UnsafeMemoryRead<EtsInt>(addr);
75 }
76
UnsafeMemoryReadInt64(void * addr)77 extern "C" EtsLong UnsafeMemoryReadInt64(void *addr)
78 {
79 return UnsafeMemoryRead<EtsLong>(addr);
80 }
81
UnsafeMemoryReadFloat32(void * addr)82 extern "C" EtsFloat UnsafeMemoryReadFloat32(void *addr)
83 {
84 return UnsafeMemoryRead<EtsFloat>(addr);
85 }
86
UnsafeMemoryReadFloat64(void * addr)87 extern "C" EtsDouble UnsafeMemoryReadFloat64(void *addr)
88 {
89 return UnsafeMemoryRead<EtsDouble>(addr);
90 }
91
UnsafeMemoryReadNumber(void * addr)92 extern "C" EtsDouble UnsafeMemoryReadNumber(void *addr)
93 {
94 return UnsafeMemoryRead<EtsDouble>(addr);
95 }
96
UnsafeMemoryWriteBoolean(void * addr,EtsBoolean val)97 extern "C" void UnsafeMemoryWriteBoolean(void *addr, EtsBoolean val)
98 {
99 UnsafeMemoryWrite<EtsByte>(addr, val);
100 }
101
UnsafeMemoryWriteInt8(void * addr,EtsByte val)102 extern "C" void UnsafeMemoryWriteInt8(void *addr, EtsByte val)
103 {
104 UnsafeMemoryWrite<EtsByte>(addr, val);
105 }
106
UnsafeMemoryWriteInt16(void * addr,EtsShort val)107 extern "C" void UnsafeMemoryWriteInt16(void *addr, EtsShort val)
108 {
109 UnsafeMemoryWrite<EtsShort>(addr, val);
110 }
111
UnsafeMemoryWriteInt32(void * addr,EtsInt val)112 extern "C" void UnsafeMemoryWriteInt32(void *addr, EtsInt val)
113 {
114 UnsafeMemoryWrite<EtsInt>(addr, val);
115 }
116
UnsafeMemoryWriteInt64(void * addr,EtsLong val)117 extern "C" void UnsafeMemoryWriteInt64(void *addr, EtsLong val)
118 {
119 UnsafeMemoryWrite<EtsLong>(addr, val);
120 }
121
UnsafeMemoryWriteFloat32(void * addr,EtsFloat val)122 extern "C" void UnsafeMemoryWriteFloat32(void *addr, EtsFloat val)
123 {
124 UnsafeMemoryWrite<EtsFloat>(addr, val);
125 }
126
UnsafeMemoryWriteFloat64(void * addr,EtsDouble val)127 extern "C" void UnsafeMemoryWriteFloat64(void *addr, EtsDouble val)
128 {
129 UnsafeMemoryWrite<EtsDouble>(addr, val);
130 }
131
UnsafeMemoryWriteNumber(void * addr,EtsDouble val)132 extern "C" void UnsafeMemoryWriteNumber(void *addr, EtsDouble val)
133 {
134 UnsafeMemoryWrite<EtsDouble>(addr, val);
135 }
136
137 /* get the size of the buffer to hold the string content add additional
138 2 bytes for the leading byte-order mark which is used in the UTF-16
139 case */
UnsafeMemoryStringGetSizeInBytes(EtsString * str)140 extern "C" int UnsafeMemoryStringGetSizeInBytes(EtsString *str)
141 {
142 auto coroutine = EtsCoroutine::GetCurrent();
143 [[maybe_unused]] HandleScope<ObjectHeader *> scope(coroutine);
144 EtsHandle<EtsString> handle(coroutine, str);
145
146 if (!EnsureBootContext()) {
147 return -1;
148 }
149
150 str = handle.GetPtr();
151
152 uint32_t shift = str->IsUtf16() ? 1 : 0;
153 uint32_t addend = str->IsUtf16() ? 2 : 0;
154 auto length = static_cast<uint32_t>(str->GetLength());
155 int size = (length << shift) + addend;
156 return size;
157 }
158
159 static constexpr uint16_t UTF16_BOM = 0xFEFF;
160
UnsafeMemoryReadString(void * buf,int len)161 extern "C" EtsString *UnsafeMemoryReadString(void *buf, int len)
162 {
163 if (!EnsureBootContext()) {
164 return nullptr;
165 }
166
167 ObjectHeader *res = nullptr;
168 auto vm = ManagedThread::GetCurrent()->GetVM();
169 auto ctx = Runtime::GetCurrent()->GetLanguageContext(panda_file::SourceLang::ETS);
170 auto mark = reinterpret_cast<uint16_t *>(buf);
171 auto size = static_cast<uint32_t>(len);
172
173 if (*mark == UTF16_BOM) {
174 auto addr = ++mark;
175 /* size is in bytes but CreateFromUtf16 takes it as chars, so,
176 we divide it by half and subtract the length of the BOM */
177 res = coretypes::String::CreateFromUtf16(addr, (size / 2U) - 1, false, ctx, vm);
178 } else {
179 auto addr = reinterpret_cast<const uint8_t *>(buf);
180 res = coretypes::String::CreateFromMUtf8(addr, size, size, true, ctx, vm);
181 }
182
183 return reinterpret_cast<EtsString *>(res);
184 }
185
UnsafeMemoryWriteString(void * addr,EtsString * str)186 extern "C" int UnsafeMemoryWriteString(void *addr, EtsString *str)
187 {
188 auto coroutine = EtsCoroutine::GetCurrent();
189 [[maybe_unused]] HandleScope<ObjectHeader *> scope(coroutine);
190 EtsHandle<EtsString> handle(coroutine, str);
191
192 if (!EnsureBootContext()) {
193 return -1;
194 }
195
196 str = handle.GetPtr();
197
198 auto size = static_cast<uint32_t>(str->GetLength());
199 if (str->IsUtf16()) {
200 *reinterpret_cast<uint16_t *>(addr) = UTF16_BOM;
201 // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
202 addr = reinterpret_cast<char *>(addr) + 2U;
203 size <<= 1U;
204 return memcpy_s(addr, size, str->GetDataUtf16(), size) == 0 ? size + 2U : -1;
205 }
206 return memcpy_s(addr, size, str->GetDataMUtf8(), size) == 0 ? size : -1;
207 }
208
209 } // namespace ark::ets::intrinsics
210