• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /**
2  * Copyright (c) 2023-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 
16 #include "include/object_header.h"
17 #include "intrinsics.h"
18 #include "plugins/ets/runtime/types/ets_arraybuffer.h"
19 #include "plugins/ets/runtime/types/ets_string.h"
20 #include "plugins/ets/runtime/ets_handle_scope.h"
21 #include "plugins/ets/runtime/ets_handle.h"
22 #include "plugins/ets/runtime/types/ets_primitives.h"
23 #include "plugins/ets/runtime/intrinsics/helpers/array_buffer_helper.h"
24 
25 using namespace std::string_view_literals;
26 constexpr std::array UTF8_ENCODINGS = {"utf8"sv, "utf-8"sv, "ascii"sv};
27 constexpr std::array UTF16_ENCODINGS = {"utf16le"sv, "ucs2"sv, "ucs-2"sv};
28 constexpr std::array BASE64_ENCODINGS = {"base64"sv, "base64url"sv};
29 constexpr std::array LATIN_ENCODINGS = {"latin1"sv, "binary"sv};
30 
31 namespace ark::ets::intrinsics {
32 
33 /// @brief Creates a new ArrayBuffer with specified size and optional initial data
CreateArrayBuffer(EtsCoroutine * coro,EtsInt byteLength,const uint8_t * data=nullptr)34 static EtsHandle<EtsEscompatArrayBuffer> CreateArrayBuffer(EtsCoroutine *coro, EtsInt byteLength,
35                                                            const uint8_t *data = nullptr)
36 {
37     void *buffer = nullptr;
38     EtsHandle<EtsEscompatArrayBuffer> newBuffer(coro, EtsEscompatArrayBuffer::Create(coro, byteLength, &buffer));
39     if (UNLIKELY(newBuffer.GetPtr() == nullptr)) {
40         return newBuffer;
41     }
42     if (data != nullptr && byteLength > 0) {
43         std::copy_n(data, byteLength, reinterpret_cast<uint8_t *>(buffer));
44     }
45     return newBuffer;
46 }
47 
48 /**
49  * @brief Converts an ETS string to bytes using specified encoding
50  * @details Supported encodings:
51  *   - UTF-8/UTF-8
52  *   - ASCII (7-bit)
53  *   - UTF-16LE/UCS2
54  *   - Base64/Base64URL
55  *   - Latin1/Binary
56  *   - Hex (must be even length)
57  */
ConvertEtsStringToBytes(EtsString * strObj,EtsString * encodingObj,EtsCoroutine * coro,const LanguageContext & ctx)58 static PandaVector<uint8_t> ConvertEtsStringToBytes(EtsString *strObj, EtsString *encodingObj, EtsCoroutine *coro,
59                                                     const LanguageContext &ctx)
60 {
61     PandaVector<uint8_t> strBuf;
62     PandaVector<uint8_t> encodingBuf;
63     PandaString input(strObj->ConvertToStringView(&strBuf));
64     PandaString encoding =
65         encodingObj != nullptr ? PandaString(encodingObj->ConvertToStringView(&encodingBuf)) : "utf8";
66     auto res = helpers::encoding::ConvertStringToBytes(input, encoding);
67     if (std::holds_alternative<helpers::Err<PandaString>>(res)) {
68         ThrowException(ctx, coro, ctx.GetIllegalArgumentExceptionClassDescriptor(),
69                        utf::CStringAsMutf8(std::get<helpers::Err<PandaString>>(res).Message().c_str()));
70         return PandaVector<uint8_t> {};
71     }
72     return std::get<PandaVector<uint8_t>>(res);
73 }
74 
75 /// @brief Calculates byte length of string when encoded
EtsStringBytesLength(EtsString * strObj,EtsString * encodingObj)76 extern "C" ets_int EtsStringBytesLength(EtsString *strObj, EtsString *encodingObj)
77 {
78     EtsCoroutine *coro = EtsCoroutine::GetCurrent();
79     auto ctx = Runtime::GetCurrent()->GetLanguageContext(panda_file::SourceLang::ETS);
80     PandaVector<uint8_t> strBuf;
81     PandaVector<uint8_t> encodingBuf;
82     PandaString input(strObj->ConvertToStringView(&strBuf));
83     PandaString encoding =
84         encodingObj != nullptr ? PandaString(encodingObj->ConvertToStringView(&encodingBuf)) : "utf8";
85     auto res = helpers::encoding::CalculateStringBytesLength(input, encoding);
86     if (std::holds_alternative<helpers::Err<PandaString>>(res)) {
87         ThrowException(ctx, coro, ctx.GetIllegalArgumentExceptionClassDescriptor(),
88                        utf::CStringAsMutf8(std::get<helpers::Err<PandaString>>(res).Message().c_str()));
89         return -1;
90     }
91     return std::get<int32_t>(res);
92 }
93 
94 /// @brief Creates ArrayBuffer from encoded string
EtsArrayBufferFromEncodedString(EtsString * strObj,EtsString * encodingObj)95 extern "C" EtsEscompatArrayBuffer *EtsArrayBufferFromEncodedString(EtsString *strObj, EtsString *encodingObj)
96 {
97     EtsCoroutine *coro = EtsCoroutine::GetCurrent();
98     LanguageContext ctx = Runtime::GetCurrent()->GetLanguageContext(panda_file::SourceLang::ETS);
99     PandaVector<uint8_t> bytes = ConvertEtsStringToBytes(strObj, encodingObj, coro, ctx);
100     auto byteLength = static_cast<EtsInt>(bytes.size());
101     [[maybe_unused]] EtsHandleScope s(coro);
102     EtsHandle<EtsEscompatArrayBuffer> newBuffer =
103         CreateArrayBuffer(coro, byteLength, byteLength > 0 ? bytes.data() : nullptr);
104     if (newBuffer.GetPtr() == nullptr) {
105         ASSERT(coro->HasPendingException());
106         return nullptr;
107     }
108     return newBuffer.GetPtr();
109 }
110 
EtsEscompatArrayBufferSetValues(EtsEscompatArrayBuffer * arrayBuffer,EtsEscompatArrayBuffer * other,EtsInt begin)111 extern "C" void EtsEscompatArrayBufferSetValues(EtsEscompatArrayBuffer *arrayBuffer, EtsEscompatArrayBuffer *other,
112                                                 EtsInt begin)
113 {
114     arrayBuffer->SetValues(other, begin);
115 }
116 
EtsEscompatArrayBufferAt(EtsEscompatArrayBuffer * arrayBuffer,EtsInt pos)117 extern "C" EtsByte EtsEscompatArrayBufferAt(EtsEscompatArrayBuffer *arrayBuffer, EtsInt pos)
118 {
119     return arrayBuffer->At(pos);
120 }
121 
EtsEscompatArrayBufferSet(EtsEscompatArrayBuffer * arrayBuffer,EtsInt pos,EtsByte val)122 extern "C" void EtsEscompatArrayBufferSet(EtsEscompatArrayBuffer *arrayBuffer, EtsInt pos, EtsByte val)
123 {
124     arrayBuffer->Set(pos, val);
125 }
126 
EtsEscompatArrayBufferAllocateNonMovable(EtsInt length)127 extern "C" ObjectHeader *EtsEscompatArrayBufferAllocateNonMovable(EtsInt length)
128 {
129     return EtsEscompatArrayBuffer::AllocateNonMovableArray(length);
130 }
131 
EtsEscompatArrayBufferGetAddress(ObjectHeader * byteArray)132 extern "C" EtsLong EtsEscompatArrayBufferGetAddress(ObjectHeader *byteArray)
133 {
134     ASSERT(byteArray != nullptr);
135     return EtsEscompatArrayBuffer::GetAddress(reinterpret_cast<EtsByteArray *>(byteArray));
136 }
137 
138 /// @brief Creates new ArrayBuffer from slice of existing buffer
EtsArrayBufferFromBufferSlice(EtsEscompatArrayBuffer * obj,ets_int offset,ets_int length)139 extern "C" EtsEscompatArrayBuffer *EtsArrayBufferFromBufferSlice(EtsEscompatArrayBuffer *obj, ets_int offset,
140                                                                  ets_int length)
141 {
142     EtsCoroutine *coro = EtsCoroutine::GetCurrent();
143     LanguageContext ctx = Runtime::GetCurrent()->GetLanguageContext(panda_file::SourceLang::ETS);
144     [[maybe_unused]] EtsHandleScope s(coro);
145     EtsHandle<EtsEscompatArrayBuffer> original(coro, obj);
146     if (original->WasDetached()) {
147         ThrowException(ctx, coro, ctx.GetIllegalArgumentExceptionClassDescriptor(),
148                        utf::CStringAsMutf8("ArrayBuffer was detached"));
149         return nullptr;
150     }
151     ASSERT(original->GetData() != nullptr);
152 
153     EtsInt origByteLength = original->GetByteLength();
154     if (offset < 0 || offset > origByteLength) {
155         ThrowException(ctx, coro, ctx.GetIndexOutOfBoundsExceptionClassDescriptor(),
156                        utf::CStringAsMutf8("Offset is out of bounds"));
157         return nullptr;
158     }
159     if (length < 0 || length > origByteLength - offset) {
160         ThrowException(ctx, coro, ctx.GetIndexOutOfBoundsExceptionClassDescriptor(),
161                        utf::CStringAsMutf8("Length is out of bounds"));
162         return nullptr;
163     }
164 
165     EtsHandle<EtsEscompatArrayBuffer> newBuffer = CreateArrayBuffer(coro, length);
166     if (newBuffer.GetPtr() == nullptr) {
167         ASSERT(coro->HasPendingException());
168         return nullptr;
169     }
170     std::copy_n(std::next(reinterpret_cast<uint8_t *>(original->GetData()), offset), length,
171                 reinterpret_cast<uint8_t *>(newBuffer->GetData()));
172     return newBuffer.GetPtr();
173 }
174 
175 /**
176  * @brief Converts ArrayBuffer content to string using specified encoding.
177  *
178  * This function validates the input buffer and indices, extracts the requested bytes,
179  * determines the encoding, and then converts the byte sequence into the desired string.
180  */
EtsArrayBufferToString(EtsEscompatArrayBuffer * buffer,EtsString * encodingObj,ets_int start,ets_int end)181 extern "C" EtsString *EtsArrayBufferToString(EtsEscompatArrayBuffer *buffer, EtsString *encodingObj, ets_int start,
182                                              ets_int end)
183 {
184     EtsCoroutine *coro = EtsCoroutine::GetCurrent();
185     auto ctx = Runtime::GetCurrent()->GetLanguageContext(panda_file::SourceLang::ETS);
186 
187     auto vb = helpers::encoding::ValidateBuffer(buffer);
188     if (std::holds_alternative<helpers::Err<PandaString>>(vb)) {
189         ThrowException(ctx, coro, ctx.GetIllegalArgumentExceptionClassDescriptor(),
190                        utf::CStringAsMutf8(std::get<helpers::Err<PandaString>>(vb).Message().c_str()));
191         return nullptr;
192     }
193 
194     [[maybe_unused]] EtsHandleScope scope(coro);
195     EtsHandle<EtsEscompatArrayBuffer> buf(coro, buffer);
196     EtsInt byteLength = buf->GetByteLength();
197 
198     auto vi = helpers::encoding::ValidateIndices(byteLength, start, end);
199     if (std::holds_alternative<helpers::Err<PandaString>>(vi)) {
200         ThrowException(ctx, coro, ctx.GetIllegalArgumentExceptionClassDescriptor(),
201                        utf::CStringAsMutf8(std::get<helpers::Err<PandaString>>(vi).Message().c_str()));
202         return nullptr;
203     }
204 
205     auto length = static_cast<size_t>(end - start);
206     PandaVector<uint8_t> bytes(length);
207     if (buf->GetData() != nullptr) {
208         std::copy_n(std::next(reinterpret_cast<uint8_t *>(buf->GetData()), start), length, bytes.data());
209     }
210 
211     PandaVector<uint8_t> encodingBuf;
212     PandaString encoding =
213         encodingObj != nullptr ? PandaString(encodingObj->ConvertToStringView(&encodingBuf)) : "utf8";
214 
215     PandaString output;
216     if (std::find(UTF8_ENCODINGS.begin(), UTF8_ENCODINGS.end(), encoding) != UTF8_ENCODINGS.end()) {
217         output = helpers::encoding::ConvertUtf8Encoding(bytes);
218     } else if (std::find(UTF16_ENCODINGS.begin(), UTF16_ENCODINGS.end(), encoding) != UTF16_ENCODINGS.end()) {
219         output = helpers::encoding::ConvertUtf16Encoding(bytes);
220     } else if (std::find(BASE64_ENCODINGS.begin(), BASE64_ENCODINGS.end(), encoding) != BASE64_ENCODINGS.end()) {
221         output = helpers::encoding::ConvertBase64Encoding(bytes, encoding);
222     } else if (encoding == "hex") {
223         output = helpers::encoding::ConvertHexEncoding(bytes);
224     } else if (std::find(LATIN_ENCODINGS.begin(), LATIN_ENCODINGS.end(), encoding) != LATIN_ENCODINGS.end()) {
225         output = helpers::encoding::ConvertLatinEncoding(bytes);
226     } else {
227         PandaString errMsg = "Unsupported encoding: " + encoding;
228         ThrowException(ctx, coro, ctx.GetIllegalArgumentExceptionClassDescriptor(),
229                        utf::CStringAsMutf8(errMsg.c_str()));
230         return nullptr;
231     }
232     return EtsString::CreateFromUtf8(output.data(), output.size());
233 }
234 
235 }  // namespace ark::ets::intrinsics
236