1 /*
2 * Copyright (c) 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 #include "ecmascript/jspandafile/js_pandafile_manager.h"
17
18 #include "ecmascript/compiler/aot_file/an_file_data_manager.h"
19 #include "ecmascript/compiler/aot_file/aot_file_manager.h"
20 #include "ecmascript/js_file_path.h"
21 #include "ecmascript/jspandafile/program_object.h"
22 #include "ecmascript/module/module_path_helper.h"
23 #include "ecmascript/pgo_profiler/pgo_profiler_manager.h"
24 #include "file.h"
25
26 namespace panda::ecmascript {
27 static const size_t MALLOC_SIZE_LIMIT = 2147483648; // Max internal memory used by the VM declared in options
28
GetInstance()29 JSPandaFileManager *JSPandaFileManager::GetInstance()
30 {
31 static JSPandaFileManager *jsFileManager = new JSPandaFileManager();
32 return jsFileManager;
33 }
34
~JSPandaFileManager()35 JSPandaFileManager::~JSPandaFileManager()
36 {
37 os::memory::LockHolder lock(jsPandaFileLock_);
38 extractors_.clear();
39 oldJSPandaFiles_.clear();
40 loadedJSPandaFiles_.clear();
41 }
42
LoadJSPandaFile(JSThread * thread,const CString & filename,std::string_view entryPoint,bool needUpdate)43 std::shared_ptr<JSPandaFile> JSPandaFileManager::LoadJSPandaFile(JSThread *thread, const CString &filename,
44 std::string_view entryPoint, bool needUpdate)
45 {
46 {
47 os::memory::LockHolder lock(jsPandaFileLock_);
48 std::shared_ptr<JSPandaFile> jsPandaFile;
49 if (needUpdate) {
50 auto pf = panda_file::OpenPandaFileOrZip(filename, panda_file::File::READ_WRITE);
51 if (pf == nullptr) {
52 LOG_ECMA(ERROR) << "open file " << filename << " error";
53 return nullptr;
54 }
55 jsPandaFile = FindJSPandaFileWithChecksum(filename, pf->GetHeader()->checksum);
56 } else {
57 jsPandaFile = FindJSPandaFileUnlocked(filename);
58 }
59 if (jsPandaFile != nullptr) {
60 InsertJSPandaFileVmUnlocked(thread->GetEcmaVM(), jsPandaFile);
61 return jsPandaFile;
62 }
63 }
64
65 EcmaVM *vm = thread->GetEcmaVM();
66 bool mode = thread->GetCurrentEcmaContext()->GetModuleManager()->GetCurrentMode();
67 std::unique_ptr<const panda_file::File> pf;
68 if (!vm->IsBundlePack() && mode) {
69 ResolveBufferCallback resolveBufferCallback = vm->GetResolveBufferCallback();
70 if (resolveBufferCallback == nullptr) {
71 LOG_ECMA(ERROR) << "resolveBufferCallback is nullptr";
72 return nullptr;
73 }
74 uint8_t *data = nullptr;
75 size_t dataSize = 0;
76 bool getBuffer = resolveBufferCallback(ModulePathHelper::ParseHapPath(filename), &data, &dataSize);
77 if (!getBuffer) {
78 LOG_ECMA(ERROR) << "resolveBufferCallback get buffer failed";
79 return nullptr;
80 }
81 #if defined(PANDA_TARGET_ANDROID) || defined(PANDA_TARGET_IOS)
82 pf = panda_file::OpenPandaFileFromMemory(data, dataSize);
83 #else
84 pf = panda_file::OpenPandaFileFromSecureMemory(data, dataSize);
85 #endif
86 } else {
87 pf = panda_file::OpenPandaFileOrZip(filename, panda_file::File::READ_WRITE);
88 }
89
90 if (pf == nullptr) {
91 LOG_ECMA(ERROR) << "open file " << filename << " error";
92 return nullptr;
93 }
94
95 std::shared_ptr<JSPandaFile> jsPandaFile = GenerateJSPandaFile(thread, pf.release(), filename, entryPoint);
96 #if defined(ECMASCRIPT_SUPPORT_CPUPROFILER)
97 if (thread->GetIsProfiling()) {
98 GetJSPtExtractorAndExtract(jsPandaFile.get());
99 }
100 #endif
101 return jsPandaFile;
102 }
103
104 // The security interface needs to be modified accordingly.
LoadJSPandaFile(JSThread * thread,const CString & filename,std::string_view entryPoint,const void * buffer,size_t size,bool needUpdate)105 std::shared_ptr<JSPandaFile> JSPandaFileManager::LoadJSPandaFile(JSThread *thread, const CString &filename,
106 std::string_view entryPoint, const void *buffer, size_t size, bool needUpdate)
107 {
108 if (buffer == nullptr || size == 0) {
109 LOG_FULL(ERROR) << "Input buffer is empty";
110 return nullptr;
111 }
112 {
113 os::memory::LockHolder lock(jsPandaFileLock_);
114 std::shared_ptr<JSPandaFile> jsPandaFile;
115 if (needUpdate) {
116 auto pf = panda_file::OpenPandaFileFromMemory(buffer, size);
117 if (pf == nullptr) {
118 LOG_ECMA(ERROR) << "open file buffer " << filename << " error";
119 return nullptr;
120 }
121 jsPandaFile = FindJSPandaFileWithChecksum(filename, pf->GetHeader()->checksum);
122 } else {
123 jsPandaFile = FindJSPandaFileUnlocked(filename);
124 }
125 if (jsPandaFile != nullptr) {
126 InsertJSPandaFileVmUnlocked(thread->GetEcmaVM(), jsPandaFile);
127 return jsPandaFile;
128 }
129 }
130
131 auto pf = panda_file::OpenPandaFileFromMemory(buffer, size);
132 if (pf == nullptr) {
133 LOG_ECMA(ERROR) << "open file " << filename << " error";
134 return nullptr;
135 }
136
137 // JSPandaFile desc cannot be empty, if buffer with empty filename, use pf filename as a descriptor.
138 const CString &desc = filename.empty() ? pf->GetFilename().c_str() : filename;
139
140 std::shared_ptr<JSPandaFile> jsPandaFile = GenerateJSPandaFile(thread, pf.release(), desc, entryPoint);
141 #if defined(ECMASCRIPT_SUPPORT_CPUPROFILER)
142 if (thread->GetIsProfiling()) {
143 GetJSPtExtractorAndExtract(jsPandaFile.get());
144 }
145 #endif
146 return jsPandaFile;
147 }
148
LoadJSPandaFileSecure(JSThread * thread,const CString & filename,std::string_view entryPoint,uint8_t * buffer,size_t size,bool needUpdate)149 std::shared_ptr<JSPandaFile> JSPandaFileManager::LoadJSPandaFileSecure(JSThread *thread, const CString &filename,
150 std::string_view entryPoint, uint8_t *buffer, size_t size, bool needUpdate)
151 {
152 if (buffer == nullptr || size == 0) {
153 LOG_FULL(ERROR) << "Input buffer is empty";
154 return nullptr;
155 }
156 {
157 os::memory::LockHolder lock(jsPandaFileLock_);
158 std::shared_ptr<JSPandaFile> jsPandaFile;
159 if (needUpdate) {
160 auto pf = panda_file::OpenPandaFileFromSecureMemory(buffer, size);
161 if (pf == nullptr) {
162 LOG_ECMA(ERROR) << "open file buffer " << filename << " error";
163 return nullptr;
164 }
165 jsPandaFile = FindJSPandaFileWithChecksum(filename, pf->GetHeader()->checksum);
166 } else {
167 jsPandaFile = FindJSPandaFileUnlocked(filename);
168 }
169 if (jsPandaFile != nullptr) {
170 InsertJSPandaFileVmUnlocked(thread->GetEcmaVM(), jsPandaFile);
171 return jsPandaFile;
172 }
173 }
174
175 auto pf = panda_file::OpenPandaFileFromSecureMemory(buffer, size);
176 if (pf == nullptr) {
177 LOG_ECMA(ERROR) << "open file " << filename << " error";
178 return nullptr;
179 }
180
181 // JSPandaFile desc cannot be empty, if buffer with empty filename, use pf filename as a descriptor.
182 const CString &desc = filename.empty() ? pf->GetFilename().c_str() : filename;
183
184 std::shared_ptr<JSPandaFile> jsPandaFile = GenerateJSPandaFile(thread, pf.release(), desc, entryPoint);
185 #if defined(ECMASCRIPT_SUPPORT_CPUPROFILER)
186 if (thread->GetIsProfiling()) {
187 GetJSPtExtractorAndExtract(jsPandaFile.get());
188 }
189 #endif
190 return jsPandaFile;
191 }
192
GenerateProgram(EcmaVM * vm,const JSPandaFile * jsPandaFile,std::string_view entryPoint)193 JSHandle<Program> JSPandaFileManager::GenerateProgram(EcmaVM *vm, const JSPandaFile *jsPandaFile,
194 std::string_view entryPoint)
195 {
196 ASSERT(GetJSPandaFile(jsPandaFile->GetPandaFile()) != nullptr);
197 if (AnFileDataManager::GetInstance()->IsEnable()) {
198 vm->GetJSThread()->GetCurrentEcmaContext()->GetAOTFileManager()->LoadAiFile(jsPandaFile);
199 }
200
201 return PandaFileTranslator::GenerateProgram(vm, jsPandaFile, entryPoint);
202 }
203
FindJSPandaFileWithChecksum(const CString & filename,uint32_t checksum)204 std::shared_ptr<JSPandaFile> JSPandaFileManager::FindJSPandaFileWithChecksum(const CString &filename, uint32_t checksum)
205 {
206 std::shared_ptr<JSPandaFile> jsPandaFile = FindJSPandaFileUnlocked(filename);
207 if (jsPandaFile == nullptr) {
208 return nullptr;
209 }
210
211 if (checksum == jsPandaFile->GetChecksum()) {
212 return jsPandaFile;
213 }
214
215 LOG_FULL(INFO) << "reload " << filename << " with new checksum";
216 ObsoleteLoadedJSPandaFile(filename);
217 return nullptr;
218 }
219
FindMergedJSPandaFile()220 std::shared_ptr<JSPandaFile> JSPandaFileManager::FindMergedJSPandaFile()
221 {
222 os::memory::LockHolder lock(jsPandaFileLock_);
223 for (const auto &iter : loadedJSPandaFiles_) {
224 const std::shared_ptr<JSPandaFile> &jsPandafile = iter.second.first;
225 if (jsPandafile->IsFirstMergedAbc()) {
226 return jsPandafile;
227 }
228 }
229 return nullptr;
230 }
231
FindJSPandaFileUnlocked(const CString & filename)232 std::shared_ptr<JSPandaFile> JSPandaFileManager::FindJSPandaFileUnlocked(const CString &filename)
233 {
234 if (filename.empty()) {
235 return nullptr;
236 }
237 const auto iter = loadedJSPandaFiles_.find(filename);
238 if (iter == loadedJSPandaFiles_.end()) {
239 return nullptr;
240 }
241 return iter->second.first;
242 }
243
FindJSPandaFile(const CString & filename)244 std::shared_ptr<JSPandaFile> JSPandaFileManager::FindJSPandaFile(const CString &filename)
245 {
246 os::memory::LockHolder lock(jsPandaFileLock_);
247 return FindJSPandaFileUnlocked(filename);
248 }
249
GetJSPandaFile(const panda_file::File * pf)250 std::shared_ptr<JSPandaFile> JSPandaFileManager::GetJSPandaFile(const panda_file::File *pf)
251 {
252 os::memory::LockHolder lock(jsPandaFileLock_);
253 for (const auto &iter : loadedJSPandaFiles_) {
254 const std::shared_ptr<JSPandaFile> &jsPandafile = iter.second.first;
255 if (jsPandafile->GetPandaFile() == pf) {
256 return jsPandafile;
257 }
258 }
259 return nullptr;
260 }
261
AddJSPandaFileVm(const EcmaVM * vm,const std::shared_ptr<JSPandaFile> & jsPandaFile)262 void JSPandaFileManager::AddJSPandaFileVm(const EcmaVM *vm, const std::shared_ptr<JSPandaFile> &jsPandaFile)
263 {
264 const auto &filename = jsPandaFile->GetJSPandaFileDesc();
265 os::memory::LockHolder lock(jsPandaFileLock_);
266 if (loadedJSPandaFiles_.find(filename) != loadedJSPandaFiles_.end()) {
267 LOG_ECMA(FATAL) << "add failed, file already exist: " << filename;
268 UNREACHABLE();
269 }
270
271 std::set<const EcmaVM *> vmSet {vm};
272 JSPandaFileVmsPair pandaFileRecord = std::make_pair(jsPandaFile, std::move(vmSet));
273 loadedJSPandaFiles_[filename] = std::move(pandaFileRecord);
274 LOG_ECMA(DEBUG) << "add file: " << filename;
275 }
276
InsertJSPandaFileVmUnlocked(const EcmaVM * vm,const std::shared_ptr<JSPandaFile> & jsPandaFile)277 void JSPandaFileManager::InsertJSPandaFileVmUnlocked(const EcmaVM *vm,
278 const std::shared_ptr<JSPandaFile> &jsPandaFile)
279 {
280 const auto &filename = jsPandaFile->GetJSPandaFileDesc();
281 auto iter = loadedJSPandaFiles_.find(filename);
282 if (iter == loadedJSPandaFiles_.end()) {
283 LOG_ECMA(FATAL) << "insert vm failed, file not exist: " << filename;
284 UNREACHABLE();
285 }
286
287 auto &vmSet = iter->second.second;
288 vmSet.emplace(vm);
289 }
290
RemoveJSPandaFileVm(const EcmaVM * vm,const JSPandaFile * jsPandaFile)291 void JSPandaFileManager::RemoveJSPandaFileVm(const EcmaVM *vm, const JSPandaFile *jsPandaFile)
292 {
293 if (jsPandaFile == nullptr) {
294 return;
295 }
296
297 os::memory::LockHolder lock(jsPandaFileLock_);
298 auto iterOld = oldJSPandaFiles_.begin();
299 while (iterOld != oldJSPandaFiles_.end()) {
300 if (iterOld->first.get() == jsPandaFile) {
301 auto &vmSet = iterOld->second;
302 vmSet.erase(vm);
303 if (vmSet.empty()) {
304 extractors_.erase(jsPandaFile);
305 oldJSPandaFiles_.erase(iterOld);
306 }
307 return;
308 }
309 iterOld++;
310 }
311 const auto &filename = jsPandaFile->GetJSPandaFileDesc();
312 auto iter = loadedJSPandaFiles_.find(filename);
313 if (iter != loadedJSPandaFiles_.end()) {
314 auto &vmSet = iter->second.second;
315 vmSet.erase(vm);
316 if (vmSet.empty()) {
317 extractors_.erase(jsPandaFile);
318 // erase shared_ptr from map, the ref count -1.
319 loadedJSPandaFiles_.erase(iter);
320 }
321 }
322 }
323
ObsoleteLoadedJSPandaFile(const CString & filename)324 void JSPandaFileManager::ObsoleteLoadedJSPandaFile(const CString &filename)
325 {
326 auto iter = loadedJSPandaFiles_.find(filename);
327 ASSERT(iter != loadedJSPandaFiles_.end());
328 std::shared_ptr<JSPandaFile> &jsPandaFile = iter->second.first;
329 if (oldJSPandaFiles_.find(jsPandaFile) == oldJSPandaFiles_.end()) {
330 oldJSPandaFiles_.emplace(jsPandaFile, iter->second.second);
331 } else {
332 auto &oldVmSet = oldJSPandaFiles_[jsPandaFile];
333 auto &vmSet = iter->second.second;
334 oldVmSet.insert(vmSet.begin(), vmSet.end());
335 }
336 loadedJSPandaFiles_.erase(iter);
337 }
338
OpenJSPandaFile(const CString & filename)339 std::shared_ptr<JSPandaFile> JSPandaFileManager::OpenJSPandaFile(const CString &filename)
340 {
341 auto pf = panda_file::OpenPandaFileOrZip(filename, panda_file::File::READ_WRITE);
342 if (pf == nullptr) {
343 LOG_ECMA(ERROR) << "open file " << filename << " error";
344 return nullptr;
345 }
346
347 return NewJSPandaFile(pf.release(), filename);
348 }
349
OpenJSPandaFileFromBuffer(uint8_t * buffer,size_t size,const CString & filename)350 std::shared_ptr<JSPandaFile> JSPandaFileManager::OpenJSPandaFileFromBuffer(uint8_t *buffer,
351 size_t size,
352 const CString &filename)
353 {
354 auto pf = panda_file::OpenPandaFileFromMemory(buffer, size);
355 if (pf == nullptr) {
356 LOG_ECMA(ERROR) << "open file " << filename << " error";
357 return nullptr;
358 }
359
360 return NewJSPandaFile(pf.release(), filename);
361 }
362
NewJSPandaFile(const panda_file::File * pf,const CString & desc)363 std::shared_ptr<JSPandaFile> JSPandaFileManager::NewJSPandaFile(const panda_file::File *pf, const CString &desc)
364 {
365 std::shared_ptr<JSPandaFile> jsPandaFile = std::make_shared<JSPandaFile>(pf, desc);
366 PGOProfilerManager::GetInstance()->SamplePandaFileInfo(jsPandaFile->GetChecksum());
367 return jsPandaFile;
368 }
369
GetJSPtExtractor(const JSPandaFile * jsPandaFile)370 DebugInfoExtractor *JSPandaFileManager::GetJSPtExtractor(const JSPandaFile *jsPandaFile)
371 {
372 LOG_ECMA_IF(jsPandaFile == nullptr, FATAL) << "GetJSPtExtractor error, js pandafile is nullptr";
373
374 os::memory::LockHolder lock(jsPandaFileLock_);
375 const auto &filename = jsPandaFile->GetJSPandaFileDesc();
376 if (loadedJSPandaFiles_.find(filename) == loadedJSPandaFiles_.end()) {
377 LOG_ECMA(FATAL) << "get extractor failed, file not exist: " << filename;
378 UNREACHABLE();
379 }
380
381 auto iter = extractors_.find(jsPandaFile);
382 if (iter == extractors_.end()) {
383 auto extractorPtr = std::make_unique<DebugInfoExtractor>(jsPandaFile);
384 DebugInfoExtractor *extractor = extractorPtr.get();
385 extractors_[jsPandaFile] = std::move(extractorPtr);
386 return extractor;
387 }
388
389 return iter->second.get();
390 }
391
GetJSPtExtractorAndExtract(const JSPandaFile * jsPandaFile)392 DebugInfoExtractor *JSPandaFileManager::GetJSPtExtractorAndExtract(const JSPandaFile *jsPandaFile)
393 {
394 LOG_ECMA_IF(jsPandaFile == nullptr, FATAL) << "GetJSPtExtractor error, js pandafile is nullptr";
395
396 os::memory::LockHolder lock(jsPandaFileLock_);
397 const auto &filename = jsPandaFile->GetJSPandaFileDesc();
398 if (loadedJSPandaFiles_.find(filename) == loadedJSPandaFiles_.end()) {
399 LOG_ECMA(FATAL) << "get extractor failed, file not exist: " << filename;
400 UNREACHABLE();
401 }
402
403 auto iter = extractors_.find(jsPandaFile);
404 if (iter == extractors_.end()) {
405 auto extractorPtr = std::make_unique<DebugInfoExtractor>(jsPandaFile);
406 DebugInfoExtractor *extractor = extractorPtr.get();
407 extractor->Extract();
408 extractors_[jsPandaFile] = std::move(extractorPtr);
409 return extractor;
410 }
411
412 return iter->second.get();
413 }
414
CpuProfilerGetJSPtExtractor(const JSPandaFile * jsPandaFile)415 DebugInfoExtractor *JSPandaFileManager::CpuProfilerGetJSPtExtractor(const JSPandaFile *jsPandaFile)
416 {
417 LOG_ECMA_IF(jsPandaFile == nullptr, FATAL) << "GetJSPtExtractor error, js pandafile is nullptr";
418
419 os::memory::LockHolder lock(jsPandaFileLock_);
420 const auto &filename = jsPandaFile->GetJSPandaFileDesc();
421 if (loadedJSPandaFiles_.find(filename) == loadedJSPandaFiles_.end()) {
422 LOG_ECMA(FATAL) << "get extractor failed, file not exist: " << filename;
423 UNREACHABLE();
424 }
425
426 DebugInfoExtractor *extractor = nullptr;
427 auto iter = extractors_.find(jsPandaFile);
428 if (iter == extractors_.end()) {
429 auto extractorPtr = std::make_unique<DebugInfoExtractor>(jsPandaFile);
430 extractor = extractorPtr.get();
431 extractors_[jsPandaFile] = std::move(extractorPtr);
432 } else {
433 extractor = iter->second.get();
434 }
435
436 extractor->Extract();
437 return extractor;
438 }
439
GenerateJSPandaFile(JSThread * thread,const panda_file::File * pf,const CString & desc,std::string_view entryPoint)440 std::shared_ptr<JSPandaFile> JSPandaFileManager::GenerateJSPandaFile(JSThread *thread, const panda_file::File *pf,
441 const CString &desc, std::string_view entryPoint)
442 {
443 ASSERT(GetJSPandaFile(pf) == nullptr);
444 std::shared_ptr<JSPandaFile> newJsPandaFile = NewJSPandaFile(pf, desc);
445 EcmaVM *vm = thread->GetEcmaVM();
446
447 CString methodName = entryPoint.data();
448 if (newJsPandaFile->IsBundlePack()) {
449 // entryPoint maybe is _GLOBAL::func_main_watch to execute func_main_watch
450 auto pos = entryPoint.find_last_of("::");
451 if (pos != std::string_view::npos) {
452 methodName = entryPoint.substr(pos + 1);
453 } else {
454 // default use func_main_0 as entryPoint
455 methodName = JSPandaFile::ENTRY_FUNCTION_NAME;
456 }
457 }
458 PandaFileTranslator::TranslateClasses(newJsPandaFile.get(), methodName);
459
460 {
461 // For worker, JSPandaFile may be created by another vm.
462 os::memory::LockHolder lock(jsPandaFileLock_);
463 std::shared_ptr<JSPandaFile> jsPandaFile = FindJSPandaFileUnlocked(desc);
464 if (jsPandaFile != nullptr) {
465 InsertJSPandaFileVmUnlocked(vm, jsPandaFile);
466 newJsPandaFile.reset();
467 return jsPandaFile;
468 } else {
469 AddJSPandaFileVm(vm, newJsPandaFile);
470 return newJsPandaFile;
471 }
472 }
473 }
474
AllocateBuffer(size_t size)475 void *JSPandaFileManager::AllocateBuffer(size_t size)
476 {
477 return JSPandaFileAllocator::AllocateBuffer(size);
478 }
479
AllocateBuffer(size_t size)480 void *JSPandaFileManager::JSPandaFileAllocator::AllocateBuffer(size_t size)
481 {
482 if (size == 0) {
483 LOG_ECMA_MEM(FATAL) << "size must have a size bigger than 0";
484 UNREACHABLE();
485 }
486 if (size >= MALLOC_SIZE_LIMIT) {
487 LOG_ECMA_MEM(FATAL) << "size must be less than the maximum";
488 UNREACHABLE();
489 }
490 // NOLINTNEXTLINE(cppcoreguidelines-no-malloc)
491 void *ptr = malloc(size);
492 if (ptr == nullptr) {
493 LOG_ECMA_MEM(FATAL) << "malloc failed";
494 UNREACHABLE();
495 }
496 #if ECMASCRIPT_ENABLE_ZAP_MEM
497 if (memset_s(ptr, size, INVALID_VALUE, size) != EOK) {
498 LOG_ECMA_MEM(FATAL) << "memset_s failed";
499 UNREACHABLE();
500 }
501 #endif
502 return ptr;
503 }
504
FreeBuffer(void * mem)505 void JSPandaFileManager::FreeBuffer(void *mem)
506 {
507 JSPandaFileAllocator::FreeBuffer(mem);
508 }
509
FreeBuffer(void * mem)510 void JSPandaFileManager::JSPandaFileAllocator::FreeBuffer(void *mem)
511 {
512 if (mem == nullptr) {
513 return;
514 }
515 // NOLINTNEXTLINE(cppcoreguidelines-no-malloc)
516 free(mem);
517 }
518 } // namespace panda::ecmascript
519